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-2012 Thomas Graf <tgraf@suug.ch>
10 */
11
12/**
13 * @ingroup rtnl
14 * @defgroup fib_lookup FIB Lookup
15 * @brief
16 * @{
17 */
18
19#include <netlink-private/netlink.h>
20#include <netlink/netlink.h>
21#include <netlink/attr.h>
22#include <netlink/utils.h>
23#include <netlink/object.h>
24#include <netlink/route/rtnl.h>
25#include <netlink/route/route.h>
26#include <netlink/fib_lookup/request.h>
27#include <netlink/fib_lookup/lookup.h>
28
29/** @cond SKIP */
30static struct nl_cache_ops fib_lookup_ops;
31static struct nl_object_ops result_obj_ops;
32
33/* not exported so far */
34struct fib_result_nl {
35	uint32_t	fl_addr;   /* To be looked up*/
36	uint32_t	fl_fwmark;
37	unsigned char	fl_tos;
38	unsigned char   fl_scope;
39	unsigned char   tb_id_in;
40
41	unsigned char   tb_id;      /* Results */
42	unsigned char	prefixlen;
43	unsigned char	nh_sel;
44	unsigned char	type;
45	unsigned char	scope;
46	int             err;
47};
48/** @endcond */
49
50static void result_free_data(struct nl_object *obj)
51{
52	struct flnl_result *res = nl_object_priv(obj);
53
54	if (res && res->fr_req)
55		nl_object_put(OBJ_CAST(res->fr_req));
56}
57
58static int result_clone(struct nl_object *_dst, struct nl_object *_src)
59{
60	struct flnl_result *dst = nl_object_priv(_dst);
61	struct flnl_result *src = nl_object_priv(_src);
62
63	if (src->fr_req)
64		if (!(dst->fr_req = (struct flnl_request *)
65				nl_object_clone(OBJ_CAST(src->fr_req))))
66			return -NLE_NOMEM;
67
68	return 0;
69}
70
71static int result_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
72			     struct nlmsghdr *n, struct nl_parser_param *pp)
73{
74	struct flnl_result *res;
75	struct fib_result_nl *fr;
76	struct nl_addr *addr;
77	int err = -NLE_INVAL;
78
79	res = flnl_result_alloc();
80	if (!res)
81		goto errout;
82
83	res->ce_msgtype = n->nlmsg_type;
84
85	res->fr_req = flnl_request_alloc();
86	if (!res->fr_req)
87		goto errout;
88
89	fr = nlmsg_data(n);
90	addr = nl_addr_build(AF_INET, &fr->fl_addr, 4);
91	if (!addr)
92		goto errout;
93	err = flnl_request_set_addr(res->fr_req, addr);
94	nl_addr_put(addr);
95	if (err < 0)
96		goto errout;
97
98	flnl_request_set_fwmark(res->fr_req, fr->fl_fwmark);
99	flnl_request_set_tos(res->fr_req, fr->fl_tos);
100	flnl_request_set_scope(res->fr_req, fr->fl_scope);
101	flnl_request_set_table(res->fr_req, fr->tb_id_in);
102
103	res->fr_table_id = fr->tb_id;
104	res->fr_prefixlen = fr->prefixlen;
105	res->fr_nh_sel = fr->nh_sel;
106	res->fr_type = fr->type;
107	res->fr_scope = fr->scope;
108	res->fr_error = fr->err;
109
110	err = pp->pp_cb((struct nl_object *) res, pp);
111	if (err < 0)
112		goto errout;
113
114	/* REAL HACK, fib_lookup doesn't support ACK nor does it
115	 * send a DONE message, enforce end of message stream
116	 * after just the first message */
117	err = NL_STOP;
118
119errout:
120	flnl_result_put(res);
121	return err;
122}
123
124static void result_dump_line(struct nl_object *obj, struct nl_dump_params *p)
125{
126	struct flnl_result *res = (struct flnl_result *) obj;
127	char buf[256];
128
129	nl_dump_line(p, "table %s prefixlen %u next-hop-selector %u\n",
130		rtnl_route_table2str(res->fr_table_id, buf, sizeof(buf)),
131		res->fr_prefixlen, res->fr_nh_sel);
132	nl_dump_line(p, "type %s ",
133		     nl_rtntype2str(res->fr_type, buf, sizeof(buf)));
134	nl_dump(p, "scope %s error %s (%d)\n",
135		rtnl_scope2str(res->fr_scope, buf, sizeof(buf)),
136		strerror_r(-res->fr_error, buf, sizeof(buf)), res->fr_error);
137}
138
139static void result_dump_details(struct nl_object *obj, struct nl_dump_params *p)
140{
141	result_dump_line(obj, p);
142}
143
144static int result_compare(struct nl_object *_a, struct nl_object *_b,
145			uint32_t attrs, int flags)
146{
147	return 0;
148}
149
150/**
151 * @name Allocation/Freeing
152 * @{
153 */
154
155struct flnl_result *flnl_result_alloc(void)
156{
157	return (struct flnl_result *) nl_object_alloc(&result_obj_ops);
158}
159
160void flnl_result_put(struct flnl_result *res)
161{
162	nl_object_put((struct nl_object *) res);
163}
164
165/** @} */
166
167/**
168 * @name Cache Management
169 * @{
170 */
171
172/**
173 * Allocate lookup result cache.
174 *
175 * Allocates a new lookup result cache and initializes it properly.
176 *
177 * @note Free the memory after usage using nl_cache_destroy_and_free().
178 * @return Newly allocated cache or NULL if an error occured.
179 */
180struct nl_cache *flnl_result_alloc_cache(void)
181{
182	return nl_cache_alloc(&fib_lookup_ops);
183}
184
185/** @} */
186
187/**
188 * @name Lookup
189 * @{
190 */
191
192/**
193 * Builds a netlink request message to do a lookup
194 * @arg req		Requested match.
195 * @arg flags		additional netlink message flags
196 * @arg result		Result pointer
197 *
198 * Builds a new netlink message requesting a change of link attributes.
199 * The netlink message header isn't fully equipped with all relevant
200 * fields and must be sent out via nl_send_auto_complete() or
201 * supplemented as needed.
202 * \a old must point to a link currently configured in the kernel
203 * and \a tmpl must contain the attributes to be changed set via
204 * \c rtnl_link_set_* functions.
205 *
206 * @return 0 on success or a negative error code.
207 */
208int 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
243errout:
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 */
259int 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
283int flnl_result_get_table_id(struct flnl_result *res)
284{
285	return res->fr_table_id;
286}
287
288int flnl_result_get_prefixlen(struct flnl_result *res)
289{
290	return res->fr_prefixlen;
291}
292
293int flnl_result_get_nexthop_sel(struct flnl_result *res)
294{
295	return res->fr_nh_sel;
296}
297
298int flnl_result_get_type(struct flnl_result *res)
299{
300	return res->fr_type;
301}
302
303int flnl_result_get_scope(struct flnl_result *res)
304{
305	return res->fr_scope;
306}
307
308int flnl_result_get_error(struct flnl_result *res)
309{
310	return res->fr_error;
311}
312
313/** @} */
314
315static 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
327static 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
339static void __init fib_lookup_init(void)
340{
341	nl_cache_mngt_register(&fib_lookup_ops);
342}
343
344static void __exit fib_lookup_exit(void)
345{
346	nl_cache_mngt_unregister(&fib_lookup_ops);
347}
348
349/** @} */
350