libnl
3.2.3
|
00001 /* 00002 * lib/route/route_obj.c Route Object 00003 * 00004 * This library is free software; you can redistribute it and/or 00005 * modify it under the terms of the GNU Lesser General Public 00006 * License as published by the Free Software Foundation version 2.1 00007 * of the License. 00008 * 00009 * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch> 00010 */ 00011 00012 /** 00013 * @ingroup route 00014 * @defgroup route_obj Route Object 00015 * 00016 * @par Attributes 00017 * @code 00018 * Name Default 00019 * ------------------------------------------------------------- 00020 * routing table RT_TABLE_MAIN 00021 * scope RT_SCOPE_NOWHERE 00022 * tos 0 00023 * protocol RTPROT_STATIC 00024 * prio 0 00025 * family AF_UNSPEC 00026 * type RTN_UNICAST 00027 * iif NULL 00028 * @endcode 00029 * 00030 * @{ 00031 */ 00032 00033 #include <netlink-local.h> 00034 #include <netlink/netlink.h> 00035 #include <netlink/cache.h> 00036 #include <netlink/utils.h> 00037 #include <netlink/data.h> 00038 #include <netlink/route/rtnl.h> 00039 #include <netlink/route/route.h> 00040 #include <netlink/route/link.h> 00041 #include <netlink/route/nexthop.h> 00042 00043 /** @cond SKIP */ 00044 #define ROUTE_ATTR_FAMILY 0x000001 00045 #define ROUTE_ATTR_TOS 0x000002 00046 #define ROUTE_ATTR_TABLE 0x000004 00047 #define ROUTE_ATTR_PROTOCOL 0x000008 00048 #define ROUTE_ATTR_SCOPE 0x000010 00049 #define ROUTE_ATTR_TYPE 0x000020 00050 #define ROUTE_ATTR_FLAGS 0x000040 00051 #define ROUTE_ATTR_DST 0x000080 00052 #define ROUTE_ATTR_SRC 0x000100 00053 #define ROUTE_ATTR_IIF 0x000200 00054 #define ROUTE_ATTR_OIF 0x000400 00055 #define ROUTE_ATTR_GATEWAY 0x000800 00056 #define ROUTE_ATTR_PRIO 0x001000 00057 #define ROUTE_ATTR_PREF_SRC 0x002000 00058 #define ROUTE_ATTR_METRICS 0x004000 00059 #define ROUTE_ATTR_MULTIPATH 0x008000 00060 #define ROUTE_ATTR_REALMS 0x010000 00061 #define ROUTE_ATTR_CACHEINFO 0x020000 00062 /** @endcond */ 00063 00064 static void route_constructor(struct nl_object *c) 00065 { 00066 struct rtnl_route *r = (struct rtnl_route *) c; 00067 00068 r->rt_family = AF_UNSPEC; 00069 r->rt_scope = RT_SCOPE_NOWHERE; 00070 r->rt_table = RT_TABLE_MAIN; 00071 r->rt_protocol = RTPROT_STATIC; 00072 r->rt_type = RTN_UNICAST; 00073 00074 nl_init_list_head(&r->rt_nexthops); 00075 } 00076 00077 static void route_free_data(struct nl_object *c) 00078 { 00079 struct rtnl_route *r = (struct rtnl_route *) c; 00080 struct rtnl_nexthop *nh, *tmp; 00081 00082 if (r == NULL) 00083 return; 00084 00085 nl_addr_put(r->rt_dst); 00086 nl_addr_put(r->rt_src); 00087 nl_addr_put(r->rt_pref_src); 00088 00089 nl_list_for_each_entry_safe(nh, tmp, &r->rt_nexthops, rtnh_list) { 00090 rtnl_route_remove_nexthop(r, nh); 00091 rtnl_route_nh_free(nh); 00092 } 00093 } 00094 00095 static int route_clone(struct nl_object *_dst, struct nl_object *_src) 00096 { 00097 struct rtnl_route *dst = (struct rtnl_route *) _dst; 00098 struct rtnl_route *src = (struct rtnl_route *) _src; 00099 struct rtnl_nexthop *nh, *new; 00100 00101 if (src->rt_dst) 00102 if (!(dst->rt_dst = nl_addr_clone(src->rt_dst))) 00103 return -NLE_NOMEM; 00104 00105 if (src->rt_src) 00106 if (!(dst->rt_src = nl_addr_clone(src->rt_src))) 00107 return -NLE_NOMEM; 00108 00109 if (src->rt_pref_src) 00110 if (!(dst->rt_pref_src = nl_addr_clone(src->rt_pref_src))) 00111 return -NLE_NOMEM; 00112 00113 nl_init_list_head(&dst->rt_nexthops); 00114 nl_list_for_each_entry(nh, &src->rt_nexthops, rtnh_list) { 00115 new = rtnl_route_nh_clone(nh); 00116 if (!new) 00117 return -NLE_NOMEM; 00118 00119 rtnl_route_add_nexthop(dst, new); 00120 } 00121 00122 return 0; 00123 } 00124 00125 static void route_dump_line(struct nl_object *a, struct nl_dump_params *p) 00126 { 00127 struct rtnl_route *r = (struct rtnl_route *) a; 00128 int cache = 0, flags; 00129 char buf[64]; 00130 00131 if (r->rt_flags & RTM_F_CLONED) 00132 cache = 1; 00133 00134 nl_dump_line(p, "%s ", nl_af2str(r->rt_family, buf, sizeof(buf))); 00135 00136 if (cache) 00137 nl_dump(p, "cache "); 00138 00139 if (!(r->ce_mask & ROUTE_ATTR_DST) || 00140 nl_addr_get_len(r->rt_dst) == 0) 00141 nl_dump(p, "default "); 00142 else 00143 nl_dump(p, "%s ", nl_addr2str(r->rt_dst, buf, sizeof(buf))); 00144 00145 if (r->ce_mask & ROUTE_ATTR_TABLE && !cache) 00146 nl_dump(p, "table %s ", 00147 rtnl_route_table2str(r->rt_table, buf, sizeof(buf))); 00148 00149 if (r->ce_mask & ROUTE_ATTR_TYPE) 00150 nl_dump(p, "type %s ", 00151 nl_rtntype2str(r->rt_type, buf, sizeof(buf))); 00152 00153 if (r->ce_mask & ROUTE_ATTR_TOS && r->rt_tos != 0) 00154 nl_dump(p, "tos %#x ", r->rt_tos); 00155 00156 if (r->ce_mask & ROUTE_ATTR_MULTIPATH) { 00157 struct rtnl_nexthop *nh; 00158 00159 nl_list_for_each_entry(nh, &r->rt_nexthops, rtnh_list) { 00160 p->dp_ivar = NH_DUMP_FROM_ONELINE; 00161 rtnl_route_nh_dump(nh, p); 00162 } 00163 } 00164 00165 flags = r->rt_flags & ~(RTM_F_CLONED); 00166 if (r->ce_mask & ROUTE_ATTR_FLAGS && flags) { 00167 00168 nl_dump(p, "<"); 00169 00170 #define PRINT_FLAG(f) if (flags & RTNH_F_##f) { \ 00171 flags &= ~RTNH_F_##f; nl_dump(p, #f "%s", flags ? "," : ""); } 00172 PRINT_FLAG(DEAD); 00173 PRINT_FLAG(ONLINK); 00174 PRINT_FLAG(PERVASIVE); 00175 #undef PRINT_FLAG 00176 00177 #define PRINT_FLAG(f) if (flags & RTM_F_##f) { \ 00178 flags &= ~RTM_F_##f; nl_dump(p, #f "%s", flags ? "," : ""); } 00179 PRINT_FLAG(NOTIFY); 00180 PRINT_FLAG(EQUALIZE); 00181 PRINT_FLAG(PREFIX); 00182 #undef PRINT_FLAG 00183 00184 #define PRINT_FLAG(f) if (flags & RTCF_##f) { \ 00185 flags &= ~RTCF_##f; nl_dump(p, #f "%s", flags ? "," : ""); } 00186 PRINT_FLAG(NOTIFY); 00187 PRINT_FLAG(REDIRECTED); 00188 PRINT_FLAG(DOREDIRECT); 00189 PRINT_FLAG(DIRECTSRC); 00190 PRINT_FLAG(DNAT); 00191 PRINT_FLAG(BROADCAST); 00192 PRINT_FLAG(MULTICAST); 00193 PRINT_FLAG(LOCAL); 00194 #undef PRINT_FLAG 00195 00196 nl_dump(p, ">"); 00197 } 00198 00199 nl_dump(p, "\n"); 00200 } 00201 00202 static void route_dump_details(struct nl_object *a, struct nl_dump_params *p) 00203 { 00204 struct rtnl_route *r = (struct rtnl_route *) a; 00205 struct nl_cache *link_cache; 00206 char buf[128]; 00207 int i; 00208 00209 link_cache = nl_cache_mngt_require("route/link"); 00210 00211 route_dump_line(a, p); 00212 nl_dump_line(p, " "); 00213 00214 if (r->ce_mask & ROUTE_ATTR_PREF_SRC) 00215 nl_dump(p, "preferred-src %s ", 00216 nl_addr2str(r->rt_pref_src, buf, sizeof(buf))); 00217 00218 if (r->ce_mask & ROUTE_ATTR_SCOPE && r->rt_scope != RT_SCOPE_NOWHERE) 00219 nl_dump(p, "scope %s ", 00220 rtnl_scope2str(r->rt_scope, buf, sizeof(buf))); 00221 00222 if (r->ce_mask & ROUTE_ATTR_PRIO) 00223 nl_dump(p, "priority %#x ", r->rt_prio); 00224 00225 if (r->ce_mask & ROUTE_ATTR_PROTOCOL) 00226 nl_dump(p, "protocol %s ", 00227 rtnl_route_proto2str(r->rt_protocol, buf, sizeof(buf))); 00228 00229 if (r->ce_mask & ROUTE_ATTR_IIF) { 00230 if (link_cache) { 00231 nl_dump(p, "iif %s ", 00232 rtnl_link_i2name(link_cache, r->rt_iif, 00233 buf, sizeof(buf))); 00234 } else 00235 nl_dump(p, "iif %d ", r->rt_iif); 00236 } 00237 00238 if (r->ce_mask & ROUTE_ATTR_SRC) 00239 nl_dump(p, "src %s ", nl_addr2str(r->rt_src, buf, sizeof(buf))); 00240 00241 nl_dump(p, "\n"); 00242 00243 if (r->ce_mask & ROUTE_ATTR_MULTIPATH) { 00244 struct rtnl_nexthop *nh; 00245 00246 nl_list_for_each_entry(nh, &r->rt_nexthops, rtnh_list) { 00247 nl_dump_line(p, " "); 00248 p->dp_ivar = NH_DUMP_FROM_DETAILS; 00249 rtnl_route_nh_dump(nh, p); 00250 nl_dump(p, "\n"); 00251 } 00252 } 00253 00254 if ((r->ce_mask & ROUTE_ATTR_CACHEINFO) && r->rt_cacheinfo.rtci_error) { 00255 nl_dump_line(p, " cacheinfo error %d (%s)\n", 00256 r->rt_cacheinfo.rtci_error, 00257 strerror(-r->rt_cacheinfo.rtci_error)); 00258 } 00259 00260 if (r->ce_mask & ROUTE_ATTR_METRICS) { 00261 nl_dump_line(p, " metrics ["); 00262 for (i = 0; i < RTAX_MAX; i++) 00263 if (r->rt_metrics_mask & (1 << i)) 00264 nl_dump(p, "%s %u ", 00265 rtnl_route_metric2str(i+1, 00266 buf, sizeof(buf)), 00267 r->rt_metrics[i]); 00268 nl_dump(p, "]\n"); 00269 } 00270 } 00271 00272 static void route_dump_stats(struct nl_object *obj, struct nl_dump_params *p) 00273 { 00274 struct rtnl_route *route = (struct rtnl_route *) obj; 00275 00276 route_dump_details(obj, p); 00277 00278 if (route->ce_mask & ROUTE_ATTR_CACHEINFO) { 00279 struct rtnl_rtcacheinfo *ci = &route->rt_cacheinfo; 00280 00281 nl_dump_line(p, " used %u refcnt %u last-use %us " 00282 "expires %us\n", 00283 ci->rtci_used, ci->rtci_clntref, 00284 ci->rtci_last_use / nl_get_user_hz(), 00285 ci->rtci_expires / nl_get_user_hz()); 00286 } 00287 } 00288 00289 static int route_compare(struct nl_object *_a, struct nl_object *_b, 00290 uint32_t attrs, int flags) 00291 { 00292 struct rtnl_route *a = (struct rtnl_route *) _a; 00293 struct rtnl_route *b = (struct rtnl_route *) _b; 00294 struct rtnl_nexthop *nh_a, *nh_b; 00295 int i, diff = 0, found; 00296 00297 #define ROUTE_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, ROUTE_ATTR_##ATTR, a, b, EXPR) 00298 00299 diff |= ROUTE_DIFF(FAMILY, a->rt_family != b->rt_family); 00300 diff |= ROUTE_DIFF(TOS, a->rt_tos != b->rt_tos); 00301 diff |= ROUTE_DIFF(TABLE, a->rt_table != b->rt_table); 00302 diff |= ROUTE_DIFF(PROTOCOL, a->rt_protocol != b->rt_protocol); 00303 diff |= ROUTE_DIFF(SCOPE, a->rt_scope != b->rt_scope); 00304 diff |= ROUTE_DIFF(TYPE, a->rt_type != b->rt_type); 00305 diff |= ROUTE_DIFF(PRIO, a->rt_prio != b->rt_prio); 00306 diff |= ROUTE_DIFF(DST, nl_addr_cmp(a->rt_dst, b->rt_dst)); 00307 diff |= ROUTE_DIFF(SRC, nl_addr_cmp(a->rt_src, b->rt_src)); 00308 diff |= ROUTE_DIFF(IIF, a->rt_iif != b->rt_iif); 00309 diff |= ROUTE_DIFF(PREF_SRC, nl_addr_cmp(a->rt_pref_src, 00310 b->rt_pref_src)); 00311 00312 if (flags & LOOSE_COMPARISON) { 00313 nl_list_for_each_entry(nh_b, &b->rt_nexthops, rtnh_list) { 00314 found = 0; 00315 nl_list_for_each_entry(nh_a, &a->rt_nexthops, 00316 rtnh_list) { 00317 if (!rtnl_route_nh_compare(nh_a, nh_b, 00318 nh_b->ce_mask, 1)) { 00319 found = 1; 00320 break; 00321 } 00322 } 00323 00324 if (!found) 00325 goto nh_mismatch; 00326 } 00327 00328 for (i = 0; i < RTAX_MAX - 1; i++) { 00329 if (a->rt_metrics_mask & (1 << i) && 00330 (!(b->rt_metrics_mask & (1 << i)) || 00331 a->rt_metrics[i] != b->rt_metrics[i])) 00332 ROUTE_DIFF(METRICS, 1); 00333 } 00334 00335 diff |= ROUTE_DIFF(FLAGS, 00336 (a->rt_flags ^ b->rt_flags) & b->rt_flag_mask); 00337 } else { 00338 if (a->rt_nr_nh != a->rt_nr_nh) 00339 goto nh_mismatch; 00340 00341 /* search for a dup in each nh of a */ 00342 nl_list_for_each_entry(nh_a, &a->rt_nexthops, rtnh_list) { 00343 found = 0; 00344 nl_list_for_each_entry(nh_b, &b->rt_nexthops, 00345 rtnh_list) { 00346 if (!rtnl_route_nh_compare(nh_a, nh_b, ~0, 0)) { 00347 found = 1; 00348 break; 00349 } 00350 } 00351 if (!found) 00352 goto nh_mismatch; 00353 } 00354 00355 /* search for a dup in each nh of b, covers case where a has 00356 * dupes itself */ 00357 nl_list_for_each_entry(nh_b, &b->rt_nexthops, rtnh_list) { 00358 found = 0; 00359 nl_list_for_each_entry(nh_a, &a->rt_nexthops, 00360 rtnh_list) { 00361 if (!rtnl_route_nh_compare(nh_a, nh_b, ~0, 0)) { 00362 found = 1; 00363 break; 00364 } 00365 } 00366 if (!found) 00367 goto nh_mismatch; 00368 } 00369 00370 for (i = 0; i < RTAX_MAX - 1; i++) { 00371 if ((a->rt_metrics_mask & (1 << i)) ^ 00372 (b->rt_metrics_mask & (1 << i))) 00373 diff |= ROUTE_DIFF(METRICS, 1); 00374 else 00375 diff |= ROUTE_DIFF(METRICS, 00376 a->rt_metrics[i] != b->rt_metrics[i]); 00377 } 00378 00379 diff |= ROUTE_DIFF(FLAGS, a->rt_flags != b->rt_flags); 00380 } 00381 00382 out: 00383 return diff; 00384 00385 nh_mismatch: 00386 diff |= ROUTE_DIFF(MULTIPATH, 1); 00387 goto out; 00388 00389 #undef ROUTE_DIFF 00390 } 00391 00392 static const struct trans_tbl route_attrs[] = { 00393 __ADD(ROUTE_ATTR_FAMILY, family) 00394 __ADD(ROUTE_ATTR_TOS, tos) 00395 __ADD(ROUTE_ATTR_TABLE, table) 00396 __ADD(ROUTE_ATTR_PROTOCOL, protocol) 00397 __ADD(ROUTE_ATTR_SCOPE, scope) 00398 __ADD(ROUTE_ATTR_TYPE, type) 00399 __ADD(ROUTE_ATTR_FLAGS, flags) 00400 __ADD(ROUTE_ATTR_DST, dst) 00401 __ADD(ROUTE_ATTR_SRC, src) 00402 __ADD(ROUTE_ATTR_IIF, iif) 00403 __ADD(ROUTE_ATTR_OIF, oif) 00404 __ADD(ROUTE_ATTR_GATEWAY, gateway) 00405 __ADD(ROUTE_ATTR_PRIO, prio) 00406 __ADD(ROUTE_ATTR_PREF_SRC, pref_src) 00407 __ADD(ROUTE_ATTR_METRICS, metrics) 00408 __ADD(ROUTE_ATTR_MULTIPATH, multipath) 00409 __ADD(ROUTE_ATTR_REALMS, realms) 00410 __ADD(ROUTE_ATTR_CACHEINFO, cacheinfo) 00411 }; 00412 00413 static char *route_attrs2str(int attrs, char *buf, size_t len) 00414 { 00415 return __flags2str(attrs, buf, len, route_attrs, 00416 ARRAY_SIZE(route_attrs)); 00417 } 00418 00419 /** 00420 * @name Allocation/Freeing 00421 * @{ 00422 */ 00423 00424 struct rtnl_route *rtnl_route_alloc(void) 00425 { 00426 return (struct rtnl_route *) nl_object_alloc(&route_obj_ops); 00427 } 00428 00429 void rtnl_route_get(struct rtnl_route *route) 00430 { 00431 nl_object_get((struct nl_object *) route); 00432 } 00433 00434 void rtnl_route_put(struct rtnl_route *route) 00435 { 00436 nl_object_put((struct nl_object *) route); 00437 } 00438 00439 /** @} */ 00440 00441 /** 00442 * @name Attributes 00443 * @{ 00444 */ 00445 00446 void rtnl_route_set_table(struct rtnl_route *route, uint32_t table) 00447 { 00448 route->rt_table = table; 00449 route->ce_mask |= ROUTE_ATTR_TABLE; 00450 } 00451 00452 uint32_t rtnl_route_get_table(struct rtnl_route *route) 00453 { 00454 return route->rt_table; 00455 } 00456 00457 void rtnl_route_set_scope(struct rtnl_route *route, uint8_t scope) 00458 { 00459 route->rt_scope = scope; 00460 route->ce_mask |= ROUTE_ATTR_SCOPE; 00461 } 00462 00463 uint8_t rtnl_route_get_scope(struct rtnl_route *route) 00464 { 00465 return route->rt_scope; 00466 } 00467 00468 void rtnl_route_set_tos(struct rtnl_route *route, uint8_t tos) 00469 { 00470 route->rt_tos = tos; 00471 route->ce_mask |= ROUTE_ATTR_TOS; 00472 } 00473 00474 uint8_t rtnl_route_get_tos(struct rtnl_route *route) 00475 { 00476 return route->rt_tos; 00477 } 00478 00479 void rtnl_route_set_protocol(struct rtnl_route *route, uint8_t protocol) 00480 { 00481 route->rt_protocol = protocol; 00482 route->ce_mask |= ROUTE_ATTR_PROTOCOL; 00483 } 00484 00485 uint8_t rtnl_route_get_protocol(struct rtnl_route *route) 00486 { 00487 return route->rt_protocol; 00488 } 00489 00490 void rtnl_route_set_priority(struct rtnl_route *route, uint32_t prio) 00491 { 00492 route->rt_prio = prio; 00493 route->ce_mask |= ROUTE_ATTR_PRIO; 00494 } 00495 00496 uint32_t rtnl_route_get_priority(struct rtnl_route *route) 00497 { 00498 return route->rt_prio; 00499 } 00500 00501 int rtnl_route_set_family(struct rtnl_route *route, uint8_t family) 00502 { 00503 if (family != AF_INET && family != AF_INET6 && family != AF_DECnet) 00504 return -NLE_AF_NOSUPPORT; 00505 00506 route->rt_family = family; 00507 route->ce_mask |= ROUTE_ATTR_FAMILY; 00508 00509 return 0; 00510 } 00511 00512 uint8_t rtnl_route_get_family(struct rtnl_route *route) 00513 { 00514 return route->rt_family; 00515 } 00516 00517 int rtnl_route_set_dst(struct rtnl_route *route, struct nl_addr *addr) 00518 { 00519 if (route->ce_mask & ROUTE_ATTR_FAMILY) { 00520 if (addr->a_family != route->rt_family) 00521 return -NLE_AF_MISMATCH; 00522 } else 00523 route->rt_family = addr->a_family; 00524 00525 if (route->rt_dst) 00526 nl_addr_put(route->rt_dst); 00527 00528 nl_addr_get(addr); 00529 route->rt_dst = addr; 00530 00531 route->ce_mask |= (ROUTE_ATTR_DST | ROUTE_ATTR_FAMILY); 00532 00533 return 0; 00534 } 00535 00536 struct nl_addr *rtnl_route_get_dst(struct rtnl_route *route) 00537 { 00538 return route->rt_dst; 00539 } 00540 00541 int rtnl_route_set_src(struct rtnl_route *route, struct nl_addr *addr) 00542 { 00543 if (addr->a_family == AF_INET) 00544 return -NLE_SRCRT_NOSUPPORT; 00545 00546 if (route->ce_mask & ROUTE_ATTR_FAMILY) { 00547 if (addr->a_family != route->rt_family) 00548 return -NLE_AF_MISMATCH; 00549 } else 00550 route->rt_family = addr->a_family; 00551 00552 if (route->rt_src) 00553 nl_addr_put(route->rt_src); 00554 00555 nl_addr_get(addr); 00556 route->rt_src = addr; 00557 route->ce_mask |= (ROUTE_ATTR_SRC | ROUTE_ATTR_FAMILY); 00558 00559 return 0; 00560 } 00561 00562 struct nl_addr *rtnl_route_get_src(struct rtnl_route *route) 00563 { 00564 return route->rt_src; 00565 } 00566 00567 int rtnl_route_set_type(struct rtnl_route *route, uint8_t type) 00568 { 00569 if (type > RTN_MAX) 00570 return -NLE_RANGE; 00571 00572 route->rt_type = type; 00573 route->ce_mask |= ROUTE_ATTR_TYPE; 00574 00575 return 0; 00576 } 00577 00578 uint8_t rtnl_route_get_type(struct rtnl_route *route) 00579 { 00580 return route->rt_type; 00581 } 00582 00583 void rtnl_route_set_flags(struct rtnl_route *route, uint32_t flags) 00584 { 00585 route->rt_flag_mask |= flags; 00586 route->rt_flags |= flags; 00587 route->ce_mask |= ROUTE_ATTR_FLAGS; 00588 } 00589 00590 void rtnl_route_unset_flags(struct rtnl_route *route, uint32_t flags) 00591 { 00592 route->rt_flag_mask |= flags; 00593 route->rt_flags &= ~flags; 00594 route->ce_mask |= ROUTE_ATTR_FLAGS; 00595 } 00596 00597 uint32_t rtnl_route_get_flags(struct rtnl_route *route) 00598 { 00599 return route->rt_flags; 00600 } 00601 00602 int rtnl_route_set_metric(struct rtnl_route *route, int metric, uint32_t value) 00603 { 00604 if (metric > RTAX_MAX || metric < 1) 00605 return -NLE_RANGE; 00606 00607 route->rt_metrics[metric - 1] = value; 00608 00609 if (!(route->rt_metrics_mask & (1 << (metric - 1)))) { 00610 route->rt_nmetrics++; 00611 route->rt_metrics_mask |= (1 << (metric - 1)); 00612 } 00613 00614 route->ce_mask |= ROUTE_ATTR_METRICS; 00615 00616 return 0; 00617 } 00618 00619 int rtnl_route_unset_metric(struct rtnl_route *route, int metric) 00620 { 00621 if (metric > RTAX_MAX || metric < 1) 00622 return -NLE_RANGE; 00623 00624 if (route->rt_metrics_mask & (1 << (metric - 1))) { 00625 route->rt_nmetrics--; 00626 route->rt_metrics_mask &= ~(1 << (metric - 1)); 00627 } 00628 00629 return 0; 00630 } 00631 00632 int rtnl_route_get_metric(struct rtnl_route *route, int metric, uint32_t *value) 00633 { 00634 if (metric > RTAX_MAX || metric < 1) 00635 return -NLE_RANGE; 00636 00637 if (!(route->rt_metrics_mask & (1 << (metric - 1)))) 00638 return -NLE_OBJ_NOTFOUND; 00639 00640 if (value) 00641 *value = route->rt_metrics[metric - 1]; 00642 00643 return 0; 00644 } 00645 00646 int rtnl_route_set_pref_src(struct rtnl_route *route, struct nl_addr *addr) 00647 { 00648 if (route->ce_mask & ROUTE_ATTR_FAMILY) { 00649 if (addr->a_family != route->rt_family) 00650 return -NLE_AF_MISMATCH; 00651 } else 00652 route->rt_family = addr->a_family; 00653 00654 if (route->rt_pref_src) 00655 nl_addr_put(route->rt_pref_src); 00656 00657 nl_addr_get(addr); 00658 route->rt_pref_src = addr; 00659 route->ce_mask |= (ROUTE_ATTR_PREF_SRC | ROUTE_ATTR_FAMILY); 00660 00661 return 0; 00662 } 00663 00664 struct nl_addr *rtnl_route_get_pref_src(struct rtnl_route *route) 00665 { 00666 return route->rt_pref_src; 00667 } 00668 00669 void rtnl_route_set_iif(struct rtnl_route *route, int ifindex) 00670 { 00671 route->rt_iif = ifindex; 00672 route->ce_mask |= ROUTE_ATTR_IIF; 00673 } 00674 00675 int rtnl_route_get_iif(struct rtnl_route *route) 00676 { 00677 return route->rt_iif; 00678 } 00679 00680 void rtnl_route_add_nexthop(struct rtnl_route *route, struct rtnl_nexthop *nh) 00681 { 00682 nl_list_add_tail(&nh->rtnh_list, &route->rt_nexthops); 00683 route->rt_nr_nh++; 00684 route->ce_mask |= ROUTE_ATTR_MULTIPATH; 00685 } 00686 00687 void rtnl_route_remove_nexthop(struct rtnl_route *route, struct rtnl_nexthop *nh) 00688 { 00689 if (route->ce_mask & ROUTE_ATTR_MULTIPATH) { 00690 route->rt_nr_nh--; 00691 nl_list_del(&nh->rtnh_list); 00692 } 00693 } 00694 00695 struct nl_list_head *rtnl_route_get_nexthops(struct rtnl_route *route) 00696 { 00697 if (route->ce_mask & ROUTE_ATTR_MULTIPATH) 00698 return &route->rt_nexthops; 00699 00700 return NULL; 00701 } 00702 00703 int rtnl_route_get_nnexthops(struct rtnl_route *route) 00704 { 00705 if (route->ce_mask & ROUTE_ATTR_MULTIPATH) 00706 return route->rt_nr_nh; 00707 00708 return 0; 00709 } 00710 00711 void rtnl_route_foreach_nexthop(struct rtnl_route *r, 00712 void (*cb)(struct rtnl_nexthop *, void *), 00713 void *arg) 00714 { 00715 struct rtnl_nexthop *nh; 00716 00717 if (r->ce_mask & ROUTE_ATTR_MULTIPATH) { 00718 nl_list_for_each_entry(nh, &r->rt_nexthops, rtnh_list) { 00719 cb(nh, arg); 00720 } 00721 } 00722 } 00723 00724 struct rtnl_nexthop *rtnl_route_nexthop_n(struct rtnl_route *r, int n) 00725 { 00726 struct rtnl_nexthop *nh; 00727 int i; 00728 00729 if (r->ce_mask & ROUTE_ATTR_MULTIPATH && r->rt_nr_nh > n) { 00730 i = 0; 00731 nl_list_for_each_entry(nh, &r->rt_nexthops, rtnh_list) { 00732 if (i == n) return nh; 00733 i++; 00734 } 00735 } 00736 return NULL; 00737 } 00738 00739 /** @} */ 00740 00741 /** 00742 * @name Utilities 00743 * @{ 00744 */ 00745 00746 /** 00747 * Guess scope of a route object. 00748 * @arg route Route object. 00749 * 00750 * Guesses the scope of a route object, based on the following rules: 00751 * @code 00752 * 1) Local route -> local scope 00753 * 2) At least one nexthop not directly connected -> universe scope 00754 * 3) All others -> link scope 00755 * @endcode 00756 * 00757 * @return Scope value. 00758 */ 00759 int rtnl_route_guess_scope(struct rtnl_route *route) 00760 { 00761 if (route->rt_type == RTN_LOCAL) 00762 return RT_SCOPE_HOST; 00763 00764 if (!nl_list_empty(&route->rt_nexthops)) { 00765 struct rtnl_nexthop *nh; 00766 00767 /* 00768 * Use scope uiniverse if there is at least one nexthop which 00769 * is not directly connected 00770 */ 00771 nl_list_for_each_entry(nh, &route->rt_nexthops, rtnh_list) { 00772 if (nh->rtnh_gateway) 00773 return RT_SCOPE_UNIVERSE; 00774 } 00775 } 00776 00777 return RT_SCOPE_LINK; 00778 } 00779 00780 /** @} */ 00781 00782 static struct nla_policy route_policy[RTA_MAX+1] = { 00783 [RTA_IIF] = { .type = NLA_U32 }, 00784 [RTA_OIF] = { .type = NLA_U32 }, 00785 [RTA_PRIORITY] = { .type = NLA_U32 }, 00786 [RTA_FLOW] = { .type = NLA_U32 }, 00787 [RTA_CACHEINFO] = { .minlen = sizeof(struct rta_cacheinfo) }, 00788 [RTA_METRICS] = { .type = NLA_NESTED }, 00789 [RTA_MULTIPATH] = { .type = NLA_NESTED }, 00790 }; 00791 00792 static int parse_multipath(struct rtnl_route *route, struct nlattr *attr) 00793 { 00794 struct rtnl_nexthop *nh = NULL; 00795 struct rtnexthop *rtnh = nla_data(attr); 00796 size_t tlen = nla_len(attr); 00797 int err; 00798 00799 while (tlen >= sizeof(*rtnh) && tlen >= rtnh->rtnh_len) { 00800 nh = rtnl_route_nh_alloc(); 00801 if (!nh) 00802 return -NLE_NOMEM; 00803 00804 rtnl_route_nh_set_weight(nh, rtnh->rtnh_hops); 00805 rtnl_route_nh_set_ifindex(nh, rtnh->rtnh_ifindex); 00806 rtnl_route_nh_set_flags(nh, rtnh->rtnh_flags); 00807 00808 if (rtnh->rtnh_len > sizeof(*rtnh)) { 00809 struct nlattr *ntb[RTA_MAX + 1]; 00810 00811 err = nla_parse(ntb, RTA_MAX, (struct nlattr *) 00812 RTNH_DATA(rtnh), 00813 rtnh->rtnh_len - sizeof(*rtnh), 00814 route_policy); 00815 if (err < 0) 00816 goto errout; 00817 00818 if (ntb[RTA_GATEWAY]) { 00819 struct nl_addr *addr; 00820 00821 addr = nl_addr_alloc_attr(ntb[RTA_GATEWAY], 00822 route->rt_family); 00823 if (!addr) { 00824 err = -NLE_NOMEM; 00825 goto errout; 00826 } 00827 00828 rtnl_route_nh_set_gateway(nh, addr); 00829 nl_addr_put(addr); 00830 } 00831 00832 if (ntb[RTA_FLOW]) { 00833 uint32_t realms; 00834 00835 realms = nla_get_u32(ntb[RTA_FLOW]); 00836 rtnl_route_nh_set_realms(nh, realms); 00837 } 00838 } 00839 00840 rtnl_route_add_nexthop(route, nh); 00841 tlen -= RTNH_ALIGN(rtnh->rtnh_len); 00842 rtnh = RTNH_NEXT(rtnh); 00843 } 00844 00845 err = 0; 00846 errout: 00847 if (err && nh) 00848 rtnl_route_nh_free(nh); 00849 00850 return err; 00851 } 00852 00853 int rtnl_route_parse(struct nlmsghdr *nlh, struct rtnl_route **result) 00854 { 00855 struct rtmsg *rtm; 00856 struct rtnl_route *route; 00857 struct nlattr *tb[RTA_MAX + 1]; 00858 struct nl_addr *src = NULL, *dst = NULL, *addr; 00859 struct rtnl_nexthop *old_nh = NULL; 00860 int err, family; 00861 00862 route = rtnl_route_alloc(); 00863 if (!route) { 00864 err = -NLE_NOMEM; 00865 goto errout; 00866 } 00867 00868 route->ce_msgtype = nlh->nlmsg_type; 00869 00870 err = nlmsg_parse(nlh, sizeof(struct rtmsg), tb, RTA_MAX, route_policy); 00871 if (err < 0) 00872 goto errout; 00873 00874 rtm = nlmsg_data(nlh); 00875 route->rt_family = family = rtm->rtm_family; 00876 route->rt_tos = rtm->rtm_tos; 00877 route->rt_table = rtm->rtm_table; 00878 route->rt_type = rtm->rtm_type; 00879 route->rt_scope = rtm->rtm_scope; 00880 route->rt_protocol = rtm->rtm_protocol; 00881 route->rt_flags = rtm->rtm_flags; 00882 00883 route->ce_mask |= ROUTE_ATTR_FAMILY | ROUTE_ATTR_TOS | 00884 ROUTE_ATTR_TABLE | ROUTE_ATTR_TYPE | 00885 ROUTE_ATTR_SCOPE | ROUTE_ATTR_PROTOCOL | 00886 ROUTE_ATTR_FLAGS; 00887 00888 if (tb[RTA_DST]) { 00889 if (!(dst = nl_addr_alloc_attr(tb[RTA_DST], family))) 00890 goto errout_nomem; 00891 } else { 00892 if (!(dst = nl_addr_alloc(0))) 00893 goto errout_nomem; 00894 nl_addr_set_family(dst, rtm->rtm_family); 00895 } 00896 00897 nl_addr_set_prefixlen(dst, rtm->rtm_dst_len); 00898 err = rtnl_route_set_dst(route, dst); 00899 if (err < 0) 00900 goto errout; 00901 00902 nl_addr_put(dst); 00903 00904 if (tb[RTA_SRC]) { 00905 if (!(src = nl_addr_alloc_attr(tb[RTA_SRC], family))) 00906 goto errout_nomem; 00907 } else if (rtm->rtm_src_len) 00908 if (!(src = nl_addr_alloc(0))) 00909 goto errout_nomem; 00910 00911 if (src) { 00912 nl_addr_set_prefixlen(src, rtm->rtm_src_len); 00913 rtnl_route_set_src(route, src); 00914 nl_addr_put(src); 00915 } 00916 00917 if (tb[RTA_IIF]) 00918 rtnl_route_set_iif(route, nla_get_u32(tb[RTA_IIF])); 00919 00920 if (tb[RTA_PRIORITY]) 00921 rtnl_route_set_priority(route, nla_get_u32(tb[RTA_PRIORITY])); 00922 00923 if (tb[RTA_PREFSRC]) { 00924 if (!(addr = nl_addr_alloc_attr(tb[RTA_PREFSRC], family))) 00925 goto errout_nomem; 00926 rtnl_route_set_pref_src(route, addr); 00927 nl_addr_put(addr); 00928 } 00929 00930 if (tb[RTA_METRICS]) { 00931 struct nlattr *mtb[RTAX_MAX + 1]; 00932 int i; 00933 00934 err = nla_parse_nested(mtb, RTAX_MAX, tb[RTA_METRICS], NULL); 00935 if (err < 0) 00936 goto errout; 00937 00938 for (i = 1; i <= RTAX_MAX; i++) { 00939 if (mtb[i] && nla_len(mtb[i]) >= sizeof(uint32_t)) { 00940 uint32_t m = nla_get_u32(mtb[i]); 00941 if (rtnl_route_set_metric(route, i, m) < 0) 00942 goto errout; 00943 } 00944 } 00945 } 00946 00947 if (tb[RTA_MULTIPATH]) 00948 if ((err = parse_multipath(route, tb[RTA_MULTIPATH])) < 0) 00949 goto errout; 00950 00951 if (tb[RTA_CACHEINFO]) { 00952 nla_memcpy(&route->rt_cacheinfo, tb[RTA_CACHEINFO], 00953 sizeof(route->rt_cacheinfo)); 00954 route->ce_mask |= ROUTE_ATTR_CACHEINFO; 00955 } 00956 00957 if (tb[RTA_OIF]) { 00958 if (!old_nh && !(old_nh = rtnl_route_nh_alloc())) 00959 goto errout; 00960 00961 rtnl_route_nh_set_ifindex(old_nh, nla_get_u32(tb[RTA_OIF])); 00962 } 00963 00964 if (tb[RTA_GATEWAY]) { 00965 if (!old_nh && !(old_nh = rtnl_route_nh_alloc())) 00966 goto errout; 00967 00968 if (!(addr = nl_addr_alloc_attr(tb[RTA_GATEWAY], family))) 00969 goto errout_nomem; 00970 00971 rtnl_route_nh_set_gateway(old_nh, addr); 00972 nl_addr_put(addr); 00973 } 00974 00975 if (tb[RTA_FLOW]) { 00976 if (!old_nh && !(old_nh = rtnl_route_nh_alloc())) 00977 goto errout; 00978 00979 rtnl_route_nh_set_realms(old_nh, nla_get_u32(tb[RTA_FLOW])); 00980 } 00981 00982 if (old_nh) { 00983 if (route->rt_nr_nh == 0) { 00984 /* If no nexthops have been provided via RTA_MULTIPATH 00985 * we add it as regular nexthop to maintain backwards 00986 * compatibility */ 00987 rtnl_route_add_nexthop(route, old_nh); 00988 } else { 00989 /* Kernel supports new style nexthop configuration, 00990 * verify that it is a duplicate and discard nexthop. */ 00991 struct rtnl_nexthop *first; 00992 00993 first = nl_list_first_entry(&route->rt_nexthops, 00994 struct rtnl_nexthop, 00995 rtnh_list); 00996 if (!first) 00997 BUG(); 00998 00999 if (rtnl_route_nh_compare(old_nh, first, 01000 old_nh->ce_mask, 0)) { 01001 err = -NLE_INVAL; 01002 goto errout; 01003 } 01004 01005 rtnl_route_nh_free(old_nh); 01006 } 01007 } 01008 01009 *result = route; 01010 return 0; 01011 01012 errout: 01013 rtnl_route_put(route); 01014 return err; 01015 01016 errout_nomem: 01017 err = -NLE_NOMEM; 01018 goto errout; 01019 } 01020 01021 int rtnl_route_build_msg(struct nl_msg *msg, struct rtnl_route *route) 01022 { 01023 int i; 01024 struct nlattr *metrics; 01025 struct rtmsg rtmsg = { 01026 .rtm_family = route->rt_family, 01027 .rtm_tos = route->rt_tos, 01028 .rtm_table = route->rt_table, 01029 .rtm_protocol = route->rt_protocol, 01030 .rtm_scope = route->rt_scope, 01031 .rtm_type = route->rt_type, 01032 .rtm_flags = route->rt_flags, 01033 }; 01034 01035 if (route->rt_dst == NULL) 01036 return -NLE_MISSING_ATTR; 01037 01038 rtmsg.rtm_dst_len = nl_addr_get_prefixlen(route->rt_dst); 01039 if (route->rt_src) 01040 rtmsg.rtm_src_len = nl_addr_get_prefixlen(route->rt_src); 01041 01042 01043 if (rtmsg.rtm_scope == RT_SCOPE_NOWHERE) 01044 rtmsg.rtm_scope = rtnl_route_guess_scope(route); 01045 01046 if (nlmsg_append(msg, &rtmsg, sizeof(rtmsg), NLMSG_ALIGNTO) < 0) 01047 goto nla_put_failure; 01048 01049 /* Additional table attribute replacing the 8bit in the header, was 01050 * required to allow more than 256 tables. */ 01051 NLA_PUT_U32(msg, RTA_TABLE, route->rt_table); 01052 01053 if (nl_addr_get_len(route->rt_dst)) 01054 NLA_PUT_ADDR(msg, RTA_DST, route->rt_dst); 01055 NLA_PUT_U32(msg, RTA_PRIORITY, route->rt_prio); 01056 01057 if (route->ce_mask & ROUTE_ATTR_SRC) 01058 NLA_PUT_ADDR(msg, RTA_SRC, route->rt_src); 01059 01060 if (route->ce_mask & ROUTE_ATTR_PREF_SRC) 01061 NLA_PUT_ADDR(msg, RTA_PREFSRC, route->rt_pref_src); 01062 01063 if (route->ce_mask & ROUTE_ATTR_IIF) 01064 NLA_PUT_U32(msg, RTA_IIF, route->rt_iif); 01065 01066 if (route->rt_nmetrics > 0) { 01067 uint32_t val; 01068 01069 metrics = nla_nest_start(msg, RTA_METRICS); 01070 if (metrics == NULL) 01071 goto nla_put_failure; 01072 01073 for (i = 1; i <= RTAX_MAX; i++) { 01074 if (!rtnl_route_get_metric(route, i, &val)) 01075 NLA_PUT_U32(msg, i, val); 01076 } 01077 01078 nla_nest_end(msg, metrics); 01079 } 01080 01081 if (rtnl_route_get_nnexthops(route) == 1) { 01082 struct rtnl_nexthop *nh; 01083 01084 nh = rtnl_route_nexthop_n(route, 0); 01085 if (nh->rtnh_gateway) 01086 NLA_PUT_ADDR(msg, RTA_GATEWAY, nh->rtnh_gateway); 01087 if (nh->rtnh_ifindex) 01088 NLA_PUT_U32(msg, RTA_OIF, nh->rtnh_ifindex); 01089 if (nh->rtnh_realms) 01090 NLA_PUT_U32(msg, RTA_FLOW, nh->rtnh_realms); 01091 } else if (rtnl_route_get_nnexthops(route) > 1) { 01092 struct nlattr *multipath; 01093 struct rtnl_nexthop *nh; 01094 01095 if (!(multipath = nla_nest_start(msg, RTA_MULTIPATH))) 01096 goto nla_put_failure; 01097 01098 nl_list_for_each_entry(nh, &route->rt_nexthops, rtnh_list) { 01099 struct rtnexthop *rtnh; 01100 01101 rtnh = nlmsg_reserve(msg, sizeof(*rtnh), NLMSG_ALIGNTO); 01102 if (!rtnh) 01103 goto nla_put_failure; 01104 01105 rtnh->rtnh_flags = nh->rtnh_flags; 01106 rtnh->rtnh_hops = nh->rtnh_weight; 01107 rtnh->rtnh_ifindex = nh->rtnh_ifindex; 01108 01109 if (nh->rtnh_gateway) 01110 NLA_PUT_ADDR(msg, RTA_GATEWAY, 01111 nh->rtnh_gateway); 01112 01113 if (nh->rtnh_realms) 01114 NLA_PUT_U32(msg, RTA_FLOW, nh->rtnh_realms); 01115 01116 rtnh->rtnh_len = nlmsg_tail(msg->nm_nlh) - 01117 (void *) rtnh; 01118 } 01119 01120 nla_nest_end(msg, multipath); 01121 } 01122 01123 return 0; 01124 01125 nla_put_failure: 01126 return -NLE_MSGSIZE; 01127 } 01128 01129 /** @cond SKIP */ 01130 struct nl_object_ops route_obj_ops = { 01131 .oo_name = "route/route", 01132 .oo_size = sizeof(struct rtnl_route), 01133 .oo_constructor = route_constructor, 01134 .oo_free_data = route_free_data, 01135 .oo_clone = route_clone, 01136 .oo_dump = { 01137 [NL_DUMP_LINE] = route_dump_line, 01138 [NL_DUMP_DETAILS] = route_dump_details, 01139 [NL_DUMP_STATS] = route_dump_stats, 01140 }, 01141 .oo_compare = route_compare, 01142 .oo_attrs2str = route_attrs2str, 01143 .oo_id_attrs = (ROUTE_ATTR_FAMILY | ROUTE_ATTR_TOS | 01144 ROUTE_ATTR_TABLE | ROUTE_ATTR_DST), 01145 }; 01146 /** @endcond */ 01147 01148 /** @} */