libnl  3.2.15
route_obj.c
1 /*
2  * lib/route/route_obj.c Route Object
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation version 2.1
7  * of the License.
8  *
9  * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
10  */
11 
12 /**
13  * @ingroup route
14  * @defgroup route_obj Route Object
15  *
16  * @par Attributes
17  * @code
18  * Name Default
19  * -------------------------------------------------------------
20  * routing table RT_TABLE_MAIN
21  * scope RT_SCOPE_NOWHERE
22  * tos 0
23  * protocol RTPROT_STATIC
24  * prio 0
25  * family AF_UNSPEC
26  * type RTN_UNICAST
27  * iif NULL
28  * @endcode
29  *
30  * @{
31  */
32 
33 #include <netlink-local.h>
34 #include <netlink/netlink.h>
35 #include <netlink/cache.h>
36 #include <netlink/utils.h>
37 #include <netlink/data.h>
38 #include <netlink/hashtable.h>
39 #include <netlink/route/rtnl.h>
40 #include <netlink/route/route.h>
41 #include <netlink/route/link.h>
42 #include <netlink/route/nexthop.h>
43 
44 /** @cond SKIP */
45 #define ROUTE_ATTR_FAMILY 0x000001
46 #define ROUTE_ATTR_TOS 0x000002
47 #define ROUTE_ATTR_TABLE 0x000004
48 #define ROUTE_ATTR_PROTOCOL 0x000008
49 #define ROUTE_ATTR_SCOPE 0x000010
50 #define ROUTE_ATTR_TYPE 0x000020
51 #define ROUTE_ATTR_FLAGS 0x000040
52 #define ROUTE_ATTR_DST 0x000080
53 #define ROUTE_ATTR_SRC 0x000100
54 #define ROUTE_ATTR_IIF 0x000200
55 #define ROUTE_ATTR_OIF 0x000400
56 #define ROUTE_ATTR_GATEWAY 0x000800
57 #define ROUTE_ATTR_PRIO 0x001000
58 #define ROUTE_ATTR_PREF_SRC 0x002000
59 #define ROUTE_ATTR_METRICS 0x004000
60 #define ROUTE_ATTR_MULTIPATH 0x008000
61 #define ROUTE_ATTR_REALMS 0x010000
62 #define ROUTE_ATTR_CACHEINFO 0x020000
63 /** @endcond */
64 
65 static void route_constructor(struct nl_object *c)
66 {
67  struct rtnl_route *r = (struct rtnl_route *) c;
68 
69  r->rt_family = AF_UNSPEC;
70  r->rt_scope = RT_SCOPE_NOWHERE;
71  r->rt_table = RT_TABLE_MAIN;
72  r->rt_protocol = RTPROT_STATIC;
73  r->rt_type = RTN_UNICAST;
74 
75  nl_init_list_head(&r->rt_nexthops);
76 }
77 
78 static void route_free_data(struct nl_object *c)
79 {
80  struct rtnl_route *r = (struct rtnl_route *) c;
81  struct rtnl_nexthop *nh, *tmp;
82 
83  if (r == NULL)
84  return;
85 
86  nl_addr_put(r->rt_dst);
87  nl_addr_put(r->rt_src);
88  nl_addr_put(r->rt_pref_src);
89 
90  nl_list_for_each_entry_safe(nh, tmp, &r->rt_nexthops, rtnh_list) {
91  rtnl_route_remove_nexthop(r, nh);
92  rtnl_route_nh_free(nh);
93  }
94 }
95 
96 static int route_clone(struct nl_object *_dst, struct nl_object *_src)
97 {
98  struct rtnl_route *dst = (struct rtnl_route *) _dst;
99  struct rtnl_route *src = (struct rtnl_route *) _src;
100  struct rtnl_nexthop *nh, *new;
101 
102  if (src->rt_dst)
103  if (!(dst->rt_dst = nl_addr_clone(src->rt_dst)))
104  return -NLE_NOMEM;
105 
106  if (src->rt_src)
107  if (!(dst->rt_src = nl_addr_clone(src->rt_src)))
108  return -NLE_NOMEM;
109 
110  if (src->rt_pref_src)
111  if (!(dst->rt_pref_src = nl_addr_clone(src->rt_pref_src)))
112  return -NLE_NOMEM;
113 
114  /* Will be inc'ed again while adding the nexthops of the source */
115  dst->rt_nr_nh = 0;
116 
117  nl_init_list_head(&dst->rt_nexthops);
118  nl_list_for_each_entry(nh, &src->rt_nexthops, rtnh_list) {
119  new = rtnl_route_nh_clone(nh);
120  if (!new)
121  return -NLE_NOMEM;
122 
123  rtnl_route_add_nexthop(dst, new);
124  }
125 
126  return 0;
127 }
128 
129 static void route_dump_line(struct nl_object *a, struct nl_dump_params *p)
130 {
131  struct rtnl_route *r = (struct rtnl_route *) a;
132  int cache = 0, flags;
133  char buf[64];
134 
135  if (r->rt_flags & RTM_F_CLONED)
136  cache = 1;
137 
138  nl_dump_line(p, "%s ", nl_af2str(r->rt_family, buf, sizeof(buf)));
139 
140  if (cache)
141  nl_dump(p, "cache ");
142 
143  if (!(r->ce_mask & ROUTE_ATTR_DST) ||
144  nl_addr_get_len(r->rt_dst) == 0)
145  nl_dump(p, "default ");
146  else
147  nl_dump(p, "%s ", nl_addr2str(r->rt_dst, buf, sizeof(buf)));
148 
149  if (r->ce_mask & ROUTE_ATTR_TABLE && !cache)
150  nl_dump(p, "table %s ",
151  rtnl_route_table2str(r->rt_table, buf, sizeof(buf)));
152 
153  if (r->ce_mask & ROUTE_ATTR_TYPE)
154  nl_dump(p, "type %s ",
155  nl_rtntype2str(r->rt_type, buf, sizeof(buf)));
156 
157  if (r->ce_mask & ROUTE_ATTR_TOS && r->rt_tos != 0)
158  nl_dump(p, "tos %#x ", r->rt_tos);
159 
160  if (r->ce_mask & ROUTE_ATTR_MULTIPATH) {
161  struct rtnl_nexthop *nh;
162 
163  nl_list_for_each_entry(nh, &r->rt_nexthops, rtnh_list) {
164  p->dp_ivar = NH_DUMP_FROM_ONELINE;
165  rtnl_route_nh_dump(nh, p);
166  }
167  }
168 
169  flags = r->rt_flags & ~(RTM_F_CLONED);
170  if (r->ce_mask & ROUTE_ATTR_FLAGS && flags) {
171 
172  nl_dump(p, "<");
173 
174 #define PRINT_FLAG(f) if (flags & RTNH_F_##f) { \
175  flags &= ~RTNH_F_##f; nl_dump(p, #f "%s", flags ? "," : ""); }
176  PRINT_FLAG(DEAD);
177  PRINT_FLAG(ONLINK);
178  PRINT_FLAG(PERVASIVE);
179 #undef PRINT_FLAG
180 
181 #define PRINT_FLAG(f) if (flags & RTM_F_##f) { \
182  flags &= ~RTM_F_##f; nl_dump(p, #f "%s", flags ? "," : ""); }
183  PRINT_FLAG(NOTIFY);
184  PRINT_FLAG(EQUALIZE);
185  PRINT_FLAG(PREFIX);
186 #undef PRINT_FLAG
187 
188 #define PRINT_FLAG(f) if (flags & RTCF_##f) { \
189  flags &= ~RTCF_##f; nl_dump(p, #f "%s", flags ? "," : ""); }
190  PRINT_FLAG(NOTIFY);
191  PRINT_FLAG(REDIRECTED);
192  PRINT_FLAG(DOREDIRECT);
193  PRINT_FLAG(DIRECTSRC);
194  PRINT_FLAG(DNAT);
195  PRINT_FLAG(BROADCAST);
196  PRINT_FLAG(MULTICAST);
197  PRINT_FLAG(LOCAL);
198 #undef PRINT_FLAG
199 
200  nl_dump(p, ">");
201  }
202 
203  nl_dump(p, "\n");
204 }
205 
206 static void route_dump_details(struct nl_object *a, struct nl_dump_params *p)
207 {
208  struct rtnl_route *r = (struct rtnl_route *) a;
209  struct nl_cache *link_cache;
210  char buf[128];
211  int i;
212 
213  link_cache = nl_cache_mngt_require_safe("route/link");
214 
215  route_dump_line(a, p);
216  nl_dump_line(p, " ");
217 
218  if (r->ce_mask & ROUTE_ATTR_PREF_SRC)
219  nl_dump(p, "preferred-src %s ",
220  nl_addr2str(r->rt_pref_src, buf, sizeof(buf)));
221 
222  if (r->ce_mask & ROUTE_ATTR_SCOPE && r->rt_scope != RT_SCOPE_NOWHERE)
223  nl_dump(p, "scope %s ",
224  rtnl_scope2str(r->rt_scope, buf, sizeof(buf)));
225 
226  if (r->ce_mask & ROUTE_ATTR_PRIO)
227  nl_dump(p, "priority %#x ", r->rt_prio);
228 
229  if (r->ce_mask & ROUTE_ATTR_PROTOCOL)
230  nl_dump(p, "protocol %s ",
231  rtnl_route_proto2str(r->rt_protocol, buf, sizeof(buf)));
232 
233  if (r->ce_mask & ROUTE_ATTR_IIF) {
234  if (link_cache) {
235  nl_dump(p, "iif %s ",
236  rtnl_link_i2name(link_cache, r->rt_iif,
237  buf, sizeof(buf)));
238  } else
239  nl_dump(p, "iif %d ", r->rt_iif);
240  }
241 
242  if (r->ce_mask & ROUTE_ATTR_SRC)
243  nl_dump(p, "src %s ", nl_addr2str(r->rt_src, buf, sizeof(buf)));
244 
245  nl_dump(p, "\n");
246 
247  if (r->ce_mask & ROUTE_ATTR_MULTIPATH) {
248  struct rtnl_nexthop *nh;
249 
250  nl_list_for_each_entry(nh, &r->rt_nexthops, rtnh_list) {
251  nl_dump_line(p, " ");
252  p->dp_ivar = NH_DUMP_FROM_DETAILS;
253  rtnl_route_nh_dump(nh, p);
254  nl_dump(p, "\n");
255  }
256  }
257 
258  if ((r->ce_mask & ROUTE_ATTR_CACHEINFO) && r->rt_cacheinfo.rtci_error) {
259  nl_dump_line(p, " cacheinfo error %d (%s)\n",
260  r->rt_cacheinfo.rtci_error,
261  strerror(-r->rt_cacheinfo.rtci_error));
262  }
263 
264  if (r->ce_mask & ROUTE_ATTR_METRICS) {
265  nl_dump_line(p, " metrics [");
266  for (i = 0; i < RTAX_MAX; i++)
267  if (r->rt_metrics_mask & (1 << i))
268  nl_dump(p, "%s %u ",
269  rtnl_route_metric2str(i+1,
270  buf, sizeof(buf)),
271  r->rt_metrics[i]);
272  nl_dump(p, "]\n");
273  }
274 
275  if (link_cache)
276  nl_cache_put(link_cache);
277 }
278 
279 static void route_dump_stats(struct nl_object *obj, struct nl_dump_params *p)
280 {
281  struct rtnl_route *route = (struct rtnl_route *) obj;
282 
283  route_dump_details(obj, p);
284 
285  if (route->ce_mask & ROUTE_ATTR_CACHEINFO) {
286  struct rtnl_rtcacheinfo *ci = &route->rt_cacheinfo;
287 
288  nl_dump_line(p, " used %u refcnt %u last-use %us "
289  "expires %us\n",
290  ci->rtci_used, ci->rtci_clntref,
291  ci->rtci_last_use / nl_get_user_hz(),
292  ci->rtci_expires / nl_get_user_hz());
293  }
294 }
295 
296 static void route_keygen(struct nl_object *obj, uint32_t *hashkey,
297  uint32_t table_sz)
298 {
299  struct rtnl_route *route = (struct rtnl_route *) obj;
300  unsigned int rkey_sz;
301  struct nl_addr *addr = NULL;
302  struct route_hash_key {
303  uint8_t rt_family;
304  uint8_t rt_tos;
305  uint32_t rt_table;
306  char rt_addr[0];
307  } __attribute__((packed)) *rkey;
308  char buf[INET6_ADDRSTRLEN+5];
309 
310  if (route->rt_dst)
311  addr = route->rt_dst;
312 
313  rkey_sz = sizeof(*rkey);
314  if (addr)
315  rkey_sz += nl_addr_get_len(addr);
316  rkey = calloc(1, rkey_sz);
317  if (!rkey) {
318  NL_DBG(2, "Warning: calloc failed for %d bytes...\n", rkey_sz);
319  *hashkey = 0;
320  return;
321  }
322  rkey->rt_family = route->rt_family;
323  rkey->rt_tos = route->rt_tos;
324  rkey->rt_table = route->rt_table;
325  if (addr)
326  memcpy(rkey->rt_addr, nl_addr_get_binary_addr(addr),
327  nl_addr_get_len(addr));
328 
329  *hashkey = nl_hash(rkey, rkey_sz, 0) % table_sz;
330 
331  NL_DBG(5, "route %p key (fam %d tos %d table %d addr %s) keysz %d "
332  "hash 0x%x\n", route, rkey->rt_family, rkey->rt_tos,
333  rkey->rt_table, nl_addr2str(addr, buf, sizeof(buf)),
334  rkey_sz, *hashkey);
335 
336  free(rkey);
337 
338  return;
339 }
340 
341 static int route_compare(struct nl_object *_a, struct nl_object *_b,
342  uint32_t attrs, int flags)
343 {
344  struct rtnl_route *a = (struct rtnl_route *) _a;
345  struct rtnl_route *b = (struct rtnl_route *) _b;
346  struct rtnl_nexthop *nh_a, *nh_b;
347  int i, diff = 0, found;
348 
349 #define ROUTE_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, ROUTE_ATTR_##ATTR, a, b, EXPR)
350 
351  diff |= ROUTE_DIFF(FAMILY, a->rt_family != b->rt_family);
352  diff |= ROUTE_DIFF(TOS, a->rt_tos != b->rt_tos);
353  diff |= ROUTE_DIFF(TABLE, a->rt_table != b->rt_table);
354  diff |= ROUTE_DIFF(PROTOCOL, a->rt_protocol != b->rt_protocol);
355  diff |= ROUTE_DIFF(SCOPE, a->rt_scope != b->rt_scope);
356  diff |= ROUTE_DIFF(TYPE, a->rt_type != b->rt_type);
357  diff |= ROUTE_DIFF(PRIO, a->rt_prio != b->rt_prio);
358  diff |= ROUTE_DIFF(DST, nl_addr_cmp(a->rt_dst, b->rt_dst));
359  diff |= ROUTE_DIFF(SRC, nl_addr_cmp(a->rt_src, b->rt_src));
360  diff |= ROUTE_DIFF(IIF, a->rt_iif != b->rt_iif);
361  diff |= ROUTE_DIFF(PREF_SRC, nl_addr_cmp(a->rt_pref_src,
362  b->rt_pref_src));
363 
364  if (flags & LOOSE_COMPARISON) {
365  nl_list_for_each_entry(nh_b, &b->rt_nexthops, rtnh_list) {
366  found = 0;
367  nl_list_for_each_entry(nh_a, &a->rt_nexthops,
368  rtnh_list) {
369  if (!rtnl_route_nh_compare(nh_a, nh_b,
370  nh_b->ce_mask, 1)) {
371  found = 1;
372  break;
373  }
374  }
375 
376  if (!found)
377  goto nh_mismatch;
378  }
379 
380  for (i = 0; i < RTAX_MAX - 1; i++) {
381  if (a->rt_metrics_mask & (1 << i) &&
382  (!(b->rt_metrics_mask & (1 << i)) ||
383  a->rt_metrics[i] != b->rt_metrics[i]))
384  diff |= ROUTE_DIFF(METRICS, 1);
385  }
386 
387  diff |= ROUTE_DIFF(FLAGS,
388  (a->rt_flags ^ b->rt_flags) & b->rt_flag_mask);
389  } else {
390  if (a->rt_nr_nh != b->rt_nr_nh)
391  goto nh_mismatch;
392 
393  /* search for a dup in each nh of a */
394  nl_list_for_each_entry(nh_a, &a->rt_nexthops, rtnh_list) {
395  found = 0;
396  nl_list_for_each_entry(nh_b, &b->rt_nexthops,
397  rtnh_list) {
398  if (!rtnl_route_nh_compare(nh_a, nh_b, ~0, 0)) {
399  found = 1;
400  break;
401  }
402  }
403  if (!found)
404  goto nh_mismatch;
405  }
406 
407  /* search for a dup in each nh of b, covers case where a has
408  * dupes itself */
409  nl_list_for_each_entry(nh_b, &b->rt_nexthops, rtnh_list) {
410  found = 0;
411  nl_list_for_each_entry(nh_a, &a->rt_nexthops,
412  rtnh_list) {
413  if (!rtnl_route_nh_compare(nh_a, nh_b, ~0, 0)) {
414  found = 1;
415  break;
416  }
417  }
418  if (!found)
419  goto nh_mismatch;
420  }
421 
422  for (i = 0; i < RTAX_MAX - 1; i++) {
423  if ((a->rt_metrics_mask & (1 << i)) ^
424  (b->rt_metrics_mask & (1 << i)))
425  diff |= ROUTE_DIFF(METRICS, 1);
426  else
427  diff |= ROUTE_DIFF(METRICS,
428  a->rt_metrics[i] != b->rt_metrics[i]);
429  }
430 
431  diff |= ROUTE_DIFF(FLAGS, a->rt_flags != b->rt_flags);
432  }
433 
434 out:
435  return diff;
436 
437 nh_mismatch:
438  diff |= ROUTE_DIFF(MULTIPATH, 1);
439  goto out;
440 
441 #undef ROUTE_DIFF
442 }
443 
444 static const struct trans_tbl route_attrs[] = {
445  __ADD(ROUTE_ATTR_FAMILY, family)
446  __ADD(ROUTE_ATTR_TOS, tos)
447  __ADD(ROUTE_ATTR_TABLE, table)
448  __ADD(ROUTE_ATTR_PROTOCOL, protocol)
449  __ADD(ROUTE_ATTR_SCOPE, scope)
450  __ADD(ROUTE_ATTR_TYPE, type)
451  __ADD(ROUTE_ATTR_FLAGS, flags)
452  __ADD(ROUTE_ATTR_DST, dst)
453  __ADD(ROUTE_ATTR_SRC, src)
454  __ADD(ROUTE_ATTR_IIF, iif)
455  __ADD(ROUTE_ATTR_OIF, oif)
456  __ADD(ROUTE_ATTR_GATEWAY, gateway)
457  __ADD(ROUTE_ATTR_PRIO, prio)
458  __ADD(ROUTE_ATTR_PREF_SRC, pref_src)
459  __ADD(ROUTE_ATTR_METRICS, metrics)
460  __ADD(ROUTE_ATTR_MULTIPATH, multipath)
461  __ADD(ROUTE_ATTR_REALMS, realms)
462  __ADD(ROUTE_ATTR_CACHEINFO, cacheinfo)
463 };
464 
465 static char *route_attrs2str(int attrs, char *buf, size_t len)
466 {
467  return __flags2str(attrs, buf, len, route_attrs,
468  ARRAY_SIZE(route_attrs));
469 }
470 
471 /**
472  * @name Allocation/Freeing
473  * @{
474  */
475 
476 struct rtnl_route *rtnl_route_alloc(void)
477 {
478  return (struct rtnl_route *) nl_object_alloc(&route_obj_ops);
479 }
480 
481 void rtnl_route_get(struct rtnl_route *route)
482 {
483  nl_object_get((struct nl_object *) route);
484 }
485 
486 void rtnl_route_put(struct rtnl_route *route)
487 {
488  nl_object_put((struct nl_object *) route);
489 }
490 
491 /** @} */
492 
493 /**
494  * @name Attributes
495  * @{
496  */
497 
498 void rtnl_route_set_table(struct rtnl_route *route, uint32_t table)
499 {
500  route->rt_table = table;
501  route->ce_mask |= ROUTE_ATTR_TABLE;
502 }
503 
504 uint32_t rtnl_route_get_table(struct rtnl_route *route)
505 {
506  return route->rt_table;
507 }
508 
509 void rtnl_route_set_scope(struct rtnl_route *route, uint8_t scope)
510 {
511  route->rt_scope = scope;
512  route->ce_mask |= ROUTE_ATTR_SCOPE;
513 }
514 
515 uint8_t rtnl_route_get_scope(struct rtnl_route *route)
516 {
517  return route->rt_scope;
518 }
519 
520 void rtnl_route_set_tos(struct rtnl_route *route, uint8_t tos)
521 {
522  route->rt_tos = tos;
523  route->ce_mask |= ROUTE_ATTR_TOS;
524 }
525 
526 uint8_t rtnl_route_get_tos(struct rtnl_route *route)
527 {
528  return route->rt_tos;
529 }
530 
531 void rtnl_route_set_protocol(struct rtnl_route *route, uint8_t protocol)
532 {
533  route->rt_protocol = protocol;
534  route->ce_mask |= ROUTE_ATTR_PROTOCOL;
535 }
536 
537 uint8_t rtnl_route_get_protocol(struct rtnl_route *route)
538 {
539  return route->rt_protocol;
540 }
541 
542 void rtnl_route_set_priority(struct rtnl_route *route, uint32_t prio)
543 {
544  route->rt_prio = prio;
545  route->ce_mask |= ROUTE_ATTR_PRIO;
546 }
547 
548 uint32_t rtnl_route_get_priority(struct rtnl_route *route)
549 {
550  return route->rt_prio;
551 }
552 
553 int rtnl_route_set_family(struct rtnl_route *route, uint8_t family)
554 {
555  if (family != AF_INET && family != AF_INET6 && family != AF_DECnet)
556  return -NLE_AF_NOSUPPORT;
557 
558  route->rt_family = family;
559  route->ce_mask |= ROUTE_ATTR_FAMILY;
560 
561  return 0;
562 }
563 
564 uint8_t rtnl_route_get_family(struct rtnl_route *route)
565 {
566  return route->rt_family;
567 }
568 
569 int rtnl_route_set_dst(struct rtnl_route *route, struct nl_addr *addr)
570 {
571  if (route->ce_mask & ROUTE_ATTR_FAMILY) {
572  if (addr->a_family != route->rt_family)
573  return -NLE_AF_MISMATCH;
574  } else
575  route->rt_family = addr->a_family;
576 
577  if (route->rt_dst)
578  nl_addr_put(route->rt_dst);
579 
580  nl_addr_get(addr);
581  route->rt_dst = addr;
582 
583  route->ce_mask |= (ROUTE_ATTR_DST | ROUTE_ATTR_FAMILY);
584 
585  return 0;
586 }
587 
588 struct nl_addr *rtnl_route_get_dst(struct rtnl_route *route)
589 {
590  return route->rt_dst;
591 }
592 
593 int rtnl_route_set_src(struct rtnl_route *route, struct nl_addr *addr)
594 {
595  if (addr->a_family == AF_INET)
596  return -NLE_SRCRT_NOSUPPORT;
597 
598  if (route->ce_mask & ROUTE_ATTR_FAMILY) {
599  if (addr->a_family != route->rt_family)
600  return -NLE_AF_MISMATCH;
601  } else
602  route->rt_family = addr->a_family;
603 
604  if (route->rt_src)
605  nl_addr_put(route->rt_src);
606 
607  nl_addr_get(addr);
608  route->rt_src = addr;
609  route->ce_mask |= (ROUTE_ATTR_SRC | ROUTE_ATTR_FAMILY);
610 
611  return 0;
612 }
613 
614 struct nl_addr *rtnl_route_get_src(struct rtnl_route *route)
615 {
616  return route->rt_src;
617 }
618 
619 int rtnl_route_set_type(struct rtnl_route *route, uint8_t type)
620 {
621  if (type > RTN_MAX)
622  return -NLE_RANGE;
623 
624  route->rt_type = type;
625  route->ce_mask |= ROUTE_ATTR_TYPE;
626 
627  return 0;
628 }
629 
630 uint8_t rtnl_route_get_type(struct rtnl_route *route)
631 {
632  return route->rt_type;
633 }
634 
635 void rtnl_route_set_flags(struct rtnl_route *route, uint32_t flags)
636 {
637  route->rt_flag_mask |= flags;
638  route->rt_flags |= flags;
639  route->ce_mask |= ROUTE_ATTR_FLAGS;
640 }
641 
642 void rtnl_route_unset_flags(struct rtnl_route *route, uint32_t flags)
643 {
644  route->rt_flag_mask |= flags;
645  route->rt_flags &= ~flags;
646  route->ce_mask |= ROUTE_ATTR_FLAGS;
647 }
648 
649 uint32_t rtnl_route_get_flags(struct rtnl_route *route)
650 {
651  return route->rt_flags;
652 }
653 
654 int rtnl_route_set_metric(struct rtnl_route *route, int metric, uint32_t value)
655 {
656  if (metric > RTAX_MAX || metric < 1)
657  return -NLE_RANGE;
658 
659  route->rt_metrics[metric - 1] = value;
660 
661  if (!(route->rt_metrics_mask & (1 << (metric - 1)))) {
662  route->rt_nmetrics++;
663  route->rt_metrics_mask |= (1 << (metric - 1));
664  }
665 
666  route->ce_mask |= ROUTE_ATTR_METRICS;
667 
668  return 0;
669 }
670 
671 int rtnl_route_unset_metric(struct rtnl_route *route, int metric)
672 {
673  if (metric > RTAX_MAX || metric < 1)
674  return -NLE_RANGE;
675 
676  if (route->rt_metrics_mask & (1 << (metric - 1))) {
677  route->rt_nmetrics--;
678  route->rt_metrics_mask &= ~(1 << (metric - 1));
679  }
680 
681  return 0;
682 }
683 
684 int rtnl_route_get_metric(struct rtnl_route *route, int metric, uint32_t *value)
685 {
686  if (metric > RTAX_MAX || metric < 1)
687  return -NLE_RANGE;
688 
689  if (!(route->rt_metrics_mask & (1 << (metric - 1))))
690  return -NLE_OBJ_NOTFOUND;
691 
692  if (value)
693  *value = route->rt_metrics[metric - 1];
694 
695  return 0;
696 }
697 
698 int rtnl_route_set_pref_src(struct rtnl_route *route, struct nl_addr *addr)
699 {
700  if (route->ce_mask & ROUTE_ATTR_FAMILY) {
701  if (addr->a_family != route->rt_family)
702  return -NLE_AF_MISMATCH;
703  } else
704  route->rt_family = addr->a_family;
705 
706  if (route->rt_pref_src)
707  nl_addr_put(route->rt_pref_src);
708 
709  nl_addr_get(addr);
710  route->rt_pref_src = addr;
711  route->ce_mask |= (ROUTE_ATTR_PREF_SRC | ROUTE_ATTR_FAMILY);
712 
713  return 0;
714 }
715 
716 struct nl_addr *rtnl_route_get_pref_src(struct rtnl_route *route)
717 {
718  return route->rt_pref_src;
719 }
720 
721 void rtnl_route_set_iif(struct rtnl_route *route, int ifindex)
722 {
723  route->rt_iif = ifindex;
724  route->ce_mask |= ROUTE_ATTR_IIF;
725 }
726 
727 int rtnl_route_get_iif(struct rtnl_route *route)
728 {
729  return route->rt_iif;
730 }
731 
732 void rtnl_route_add_nexthop(struct rtnl_route *route, struct rtnl_nexthop *nh)
733 {
734  nl_list_add_tail(&nh->rtnh_list, &route->rt_nexthops);
735  route->rt_nr_nh++;
736  route->ce_mask |= ROUTE_ATTR_MULTIPATH;
737 }
738 
739 void rtnl_route_remove_nexthop(struct rtnl_route *route, struct rtnl_nexthop *nh)
740 {
741  if (route->ce_mask & ROUTE_ATTR_MULTIPATH) {
742  route->rt_nr_nh--;
743  nl_list_del(&nh->rtnh_list);
744  }
745 }
746 
747 struct nl_list_head *rtnl_route_get_nexthops(struct rtnl_route *route)
748 {
749  if (route->ce_mask & ROUTE_ATTR_MULTIPATH)
750  return &route->rt_nexthops;
751 
752  return NULL;
753 }
754 
755 int rtnl_route_get_nnexthops(struct rtnl_route *route)
756 {
757  if (route->ce_mask & ROUTE_ATTR_MULTIPATH)
758  return route->rt_nr_nh;
759 
760  return 0;
761 }
762 
763 void rtnl_route_foreach_nexthop(struct rtnl_route *r,
764  void (*cb)(struct rtnl_nexthop *, void *),
765  void *arg)
766 {
767  struct rtnl_nexthop *nh;
768 
769  if (r->ce_mask & ROUTE_ATTR_MULTIPATH) {
770  nl_list_for_each_entry(nh, &r->rt_nexthops, rtnh_list) {
771  cb(nh, arg);
772  }
773  }
774 }
775 
776 struct rtnl_nexthop *rtnl_route_nexthop_n(struct rtnl_route *r, int n)
777 {
778  struct rtnl_nexthop *nh;
779  uint32_t i;
780 
781  if (r->ce_mask & ROUTE_ATTR_MULTIPATH && r->rt_nr_nh > n) {
782  i = 0;
783  nl_list_for_each_entry(nh, &r->rt_nexthops, rtnh_list) {
784  if (i == n) return nh;
785  i++;
786  }
787  }
788  return NULL;
789 }
790 
791 /** @} */
792 
793 /**
794  * @name Utilities
795  * @{
796  */
797 
798 /**
799  * Guess scope of a route object.
800  * @arg route Route object.
801  *
802  * Guesses the scope of a route object, based on the following rules:
803  * @code
804  * 1) Local route -> local scope
805  * 2) At least one nexthop not directly connected -> universe scope
806  * 3) All others -> link scope
807  * @endcode
808  *
809  * @return Scope value.
810  */
811 int rtnl_route_guess_scope(struct rtnl_route *route)
812 {
813  if (route->rt_type == RTN_LOCAL)
814  return RT_SCOPE_HOST;
815 
816  if (!nl_list_empty(&route->rt_nexthops)) {
817  struct rtnl_nexthop *nh;
818 
819  /*
820  * Use scope uiniverse if there is at least one nexthop which
821  * is not directly connected
822  */
823  nl_list_for_each_entry(nh, &route->rt_nexthops, rtnh_list) {
824  if (nh->rtnh_gateway)
825  return RT_SCOPE_UNIVERSE;
826  }
827  }
828 
829  return RT_SCOPE_LINK;
830 }
831 
832 /** @} */
833 
834 static struct nla_policy route_policy[RTA_MAX+1] = {
835  [RTA_IIF] = { .type = NLA_U32 },
836  [RTA_OIF] = { .type = NLA_U32 },
837  [RTA_PRIORITY] = { .type = NLA_U32 },
838  [RTA_FLOW] = { .type = NLA_U32 },
839  [RTA_CACHEINFO] = { .minlen = sizeof(struct rta_cacheinfo) },
840  [RTA_METRICS] = { .type = NLA_NESTED },
841  [RTA_MULTIPATH] = { .type = NLA_NESTED },
842 };
843 
844 static int parse_multipath(struct rtnl_route *route, struct nlattr *attr)
845 {
846  struct rtnl_nexthop *nh = NULL;
847  struct rtnexthop *rtnh = nla_data(attr);
848  size_t tlen = nla_len(attr);
849  int err;
850 
851  while (tlen >= sizeof(*rtnh) && tlen >= rtnh->rtnh_len) {
852  nh = rtnl_route_nh_alloc();
853  if (!nh)
854  return -NLE_NOMEM;
855 
856  rtnl_route_nh_set_weight(nh, rtnh->rtnh_hops);
857  rtnl_route_nh_set_ifindex(nh, rtnh->rtnh_ifindex);
858  rtnl_route_nh_set_flags(nh, rtnh->rtnh_flags);
859 
860  if (rtnh->rtnh_len > sizeof(*rtnh)) {
861  struct nlattr *ntb[RTA_MAX + 1];
862 
863  err = nla_parse(ntb, RTA_MAX, (struct nlattr *)
864  RTNH_DATA(rtnh),
865  rtnh->rtnh_len - sizeof(*rtnh),
866  route_policy);
867  if (err < 0)
868  goto errout;
869 
870  if (ntb[RTA_GATEWAY]) {
871  struct nl_addr *addr;
872 
873  addr = nl_addr_alloc_attr(ntb[RTA_GATEWAY],
874  route->rt_family);
875  if (!addr) {
876  err = -NLE_NOMEM;
877  goto errout;
878  }
879 
880  rtnl_route_nh_set_gateway(nh, addr);
881  nl_addr_put(addr);
882  }
883 
884  if (ntb[RTA_FLOW]) {
885  uint32_t realms;
886 
887  realms = nla_get_u32(ntb[RTA_FLOW]);
888  rtnl_route_nh_set_realms(nh, realms);
889  }
890  }
891 
892  rtnl_route_add_nexthop(route, nh);
893  tlen -= RTNH_ALIGN(rtnh->rtnh_len);
894  rtnh = RTNH_NEXT(rtnh);
895  }
896 
897  err = 0;
898 errout:
899  if (err && nh)
900  rtnl_route_nh_free(nh);
901 
902  return err;
903 }
904 
905 int rtnl_route_parse(struct nlmsghdr *nlh, struct rtnl_route **result)
906 {
907  struct rtmsg *rtm;
908  struct rtnl_route *route;
909  struct nlattr *tb[RTA_MAX + 1];
910  struct nl_addr *src = NULL, *dst = NULL, *addr;
911  struct rtnl_nexthop *old_nh = NULL;
912  int err, family;
913 
914  route = rtnl_route_alloc();
915  if (!route) {
916  err = -NLE_NOMEM;
917  goto errout;
918  }
919 
920  route->ce_msgtype = nlh->nlmsg_type;
921 
922  err = nlmsg_parse(nlh, sizeof(struct rtmsg), tb, RTA_MAX, route_policy);
923  if (err < 0)
924  goto errout;
925 
926  rtm = nlmsg_data(nlh);
927  route->rt_family = family = rtm->rtm_family;
928  route->rt_tos = rtm->rtm_tos;
929  route->rt_table = rtm->rtm_table;
930  route->rt_type = rtm->rtm_type;
931  route->rt_scope = rtm->rtm_scope;
932  route->rt_protocol = rtm->rtm_protocol;
933  route->rt_flags = rtm->rtm_flags;
934 
935  route->ce_mask |= ROUTE_ATTR_FAMILY | ROUTE_ATTR_TOS |
936  ROUTE_ATTR_TABLE | ROUTE_ATTR_TYPE |
937  ROUTE_ATTR_SCOPE | ROUTE_ATTR_PROTOCOL |
938  ROUTE_ATTR_FLAGS;
939 
940  if (tb[RTA_DST]) {
941  if (!(dst = nl_addr_alloc_attr(tb[RTA_DST], family)))
942  goto errout_nomem;
943  } else {
944  if (!(dst = nl_addr_alloc(0)))
945  goto errout_nomem;
946  nl_addr_set_family(dst, rtm->rtm_family);
947  }
948 
949  nl_addr_set_prefixlen(dst, rtm->rtm_dst_len);
950  err = rtnl_route_set_dst(route, dst);
951  if (err < 0)
952  goto errout;
953 
954  nl_addr_put(dst);
955 
956  if (tb[RTA_SRC]) {
957  if (!(src = nl_addr_alloc_attr(tb[RTA_SRC], family)))
958  goto errout_nomem;
959  } else if (rtm->rtm_src_len)
960  if (!(src = nl_addr_alloc(0)))
961  goto errout_nomem;
962 
963  if (src) {
964  nl_addr_set_prefixlen(src, rtm->rtm_src_len);
965  rtnl_route_set_src(route, src);
966  nl_addr_put(src);
967  }
968 
969  if (tb[RTA_TABLE])
970  rtnl_route_set_table(route, nla_get_u32(tb[RTA_TABLE]));
971 
972  if (tb[RTA_IIF])
973  rtnl_route_set_iif(route, nla_get_u32(tb[RTA_IIF]));
974 
975  if (tb[RTA_PRIORITY])
976  rtnl_route_set_priority(route, nla_get_u32(tb[RTA_PRIORITY]));
977 
978  if (tb[RTA_PREFSRC]) {
979  if (!(addr = nl_addr_alloc_attr(tb[RTA_PREFSRC], family)))
980  goto errout_nomem;
981  rtnl_route_set_pref_src(route, addr);
982  nl_addr_put(addr);
983  }
984 
985  if (tb[RTA_METRICS]) {
986  struct nlattr *mtb[RTAX_MAX + 1];
987  int i;
988 
989  err = nla_parse_nested(mtb, RTAX_MAX, tb[RTA_METRICS], NULL);
990  if (err < 0)
991  goto errout;
992 
993  for (i = 1; i <= RTAX_MAX; i++) {
994  if (mtb[i] && nla_len(mtb[i]) >= sizeof(uint32_t)) {
995  uint32_t m = nla_get_u32(mtb[i]);
996  if (rtnl_route_set_metric(route, i, m) < 0)
997  goto errout;
998  }
999  }
1000  }
1001 
1002  if (tb[RTA_MULTIPATH])
1003  if ((err = parse_multipath(route, tb[RTA_MULTIPATH])) < 0)
1004  goto errout;
1005 
1006  if (tb[RTA_CACHEINFO]) {
1007  nla_memcpy(&route->rt_cacheinfo, tb[RTA_CACHEINFO],
1008  sizeof(route->rt_cacheinfo));
1009  route->ce_mask |= ROUTE_ATTR_CACHEINFO;
1010  }
1011 
1012  if (tb[RTA_OIF]) {
1013  if (!old_nh && !(old_nh = rtnl_route_nh_alloc()))
1014  goto errout;
1015 
1016  rtnl_route_nh_set_ifindex(old_nh, nla_get_u32(tb[RTA_OIF]));
1017  }
1018 
1019  if (tb[RTA_GATEWAY]) {
1020  if (!old_nh && !(old_nh = rtnl_route_nh_alloc()))
1021  goto errout;
1022 
1023  if (!(addr = nl_addr_alloc_attr(tb[RTA_GATEWAY], family)))
1024  goto errout_nomem;
1025 
1026  rtnl_route_nh_set_gateway(old_nh, addr);
1027  nl_addr_put(addr);
1028  }
1029 
1030  if (tb[RTA_FLOW]) {
1031  if (!old_nh && !(old_nh = rtnl_route_nh_alloc()))
1032  goto errout;
1033 
1034  rtnl_route_nh_set_realms(old_nh, nla_get_u32(tb[RTA_FLOW]));
1035  }
1036 
1037  if (old_nh) {
1038  rtnl_route_nh_set_flags(old_nh, rtm->rtm_flags & 0xff);
1039  if (route->rt_nr_nh == 0) {
1040  /* If no nexthops have been provided via RTA_MULTIPATH
1041  * we add it as regular nexthop to maintain backwards
1042  * compatibility */
1043  rtnl_route_add_nexthop(route, old_nh);
1044  } else {
1045  /* Kernel supports new style nexthop configuration,
1046  * verify that it is a duplicate and discard nexthop. */
1047  struct rtnl_nexthop *first;
1048 
1049  first = nl_list_first_entry(&route->rt_nexthops,
1050  struct rtnl_nexthop,
1051  rtnh_list);
1052  if (!first)
1053  BUG();
1054 
1055  if (rtnl_route_nh_compare(old_nh, first,
1056  old_nh->ce_mask, 0)) {
1057  err = -NLE_INVAL;
1058  goto errout;
1059  }
1060 
1061  rtnl_route_nh_free(old_nh);
1062  }
1063  }
1064 
1065  *result = route;
1066  return 0;
1067 
1068 errout:
1069  rtnl_route_put(route);
1070  return err;
1071 
1072 errout_nomem:
1073  err = -NLE_NOMEM;
1074  goto errout;
1075 }
1076 
1077 int rtnl_route_build_msg(struct nl_msg *msg, struct rtnl_route *route)
1078 {
1079  int i;
1080  struct nlattr *metrics;
1081  struct rtmsg rtmsg = {
1082  .rtm_family = route->rt_family,
1083  .rtm_tos = route->rt_tos,
1084  .rtm_table = route->rt_table,
1085  .rtm_protocol = route->rt_protocol,
1086  .rtm_scope = route->rt_scope,
1087  .rtm_type = route->rt_type,
1088  .rtm_flags = route->rt_flags,
1089  };
1090 
1091  if (route->rt_dst == NULL)
1092  return -NLE_MISSING_ATTR;
1093 
1094  rtmsg.rtm_dst_len = nl_addr_get_prefixlen(route->rt_dst);
1095  if (route->rt_src)
1096  rtmsg.rtm_src_len = nl_addr_get_prefixlen(route->rt_src);
1097 
1098  if (rtmsg.rtm_scope == RT_SCOPE_NOWHERE)
1099  rtmsg.rtm_scope = rtnl_route_guess_scope(route);
1100 
1101  if (rtnl_route_get_nnexthops(route) == 1) {
1102  struct rtnl_nexthop *nh;
1103  nh = rtnl_route_nexthop_n(route, 0);
1104  rtmsg.rtm_flags |= nh->rtnh_flags;
1105  }
1106 
1107  if (nlmsg_append(msg, &rtmsg, sizeof(rtmsg), NLMSG_ALIGNTO) < 0)
1108  goto nla_put_failure;
1109 
1110  /* Additional table attribute replacing the 8bit in the header, was
1111  * required to allow more than 256 tables. */
1112  NLA_PUT_U32(msg, RTA_TABLE, route->rt_table);
1113 
1114  if (nl_addr_get_len(route->rt_dst))
1115  NLA_PUT_ADDR(msg, RTA_DST, route->rt_dst);
1116  NLA_PUT_U32(msg, RTA_PRIORITY, route->rt_prio);
1117 
1118  if (route->ce_mask & ROUTE_ATTR_SRC)
1119  NLA_PUT_ADDR(msg, RTA_SRC, route->rt_src);
1120 
1121  if (route->ce_mask & ROUTE_ATTR_PREF_SRC)
1122  NLA_PUT_ADDR(msg, RTA_PREFSRC, route->rt_pref_src);
1123 
1124  if (route->ce_mask & ROUTE_ATTR_IIF)
1125  NLA_PUT_U32(msg, RTA_IIF, route->rt_iif);
1126 
1127  if (route->rt_nmetrics > 0) {
1128  uint32_t val;
1129 
1130  metrics = nla_nest_start(msg, RTA_METRICS);
1131  if (metrics == NULL)
1132  goto nla_put_failure;
1133 
1134  for (i = 1; i <= RTAX_MAX; i++) {
1135  if (!rtnl_route_get_metric(route, i, &val))
1136  NLA_PUT_U32(msg, i, val);
1137  }
1138 
1139  nla_nest_end(msg, metrics);
1140  }
1141 
1142  if (rtnl_route_get_nnexthops(route) == 1) {
1143  struct rtnl_nexthop *nh;
1144 
1145  nh = rtnl_route_nexthop_n(route, 0);
1146  if (nh->rtnh_gateway)
1147  NLA_PUT_ADDR(msg, RTA_GATEWAY, nh->rtnh_gateway);
1148  if (nh->rtnh_ifindex)
1149  NLA_PUT_U32(msg, RTA_OIF, nh->rtnh_ifindex);
1150  if (nh->rtnh_realms)
1151  NLA_PUT_U32(msg, RTA_FLOW, nh->rtnh_realms);
1152  } else if (rtnl_route_get_nnexthops(route) > 1) {
1153  struct nlattr *multipath;
1154  struct rtnl_nexthop *nh;
1155 
1156  if (!(multipath = nla_nest_start(msg, RTA_MULTIPATH)))
1157  goto nla_put_failure;
1158 
1159  nl_list_for_each_entry(nh, &route->rt_nexthops, rtnh_list) {
1160  struct rtnexthop *rtnh;
1161 
1162  rtnh = nlmsg_reserve(msg, sizeof(*rtnh), NLMSG_ALIGNTO);
1163  if (!rtnh)
1164  goto nla_put_failure;
1165 
1166  rtnh->rtnh_flags = nh->rtnh_flags;
1167  rtnh->rtnh_hops = nh->rtnh_weight;
1168  rtnh->rtnh_ifindex = nh->rtnh_ifindex;
1169 
1170  if (nh->rtnh_gateway)
1171  NLA_PUT_ADDR(msg, RTA_GATEWAY,
1172  nh->rtnh_gateway);
1173 
1174  if (nh->rtnh_realms)
1175  NLA_PUT_U32(msg, RTA_FLOW, nh->rtnh_realms);
1176 
1177  rtnh->rtnh_len = nlmsg_tail(msg->nm_nlh) -
1178  (void *) rtnh;
1179  }
1180 
1181  nla_nest_end(msg, multipath);
1182  }
1183 
1184  return 0;
1185 
1186 nla_put_failure:
1187  return -NLE_MSGSIZE;
1188 }
1189 
1190 /** @cond SKIP */
1191 struct nl_object_ops route_obj_ops = {
1192  .oo_name = "route/route",
1193  .oo_size = sizeof(struct rtnl_route),
1194  .oo_constructor = route_constructor,
1195  .oo_free_data = route_free_data,
1196  .oo_clone = route_clone,
1197  .oo_dump = {
1198  [NL_DUMP_LINE] = route_dump_line,
1199  [NL_DUMP_DETAILS] = route_dump_details,
1200  [NL_DUMP_STATS] = route_dump_stats,
1201  },
1202  .oo_compare = route_compare,
1203  .oo_keygen = route_keygen,
1204  .oo_attrs2str = route_attrs2str,
1205  .oo_id_attrs = (ROUTE_ATTR_FAMILY | ROUTE_ATTR_TOS |
1206  ROUTE_ATTR_TABLE | ROUTE_ATTR_DST),
1207 };
1208 /** @endcond */
1209 
1210 /** @} */