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