• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* SPDX-License-Identifier: LGPL-2.1-only */
2 /*
3  * lib/route/route_obj.c	Route Object
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-2008 Thomas Graf <tgraf@suug.ch>
11  */
12 
13 /**
14  * @ingroup route
15  * @defgroup route_obj Route Object
16  *
17  * @par Attributes
18  * @code
19  * Name                                           Default
20  * -------------------------------------------------------------
21  * routing table                                  RT_TABLE_MAIN
22  * scope                                          RT_SCOPE_NOWHERE
23  * tos                                            0
24  * protocol                                       RTPROT_STATIC
25  * prio                                           0
26  * family                                         AF_UNSPEC
27  * type                                           RTN_UNICAST
28  * iif                                            NULL
29  * @endcode
30  *
31  * @{
32  */
33 
34 #include <netlink-private/netlink.h>
35 #include <netlink-private/utils.h>
36 #include <netlink-private/route/nexthop-encap.h>
37 #include <netlink/netlink.h>
38 #include <netlink/cache.h>
39 #include <netlink/utils.h>
40 #include <netlink/data.h>
41 #include <netlink/hashtable.h>
42 #include <netlink/route/rtnl.h>
43 #include <netlink/route/route.h>
44 #include <netlink/route/link.h>
45 #include <netlink/route/nexthop.h>
46 #include <linux/in_route.h>
47 
48 /** @cond SKIP */
49 #define ROUTE_ATTR_FAMILY    0x000001
50 #define ROUTE_ATTR_TOS       0x000002
51 #define ROUTE_ATTR_TABLE     0x000004
52 #define ROUTE_ATTR_PROTOCOL  0x000008
53 #define ROUTE_ATTR_SCOPE     0x000010
54 #define ROUTE_ATTR_TYPE      0x000020
55 #define ROUTE_ATTR_FLAGS     0x000040
56 #define ROUTE_ATTR_DST       0x000080
57 #define ROUTE_ATTR_SRC       0x000100
58 #define ROUTE_ATTR_IIF       0x000200
59 #define ROUTE_ATTR_OIF       0x000400
60 #define ROUTE_ATTR_GATEWAY   0x000800
61 #define ROUTE_ATTR_PRIO      0x001000
62 #define ROUTE_ATTR_PREF_SRC  0x002000
63 #define ROUTE_ATTR_METRICS   0x004000
64 #define ROUTE_ATTR_MULTIPATH 0x008000
65 #define ROUTE_ATTR_REALMS    0x010000
66 #define ROUTE_ATTR_CACHEINFO 0x020000
67 #define ROUTE_ATTR_TTL_PROPAGATE 0x040000
68 /** @endcond */
69 
route_constructor(struct nl_object * c)70 static void route_constructor(struct nl_object *c)
71 {
72 	struct rtnl_route *r = (struct rtnl_route *) c;
73 
74 	r->rt_family = AF_UNSPEC;
75 	r->rt_scope = RT_SCOPE_NOWHERE;
76 	r->rt_table = RT_TABLE_MAIN;
77 	r->rt_protocol = RTPROT_STATIC;
78 	r->rt_type = RTN_UNICAST;
79 	r->rt_prio = 0;
80 
81 	nl_init_list_head(&r->rt_nexthops);
82 }
83 
route_free_data(struct nl_object * c)84 static void route_free_data(struct nl_object *c)
85 {
86 	struct rtnl_route *r = (struct rtnl_route *) c;
87 	struct rtnl_nexthop *nh, *tmp;
88 
89 	if (r == NULL)
90 		return;
91 
92 	nl_addr_put(r->rt_dst);
93 	nl_addr_put(r->rt_src);
94 	nl_addr_put(r->rt_pref_src);
95 
96 	nl_list_for_each_entry_safe(nh, tmp, &r->rt_nexthops, rtnh_list) {
97 		rtnl_route_remove_nexthop(r, nh);
98 		rtnl_route_nh_free(nh);
99 	}
100 }
101 
route_clone(struct nl_object * _dst,struct nl_object * _src)102 static int route_clone(struct nl_object *_dst, struct nl_object *_src)
103 {
104 	struct rtnl_route *dst = (struct rtnl_route *) _dst;
105 	struct rtnl_route *src = (struct rtnl_route *) _src;
106 	struct rtnl_nexthop *nh, *new;
107 
108 	if (src->rt_dst)
109 		if (!(dst->rt_dst = nl_addr_clone(src->rt_dst)))
110 			return -NLE_NOMEM;
111 
112 	if (src->rt_src)
113 		if (!(dst->rt_src = nl_addr_clone(src->rt_src)))
114 			return -NLE_NOMEM;
115 
116 	if (src->rt_pref_src)
117 		if (!(dst->rt_pref_src = nl_addr_clone(src->rt_pref_src)))
118 			return -NLE_NOMEM;
119 
120 	/* Will be inc'ed again while adding the nexthops of the source */
121 	dst->rt_nr_nh = 0;
122 
123 	nl_init_list_head(&dst->rt_nexthops);
124 	nl_list_for_each_entry(nh, &src->rt_nexthops, rtnh_list) {
125 		new = rtnl_route_nh_clone(nh);
126 		if (!new)
127 			return -NLE_NOMEM;
128 
129 		rtnl_route_add_nexthop(dst, new);
130 	}
131 
132 	return 0;
133 }
134 
route_dump_line(struct nl_object * a,struct nl_dump_params * p)135 static void route_dump_line(struct nl_object *a, struct nl_dump_params *p)
136 {
137 	struct rtnl_route *r = (struct rtnl_route *) a;
138 	int cache = 0, flags;
139 	char buf[64];
140 
141 	if (r->rt_flags & RTM_F_CLONED)
142 		cache = 1;
143 
144 	nl_dump_line(p, "%s ", nl_af2str(r->rt_family, buf, sizeof(buf)));
145 
146 	if (cache)
147 		nl_dump(p, "cache ");
148 
149 	if (!(r->ce_mask & ROUTE_ATTR_DST) ||
150 	    nl_addr_get_len(r->rt_dst) == 0)
151 		nl_dump(p, "default ");
152 	else
153 		nl_dump(p, "%s ", nl_addr2str(r->rt_dst, buf, sizeof(buf)));
154 
155 	if (r->ce_mask & ROUTE_ATTR_TABLE && !cache)
156 		nl_dump(p, "table %s ",
157 			rtnl_route_table2str(r->rt_table, buf, sizeof(buf)));
158 
159 	if (r->ce_mask & ROUTE_ATTR_TYPE)
160 		nl_dump(p, "type %s ",
161 			nl_rtntype2str(r->rt_type, buf, sizeof(buf)));
162 
163 	if (r->ce_mask & ROUTE_ATTR_TOS && r->rt_tos != 0)
164 		nl_dump(p, "tos %#x ", r->rt_tos);
165 
166 	if (r->ce_mask & ROUTE_ATTR_MULTIPATH) {
167 		struct rtnl_nexthop *nh;
168 
169 		nl_list_for_each_entry(nh, &r->rt_nexthops, rtnh_list) {
170 			p->dp_ivar = NH_DUMP_FROM_ONELINE;
171 			rtnl_route_nh_dump(nh, p);
172 		}
173 	}
174 
175 	flags = r->rt_flags & ~(RTM_F_CLONED);
176 	if (r->ce_mask & ROUTE_ATTR_FLAGS && flags) {
177 
178 		nl_dump(p, "<");
179 
180 #define PRINT_FLAG(f) if (flags & RTNH_F_##f) { \
181 		flags &= ~RTNH_F_##f; nl_dump(p, #f "%s", flags ? "," : ""); }
182 		PRINT_FLAG(DEAD);
183 		PRINT_FLAG(ONLINK);
184 		PRINT_FLAG(PERVASIVE);
185 #undef PRINT_FLAG
186 
187 #define PRINT_FLAG(f) if (flags & RTM_F_##f) { \
188 		flags &= ~RTM_F_##f; nl_dump(p, #f "%s", flags ? "," : ""); }
189 		PRINT_FLAG(NOTIFY);
190 		PRINT_FLAG(EQUALIZE);
191 		PRINT_FLAG(PREFIX);
192 #undef PRINT_FLAG
193 
194 #define PRINT_FLAG(f) if (flags & RTCF_##f) { \
195 		flags &= ~RTCF_##f; nl_dump(p, #f "%s", flags ? "," : ""); }
196 		PRINT_FLAG(NOTIFY);
197 		PRINT_FLAG(REDIRECTED);
198 		PRINT_FLAG(DOREDIRECT);
199 		PRINT_FLAG(DIRECTSRC);
200 		PRINT_FLAG(DNAT);
201 		PRINT_FLAG(BROADCAST);
202 		PRINT_FLAG(MULTICAST);
203 		PRINT_FLAG(LOCAL);
204 #undef PRINT_FLAG
205 
206 		nl_dump(p, ">");
207 	}
208 
209 	nl_dump(p, "\n");
210 }
211 
route_dump_details(struct nl_object * a,struct nl_dump_params * p)212 static void route_dump_details(struct nl_object *a, struct nl_dump_params *p)
213 {
214 	struct rtnl_route *r = (struct rtnl_route *) a;
215 	struct nl_cache *link_cache;
216 	char buf[256];
217 	int i;
218 
219 	link_cache = nl_cache_mngt_require_safe("route/link");
220 
221 	route_dump_line(a, p);
222 	nl_dump_line(p, "    ");
223 
224 	if (r->ce_mask & ROUTE_ATTR_PREF_SRC)
225 		nl_dump(p, "preferred-src %s ",
226 			nl_addr2str(r->rt_pref_src, buf, sizeof(buf)));
227 
228 	if (r->ce_mask & ROUTE_ATTR_SCOPE && r->rt_scope != RT_SCOPE_NOWHERE)
229 		nl_dump(p, "scope %s ",
230 			rtnl_scope2str(r->rt_scope, buf, sizeof(buf)));
231 
232 	if (r->ce_mask & ROUTE_ATTR_PRIO)
233 		nl_dump(p, "priority %#x ", r->rt_prio);
234 
235 	if (r->ce_mask & ROUTE_ATTR_PROTOCOL)
236 		nl_dump(p, "protocol %s ",
237 			rtnl_route_proto2str(r->rt_protocol, buf, sizeof(buf)));
238 
239 	if (r->ce_mask & ROUTE_ATTR_IIF) {
240 		if (link_cache) {
241 			nl_dump(p, "iif %s ",
242 				rtnl_link_i2name(link_cache, r->rt_iif,
243 						 buf, sizeof(buf)));
244 		} else
245 			nl_dump(p, "iif %d ", r->rt_iif);
246 	}
247 
248 	if (r->ce_mask & ROUTE_ATTR_SRC)
249 		nl_dump(p, "src %s ", nl_addr2str(r->rt_src, buf, sizeof(buf)));
250 
251 	if (r->ce_mask & ROUTE_ATTR_TTL_PROPAGATE) {
252 		nl_dump(p, " ttl-propagate %s",
253 			r->rt_ttl_propagate ? "enabled" : "disabled");
254 	}
255 
256 	nl_dump(p, "\n");
257 
258 	if (r->ce_mask & ROUTE_ATTR_MULTIPATH) {
259 		struct rtnl_nexthop *nh;
260 
261 		nl_list_for_each_entry(nh, &r->rt_nexthops, rtnh_list) {
262 			nl_dump_line(p, "    ");
263 			p->dp_ivar = NH_DUMP_FROM_DETAILS;
264 			rtnl_route_nh_dump(nh, p);
265 			nl_dump(p, "\n");
266 		}
267 	}
268 
269 	if ((r->ce_mask & ROUTE_ATTR_CACHEINFO) && r->rt_cacheinfo.rtci_error) {
270 		nl_dump_line(p, "    cacheinfo error %d (%s)\n",
271 			r->rt_cacheinfo.rtci_error,
272 			nl_strerror_l(-r->rt_cacheinfo.rtci_error));
273 	}
274 
275 	if (r->ce_mask & ROUTE_ATTR_METRICS) {
276 		nl_dump_line(p, "    metrics [");
277 		for (i = 0; i < RTAX_MAX; i++)
278 			if (r->rt_metrics_mask & (1 << i))
279 				nl_dump(p, "%s %u ",
280 					rtnl_route_metric2str(i+1,
281 							      buf, sizeof(buf)),
282 					r->rt_metrics[i]);
283 		nl_dump(p, "]\n");
284 	}
285 
286 	if (link_cache)
287 		nl_cache_put(link_cache);
288 }
289 
route_dump_stats(struct nl_object * obj,struct nl_dump_params * p)290 static void route_dump_stats(struct nl_object *obj, struct nl_dump_params *p)
291 {
292 	struct rtnl_route *route = (struct rtnl_route *) obj;
293 
294 	route_dump_details(obj, p);
295 
296 	if (route->ce_mask & ROUTE_ATTR_CACHEINFO) {
297 		struct rtnl_rtcacheinfo *ci = &route->rt_cacheinfo;
298 
299 		nl_dump_line(p, "    used %u refcnt %u last-use %us "
300 				"expires %us\n",
301 			     ci->rtci_used, ci->rtci_clntref,
302 			     ci->rtci_last_use / nl_get_user_hz(),
303 			     ci->rtci_expires / nl_get_user_hz());
304 	}
305 }
306 
route_keygen(struct nl_object * obj,uint32_t * hashkey,uint32_t table_sz)307 static void route_keygen(struct nl_object *obj, uint32_t *hashkey,
308 			  uint32_t table_sz)
309 {
310 	struct rtnl_route *route = (struct rtnl_route *) obj;
311 	unsigned int rkey_sz;
312 	struct nl_addr *addr = NULL;
313 	struct route_hash_key {
314 		uint8_t		rt_family;
315 		uint8_t		rt_tos;
316 		uint32_t	rt_table;
317 		uint32_t	rt_prio;
318 		char 		rt_addr[0];
319 	} __attribute__((packed)) *rkey;
320 #ifdef NL_DEBUG
321 	char buf[INET6_ADDRSTRLEN+5];
322 #endif
323 
324 	if (route->rt_dst)
325 		addr = route->rt_dst;
326 
327 	rkey_sz = sizeof(*rkey);
328 	if (addr)
329 		rkey_sz += nl_addr_get_len(addr);
330 	rkey = calloc(1, rkey_sz);
331 	if (!rkey) {
332 		NL_DBG(2, "Warning: calloc failed for %d bytes...\n", rkey_sz);
333 		*hashkey = 0;
334 		return;
335 	}
336 	rkey->rt_family = route->rt_family;
337 	rkey->rt_tos = route->rt_tos;
338 	rkey->rt_table = route->rt_table;
339 	rkey->rt_prio = route->rt_prio;
340 	if (addr)
341 		memcpy(rkey->rt_addr, nl_addr_get_binary_addr(addr),
342 			nl_addr_get_len(addr));
343 
344 	*hashkey = nl_hash(rkey, rkey_sz, 0) % table_sz;
345 
346 	NL_DBG(5, "route %p key (fam %d tos %d table %d addr %s) keysz %d "
347 		"hash 0x%x\n", route, rkey->rt_family, rkey->rt_tos,
348 		rkey->rt_table, nl_addr2str(addr, buf, sizeof(buf)),
349 		rkey_sz, *hashkey);
350 
351 	free(rkey);
352 
353 	return;
354 }
355 
route_id_attrs_get(struct nl_object * obj)356 static uint32_t route_id_attrs_get(struct nl_object *obj)
357 {
358 	struct rtnl_route *route = (struct rtnl_route *)obj;
359 	struct nl_object_ops *ops = obj->ce_ops;
360 	uint32_t rv = ops->oo_id_attrs;
361 
362 	/* MPLS address family does not allow RTA_PRIORITY to be set */
363 	if (route->rt_family == AF_MPLS)
364 		rv &= ~ROUTE_ATTR_PRIO;
365 
366 	return rv;
367 }
368 
route_compare(struct nl_object * _a,struct nl_object * _b,uint64_t attrs,int flags)369 static uint64_t route_compare(struct nl_object *_a, struct nl_object *_b,
370 			      uint64_t attrs, int flags)
371 {
372 	struct rtnl_route *a = (struct rtnl_route *) _a;
373 	struct rtnl_route *b = (struct rtnl_route *) _b;
374 	struct rtnl_nexthop *nh_a, *nh_b;
375 	int i, found;
376 	uint64_t diff = 0;
377 
378 #define ROUTE_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, ROUTE_ATTR_##ATTR, a, b, EXPR)
379 
380 	diff |= ROUTE_DIFF(FAMILY,	a->rt_family != b->rt_family);
381 	diff |= ROUTE_DIFF(TOS,		a->rt_tos != b->rt_tos);
382 	diff |= ROUTE_DIFF(TABLE,	a->rt_table != b->rt_table);
383 	diff |= ROUTE_DIFF(PROTOCOL,	a->rt_protocol != b->rt_protocol);
384 	diff |= ROUTE_DIFF(SCOPE,	a->rt_scope != b->rt_scope);
385 	diff |= ROUTE_DIFF(TYPE,	a->rt_type != b->rt_type);
386 	diff |= ROUTE_DIFF(PRIO,	a->rt_prio != b->rt_prio);
387 	diff |= ROUTE_DIFF(DST,		nl_addr_cmp(a->rt_dst, b->rt_dst));
388 	diff |= ROUTE_DIFF(SRC,		nl_addr_cmp(a->rt_src, b->rt_src));
389 	diff |= ROUTE_DIFF(IIF,		a->rt_iif != b->rt_iif);
390 	diff |= ROUTE_DIFF(PREF_SRC,	nl_addr_cmp(a->rt_pref_src,
391 						    b->rt_pref_src));
392 	diff |= ROUTE_DIFF(TTL_PROPAGATE,
393 			   a->rt_ttl_propagate != b->rt_ttl_propagate);
394 
395 	if (flags & LOOSE_COMPARISON) {
396 		nl_list_for_each_entry(nh_b, &b->rt_nexthops, rtnh_list) {
397 			found = 0;
398 			nl_list_for_each_entry(nh_a, &a->rt_nexthops,
399 					       rtnh_list) {
400 				if (!rtnl_route_nh_compare(nh_a, nh_b,
401 							nh_b->ce_mask, 1)) {
402 					found = 1;
403 					break;
404 				}
405 			}
406 
407 			if (!found)
408 				goto nh_mismatch;
409 		}
410 
411 		for (i = 0; i < RTAX_MAX - 1; i++) {
412 			if (a->rt_metrics_mask & (1 << i) &&
413 			    (!(b->rt_metrics_mask & (1 << i)) ||
414 			     a->rt_metrics[i] != b->rt_metrics[i]))
415 				diff |= ROUTE_DIFF(METRICS, 1);
416 		}
417 
418 		diff |= ROUTE_DIFF(FLAGS,
419 			  (a->rt_flags ^ b->rt_flags) & b->rt_flag_mask);
420 	} else {
421 		if (a->rt_nr_nh != b->rt_nr_nh)
422 			goto nh_mismatch;
423 
424 		/* search for a dup in each nh of a */
425 		nl_list_for_each_entry(nh_a, &a->rt_nexthops, rtnh_list) {
426 			found = 0;
427 			nl_list_for_each_entry(nh_b, &b->rt_nexthops,
428 					       rtnh_list) {
429 				if (!rtnl_route_nh_compare(nh_a, nh_b, ~0, 0)) {
430 					found = 1;
431 					break;
432 				}
433 			}
434 			if (!found)
435 				goto nh_mismatch;
436 		}
437 
438 		/* search for a dup in each nh of b, covers case where a has
439 		 * dupes itself */
440 		nl_list_for_each_entry(nh_b, &b->rt_nexthops, rtnh_list) {
441 			found = 0;
442 			nl_list_for_each_entry(nh_a, &a->rt_nexthops,
443 					       rtnh_list) {
444 				if (!rtnl_route_nh_compare(nh_a, nh_b, ~0, 0)) {
445 					found = 1;
446 					break;
447 				}
448 			}
449 			if (!found)
450 				goto nh_mismatch;
451 		}
452 
453 		for (i = 0; i < RTAX_MAX - 1; i++) {
454 			if ((a->rt_metrics_mask & (1 << i)) ^
455 			    (b->rt_metrics_mask & (1 << i)))
456 				diff |= ROUTE_DIFF(METRICS, 1);
457 			else
458 				diff |= ROUTE_DIFF(METRICS,
459 					a->rt_metrics[i] != b->rt_metrics[i]);
460 		}
461 
462 		diff |= ROUTE_DIFF(FLAGS, a->rt_flags != b->rt_flags);
463 	}
464 
465 out:
466 	return diff;
467 
468 nh_mismatch:
469 	diff |= ROUTE_DIFF(MULTIPATH, 1);
470 	goto out;
471 
472 #undef ROUTE_DIFF
473 }
474 
route_update(struct nl_object * old_obj,struct nl_object * new_obj)475 static int route_update(struct nl_object *old_obj, struct nl_object *new_obj)
476 {
477 	struct rtnl_route *new_route = (struct rtnl_route *) new_obj;
478 	struct rtnl_route *old_route = (struct rtnl_route *) old_obj;
479 	struct rtnl_nexthop *new_nh;
480 	int action = new_obj->ce_msgtype;
481 #ifdef NL_DEBUG
482 	char buf[INET6_ADDRSTRLEN+5];
483 #endif
484 
485 	/*
486 	 * ipv6 ECMP route notifications from the kernel come as
487 	 * separate notifications, one for every nexthop. This update
488 	 * function collapses such route msgs into a single
489 	 * route with multiple nexthops. The resulting object looks
490 	 * similar to a ipv4 ECMP route
491 	 */
492 	if (new_route->rt_family != AF_INET6 ||
493 	    new_route->rt_table == RT_TABLE_LOCAL)
494 		return -NLE_OPNOTSUPP;
495 
496 	/*
497 	 * For routes that are already multipath,
498 	 * or dont have a nexthop dont do anything
499 	 */
500 	if (rtnl_route_get_nnexthops(new_route) != 1)
501 		return -NLE_OPNOTSUPP;
502 
503 	/*
504 	 * Get the only nexthop entry from the new route. For
505 	 * IPv6 we always get a route with a 0th NH
506 	 * filled or nothing at all
507 	 */
508 	new_nh = rtnl_route_nexthop_n(new_route, 0);
509 	if (!new_nh || !rtnl_route_nh_get_gateway(new_nh))
510 		return -NLE_OPNOTSUPP;
511 
512 	switch(action) {
513 	case RTM_NEWROUTE : {
514 		struct rtnl_nexthop *cloned_nh;
515 
516 		/*
517 		 * Add the nexthop to old route
518 		 */
519 		cloned_nh = rtnl_route_nh_clone(new_nh);
520 		if (!cloned_nh)
521 			return -NLE_NOMEM;
522 		rtnl_route_add_nexthop(old_route, cloned_nh);
523 
524 		NL_DBG(2, "Route obj %p updated. Added "
525 			"nexthop %p via %s\n", old_route, cloned_nh,
526 			nl_addr2str(cloned_nh->rtnh_gateway, buf,
527 					sizeof(buf)));
528 	}
529 		break;
530 	case RTM_DELROUTE : {
531 		struct rtnl_nexthop *old_nh;
532 
533 		/*
534 		 * Only take care of nexthop deletes and not
535 		 * route deletes. So, if there is only one nexthop
536 		 * quite likely we did not update it. So dont do
537 		 * anything and return
538 		 */
539 		if (rtnl_route_get_nnexthops(old_route) <= 1)
540 			return -NLE_OPNOTSUPP;
541 
542 		/*
543 		 * Find the next hop in old route and delete it
544 		 */
545 		nl_list_for_each_entry(old_nh, &old_route->rt_nexthops,
546 			rtnh_list) {
547 			if (!rtnl_route_nh_compare(old_nh, new_nh, ~0, 0)) {
548 
549 				rtnl_route_remove_nexthop(old_route, old_nh);
550 
551 				NL_DBG(2, "Route obj %p updated. Removed "
552 					"nexthop %p via %s\n", old_route,
553 					old_nh,
554 					nl_addr2str(old_nh->rtnh_gateway, buf,
555 					sizeof(buf)));
556 
557 				rtnl_route_nh_free(old_nh);
558 				break;
559 			}
560 		}
561 	}
562 		break;
563 	default:
564 		NL_DBG(2, "Unknown action associated "
565 			"to object %p during route update\n", new_obj);
566 		return -NLE_OPNOTSUPP;
567 	}
568 
569 	return NLE_SUCCESS;
570 }
571 
572 static const struct trans_tbl route_attrs[] = {
573 	__ADD(ROUTE_ATTR_FAMILY, family),
574 	__ADD(ROUTE_ATTR_TOS, tos),
575 	__ADD(ROUTE_ATTR_TABLE, table),
576 	__ADD(ROUTE_ATTR_PROTOCOL, protocol),
577 	__ADD(ROUTE_ATTR_SCOPE, scope),
578 	__ADD(ROUTE_ATTR_TYPE, type),
579 	__ADD(ROUTE_ATTR_FLAGS, flags),
580 	__ADD(ROUTE_ATTR_DST, dst),
581 	__ADD(ROUTE_ATTR_SRC, src),
582 	__ADD(ROUTE_ATTR_IIF, iif),
583 	__ADD(ROUTE_ATTR_OIF, oif),
584 	__ADD(ROUTE_ATTR_GATEWAY, gateway),
585 	__ADD(ROUTE_ATTR_PRIO, prio),
586 	__ADD(ROUTE_ATTR_PREF_SRC, pref_src),
587 	__ADD(ROUTE_ATTR_METRICS, metrics),
588 	__ADD(ROUTE_ATTR_MULTIPATH, multipath),
589 	__ADD(ROUTE_ATTR_REALMS, realms),
590 	__ADD(ROUTE_ATTR_CACHEINFO, cacheinfo),
591 	__ADD(ROUTE_ATTR_TTL_PROPAGATE, ttl_propagate),
592 };
593 
route_attrs2str(int attrs,char * buf,size_t len)594 static char *route_attrs2str(int attrs, char *buf, size_t len)
595 {
596 	return __flags2str(attrs, buf, len, route_attrs,
597 			   ARRAY_SIZE(route_attrs));
598 }
599 
600 /**
601  * @name Allocation/Freeing
602  * @{
603  */
604 
rtnl_route_alloc(void)605 struct rtnl_route *rtnl_route_alloc(void)
606 {
607 	return (struct rtnl_route *) nl_object_alloc(&route_obj_ops);
608 }
609 
rtnl_route_get(struct rtnl_route * route)610 void rtnl_route_get(struct rtnl_route *route)
611 {
612 	nl_object_get((struct nl_object *) route);
613 }
614 
rtnl_route_put(struct rtnl_route * route)615 void rtnl_route_put(struct rtnl_route *route)
616 {
617 	nl_object_put((struct nl_object *) route);
618 }
619 
620 /** @} */
621 
622 /**
623  * @name Attributes
624  * @{
625  */
626 
rtnl_route_set_table(struct rtnl_route * route,uint32_t table)627 void rtnl_route_set_table(struct rtnl_route *route, uint32_t table)
628 {
629 	route->rt_table = table;
630 	route->ce_mask |= ROUTE_ATTR_TABLE;
631 }
632 
rtnl_route_get_table(struct rtnl_route * route)633 uint32_t rtnl_route_get_table(struct rtnl_route *route)
634 {
635 	return route->rt_table;
636 }
637 
rtnl_route_set_scope(struct rtnl_route * route,uint8_t scope)638 void rtnl_route_set_scope(struct rtnl_route *route, uint8_t scope)
639 {
640 	route->rt_scope = scope;
641 	route->ce_mask |= ROUTE_ATTR_SCOPE;
642 }
643 
rtnl_route_get_scope(struct rtnl_route * route)644 uint8_t rtnl_route_get_scope(struct rtnl_route *route)
645 {
646 	return route->rt_scope;
647 }
648 
rtnl_route_set_tos(struct rtnl_route * route,uint8_t tos)649 void rtnl_route_set_tos(struct rtnl_route *route, uint8_t tos)
650 {
651 	route->rt_tos = tos;
652 	route->ce_mask |= ROUTE_ATTR_TOS;
653 }
654 
rtnl_route_get_tos(struct rtnl_route * route)655 uint8_t rtnl_route_get_tos(struct rtnl_route *route)
656 {
657 	return route->rt_tos;
658 }
659 
rtnl_route_set_protocol(struct rtnl_route * route,uint8_t protocol)660 void rtnl_route_set_protocol(struct rtnl_route *route, uint8_t protocol)
661 {
662 	route->rt_protocol = protocol;
663 	route->ce_mask |= ROUTE_ATTR_PROTOCOL;
664 }
665 
rtnl_route_get_protocol(struct rtnl_route * route)666 uint8_t rtnl_route_get_protocol(struct rtnl_route *route)
667 {
668 	return route->rt_protocol;
669 }
670 
rtnl_route_set_priority(struct rtnl_route * route,uint32_t prio)671 void rtnl_route_set_priority(struct rtnl_route *route, uint32_t prio)
672 {
673 	route->rt_prio = prio;
674 	route->ce_mask |= ROUTE_ATTR_PRIO;
675 }
676 
rtnl_route_get_priority(struct rtnl_route * route)677 uint32_t rtnl_route_get_priority(struct rtnl_route *route)
678 {
679 	return route->rt_prio;
680 }
681 
rtnl_route_set_family(struct rtnl_route * route,uint8_t family)682 int rtnl_route_set_family(struct rtnl_route *route, uint8_t family)
683 {
684 	switch(family) {
685 	case AF_INET:
686 	case AF_INET6:
687 	case AF_DECnet:
688 	case AF_MPLS:
689 		route->rt_family = family;
690 		route->ce_mask |= ROUTE_ATTR_FAMILY;
691 		return 0;
692 	}
693 
694 	return -NLE_AF_NOSUPPORT;
695 }
696 
rtnl_route_get_family(struct rtnl_route * route)697 uint8_t rtnl_route_get_family(struct rtnl_route *route)
698 {
699 	return route->rt_family;
700 }
701 
rtnl_route_set_dst(struct rtnl_route * route,struct nl_addr * addr)702 int rtnl_route_set_dst(struct rtnl_route *route, struct nl_addr *addr)
703 {
704 	if (route->ce_mask & ROUTE_ATTR_FAMILY) {
705 		if (addr->a_family != route->rt_family)
706 			return -NLE_AF_MISMATCH;
707 	} else
708 		route->rt_family = addr->a_family;
709 
710 	if (route->rt_dst)
711 		nl_addr_put(route->rt_dst);
712 
713 	nl_addr_get(addr);
714 	route->rt_dst = addr;
715 
716 	route->ce_mask |= (ROUTE_ATTR_DST | ROUTE_ATTR_FAMILY);
717 
718 	return 0;
719 }
720 
rtnl_route_get_dst(struct rtnl_route * route)721 struct nl_addr *rtnl_route_get_dst(struct rtnl_route *route)
722 {
723 	return route->rt_dst;
724 }
725 
rtnl_route_set_src(struct rtnl_route * route,struct nl_addr * addr)726 int rtnl_route_set_src(struct rtnl_route *route, struct nl_addr *addr)
727 {
728 	if (addr->a_family == AF_INET)
729 		return -NLE_SRCRT_NOSUPPORT;
730 
731 	if (route->ce_mask & ROUTE_ATTR_FAMILY) {
732 		if (addr->a_family != route->rt_family)
733 			return -NLE_AF_MISMATCH;
734 	} else
735 		route->rt_family = addr->a_family;
736 
737 	if (route->rt_src)
738 		nl_addr_put(route->rt_src);
739 
740 	nl_addr_get(addr);
741 	route->rt_src = addr;
742 	route->ce_mask |= (ROUTE_ATTR_SRC | ROUTE_ATTR_FAMILY);
743 
744 	return 0;
745 }
746 
rtnl_route_get_src(struct rtnl_route * route)747 struct nl_addr *rtnl_route_get_src(struct rtnl_route *route)
748 {
749 	return route->rt_src;
750 }
751 
rtnl_route_set_type(struct rtnl_route * route,uint8_t type)752 int rtnl_route_set_type(struct rtnl_route *route, uint8_t type)
753 {
754 	if (type > RTN_MAX)
755 		return -NLE_RANGE;
756 
757 	route->rt_type = type;
758 	route->ce_mask |= ROUTE_ATTR_TYPE;
759 
760 	return 0;
761 }
762 
rtnl_route_get_type(struct rtnl_route * route)763 uint8_t rtnl_route_get_type(struct rtnl_route *route)
764 {
765 	return route->rt_type;
766 }
767 
rtnl_route_set_flags(struct rtnl_route * route,uint32_t flags)768 void rtnl_route_set_flags(struct rtnl_route *route, uint32_t flags)
769 {
770 	route->rt_flag_mask |= flags;
771 	route->rt_flags |= flags;
772 	route->ce_mask |= ROUTE_ATTR_FLAGS;
773 }
774 
rtnl_route_unset_flags(struct rtnl_route * route,uint32_t flags)775 void rtnl_route_unset_flags(struct rtnl_route *route, uint32_t flags)
776 {
777 	route->rt_flag_mask |= flags;
778 	route->rt_flags &= ~flags;
779 	route->ce_mask |= ROUTE_ATTR_FLAGS;
780 }
781 
rtnl_route_get_flags(struct rtnl_route * route)782 uint32_t rtnl_route_get_flags(struct rtnl_route *route)
783 {
784 	return route->rt_flags;
785 }
786 
rtnl_route_set_metric(struct rtnl_route * route,int metric,uint32_t value)787 int rtnl_route_set_metric(struct rtnl_route *route, int metric, uint32_t value)
788 {
789 	if (metric > RTAX_MAX || metric < 1)
790 		return -NLE_RANGE;
791 
792 	route->rt_metrics[metric - 1] = value;
793 
794 	if (!(route->rt_metrics_mask & (1 << (metric - 1)))) {
795 		route->rt_nmetrics++;
796 		route->rt_metrics_mask |= (1 << (metric - 1));
797 	}
798 
799 	route->ce_mask |= ROUTE_ATTR_METRICS;
800 
801 	return 0;
802 }
803 
rtnl_route_unset_metric(struct rtnl_route * route,int metric)804 int rtnl_route_unset_metric(struct rtnl_route *route, int metric)
805 {
806 	if (metric > RTAX_MAX || metric < 1)
807 		return -NLE_RANGE;
808 
809 	if (route->rt_metrics_mask & (1 << (metric - 1))) {
810 		route->rt_nmetrics--;
811 		route->rt_metrics_mask &= ~(1 << (metric - 1));
812 	}
813 
814 	return 0;
815 }
816 
rtnl_route_get_metric(struct rtnl_route * route,int metric,uint32_t * value)817 int rtnl_route_get_metric(struct rtnl_route *route, int metric, uint32_t *value)
818 {
819 	if (metric > RTAX_MAX || metric < 1)
820 		return -NLE_RANGE;
821 
822 	if (!(route->rt_metrics_mask & (1 << (metric - 1))))
823 		return -NLE_OBJ_NOTFOUND;
824 
825 	if (value)
826 		*value = route->rt_metrics[metric - 1];
827 
828 	return 0;
829 }
830 
rtnl_route_set_pref_src(struct rtnl_route * route,struct nl_addr * addr)831 int rtnl_route_set_pref_src(struct rtnl_route *route, struct nl_addr *addr)
832 {
833 	if (route->ce_mask & ROUTE_ATTR_FAMILY) {
834 		if (addr->a_family != route->rt_family)
835 			return -NLE_AF_MISMATCH;
836 	} else
837 		route->rt_family = addr->a_family;
838 
839 	if (route->rt_pref_src)
840 		nl_addr_put(route->rt_pref_src);
841 
842 	nl_addr_get(addr);
843 	route->rt_pref_src = addr;
844 	route->ce_mask |= (ROUTE_ATTR_PREF_SRC | ROUTE_ATTR_FAMILY);
845 
846 	return 0;
847 }
848 
rtnl_route_get_pref_src(struct rtnl_route * route)849 struct nl_addr *rtnl_route_get_pref_src(struct rtnl_route *route)
850 {
851 	return route->rt_pref_src;
852 }
853 
rtnl_route_set_iif(struct rtnl_route * route,int ifindex)854 void rtnl_route_set_iif(struct rtnl_route *route, int ifindex)
855 {
856 	route->rt_iif = ifindex;
857 	route->ce_mask |= ROUTE_ATTR_IIF;
858 }
859 
rtnl_route_get_iif(struct rtnl_route * route)860 int rtnl_route_get_iif(struct rtnl_route *route)
861 {
862 	return route->rt_iif;
863 }
864 
rtnl_route_add_nexthop(struct rtnl_route * route,struct rtnl_nexthop * nh)865 void rtnl_route_add_nexthop(struct rtnl_route *route, struct rtnl_nexthop *nh)
866 {
867 	nl_list_add_tail(&nh->rtnh_list, &route->rt_nexthops);
868 	route->rt_nr_nh++;
869 	route->ce_mask |= ROUTE_ATTR_MULTIPATH;
870 }
871 
rtnl_route_remove_nexthop(struct rtnl_route * route,struct rtnl_nexthop * nh)872 void rtnl_route_remove_nexthop(struct rtnl_route *route, struct rtnl_nexthop *nh)
873 {
874 	if (route->ce_mask & ROUTE_ATTR_MULTIPATH) {
875 		route->rt_nr_nh--;
876 		nl_list_del(&nh->rtnh_list);
877 	}
878 }
879 
rtnl_route_get_nexthops(struct rtnl_route * route)880 struct nl_list_head *rtnl_route_get_nexthops(struct rtnl_route *route)
881 {
882 	if (route->ce_mask & ROUTE_ATTR_MULTIPATH)
883 		return &route->rt_nexthops;
884 
885 	return NULL;
886 }
887 
rtnl_route_get_nnexthops(struct rtnl_route * route)888 int rtnl_route_get_nnexthops(struct rtnl_route *route)
889 {
890 	if (route->ce_mask & ROUTE_ATTR_MULTIPATH)
891 		return route->rt_nr_nh;
892 
893 	return 0;
894 }
895 
rtnl_route_foreach_nexthop(struct rtnl_route * r,void (* cb)(struct rtnl_nexthop *,void *),void * arg)896 void rtnl_route_foreach_nexthop(struct rtnl_route *r,
897                                 void (*cb)(struct rtnl_nexthop *, void *),
898                                 void *arg)
899 {
900 	struct rtnl_nexthop *nh;
901 
902 	if (r->ce_mask & ROUTE_ATTR_MULTIPATH) {
903 		nl_list_for_each_entry(nh, &r->rt_nexthops, rtnh_list) {
904 			cb(nh, arg);
905 		}
906 	}
907 }
908 
rtnl_route_nexthop_n(struct rtnl_route * r,int n)909 struct rtnl_nexthop *rtnl_route_nexthop_n(struct rtnl_route *r, int n)
910 {
911 	struct rtnl_nexthop *nh;
912 	uint32_t i;
913 
914 	if (r->ce_mask & ROUTE_ATTR_MULTIPATH && r->rt_nr_nh > n) {
915 		i = 0;
916 		nl_list_for_each_entry(nh, &r->rt_nexthops, rtnh_list) {
917 			if (i == n) return nh;
918 			i++;
919 		}
920 	}
921 	return NULL;
922 }
923 
rtnl_route_set_ttl_propagate(struct rtnl_route * route,uint8_t ttl_prop)924 void rtnl_route_set_ttl_propagate(struct rtnl_route *route, uint8_t ttl_prop)
925 {
926 	route->rt_ttl_propagate = ttl_prop;
927 	route->ce_mask |= ROUTE_ATTR_TTL_PROPAGATE;
928 }
929 
rtnl_route_get_ttl_propagate(struct rtnl_route * route)930 int rtnl_route_get_ttl_propagate(struct rtnl_route *route)
931 {
932 	if (!route)
933 		return -NLE_INVAL;
934 	if (!(route->ce_mask & ROUTE_ATTR_TTL_PROPAGATE))
935 		return -NLE_MISSING_ATTR;
936 	return route->rt_ttl_propagate;
937 }
938 
939 /** @} */
940 
941 /**
942  * @name Utilities
943  * @{
944  */
945 
946 /**
947  * Guess scope of a route object.
948  * @arg route		Route object.
949  *
950  * Guesses the scope of a route object, based on the following rules:
951  * @code
952  *   1) Local route -> local scope
953  *   2) At least one nexthop not directly connected -> universe scope
954  *   3) All others -> link scope
955  * @endcode
956  *
957  * @return Scope value.
958  */
rtnl_route_guess_scope(struct rtnl_route * route)959 int rtnl_route_guess_scope(struct rtnl_route *route)
960 {
961 	if (route->rt_type == RTN_LOCAL)
962 		return RT_SCOPE_HOST;
963 
964 	if (route->rt_family == AF_MPLS)
965 		return RT_SCOPE_UNIVERSE;
966 
967 	if (!nl_list_empty(&route->rt_nexthops)) {
968 		struct rtnl_nexthop *nh;
969 
970 		/*
971 		 * Use scope uiniverse if there is at least one nexthop which
972 		 * is not directly connected
973 		 */
974 		nl_list_for_each_entry(nh, &route->rt_nexthops, rtnh_list) {
975 			if (nh->rtnh_gateway)
976 				return RT_SCOPE_UNIVERSE;
977 		}
978 	}
979 
980 	return RT_SCOPE_LINK;
981 }
982 
983 /** @} */
984 
rtnl_route_parse_via(struct nlattr * nla)985 static struct nl_addr *rtnl_route_parse_via(struct nlattr *nla)
986 {
987 	int alen = nla_len(nla) - offsetof(struct rtvia, rtvia_addr);
988 	struct rtvia *via = nla_data(nla);
989 
990 	return nl_addr_build(via->rtvia_family, via->rtvia_addr, alen);
991 }
992 
rtnl_route_put_via(struct nl_msg * msg,struct nl_addr * addr)993 static int rtnl_route_put_via(struct nl_msg *msg, struct nl_addr *addr)
994 {
995 	unsigned int alen = nl_addr_get_len(addr);
996 	struct nlattr *nla;
997 	struct rtvia *via;
998 
999 	nla = nla_reserve(msg, RTA_VIA, alen + sizeof(*via));
1000 	if (!nla)
1001 		return -EMSGSIZE;
1002 
1003 	via = nla_data(nla);
1004 	via->rtvia_family = nl_addr_get_family(addr);
1005 	memcpy(via->rtvia_addr, nl_addr_get_binary_addr(addr), alen);
1006 
1007 	return 0;
1008 }
1009 
1010 static struct nla_policy route_policy[RTA_MAX+1] = {
1011 	[RTA_IIF]	= { .type = NLA_U32 },
1012 	[RTA_OIF]	= { .type = NLA_U32 },
1013 	[RTA_PRIORITY]	= { .type = NLA_U32 },
1014 	[RTA_FLOW]	= { .type = NLA_U32 },
1015 	[RTA_CACHEINFO]	= { .minlen = sizeof(struct rta_cacheinfo) },
1016 	[RTA_METRICS]	= { .type = NLA_NESTED },
1017 	[RTA_MULTIPATH]	= { .type = NLA_NESTED },
1018 	[RTA_TTL_PROPAGATE] = { .type = NLA_U8 },
1019 	[RTA_ENCAP]	= { .type = NLA_NESTED },
1020 	[RTA_ENCAP_TYPE] = { .type = NLA_U16 },
1021 };
1022 
parse_multipath(struct rtnl_route * route,struct nlattr * attr)1023 static int parse_multipath(struct rtnl_route *route, struct nlattr *attr)
1024 {
1025 	struct rtnl_nexthop *nh = NULL;
1026 	struct rtnexthop *rtnh = nla_data(attr);
1027 	size_t tlen = nla_len(attr);
1028 	int err;
1029 
1030 	while (tlen >= sizeof(*rtnh) && tlen >= rtnh->rtnh_len) {
1031 		nh = rtnl_route_nh_alloc();
1032 		if (!nh)
1033 			return -NLE_NOMEM;
1034 
1035 		rtnl_route_nh_set_weight(nh, rtnh->rtnh_hops);
1036 		rtnl_route_nh_set_ifindex(nh, rtnh->rtnh_ifindex);
1037 		rtnl_route_nh_set_flags(nh, rtnh->rtnh_flags);
1038 
1039 		if (rtnh->rtnh_len > sizeof(*rtnh)) {
1040 			struct nlattr *ntb[RTA_MAX + 1];
1041 
1042 			err = nla_parse(ntb, RTA_MAX, (struct nlattr *)
1043 					RTNH_DATA(rtnh),
1044 					rtnh->rtnh_len - sizeof(*rtnh),
1045 					route_policy);
1046 			if (err < 0)
1047 				goto errout;
1048 
1049 			if (ntb[RTA_GATEWAY]) {
1050 				struct nl_addr *addr;
1051 
1052 				addr = nl_addr_alloc_attr(ntb[RTA_GATEWAY],
1053 							  route->rt_family);
1054 				if (!addr) {
1055 					err = -NLE_NOMEM;
1056 					goto errout;
1057 				}
1058 
1059 				rtnl_route_nh_set_gateway(nh, addr);
1060 				nl_addr_put(addr);
1061 			}
1062 
1063 			if (ntb[RTA_FLOW]) {
1064 				uint32_t realms;
1065 
1066 				realms = nla_get_u32(ntb[RTA_FLOW]);
1067 				rtnl_route_nh_set_realms(nh, realms);
1068 			}
1069 
1070 			if (ntb[RTA_NEWDST]) {
1071 				struct nl_addr *addr;
1072 
1073 				addr = nl_addr_alloc_attr(ntb[RTA_NEWDST],
1074 							  route->rt_family);
1075 				if (!addr)
1076 					goto errout;
1077 
1078 				err = rtnl_route_nh_set_newdst(nh, addr);
1079 				nl_addr_put(addr);
1080 				if (err)
1081 					goto errout;
1082 			}
1083 
1084 			if (ntb[RTA_VIA]) {
1085 				struct nl_addr *addr;
1086 
1087 				addr = rtnl_route_parse_via(ntb[RTA_VIA]);
1088 				if (!addr)
1089 					goto errout;
1090 
1091 				err = rtnl_route_nh_set_via(nh, addr);
1092 				nl_addr_put(addr);
1093 				if (err)
1094 					goto errout;
1095 			}
1096 
1097 			if (ntb[RTA_ENCAP] && ntb[RTA_ENCAP_TYPE]) {
1098 				err = nh_encap_parse_msg(ntb[RTA_ENCAP],
1099 							 ntb[RTA_ENCAP_TYPE],
1100 							 nh);
1101 				if (err)
1102 					goto errout;
1103 			}
1104 		}
1105 
1106 		rtnl_route_add_nexthop(route, nh);
1107 		tlen -= RTNH_ALIGN(rtnh->rtnh_len);
1108 		rtnh = RTNH_NEXT(rtnh);
1109 	}
1110 
1111 	err = 0;
1112 errout:
1113 	if (err && nh)
1114 		rtnl_route_nh_free(nh);
1115 
1116 	return err;
1117 }
1118 
rtnl_route_parse(struct nlmsghdr * nlh,struct rtnl_route ** result)1119 int rtnl_route_parse(struct nlmsghdr *nlh, struct rtnl_route **result)
1120 {
1121 	struct rtmsg *rtm;
1122 	struct rtnl_route *route;
1123 	struct nlattr *tb[RTA_MAX + 1];
1124 	struct nl_addr *src = NULL, *dst = NULL, *addr;
1125 	struct rtnl_nexthop *old_nh = NULL;
1126 	int err, family;
1127 
1128 	route = rtnl_route_alloc();
1129 	if (!route)
1130 		goto errout_nomem;
1131 
1132 	route->ce_msgtype = nlh->nlmsg_type;
1133 
1134 	err = nlmsg_parse(nlh, sizeof(struct rtmsg), tb, RTA_MAX, route_policy);
1135 	if (err < 0)
1136 		goto errout;
1137 
1138 	rtm = nlmsg_data(nlh);
1139 	route->rt_family = family = rtm->rtm_family;
1140 	route->rt_tos = rtm->rtm_tos;
1141 	route->rt_table = rtm->rtm_table;
1142 	route->rt_type = rtm->rtm_type;
1143 	route->rt_scope = rtm->rtm_scope;
1144 	route->rt_protocol = rtm->rtm_protocol;
1145 	route->rt_flags = rtm->rtm_flags;
1146 	route->rt_prio = 0;
1147 
1148 	route->ce_mask |= ROUTE_ATTR_FAMILY | ROUTE_ATTR_TOS |
1149 			  ROUTE_ATTR_TABLE | ROUTE_ATTR_TYPE |
1150 			  ROUTE_ATTR_SCOPE | ROUTE_ATTR_PROTOCOL |
1151 			  ROUTE_ATTR_FLAGS;
1152 
1153 	/* right now MPLS does not allow rt_prio to be set, so don't
1154 	 * assume it is unless it comes from an attribute
1155 	 */
1156 	if (family != AF_MPLS)
1157 		route->ce_mask |= ROUTE_ATTR_PRIO;
1158 
1159 	if (tb[RTA_DST]) {
1160 		if (!(dst = nl_addr_alloc_attr(tb[RTA_DST], family)))
1161 			goto errout_nomem;
1162 	} else {
1163 		if (!(dst = nl_addr_alloc(0)))
1164 			goto errout_nomem;
1165 		nl_addr_set_family(dst, rtm->rtm_family);
1166 	}
1167 
1168 	nl_addr_set_prefixlen(dst, rtm->rtm_dst_len);
1169 	err = rtnl_route_set_dst(route, dst);
1170 	if (err < 0)
1171 		goto errout;
1172 
1173 	nl_addr_put(dst);
1174 
1175 	if (tb[RTA_SRC]) {
1176 		if (!(src = nl_addr_alloc_attr(tb[RTA_SRC], family)))
1177 			goto errout_nomem;
1178 	} else if (rtm->rtm_src_len)
1179 		if (!(src = nl_addr_alloc(0)))
1180 			goto errout_nomem;
1181 
1182 	if (src) {
1183 		nl_addr_set_prefixlen(src, rtm->rtm_src_len);
1184 		rtnl_route_set_src(route, src);
1185 		nl_addr_put(src);
1186 	}
1187 
1188 	if (tb[RTA_TABLE])
1189 		rtnl_route_set_table(route, nla_get_u32(tb[RTA_TABLE]));
1190 
1191 	if (tb[RTA_IIF])
1192 		rtnl_route_set_iif(route, nla_get_u32(tb[RTA_IIF]));
1193 
1194 	if (tb[RTA_PRIORITY])
1195 		rtnl_route_set_priority(route, nla_get_u32(tb[RTA_PRIORITY]));
1196 
1197 	if (tb[RTA_PREFSRC]) {
1198 		if (!(addr = nl_addr_alloc_attr(tb[RTA_PREFSRC], family)))
1199 			goto errout_nomem;
1200 		rtnl_route_set_pref_src(route, addr);
1201 		nl_addr_put(addr);
1202 	}
1203 
1204 	if (tb[RTA_METRICS]) {
1205 		struct nlattr *mtb[RTAX_MAX + 1];
1206 		int i;
1207 
1208 		err = nla_parse_nested(mtb, RTAX_MAX, tb[RTA_METRICS], NULL);
1209 		if (err < 0)
1210 			goto errout;
1211 
1212 		for (i = 1; i <= RTAX_MAX; i++) {
1213 			if (mtb[i] && nla_len(mtb[i]) >= sizeof(uint32_t)) {
1214 				uint32_t m = nla_get_u32(mtb[i]);
1215 
1216 				err = rtnl_route_set_metric(route, i, m);
1217 				if (err < 0)
1218 					goto errout;
1219 			}
1220 		}
1221 	}
1222 
1223 	if (tb[RTA_MULTIPATH])
1224 		if ((err = parse_multipath(route, tb[RTA_MULTIPATH])) < 0)
1225 			goto errout;
1226 
1227 	if (tb[RTA_CACHEINFO]) {
1228 		nla_memcpy(&route->rt_cacheinfo, tb[RTA_CACHEINFO],
1229 			   sizeof(route->rt_cacheinfo));
1230 		route->ce_mask |= ROUTE_ATTR_CACHEINFO;
1231 	}
1232 
1233 	if (tb[RTA_OIF]) {
1234 		if (!old_nh && !(old_nh = rtnl_route_nh_alloc()))
1235 			goto errout_nomem;
1236 
1237 		rtnl_route_nh_set_ifindex(old_nh, nla_get_u32(tb[RTA_OIF]));
1238 	}
1239 
1240 	if (tb[RTA_GATEWAY]) {
1241 		if (!old_nh && !(old_nh = rtnl_route_nh_alloc()))
1242 			goto errout_nomem;
1243 
1244 		if (!(addr = nl_addr_alloc_attr(tb[RTA_GATEWAY], family)))
1245 			goto errout_nomem;
1246 
1247 		rtnl_route_nh_set_gateway(old_nh, addr);
1248 		nl_addr_put(addr);
1249 	}
1250 
1251 	if (tb[RTA_FLOW]) {
1252 		if (!old_nh && !(old_nh = rtnl_route_nh_alloc()))
1253 			goto errout_nomem;
1254 
1255 		rtnl_route_nh_set_realms(old_nh, nla_get_u32(tb[RTA_FLOW]));
1256 	}
1257 
1258 	if (tb[RTA_NEWDST]) {
1259 		struct nl_addr *addr;
1260 
1261 		if (!old_nh && !(old_nh = rtnl_route_nh_alloc()))
1262 			goto errout_nomem;
1263 
1264 		addr = nl_addr_alloc_attr(tb[RTA_NEWDST], route->rt_family);
1265 		if (!addr)
1266 			goto errout_nomem;
1267 
1268 		err = rtnl_route_nh_set_newdst(old_nh, addr);
1269 		nl_addr_put(addr);
1270 		if (err)
1271 			goto errout;
1272 	}
1273 
1274 	if (tb[RTA_VIA]) {
1275 		int alen = nla_len(tb[RTA_VIA]) - offsetof(struct rtvia, rtvia_addr);
1276 		struct rtvia *via = nla_data(tb[RTA_VIA]);
1277 
1278 		if (!old_nh && !(old_nh = rtnl_route_nh_alloc()))
1279 			goto errout_nomem;
1280 
1281 		addr = nl_addr_build(via->rtvia_family, via->rtvia_addr, alen);
1282 		if (!addr)
1283 			goto errout_nomem;
1284 
1285 		err = rtnl_route_nh_set_via(old_nh, addr);
1286 		nl_addr_put(addr);
1287 		if (err)
1288 			goto errout;
1289 	}
1290 
1291 	if (tb[RTA_TTL_PROPAGATE]) {
1292 		rtnl_route_set_ttl_propagate(route,
1293 					     nla_get_u8(tb[RTA_TTL_PROPAGATE]));
1294 	}
1295 
1296 	if (tb[RTA_ENCAP] && tb[RTA_ENCAP_TYPE]) {
1297 		if (!old_nh && !(old_nh = rtnl_route_nh_alloc()))
1298 			goto errout_nomem;
1299 
1300 		err = nh_encap_parse_msg(tb[RTA_ENCAP],
1301 					 tb[RTA_ENCAP_TYPE], old_nh);
1302 		if (err)
1303 			goto errout;
1304 	}
1305 
1306 	if (old_nh) {
1307 		rtnl_route_nh_set_flags(old_nh, rtm->rtm_flags & 0xff);
1308 		if (route->rt_nr_nh == 0) {
1309 			/* If no nexthops have been provided via RTA_MULTIPATH
1310 			 * we add it as regular nexthop to maintain backwards
1311 			 * compatibility */
1312 			rtnl_route_add_nexthop(route, old_nh);
1313 		} else {
1314 			/* Kernel supports new style nexthop configuration,
1315 			 * verify that it is a duplicate and discard nexthop. */
1316 			struct rtnl_nexthop *first;
1317 
1318 			first = nl_list_first_entry(&route->rt_nexthops,
1319 						    struct rtnl_nexthop,
1320 						    rtnh_list);
1321 			if (!first)
1322 				BUG();
1323 
1324 			if (rtnl_route_nh_compare(old_nh, first,
1325 						  old_nh->ce_mask, 0)) {
1326 				err = -NLE_INVAL;
1327 				goto errout;
1328 			}
1329 
1330 			rtnl_route_nh_free(old_nh);
1331 		}
1332 		old_nh = NULL;
1333 	}
1334 
1335 	*result = route;
1336 	return 0;
1337 
1338 errout:
1339 	if (old_nh)
1340 		rtnl_route_nh_free(old_nh);
1341 	rtnl_route_put(route);
1342 	return err;
1343 
1344 errout_nomem:
1345 	err = -NLE_NOMEM;
1346 	goto errout;
1347 }
1348 
rtnl_route_build_msg(struct nl_msg * msg,struct rtnl_route * route)1349 int rtnl_route_build_msg(struct nl_msg *msg, struct rtnl_route *route)
1350 {
1351 	int i;
1352 	struct nlattr *metrics;
1353 	struct rtmsg rtmsg = {
1354 		.rtm_family = route->rt_family,
1355 		.rtm_tos = route->rt_tos,
1356 		.rtm_table = route->rt_table,
1357 		.rtm_protocol = route->rt_protocol,
1358 		.rtm_scope = route->rt_scope,
1359 		.rtm_type = route->rt_type,
1360 		.rtm_flags = route->rt_flags,
1361 	};
1362 
1363 	if (route->rt_dst == NULL)
1364 		return -NLE_MISSING_ATTR;
1365 
1366 	rtmsg.rtm_dst_len = nl_addr_get_prefixlen(route->rt_dst);
1367 	if (route->rt_src)
1368 		rtmsg.rtm_src_len = nl_addr_get_prefixlen(route->rt_src);
1369 
1370 	if (!(route->ce_mask & ROUTE_ATTR_SCOPE))
1371 		rtmsg.rtm_scope = rtnl_route_guess_scope(route);
1372 
1373 	if (rtnl_route_get_nnexthops(route) == 1) {
1374 		struct rtnl_nexthop *nh;
1375 		nh = rtnl_route_nexthop_n(route, 0);
1376 		rtmsg.rtm_flags |= nh->rtnh_flags;
1377 	}
1378 
1379 	if (nlmsg_append(msg, &rtmsg, sizeof(rtmsg), NLMSG_ALIGNTO) < 0)
1380 		goto nla_put_failure;
1381 
1382 	/* Additional table attribute replacing the 8bit in the header, was
1383 	 * required to allow more than 256 tables. MPLS does not allow the
1384 	 * table attribute to be set
1385 	 */
1386 	if (route->rt_family != AF_MPLS)
1387 		NLA_PUT_U32(msg, RTA_TABLE, route->rt_table);
1388 
1389 	if (nl_addr_get_len(route->rt_dst))
1390 		NLA_PUT_ADDR(msg, RTA_DST, route->rt_dst);
1391 
1392 	if (route->ce_mask & ROUTE_ATTR_PRIO)
1393 		NLA_PUT_U32(msg, RTA_PRIORITY, route->rt_prio);
1394 
1395 	if (route->ce_mask & ROUTE_ATTR_SRC)
1396 		NLA_PUT_ADDR(msg, RTA_SRC, route->rt_src);
1397 
1398 	if (route->ce_mask & ROUTE_ATTR_PREF_SRC)
1399 		NLA_PUT_ADDR(msg, RTA_PREFSRC, route->rt_pref_src);
1400 
1401 	if (route->ce_mask & ROUTE_ATTR_IIF)
1402 		NLA_PUT_U32(msg, RTA_IIF, route->rt_iif);
1403 
1404 	if (route->ce_mask & ROUTE_ATTR_TTL_PROPAGATE)
1405 		NLA_PUT_U8(msg, RTA_TTL_PROPAGATE, route->rt_ttl_propagate);
1406 
1407 	if (route->rt_nmetrics > 0) {
1408 		uint32_t val;
1409 
1410 		metrics = nla_nest_start(msg, RTA_METRICS);
1411 		if (metrics == NULL)
1412 			goto nla_put_failure;
1413 
1414 		for (i = 1; i <= RTAX_MAX; i++) {
1415 			if (!rtnl_route_get_metric(route, i, &val))
1416 				NLA_PUT_U32(msg, i, val);
1417 		}
1418 
1419 		nla_nest_end(msg, metrics);
1420 	}
1421 
1422 	if (rtnl_route_get_nnexthops(route) == 1) {
1423 		struct rtnl_nexthop *nh;
1424 
1425 		nh = rtnl_route_nexthop_n(route, 0);
1426 		if (nh->rtnh_gateway)
1427 			NLA_PUT_ADDR(msg, RTA_GATEWAY, nh->rtnh_gateway);
1428 		if (nh->rtnh_ifindex)
1429 			NLA_PUT_U32(msg, RTA_OIF, nh->rtnh_ifindex);
1430 		if (nh->rtnh_realms)
1431 			NLA_PUT_U32(msg, RTA_FLOW, nh->rtnh_realms);
1432 		if (nh->rtnh_newdst)
1433 			NLA_PUT_ADDR(msg, RTA_NEWDST, nh->rtnh_newdst);
1434 		if (nh->rtnh_via && rtnl_route_put_via(msg, nh->rtnh_via) < 0)
1435 			goto nla_put_failure;
1436 		if (nh->rtnh_encap &&
1437 		    nh_encap_build_msg(msg, nh->rtnh_encap) < 0)
1438 			goto nla_put_failure;
1439 	} else if (rtnl_route_get_nnexthops(route) > 1) {
1440 		struct nlattr *multipath;
1441 		struct rtnl_nexthop *nh;
1442 
1443 		if (!(multipath = nla_nest_start(msg, RTA_MULTIPATH)))
1444 			goto nla_put_failure;
1445 
1446 		nl_list_for_each_entry(nh, &route->rt_nexthops, rtnh_list) {
1447 			struct rtnexthop *rtnh;
1448 
1449 			rtnh = nlmsg_reserve(msg, sizeof(*rtnh), NLMSG_ALIGNTO);
1450 			if (!rtnh)
1451 				goto nla_put_failure;
1452 
1453 			rtnh->rtnh_flags = nh->rtnh_flags;
1454 			rtnh->rtnh_hops = nh->rtnh_weight;
1455 			rtnh->rtnh_ifindex = nh->rtnh_ifindex;
1456 
1457 			if (nh->rtnh_gateway)
1458 				NLA_PUT_ADDR(msg, RTA_GATEWAY,
1459 					     nh->rtnh_gateway);
1460 
1461 			if (nh->rtnh_newdst)
1462 				NLA_PUT_ADDR(msg, RTA_NEWDST, nh->rtnh_newdst);
1463 
1464 			if (nh->rtnh_via &&
1465 			    rtnl_route_put_via(msg, nh->rtnh_via) < 0)
1466 				goto nla_put_failure;
1467 
1468 			if (nh->rtnh_realms)
1469 				NLA_PUT_U32(msg, RTA_FLOW, nh->rtnh_realms);
1470 
1471 			if (nh->rtnh_encap &&
1472 			    nh_encap_build_msg(msg, nh->rtnh_encap) < 0)
1473 				goto nla_put_failure;
1474 
1475 			rtnh->rtnh_len = (char *) nlmsg_tail(msg->nm_nlh) -
1476 						(char *) rtnh;
1477 		}
1478 
1479 		nla_nest_end(msg, multipath);
1480 	}
1481 
1482 	return 0;
1483 
1484 nla_put_failure:
1485 	return -NLE_MSGSIZE;
1486 }
1487 
1488 /** @cond SKIP */
1489 struct nl_object_ops route_obj_ops = {
1490 	.oo_name		= "route/route",
1491 	.oo_size		= sizeof(struct rtnl_route),
1492 	.oo_constructor		= route_constructor,
1493 	.oo_free_data		= route_free_data,
1494 	.oo_clone		= route_clone,
1495 	.oo_dump = {
1496 	    [NL_DUMP_LINE]	= route_dump_line,
1497 	    [NL_DUMP_DETAILS]	= route_dump_details,
1498 	    [NL_DUMP_STATS]	= route_dump_stats,
1499 	},
1500 	.oo_compare		= route_compare,
1501 	.oo_keygen		= route_keygen,
1502 	.oo_update		= route_update,
1503 	.oo_attrs2str		= route_attrs2str,
1504 	.oo_id_attrs		= (ROUTE_ATTR_FAMILY | ROUTE_ATTR_TOS |
1505 				   ROUTE_ATTR_TABLE | ROUTE_ATTR_DST |
1506 				   ROUTE_ATTR_PRIO),
1507 	.oo_id_attrs_get	= route_id_attrs_get,
1508 };
1509 /** @endcond */
1510 
1511 /** @} */
1512