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