1 /* SPDX-License-Identifier: LGPL-2.1-only */
2 /*
3 * lib/fib_lookup/lookup.c FIB Lookup
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation version 2.1
8 * of the License.
9 *
10 * Copyright (c) 2003-2012 Thomas Graf <tgraf@suug.ch>
11 */
12
13 /**
14 * @ingroup rtnl
15 * @defgroup fib_lookup FIB Lookup
16 * @brief
17 * @{
18 */
19
20 #include <netlink-private/netlink.h>
21 #include <netlink-private/utils.h>
22 #include <netlink/netlink.h>
23 #include <netlink/attr.h>
24 #include <netlink/utils.h>
25 #include <netlink/object.h>
26 #include <netlink/route/rtnl.h>
27 #include <netlink/route/route.h>
28 #include <netlink/fib_lookup/request.h>
29 #include <netlink/fib_lookup/lookup.h>
30
31 /** @cond SKIP */
32 static struct nl_cache_ops fib_lookup_ops;
33 static struct nl_object_ops result_obj_ops;
34
35 /* not exported so far */
36 struct fib_result_nl {
37 uint32_t fl_addr; /* To be looked up*/
38 uint32_t fl_fwmark;
39 unsigned char fl_tos;
40 unsigned char fl_scope;
41 unsigned char tb_id_in;
42
43 unsigned char tb_id; /* Results */
44 unsigned char prefixlen;
45 unsigned char nh_sel;
46 unsigned char type;
47 unsigned char scope;
48 int err;
49 };
50 /** @endcond */
51
result_free_data(struct nl_object * obj)52 static void result_free_data(struct nl_object *obj)
53 {
54 struct flnl_result *res = nl_object_priv(obj);
55
56 if (res && res->fr_req)
57 nl_object_put(OBJ_CAST(res->fr_req));
58 }
59
result_clone(struct nl_object * _dst,struct nl_object * _src)60 static int result_clone(struct nl_object *_dst, struct nl_object *_src)
61 {
62 struct flnl_result *dst = nl_object_priv(_dst);
63 struct flnl_result *src = nl_object_priv(_src);
64
65 if (src->fr_req)
66 if (!(dst->fr_req = (struct flnl_request *)
67 nl_object_clone(OBJ_CAST(src->fr_req))))
68 return -NLE_NOMEM;
69
70 return 0;
71 }
72
result_msg_parser(struct nl_cache_ops * ops,struct sockaddr_nl * who,struct nlmsghdr * n,struct nl_parser_param * pp)73 static int result_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
74 struct nlmsghdr *n, struct nl_parser_param *pp)
75 {
76 struct flnl_result *res;
77 struct fib_result_nl *fr;
78 struct nl_addr *addr;
79 int err = -NLE_INVAL;
80
81 res = flnl_result_alloc();
82 if (!res)
83 goto errout;
84
85 res->ce_msgtype = n->nlmsg_type;
86
87 res->fr_req = flnl_request_alloc();
88 if (!res->fr_req)
89 goto errout;
90
91 fr = nlmsg_data(n);
92 addr = nl_addr_build(AF_INET, &fr->fl_addr, 4);
93 if (!addr)
94 goto errout;
95 err = flnl_request_set_addr(res->fr_req, addr);
96 nl_addr_put(addr);
97 if (err < 0)
98 goto errout;
99
100 flnl_request_set_fwmark(res->fr_req, fr->fl_fwmark);
101 flnl_request_set_tos(res->fr_req, fr->fl_tos);
102 flnl_request_set_scope(res->fr_req, fr->fl_scope);
103 flnl_request_set_table(res->fr_req, fr->tb_id_in);
104
105 res->fr_table_id = fr->tb_id;
106 res->fr_prefixlen = fr->prefixlen;
107 res->fr_nh_sel = fr->nh_sel;
108 res->fr_type = fr->type;
109 res->fr_scope = fr->scope;
110 res->fr_error = fr->err;
111
112 err = pp->pp_cb((struct nl_object *) res, pp);
113 if (err < 0)
114 goto errout;
115
116 /* REAL HACK, fib_lookup doesn't support ACK nor does it
117 * send a DONE message, enforce end of message stream
118 * after just the first message */
119 err = NL_STOP;
120
121 errout:
122 flnl_result_put(res);
123 return err;
124 }
125
result_dump_line(struct nl_object * obj,struct nl_dump_params * p)126 static void result_dump_line(struct nl_object *obj, struct nl_dump_params *p)
127 {
128 struct flnl_result *res = (struct flnl_result *) obj;
129 char buf[256];
130
131 nl_dump_line(p, "table %s prefixlen %u next-hop-selector %u\n",
132 rtnl_route_table2str(res->fr_table_id, buf, sizeof(buf)),
133 res->fr_prefixlen, res->fr_nh_sel);
134 nl_dump_line(p, "type %s ",
135 nl_rtntype2str(res->fr_type, buf, sizeof(buf)));
136 nl_dump(p, "scope %s error %s (%d)\n",
137 rtnl_scope2str(res->fr_scope, buf, sizeof(buf)),
138 nl_strerror_l(-res->fr_error), res->fr_error);
139 }
140
result_dump_details(struct nl_object * obj,struct nl_dump_params * p)141 static void result_dump_details(struct nl_object *obj, struct nl_dump_params *p)
142 {
143 result_dump_line(obj, p);
144 }
145
result_compare(struct nl_object * _a,struct nl_object * _b,uint64_t attrs,int flags)146 static uint64_t result_compare(struct nl_object *_a, struct nl_object *_b,
147 uint64_t attrs, int flags)
148 {
149 return 0;
150 }
151
152 /**
153 * @name Allocation/Freeing
154 * @{
155 */
156
flnl_result_alloc(void)157 struct flnl_result *flnl_result_alloc(void)
158 {
159 return (struct flnl_result *) nl_object_alloc(&result_obj_ops);
160 }
161
flnl_result_put(struct flnl_result * res)162 void flnl_result_put(struct flnl_result *res)
163 {
164 nl_object_put((struct nl_object *) res);
165 }
166
167 /** @} */
168
169 /**
170 * @name Cache Management
171 * @{
172 */
173
174 /**
175 * Allocate lookup result cache.
176 *
177 * Allocates a new lookup result cache and initializes it properly.
178 *
179 * @note Free the memory after usage using nl_cache_destroy_and_free().
180 * @return Newly allocated cache or NULL if an error occured.
181 */
flnl_result_alloc_cache(void)182 struct nl_cache *flnl_result_alloc_cache(void)
183 {
184 return nl_cache_alloc(&fib_lookup_ops);
185 }
186
187 /** @} */
188
189 /**
190 * @name Lookup
191 * @{
192 */
193
194 /**
195 * Builds a netlink request message to do a lookup
196 * @arg req Requested match.
197 * @arg flags additional netlink message flags
198 * @arg result Result pointer
199 *
200 * Builds a new netlink message requesting a change of link attributes.
201 * The netlink message header isn't fully equipped with all relevant
202 * fields and must be sent out via nl_send_auto_complete() or
203 * supplemented as needed.
204 * \a old must point to a link currently configured in the kernel
205 * and \a tmpl must contain the attributes to be changed set via
206 * \c rtnl_link_set_* functions.
207 *
208 * @return 0 on success or a negative error code.
209 */
flnl_lookup_build_request(struct flnl_request * req,int flags,struct nl_msg ** result)210 int flnl_lookup_build_request(struct flnl_request *req, int flags,
211 struct nl_msg **result)
212 {
213 struct nl_msg *msg;
214 struct nl_addr *addr;
215 uint64_t fwmark;
216 int tos, scope, table;
217 struct fib_result_nl fr = {0};
218
219 fwmark = flnl_request_get_fwmark(req);
220 tos = flnl_request_get_tos(req);
221 scope = flnl_request_get_scope(req);
222 table = flnl_request_get_table(req);
223
224 fr.fl_fwmark = fwmark != UINT_LEAST64_MAX ? fwmark : 0;
225 fr.fl_tos = tos >= 0 ? tos : 0;
226 fr.fl_scope = scope >= 0 ? scope : RT_SCOPE_UNIVERSE;
227 fr.tb_id_in = table >= 0 ? table : RT_TABLE_UNSPEC;
228
229 addr = flnl_request_get_addr(req);
230 if (!addr)
231 return -NLE_MISSING_ATTR;
232
233 fr.fl_addr = *(uint32_t *) nl_addr_get_binary_addr(addr);
234
235 msg = nlmsg_alloc_simple(0, flags);
236 if (!msg)
237 return -NLE_NOMEM;
238
239 if (nlmsg_append(msg, &fr, sizeof(fr), NLMSG_ALIGNTO) < 0)
240 goto errout;
241
242 *result = msg;
243 return 0;
244
245 errout:
246 nlmsg_free(msg);
247 return -NLE_MSGSIZE;
248 }
249
250 /**
251 * Perform FIB Lookup
252 * @arg sk Netlink socket.
253 * @arg req Lookup request object.
254 * @arg cache Cache for result.
255 *
256 * Builds a netlink message to request a FIB lookup, waits for the
257 * reply and adds the result to the specified cache.
258 *
259 * @return 0 on success or a negative error code.
260 */
flnl_lookup(struct nl_sock * sk,struct flnl_request * req,struct nl_cache * cache)261 int flnl_lookup(struct nl_sock *sk, struct flnl_request *req,
262 struct nl_cache *cache)
263 {
264 struct nl_msg *msg;
265 int err;
266
267 if ((err = flnl_lookup_build_request(req, 0, &msg)) < 0)
268 return err;
269
270 err = nl_send_auto_complete(sk, msg);
271 nlmsg_free(msg);
272 if (err < 0)
273 return err;
274
275 return nl_cache_pickup_checkdup(sk, cache);
276 }
277
278 /** @} */
279
280 /**
281 * @name Attribute Access
282 * @{
283 */
284
flnl_result_get_table_id(struct flnl_result * res)285 int flnl_result_get_table_id(struct flnl_result *res)
286 {
287 return res->fr_table_id;
288 }
289
flnl_result_get_prefixlen(struct flnl_result * res)290 int flnl_result_get_prefixlen(struct flnl_result *res)
291 {
292 return res->fr_prefixlen;
293 }
294
flnl_result_get_nexthop_sel(struct flnl_result * res)295 int flnl_result_get_nexthop_sel(struct flnl_result *res)
296 {
297 return res->fr_nh_sel;
298 }
299
flnl_result_get_type(struct flnl_result * res)300 int flnl_result_get_type(struct flnl_result *res)
301 {
302 return res->fr_type;
303 }
304
flnl_result_get_scope(struct flnl_result * res)305 int flnl_result_get_scope(struct flnl_result *res)
306 {
307 return res->fr_scope;
308 }
309
flnl_result_get_error(struct flnl_result * res)310 int flnl_result_get_error(struct flnl_result *res)
311 {
312 return res->fr_error;
313 }
314
315 /** @} */
316
317 static struct nl_object_ops result_obj_ops = {
318 .oo_name = "fib_lookup/result",
319 .oo_size = sizeof(struct flnl_result),
320 .oo_free_data = result_free_data,
321 .oo_clone = result_clone,
322 .oo_dump = {
323 [NL_DUMP_LINE] = result_dump_line,
324 [NL_DUMP_DETAILS] = result_dump_details,
325 },
326 .oo_compare = result_compare,
327 };
328
329 static struct nl_cache_ops fib_lookup_ops = {
330 .co_name = "fib_lookup/fib_lookup",
331 .co_hdrsize = sizeof(struct fib_result_nl),
332 .co_msgtypes = {
333 { 0, NL_ACT_UNSPEC, "any" },
334 END_OF_MSGTYPES_LIST,
335 },
336 .co_protocol = NETLINK_FIB_LOOKUP,
337 .co_msg_parser = result_msg_parser,
338 .co_obj_ops = &result_obj_ops,
339 };
340
fib_lookup_init(void)341 static void __init fib_lookup_init(void)
342 {
343 nl_cache_mngt_register(&fib_lookup_ops);
344 }
345
fib_lookup_exit(void)346 static void __exit fib_lookup_exit(void)
347 {
348 nl_cache_mngt_unregister(&fib_lookup_ops);
349 }
350
351 /** @} */
352