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