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