libnl  3.5.0
route_obj.c
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
605 struct rtnl_route *rtnl_route_alloc(void)
606 {
607  return (struct rtnl_route *) nl_object_alloc(&route_obj_ops);
608 }
609 
610 void rtnl_route_get(struct rtnl_route *route)
611 {
612  nl_object_get((struct nl_object *) route);
613 }
614 
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 
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 
633 uint32_t rtnl_route_get_table(struct rtnl_route *route)
634 {
635  return route->rt_table;
636 }
637 
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 
644 uint8_t rtnl_route_get_scope(struct rtnl_route *route)
645 {
646  return route->rt_scope;
647 }
648 
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 
655 uint8_t rtnl_route_get_tos(struct rtnl_route *route)
656 {
657  return route->rt_tos;
658 }
659 
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 
666 uint8_t rtnl_route_get_protocol(struct rtnl_route *route)
667 {
668  return route->rt_protocol;
669 }
670 
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 
677 uint32_t rtnl_route_get_priority(struct rtnl_route *route)
678 {
679  return route->rt_prio;
680 }
681 
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 
697 uint8_t rtnl_route_get_family(struct rtnl_route *route)
698 {
699  return route->rt_family;
700 }
701 
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 
721 struct nl_addr *rtnl_route_get_dst(struct rtnl_route *route)
722 {
723  return route->rt_dst;
724 }
725 
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 
747 struct nl_addr *rtnl_route_get_src(struct rtnl_route *route)
748 {
749  return route->rt_src;
750 }
751 
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 
763 uint8_t rtnl_route_get_type(struct rtnl_route *route)
764 {
765  return route->rt_type;
766 }
767 
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 
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 
782 uint32_t rtnl_route_get_flags(struct rtnl_route *route)
783 {
784  return route->rt_flags;
785 }
786 
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 
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 
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 
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 
849 struct nl_addr *rtnl_route_get_pref_src(struct rtnl_route *route)
850 {
851  return route->rt_pref_src;
852 }
853 
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 
860 int rtnl_route_get_iif(struct rtnl_route *route)
861 {
862  return route->rt_iif;
863 }
864 
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 
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 
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 
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 
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 
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 
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 
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  */
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 
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 
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 
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 
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 
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 /** @} */
struct nl_addr * nl_addr_clone(const struct nl_addr *addr)
Clone existing abstract address object.
Definition: addr.c:494
Dump object briefly on one line.
Definition: types.h:22
struct nl_addr * nl_addr_alloc(size_t maxsize)
Allocate empty abstract address.
Definition: addr.c:187
int nl_get_user_hz(void)
Return the value of HZ.
Definition: utils.c:509
8 bit integer
Definition: attr.h:41
int nla_parse(struct nlattr *tb[], int maxtype, struct nlattr *head, int len, const struct nla_policy *policy)
Create attribute index based on a stream of attributes.
Definition: attr.c:243
void nl_addr_set_prefixlen(struct nl_addr *addr, int prefixlen)
Set the prefix length of an abstract address.
Definition: addr.c:966
int nlmsg_parse(struct nlmsghdr *nlh, int hdrlen, struct nlattr *tb[], int maxtype, const struct nla_policy *policy)
parse attributes of a netlink message
Definition: msg.c:215
int nl_addr_cmp(const struct nl_addr *a, const struct nl_addr *b)
Compare abstract addresses.
Definition: addr.c:586
void * nlmsg_data(const struct nlmsghdr *nlh)
Return pointer to message payload.
Definition: msg.c:107
#define NLA_PUT_ADDR(msg, attrtype, addr)
Add address attribute to netlink message.
Definition: attr.h:289
unsigned int nl_addr_get_prefixlen(const struct nl_addr *addr)
Return prefix length of abstract address object.
Definition: addr.c:977
struct nl_object * nl_object_alloc(struct nl_object_ops *ops)
Allocate a new object of kind specified by the operations handle.
Definition: object.c:55
void * nlmsg_reserve(struct nl_msg *n, size_t len, int pad)
Reserve room for additional data in a netlink message.
Definition: msg.c:411
Attribute validation policy.
Definition: attr.h:69
uint8_t nla_get_u8(const struct nlattr *nla)
Return value of 8 bit integer attribute.
Definition: attr.c:607
struct nl_cache * nl_cache_mngt_require_safe(const char *name)
Return cache previously provided via nl_cache_mngt_provide()
Definition: cache_mngt.c:431
void nl_object_get(struct nl_object *obj)
Acquire a reference on a object.
Definition: object.c:205
struct nl_addr * nl_addr_build(int family, const void *buf, size_t size)
Allocate abstract address based on a binary address.
Definition: addr.c:218
uint32_t nla_get_u32(const struct nlattr *nla)
Return payload of 32 bit integer attribute.
Definition: attr.c:707
struct nlattr * nla_reserve(struct nl_msg *msg, int attrtype, int attrlen)
Reserve space for a attribute.
Definition: attr.c:457
struct nl_addr * nl_addr_get(struct nl_addr *addr)
Increase the reference counter of an abstract address.
Definition: addr.c:524
void nl_addr_set_family(struct nl_addr *addr, int family)
Set address family.
Definition: addr.c:881
struct nl_addr * nl_addr_alloc_attr(const struct nlattr *nla, int family)
Allocate abstract address based on Netlink attribute.
Definition: addr.c:263
#define NLA_PUT_U8(msg, attrtype, value)
Add 8 bit integer attribute to netlink message.
Definition: attr.h:200
Dump all attributes but no statistics.
Definition: types.h:23
int nla_nest_end(struct nl_msg *msg, struct nlattr *start)
Finalize nesting of attributes.
Definition: attr.c:966
int nla_parse_nested(struct nlattr *tb[], int maxtype, struct nlattr *nla, const struct nla_policy *policy)
Create attribute index based on nested attribute.
Definition: attr.c:1021
int nla_memcpy(void *dest, const struct nlattr *src, int count)
Copy attribute payload to another memory area.
Definition: attr.c:354
16 bit integer
Definition: attr.h:42
void * nla_data(const struct nlattr *nla)
Return pointer to the payload section.
Definition: attr.c:121
#define NLA_PUT_U32(msg, attrtype, value)
Add 32 bit integer attribute to netlink message.
Definition: attr.h:236
int nla_len(const struct nlattr *nla)
Return length of the payload .
Definition: attr.c:132
int rtnl_route_guess_scope(struct rtnl_route *route)
Guess scope of a route object.
Definition: route_obj.c:959
int nlmsg_append(struct nl_msg *n, void *data, size_t len, int pad)
Append data to tail of a netlink message.
Definition: msg.c:449
void nl_object_put(struct nl_object *obj)
Release a reference from an object.
Definition: object.c:216
Nested attributes.
Definition: attr.h:48
void nl_addr_put(struct nl_addr *addr)
Decrease the reference counter of an abstract address.
Definition: addr.c:540
uint16_t type
Type of attribute or NLA_UNSPEC.
Definition: attr.h:71
32 bit integer
Definition: attr.h:43
Dumping parameters.
Definition: types.h:33
void nl_dump(struct nl_dump_params *params, const char *fmt,...)
Dump a formatted character string.
Definition: utils.c:962
unsigned int nl_addr_get_len(const struct nl_addr *addr)
Get length of binary address of abstract address object.
Definition: addr.c:954
int dp_ivar
PRIVATE Owned by the current caller.
Definition: types.h:105
Dump all attributes including statistics.
Definition: types.h:24
void * nl_addr_get_binary_addr(const struct nl_addr *addr)
Get binary address of abstract address object.
Definition: addr.c:942
struct nlattr * nla_nest_start(struct nl_msg *msg, int attrtype)
Start a new level of nested attributes.
Definition: attr.c:903
char * nl_addr2str(const struct nl_addr *addr, char *buf, size_t size)
Convert abstract address object to character string.
Definition: addr.c:1000
int nl_addr_get_family(const struct nl_addr *addr)
Return address family.
Definition: addr.c:894