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