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