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