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