Return to krt-sock.c CVS log | Up to [ELWIX - Embedded LightWeight unIX -] / embedaddon / bird / sysdep / bsd |
1.1 ! misho 1: /* ! 2: * BIRD -- BSD Routing Table Syncing ! 3: * ! 4: * (c) 2004 Ondrej Filip <feela@network.cz> ! 5: * ! 6: * Can be freely distributed and used under the terms of the GNU GPL. ! 7: */ ! 8: ! 9: #include <stdio.h> ! 10: #include <stdlib.h> ! 11: #include <ctype.h> ! 12: #include <fcntl.h> ! 13: #include <unistd.h> ! 14: #include <sys/param.h> ! 15: #include <sys/types.h> ! 16: #include <sys/socket.h> ! 17: #include <sys/sysctl.h> ! 18: #include <sys/ioctl.h> ! 19: #include <netinet/in.h> ! 20: #include <net/route.h> ! 21: #include <net/if.h> ! 22: #include <net/if_dl.h> ! 23: ! 24: #undef LOCAL_DEBUG ! 25: ! 26: #include "nest/bird.h" ! 27: #include "nest/iface.h" ! 28: #include "nest/route.h" ! 29: #include "nest/protocol.h" ! 30: #include "nest/iface.h" ! 31: #include "lib/timer.h" ! 32: #include "lib/unix.h" ! 33: #include "lib/krt.h" ! 34: #include "lib/string.h" ! 35: #include "lib/socket.h" ! 36: ! 37: ! 38: /* ! 39: * There are significant differences in multiple tables support between BSD variants. ! 40: * ! 41: * OpenBSD has table_id field for routes in route socket protocol, therefore all ! 42: * tables could be managed by one kernel socket. FreeBSD lacks such field, ! 43: * therefore multiple sockets (locked to specific table using SO_SETFIB socket ! 44: * option) must be used. ! 45: * ! 46: * Both FreeBSD and OpenBSD uses separate scans for each table. In OpenBSD, ! 47: * table_id is specified explicitly as sysctl scan argument, while in FreeBSD it ! 48: * is handled implicitly by changing default table using setfib() syscall. ! 49: * ! 50: * KRT_SHARED_SOCKET - use shared kernel socked instead of one for each krt_proto ! 51: * KRT_USE_SETFIB_SCAN - use setfib() for sysctl() route scan ! 52: * KRT_USE_SETFIB_SOCK - use SO_SETFIB socket option for kernel sockets ! 53: * KRT_USE_SYSCTL_7 - use 7-th arg of sysctl() as table id for route scans ! 54: * KRT_USE_SYSCTL_NET_FIBS - use net.fibs sysctl() for dynamic max number of fibs ! 55: */ ! 56: ! 57: #ifdef __FreeBSD__ ! 58: #define KRT_MAX_TABLES 256 ! 59: #define KRT_USE_SETFIB_SCAN ! 60: #define KRT_USE_SETFIB_SOCK ! 61: #define KRT_USE_SYSCTL_NET_FIBS ! 62: #endif ! 63: ! 64: #ifdef __OpenBSD__ ! 65: #define KRT_MAX_TABLES (RT_TABLEID_MAX+1) ! 66: #define KRT_SHARED_SOCKET ! 67: #define KRT_USE_SYSCTL_7 ! 68: #endif ! 69: ! 70: #ifndef KRT_MAX_TABLES ! 71: #define KRT_MAX_TABLES 1 ! 72: #endif ! 73: ! 74: ! 75: ! 76: /* Dynamic max number of tables */ ! 77: ! 78: int krt_max_tables; ! 79: ! 80: #ifdef KRT_USE_SYSCTL_NET_FIBS ! 81: ! 82: static int ! 83: krt_get_max_tables(void) ! 84: { ! 85: int fibs; ! 86: size_t fibs_len = sizeof(fibs); ! 87: ! 88: if (sysctlbyname("net.fibs", &fibs, &fibs_len, NULL, 0) < 0) ! 89: { ! 90: log(L_WARN "KRT: unable to get max number of fib tables: %m"); ! 91: return 1; ! 92: } ! 93: ! 94: return MIN(fibs, KRT_MAX_TABLES); ! 95: } ! 96: ! 97: #else ! 98: ! 99: static int ! 100: krt_get_max_tables(void) ! 101: { ! 102: return KRT_MAX_TABLES; ! 103: } ! 104: ! 105: #endif /* KRT_USE_SYSCTL_NET_FIBS */ ! 106: ! 107: ! 108: /* setfib() syscall for FreeBSD scans */ ! 109: ! 110: #ifdef KRT_USE_SETFIB_SCAN ! 111: ! 112: /* ! 113: static int krt_default_fib; ! 114: ! 115: static int ! 116: krt_get_active_fib(void) ! 117: { ! 118: int fib; ! 119: size_t fib_len = sizeof(fib); ! 120: ! 121: if (sysctlbyname("net.my_fibnum", &fib, &fib_len, NULL, 0) < 0) ! 122: { ! 123: log(L_WARN "KRT: unable to get active fib number: %m"); ! 124: return 0; ! 125: } ! 126: ! 127: return fib; ! 128: } ! 129: */ ! 130: ! 131: extern int setfib(int fib); ! 132: ! 133: #endif /* KRT_USE_SETFIB_SCAN */ ! 134: ! 135: ! 136: /* table_id -> krt_proto map */ ! 137: ! 138: #ifdef KRT_SHARED_SOCKET ! 139: static struct krt_proto *krt_table_map[KRT_MAX_TABLES]; ! 140: #endif ! 141: ! 142: ! 143: /* Route socket message processing */ ! 144: ! 145: int ! 146: krt_capable(rte *e) ! 147: { ! 148: rta *a = e->attrs; ! 149: ! 150: return ! 151: a->cast == RTC_UNICAST && ! 152: (a->dest == RTD_ROUTER ! 153: || a->dest == RTD_DEVICE ! 154: #ifdef RTF_REJECT ! 155: || a->dest == RTD_UNREACHABLE ! 156: #endif ! 157: #ifdef RTF_BLACKHOLE ! 158: || a->dest == RTD_BLACKHOLE ! 159: #endif ! 160: ); ! 161: } ! 162: ! 163: #ifndef RTAX_MAX ! 164: #define RTAX_MAX 8 ! 165: #endif ! 166: ! 167: struct ks_msg ! 168: { ! 169: struct rt_msghdr rtm; ! 170: struct sockaddr_storage buf[RTAX_MAX]; ! 171: }; ! 172: ! 173: #define ROUNDUP(a) \ ! 174: ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) ! 175: ! 176: #define NEXTADDR(w, u) \ ! 177: if (msg.rtm.rtm_addrs & (w)) {\ ! 178: l = ROUNDUP(((struct sockaddr *)&(u))->sa_len);\ ! 179: memmove(body, &(u), l); body += l;} ! 180: ! 181: #define GETADDR(p, F) \ ! 182: bzero(p, sizeof(*p));\ ! 183: if ((addrs & (F)) && ((struct sockaddr *)body)->sa_len) {\ ! 184: uint l = ROUNDUP(((struct sockaddr *)body)->sa_len);\ ! 185: memcpy(p, body, (l > sizeof(*p) ? sizeof(*p) : l));\ ! 186: body += l;} ! 187: ! 188: static int ! 189: krt_send_route(struct krt_proto *p, int cmd, rte *e) ! 190: { ! 191: net *net = e->net; ! 192: rta *a = e->attrs; ! 193: static int msg_seq; ! 194: struct iface *j, *i = a->iface; ! 195: int l; ! 196: struct ks_msg msg; ! 197: char *body = (char *)msg.buf; ! 198: sockaddr gate, mask, dst; ! 199: ip_addr gw; ! 200: ! 201: DBG("krt-sock: send %I/%d via %I\n", net->n.prefix, net->n.pxlen, a->gw); ! 202: ! 203: bzero(&msg,sizeof (struct rt_msghdr)); ! 204: msg.rtm.rtm_version = RTM_VERSION; ! 205: msg.rtm.rtm_type = cmd; ! 206: msg.rtm.rtm_seq = msg_seq++; ! 207: msg.rtm.rtm_addrs = RTA_DST; ! 208: msg.rtm.rtm_flags = RTF_UP | RTF_PROTO1; ! 209: ! 210: if (net->n.pxlen == MAX_PREFIX_LENGTH) ! 211: msg.rtm.rtm_flags |= RTF_HOST; ! 212: else ! 213: msg.rtm.rtm_addrs |= RTA_NETMASK; ! 214: ! 215: #ifdef KRT_SHARED_SOCKET ! 216: msg.rtm.rtm_tableid = KRT_CF->sys.table_id; ! 217: #endif ! 218: ! 219: #ifdef RTF_REJECT ! 220: if(a->dest == RTD_UNREACHABLE) ! 221: msg.rtm.rtm_flags |= RTF_REJECT; ! 222: #endif ! 223: #ifdef RTF_BLACKHOLE ! 224: if(a->dest == RTD_BLACKHOLE) ! 225: msg.rtm.rtm_flags |= RTF_BLACKHOLE; ! 226: #endif ! 227: ! 228: /* This is really very nasty, but I'm not able ! 229: * to add "(reject|blackhole)" route without ! 230: * gateway set ! 231: */ ! 232: if(!i) ! 233: { ! 234: i = HEAD(iface_list); ! 235: ! 236: WALK_LIST(j, iface_list) ! 237: { ! 238: if (j->flags & IF_LOOPBACK) ! 239: { ! 240: i = j; ! 241: break; ! 242: } ! 243: } ! 244: } ! 245: ! 246: gw = a->gw; ! 247: ! 248: #ifdef IPV6 ! 249: /* Embed interface ID to link-local address */ ! 250: if (ipa_is_link_local(gw)) ! 251: _I0(gw) = 0xfe800000 | (i->index & 0x0000ffff); ! 252: #endif ! 253: ! 254: sockaddr_fill(&dst, BIRD_AF, net->n.prefix, NULL, 0); ! 255: sockaddr_fill(&mask, BIRD_AF, ipa_mkmask(net->n.pxlen), NULL, 0); ! 256: sockaddr_fill(&gate, BIRD_AF, gw, NULL, 0); ! 257: ! 258: switch (a->dest) ! 259: { ! 260: case RTD_ROUTER: ! 261: msg.rtm.rtm_flags |= RTF_GATEWAY; ! 262: msg.rtm.rtm_addrs |= RTA_GATEWAY; ! 263: break; ! 264: ! 265: #ifdef RTF_REJECT ! 266: case RTD_UNREACHABLE: ! 267: #endif ! 268: #ifdef RTF_BLACKHOLE ! 269: case RTD_BLACKHOLE: ! 270: #endif ! 271: case RTD_DEVICE: ! 272: if(i) ! 273: { ! 274: #ifdef RTF_CLONING ! 275: if (cmd == RTM_ADD && (i->flags & IF_MULTIACCESS) != IF_MULTIACCESS) /* PTP */ ! 276: msg.rtm.rtm_flags |= RTF_CLONING; ! 277: #endif ! 278: ! 279: if(!i->addr) { ! 280: log(L_ERR "KRT: interface %s has no IP addess", i->name); ! 281: return -1; ! 282: } ! 283: ! 284: sockaddr_fill(&gate, BIRD_AF, i->addr->ip, NULL, 0); ! 285: msg.rtm.rtm_addrs |= RTA_GATEWAY; ! 286: } ! 287: break; ! 288: default: ! 289: bug("krt-sock: unknown flags, but not filtered"); ! 290: } ! 291: ! 292: msg.rtm.rtm_index = i->index; ! 293: ! 294: NEXTADDR(RTA_DST, dst); ! 295: NEXTADDR(RTA_GATEWAY, gate); ! 296: NEXTADDR(RTA_NETMASK, mask); ! 297: ! 298: l = body - (char *)&msg; ! 299: msg.rtm.rtm_msglen = l; ! 300: ! 301: if ((l = write(p->sys.sk->fd, (char *)&msg, l)) < 0) { ! 302: log(L_ERR "KRT: Error sending route %I/%d to kernel: %m", net->n.prefix, net->n.pxlen); ! 303: return -1; ! 304: } ! 305: ! 306: return 0; ! 307: } ! 308: ! 309: void ! 310: krt_replace_rte(struct krt_proto *p, net *n, rte *new, rte *old, ! 311: struct ea_list *eattrs UNUSED) ! 312: { ! 313: int err = 0; ! 314: ! 315: if (old) ! 316: krt_send_route(p, RTM_DELETE, old); ! 317: ! 318: if (new) ! 319: err = krt_send_route(p, RTM_ADD, new); ! 320: ! 321: if (err < 0) ! 322: n->n.flags |= KRF_SYNC_ERROR; ! 323: else ! 324: n->n.flags &= ~KRF_SYNC_ERROR; ! 325: } ! 326: ! 327: #define SKIP(ARG...) do { DBG("KRT: Ignoring route - " ARG); return; } while(0) ! 328: ! 329: static void ! 330: krt_read_route(struct ks_msg *msg, struct krt_proto *p, int scan) ! 331: { ! 332: /* p is NULL iff KRT_SHARED_SOCKET and !scan */ ! 333: ! 334: rte *e; ! 335: net *net; ! 336: sockaddr dst, gate, mask; ! 337: ip_addr idst, igate, imask; ! 338: void *body = (char *)msg->buf; ! 339: int new = (msg->rtm.rtm_type != RTM_DELETE); ! 340: char *errmsg = "KRT: Invalid route received"; ! 341: int flags = msg->rtm.rtm_flags; ! 342: int addrs = msg->rtm.rtm_addrs; ! 343: int src; ! 344: byte src2; ! 345: ! 346: if (!(flags & RTF_UP) && scan) ! 347: SKIP("not up in scan\n"); ! 348: ! 349: if (!(flags & RTF_DONE) && !scan) ! 350: SKIP("not done in async\n"); ! 351: ! 352: if (flags & RTF_LLINFO) ! 353: SKIP("link-local\n"); ! 354: ! 355: #ifdef KRT_SHARED_SOCKET ! 356: if (!scan) ! 357: { ! 358: int table_id = msg->rtm.rtm_tableid; ! 359: p = (table_id < KRT_MAX_TABLES) ? krt_table_map[table_id] : NULL; ! 360: ! 361: if (!p) ! 362: SKIP("unknown table id %d\n", table_id); ! 363: } ! 364: #endif ! 365: ! 366: GETADDR(&dst, RTA_DST); ! 367: GETADDR(&gate, RTA_GATEWAY); ! 368: GETADDR(&mask, RTA_NETMASK); ! 369: ! 370: if (dst.sa.sa_family != BIRD_AF) ! 371: SKIP("invalid DST"); ! 372: ! 373: idst = ipa_from_sa(&dst); ! 374: imask = ipa_from_sa(&mask); ! 375: igate = (gate.sa.sa_family == BIRD_AF) ? ipa_from_sa(&gate) : IPA_NONE; ! 376: ! 377: /* We do not test family for RTA_NETMASK, because BSD sends us ! 378: some strange values, but interpreting them as IPv4/IPv6 works */ ! 379: ! 380: ! 381: int c = ipa_classify_net(idst); ! 382: if ((c < 0) || !(c & IADDR_HOST) || ((c & IADDR_SCOPE_MASK) <= SCOPE_LINK)) ! 383: SKIP("strange class/scope\n"); ! 384: ! 385: int pxlen = (flags & RTF_HOST) ? MAX_PREFIX_LENGTH : ipa_masklen(imask); ! 386: if (pxlen < 0) ! 387: { log(L_ERR "%s (%I) - netmask %I", errmsg, idst, imask); return; } ! 388: ! 389: if ((flags & RTF_GATEWAY) && ipa_zero(igate)) ! 390: { log(L_ERR "%s (%I/%d) - missing gateway", errmsg, idst, pxlen); return; } ! 391: ! 392: u32 self_mask = RTF_PROTO1; ! 393: u32 alien_mask = RTF_STATIC | RTF_PROTO1 | RTF_GATEWAY; ! 394: ! 395: src2 = (flags & RTF_STATIC) ? 1 : 0; ! 396: src2 |= (flags & RTF_PROTO1) ? 2 : 0; ! 397: ! 398: #ifdef RTF_PROTO2 ! 399: alien_mask |= RTF_PROTO2; ! 400: src2 |= (flags & RTF_PROTO2) ? 4 : 0; ! 401: #endif ! 402: ! 403: #ifdef RTF_PROTO3 ! 404: alien_mask |= RTF_PROTO3; ! 405: src2 |= (flags & RTF_PROTO3) ? 8 : 0; ! 406: #endif ! 407: ! 408: #ifdef RTF_REJECT ! 409: alien_mask |= RTF_REJECT; ! 410: #endif ! 411: ! 412: #ifdef RTF_BLACKHOLE ! 413: alien_mask |= RTF_BLACKHOLE; ! 414: #endif ! 415: ! 416: if (flags & (RTF_DYNAMIC | RTF_MODIFIED)) ! 417: src = KRT_SRC_REDIRECT; ! 418: else if (flags & self_mask) ! 419: { ! 420: if (!scan) ! 421: SKIP("echo\n"); ! 422: src = KRT_SRC_BIRD; ! 423: } ! 424: else if (flags & alien_mask) ! 425: src = KRT_SRC_ALIEN; ! 426: else ! 427: src = KRT_SRC_KERNEL; ! 428: ! 429: net = net_get(p->p.table, idst, pxlen); ! 430: ! 431: rta a = { ! 432: .src = p->p.main_source, ! 433: .source = RTS_INHERIT, ! 434: .scope = SCOPE_UNIVERSE, ! 435: .cast = RTC_UNICAST ! 436: }; ! 437: ! 438: /* reject/blackhole routes have also set RTF_GATEWAY, ! 439: we wil check them first. */ ! 440: ! 441: #ifdef RTF_REJECT ! 442: if(flags & RTF_REJECT) { ! 443: a.dest = RTD_UNREACHABLE; ! 444: goto done; ! 445: } ! 446: #endif ! 447: ! 448: #ifdef RTF_BLACKHOLE ! 449: if(flags & RTF_BLACKHOLE) { ! 450: a.dest = RTD_BLACKHOLE; ! 451: goto done; ! 452: } ! 453: #endif ! 454: ! 455: a.iface = if_find_by_index(msg->rtm.rtm_index); ! 456: if (!a.iface) ! 457: { ! 458: log(L_ERR "KRT: Received route %I/%d with unknown ifindex %u", ! 459: net->n.prefix, net->n.pxlen, msg->rtm.rtm_index); ! 460: return; ! 461: } ! 462: ! 463: if (flags & RTF_GATEWAY) ! 464: { ! 465: neighbor *ng; ! 466: a.dest = RTD_ROUTER; ! 467: a.gw = igate; ! 468: ! 469: #ifdef IPV6 ! 470: /* Clean up embedded interface ID returned in link-local address */ ! 471: if (ipa_is_link_local(a.gw)) ! 472: _I0(a.gw) = 0xfe800000; ! 473: #endif ! 474: ! 475: ng = neigh_find2(&p->p, &a.gw, a.iface, 0); ! 476: if (!ng || (ng->scope == SCOPE_HOST)) ! 477: { ! 478: /* Ignore routes with next-hop 127.0.0.1, host routes with such ! 479: next-hop appear on OpenBSD for address aliases. */ ! 480: if (ipa_classify(a.gw) == (IADDR_HOST | SCOPE_HOST)) ! 481: return; ! 482: ! 483: log(L_ERR "KRT: Received route %I/%d with strange next-hop %I", ! 484: net->n.prefix, net->n.pxlen, a.gw); ! 485: return; ! 486: } ! 487: } ! 488: else ! 489: a.dest = RTD_DEVICE; ! 490: ! 491: done: ! 492: e = rte_get_temp(&a); ! 493: e->net = net; ! 494: e->u.krt.src = src; ! 495: e->u.krt.proto = src2; ! 496: e->u.krt.seen = 0; ! 497: e->u.krt.best = 0; ! 498: e->u.krt.metric = 0; ! 499: ! 500: if (scan) ! 501: krt_got_route(p, e); ! 502: else ! 503: krt_got_route_async(p, e, new); ! 504: } ! 505: ! 506: static void ! 507: krt_read_ifannounce(struct ks_msg *msg) ! 508: { ! 509: struct if_announcemsghdr *ifam = (struct if_announcemsghdr *)&msg->rtm; ! 510: ! 511: if (ifam->ifan_what == IFAN_ARRIVAL) ! 512: { ! 513: /* Not enough info to create the iface, so we just trigger iface scan */ ! 514: kif_request_scan(); ! 515: } ! 516: else if (ifam->ifan_what == IFAN_DEPARTURE) ! 517: { ! 518: struct iface *iface = if_find_by_index(ifam->ifan_index); ! 519: ! 520: /* Interface is destroyed */ ! 521: if (!iface) ! 522: { ! 523: DBG("KRT: unknown interface (%s, #%d) going down. Ignoring\n", ifam->ifan_name, ifam->ifan_index); ! 524: return; ! 525: } ! 526: ! 527: if_delete(iface); ! 528: } ! 529: ! 530: DBG("KRT: IFANNOUNCE what: %d index %d name %s\n", ifam->ifan_what, ifam->ifan_index, ifam->ifan_name); ! 531: } ! 532: ! 533: static void ! 534: krt_read_ifinfo(struct ks_msg *msg, int scan) ! 535: { ! 536: struct if_msghdr *ifm = (struct if_msghdr *)&msg->rtm; ! 537: void *body = (void *)(ifm + 1); ! 538: struct sockaddr_dl *dl = NULL; ! 539: uint i; ! 540: struct iface *iface = NULL, f = {}; ! 541: int fl = ifm->ifm_flags; ! 542: int nlen = 0; ! 543: ! 544: for (i = 1; i<=RTA_IFP; i <<= 1) ! 545: { ! 546: if (i & ifm->ifm_addrs) ! 547: { ! 548: if (i == RTA_IFP) ! 549: { ! 550: dl = (struct sockaddr_dl *)body; ! 551: break; ! 552: } ! 553: body += ROUNDUP(((struct sockaddr *)&(body))->sa_len); ! 554: } ! 555: } ! 556: ! 557: if (dl && (dl->sdl_family != AF_LINK)) ! 558: { ! 559: log(L_WARN "Ignoring strange IFINFO"); ! 560: return; ! 561: } ! 562: ! 563: if (dl) ! 564: nlen = MIN(sizeof(f.name)-1, dl->sdl_nlen); ! 565: ! 566: /* Note that asynchronous IFINFO messages do not contain iface ! 567: name, so we have to found an existing iface by iface index */ ! 568: ! 569: iface = if_find_by_index(ifm->ifm_index); ! 570: if (!iface) ! 571: { ! 572: /* New interface */ ! 573: if (!dl) ! 574: return; /* No interface name, ignoring */ ! 575: ! 576: memcpy(f.name, dl->sdl_data, nlen); ! 577: DBG("New interface '%s' found\n", f.name); ! 578: } ! 579: else if (dl && memcmp(iface->name, dl->sdl_data, nlen)) ! 580: { ! 581: /* Interface renamed */ ! 582: if_delete(iface); ! 583: memcpy(f.name, dl->sdl_data, nlen); ! 584: } ! 585: else ! 586: { ! 587: /* Old interface */ ! 588: memcpy(f.name, iface->name, sizeof(f.name)); ! 589: } ! 590: ! 591: f.index = ifm->ifm_index; ! 592: f.mtu = ifm->ifm_data.ifi_mtu; ! 593: ! 594: if (fl & IFF_UP) ! 595: f.flags |= IF_ADMIN_UP; ! 596: if (ifm->ifm_data.ifi_link_state != LINK_STATE_DOWN) ! 597: f.flags |= IF_LINK_UP; /* up or unknown */ ! 598: if (fl & IFF_LOOPBACK) /* Loopback */ ! 599: f.flags |= IF_MULTIACCESS | IF_LOOPBACK | IF_IGNORE; ! 600: else if (fl & IFF_POINTOPOINT) /* PtP */ ! 601: f.flags |= IF_MULTICAST; ! 602: else if (fl & IFF_BROADCAST) /* Broadcast */ ! 603: f.flags |= IF_MULTIACCESS | IF_BROADCAST | IF_MULTICAST; ! 604: else ! 605: f.flags |= IF_MULTIACCESS; /* NBMA */ ! 606: ! 607: iface = if_update(&f); ! 608: ! 609: if (!scan) ! 610: if_end_partial_update(iface); ! 611: } ! 612: ! 613: static void ! 614: krt_read_addr(struct ks_msg *msg, int scan) ! 615: { ! 616: struct ifa_msghdr *ifam = (struct ifa_msghdr *)&msg->rtm; ! 617: void *body = (void *)(ifam + 1); ! 618: sockaddr addr, mask, brd; ! 619: struct iface *iface = NULL; ! 620: struct ifa ifa; ! 621: struct sockaddr null; ! 622: ip_addr iaddr, imask, ibrd; ! 623: int addrs = ifam->ifam_addrs; ! 624: int scope, masklen = -1; ! 625: int new = (ifam->ifam_type == RTM_NEWADDR); ! 626: ! 627: /* Strange messages with zero (invalid) ifindex appear on OpenBSD */ ! 628: if (ifam->ifam_index == 0) ! 629: return; ! 630: ! 631: if(!(iface = if_find_by_index(ifam->ifam_index))) ! 632: { ! 633: log(L_ERR "KIF: Received address message for unknown interface %d", ifam->ifam_index); ! 634: return; ! 635: } ! 636: ! 637: GETADDR (&null, RTA_DST); ! 638: GETADDR (&null, RTA_GATEWAY); ! 639: GETADDR (&mask, RTA_NETMASK); ! 640: GETADDR (&null, RTA_GENMASK); ! 641: GETADDR (&null, RTA_IFP); ! 642: GETADDR (&addr, RTA_IFA); ! 643: GETADDR (&null, RTA_AUTHOR); ! 644: GETADDR (&brd, RTA_BRD); ! 645: ! 646: /* Some other family address */ ! 647: if (addr.sa.sa_family != BIRD_AF) ! 648: return; ! 649: ! 650: iaddr = ipa_from_sa(&addr); ! 651: imask = ipa_from_sa(&mask); ! 652: ibrd = ipa_from_sa(&brd); ! 653: ! 654: ! 655: if ((masklen = ipa_masklen(imask)) < 0) ! 656: { ! 657: log(L_ERR "KIF: Invalid masklen %I for %s", imask, iface->name); ! 658: return; ! 659: } ! 660: ! 661: #ifdef IPV6 ! 662: /* Clean up embedded interface ID returned in link-local address */ ! 663: ! 664: if (ipa_is_link_local(iaddr)) ! 665: _I0(iaddr) = 0xfe800000; ! 666: ! 667: if (ipa_is_link_local(ibrd)) ! 668: _I0(ibrd) = 0xfe800000; ! 669: #endif ! 670: ! 671: ! 672: bzero(&ifa, sizeof(ifa)); ! 673: ifa.iface = iface; ! 674: ifa.ip = iaddr; ! 675: ifa.pxlen = masklen; ! 676: ! 677: scope = ipa_classify(ifa.ip); ! 678: if (scope < 0) ! 679: { ! 680: log(L_ERR "KIF: Invalid interface address %I for %s", ifa.ip, iface->name); ! 681: return; ! 682: } ! 683: ifa.scope = scope & IADDR_SCOPE_MASK; ! 684: ! 685: if (masklen < BITS_PER_IP_ADDRESS) ! 686: { ! 687: ifa.prefix = ipa_and(ifa.ip, ipa_mkmask(masklen)); ! 688: ! 689: if (masklen == (BITS_PER_IP_ADDRESS - 1)) ! 690: ifa.opposite = ipa_opposite_m1(ifa.ip); ! 691: ! 692: #ifndef IPV6 ! 693: if (masklen == (BITS_PER_IP_ADDRESS - 2)) ! 694: ifa.opposite = ipa_opposite_m2(ifa.ip); ! 695: #endif ! 696: ! 697: if (iface->flags & IF_BROADCAST) ! 698: ifa.brd = ibrd; ! 699: ! 700: if (!(iface->flags & IF_MULTIACCESS)) ! 701: ifa.opposite = ibrd; ! 702: } ! 703: else if (!(iface->flags & IF_MULTIACCESS) && ipa_nonzero(ibrd)) ! 704: { ! 705: ifa.prefix = ifa.opposite = ibrd; ! 706: ifa.flags |= IA_PEER; ! 707: } ! 708: else ! 709: { ! 710: ifa.prefix = ifa.ip; ! 711: ifa.flags |= IA_HOST; ! 712: } ! 713: ! 714: if (new) ! 715: ifa_update(&ifa); ! 716: else ! 717: ifa_delete(&ifa); ! 718: ! 719: if (!scan) ! 720: if_end_partial_update(iface); ! 721: } ! 722: ! 723: static void ! 724: krt_read_msg(struct proto *p, struct ks_msg *msg, int scan) ! 725: { ! 726: /* p is NULL iff KRT_SHARED_SOCKET and !scan */ ! 727: ! 728: switch (msg->rtm.rtm_type) ! 729: { ! 730: case RTM_GET: ! 731: if(!scan) return; ! 732: case RTM_ADD: ! 733: case RTM_DELETE: ! 734: case RTM_CHANGE: ! 735: krt_read_route(msg, (struct krt_proto *)p, scan); ! 736: break; ! 737: case RTM_IFANNOUNCE: ! 738: krt_read_ifannounce(msg); ! 739: break; ! 740: case RTM_IFINFO: ! 741: krt_read_ifinfo(msg, scan); ! 742: break; ! 743: case RTM_NEWADDR: ! 744: case RTM_DELADDR: ! 745: krt_read_addr(msg, scan); ! 746: break; ! 747: default: ! 748: break; ! 749: } ! 750: } ! 751: ! 752: ! 753: /* Sysctl based scans */ ! 754: ! 755: static byte *krt_buffer; ! 756: static size_t krt_buflen, krt_bufmin; ! 757: static struct proto *krt_buffer_owner; ! 758: ! 759: static byte * ! 760: krt_buffer_update(struct proto *p, size_t *needed) ! 761: { ! 762: size_t req = *needed; ! 763: ! 764: if ((req > krt_buflen) || ! 765: ((p == krt_buffer_owner) && (req < krt_bufmin))) ! 766: { ! 767: /* min buflen is 32 kB, step is 8 kB, or 128 kB if > 1 MB */ ! 768: size_t step = (req < 0x100000) ? 0x2000 : 0x20000; ! 769: krt_buflen = (req < 0x6000) ? 0x8000 : (req + step); ! 770: krt_bufmin = (req < 0x8000) ? 0 : (req - 2*step); ! 771: ! 772: if (krt_buffer) ! 773: mb_free(krt_buffer); ! 774: krt_buffer = mb_alloc(krt_pool, krt_buflen); ! 775: krt_buffer_owner = p; ! 776: } ! 777: ! 778: *needed = krt_buflen; ! 779: return krt_buffer; ! 780: } ! 781: ! 782: static void ! 783: krt_buffer_release(struct proto *p) ! 784: { ! 785: if (p == krt_buffer_owner) ! 786: { ! 787: mb_free(krt_buffer); ! 788: krt_buffer = NULL; ! 789: krt_buflen = 0; ! 790: krt_buffer_owner = 0; ! 791: } ! 792: } ! 793: ! 794: static void ! 795: krt_sysctl_scan(struct proto *p, int cmd, int table_id) ! 796: { ! 797: byte *buf, *next; ! 798: int mib[7], mcnt; ! 799: size_t needed; ! 800: struct ks_msg *m; ! 801: int retries = 3; ! 802: int rv; ! 803: ! 804: mib[0] = CTL_NET; ! 805: mib[1] = PF_ROUTE; ! 806: mib[2] = 0; ! 807: mib[3] = BIRD_AF; ! 808: mib[4] = cmd; ! 809: mib[5] = 0; ! 810: mcnt = 6; ! 811: ! 812: #ifdef KRT_USE_SYSCTL_7 ! 813: if (table_id >= 0) ! 814: { ! 815: mib[6] = table_id; ! 816: mcnt = 7; ! 817: } ! 818: #endif ! 819: ! 820: #ifdef KRT_USE_SETFIB_SCAN ! 821: if (table_id > 0) ! 822: if (setfib(table_id) < 0) ! 823: { ! 824: log(L_ERR "KRT: setfib(%d) failed: %m", table_id); ! 825: return; ! 826: } ! 827: #endif ! 828: ! 829: try: ! 830: rv = sysctl(mib, mcnt, NULL, &needed, NULL, 0); ! 831: if (rv < 0) ! 832: { ! 833: /* OpenBSD returns EINVAL for not yet used tables */ ! 834: if ((errno == EINVAL) && (table_id > 0)) ! 835: goto exit; ! 836: ! 837: log(L_ERR "KRT: Route scan estimate failed: %m"); ! 838: goto exit; ! 839: } ! 840: ! 841: /* The table is empty */ ! 842: if (needed == 0) ! 843: goto exit; ! 844: ! 845: buf = krt_buffer_update(p, &needed); ! 846: ! 847: rv = sysctl(mib, mcnt, buf, &needed, NULL, 0); ! 848: if (rv < 0) ! 849: { ! 850: /* The buffer size changed since last sysctl ('needed' is not changed) */ ! 851: if ((errno == ENOMEM) && retries--) ! 852: goto try; ! 853: ! 854: log(L_ERR "KRT: Route scan failed: %m"); ! 855: goto exit; ! 856: } ! 857: ! 858: #ifdef KRT_USE_SETFIB_SCAN ! 859: if (table_id > 0) ! 860: if (setfib(0) < 0) ! 861: die("KRT: setfib(%d) failed: %m", 0); ! 862: #endif ! 863: ! 864: /* Process received messages */ ! 865: for (next = buf; next < (buf + needed); next += m->rtm.rtm_msglen) ! 866: { ! 867: m = (struct ks_msg *)next; ! 868: krt_read_msg(p, m, 1); ! 869: } ! 870: ! 871: return; ! 872: ! 873: exit: ! 874: krt_buffer_release(p); ! 875: ! 876: #ifdef KRT_USE_SETFIB_SCAN ! 877: if (table_id > 0) ! 878: if (setfib(0) < 0) ! 879: die("KRT: setfib(%d) failed: %m", 0); ! 880: #endif ! 881: } ! 882: ! 883: void ! 884: krt_do_scan(struct krt_proto *p) ! 885: { ! 886: krt_sysctl_scan(&p->p, NET_RT_DUMP, KRT_CF->sys.table_id); ! 887: } ! 888: ! 889: void ! 890: kif_do_scan(struct kif_proto *p) ! 891: { ! 892: if_start_update(); ! 893: krt_sysctl_scan(&p->p, NET_RT_IFLIST, -1); ! 894: if_end_update(); ! 895: } ! 896: ! 897: ! 898: /* Kernel sockets */ ! 899: ! 900: static int ! 901: krt_sock_hook(sock *sk, uint size UNUSED) ! 902: { ! 903: struct ks_msg msg; ! 904: int l = read(sk->fd, (char *)&msg, sizeof(msg)); ! 905: ! 906: if (l <= 0) ! 907: log(L_ERR "krt-sock: read failed"); ! 908: else ! 909: krt_read_msg((struct proto *) sk->data, &msg, 0); ! 910: ! 911: return 0; ! 912: } ! 913: ! 914: static void ! 915: krt_sock_err_hook(sock *sk, int e UNUSED) ! 916: { ! 917: krt_sock_hook(sk, 0); ! 918: } ! 919: ! 920: static sock * ! 921: krt_sock_open(pool *pool, void *data, int table_id UNUSED) ! 922: { ! 923: sock *sk; ! 924: int fd; ! 925: ! 926: fd = socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC); ! 927: if (fd < 0) ! 928: die("Cannot open kernel socket for routes"); ! 929: ! 930: #ifdef KRT_USE_SETFIB_SOCK ! 931: if (table_id > 0) ! 932: { ! 933: if (setsockopt(fd, SOL_SOCKET, SO_SETFIB, &table_id, sizeof(table_id)) < 0) ! 934: die("Cannot set FIB %d for kernel socket: %m", table_id); ! 935: } ! 936: #endif ! 937: ! 938: sk = sk_new(pool); ! 939: sk->type = SK_MAGIC; ! 940: sk->rx_hook = krt_sock_hook; ! 941: sk->err_hook = krt_sock_err_hook; ! 942: sk->fd = fd; ! 943: sk->data = data; ! 944: ! 945: if (sk_open(sk) < 0) ! 946: bug("krt-sock: sk_open failed"); ! 947: ! 948: return sk; ! 949: } ! 950: ! 951: ! 952: #ifdef KRT_SHARED_SOCKET ! 953: ! 954: static sock *krt_sock; ! 955: static int krt_sock_count; ! 956: ! 957: ! 958: static void ! 959: krt_sock_open_shared(void) ! 960: { ! 961: if (!krt_sock_count) ! 962: krt_sock = krt_sock_open(krt_pool, NULL, -1); ! 963: ! 964: krt_sock_count++; ! 965: } ! 966: ! 967: static void ! 968: krt_sock_close_shared(void) ! 969: { ! 970: krt_sock_count--; ! 971: ! 972: if (!krt_sock_count) ! 973: { ! 974: rfree(krt_sock); ! 975: krt_sock = NULL; ! 976: } ! 977: } ! 978: ! 979: int ! 980: krt_sys_start(struct krt_proto *p) ! 981: { ! 982: krt_table_map[KRT_CF->sys.table_id] = p; ! 983: ! 984: krt_sock_open_shared(); ! 985: p->sys.sk = krt_sock; ! 986: ! 987: return 1; ! 988: } ! 989: ! 990: void ! 991: krt_sys_shutdown(struct krt_proto *p) ! 992: { ! 993: krt_sock_close_shared(); ! 994: p->sys.sk = NULL; ! 995: ! 996: krt_table_map[KRT_CF->sys.table_id] = NULL; ! 997: ! 998: krt_buffer_release(&p->p); ! 999: } ! 1000: ! 1001: #else ! 1002: ! 1003: int ! 1004: krt_sys_start(struct krt_proto *p) ! 1005: { ! 1006: p->sys.sk = krt_sock_open(p->p.pool, p, KRT_CF->sys.table_id); ! 1007: return 1; ! 1008: } ! 1009: ! 1010: void ! 1011: krt_sys_shutdown(struct krt_proto *p) ! 1012: { ! 1013: rfree(p->sys.sk); ! 1014: p->sys.sk = NULL; ! 1015: ! 1016: krt_buffer_release(&p->p); ! 1017: } ! 1018: ! 1019: #endif /* KRT_SHARED_SOCKET */ ! 1020: ! 1021: ! 1022: /* KRT configuration callbacks */ ! 1023: ! 1024: static u32 krt_table_cf[(KRT_MAX_TABLES+31) / 32]; ! 1025: ! 1026: int ! 1027: krt_sys_reconfigure(struct krt_proto *p UNUSED, struct krt_config *n, struct krt_config *o) ! 1028: { ! 1029: return n->sys.table_id == o->sys.table_id; ! 1030: } ! 1031: ! 1032: void ! 1033: krt_sys_preconfig(struct config *c UNUSED) ! 1034: { ! 1035: krt_max_tables = krt_get_max_tables(); ! 1036: bzero(&krt_table_cf, sizeof(krt_table_cf)); ! 1037: } ! 1038: ! 1039: void ! 1040: krt_sys_postconfig(struct krt_config *x) ! 1041: { ! 1042: u32 *tbl = krt_table_cf; ! 1043: int id = x->sys.table_id; ! 1044: ! 1045: if (tbl[id/32] & (1 << (id%32))) ! 1046: cf_error("Multiple kernel syncers defined for table #%d", id); ! 1047: ! 1048: tbl[id/32] |= (1 << (id%32)); ! 1049: } ! 1050: ! 1051: void krt_sys_init_config(struct krt_config *c) ! 1052: { ! 1053: c->sys.table_id = 0; /* Default table */ ! 1054: } ! 1055: ! 1056: void krt_sys_copy_config(struct krt_config *d, struct krt_config *s) ! 1057: { ! 1058: d->sys.table_id = s->sys.table_id; ! 1059: } ! 1060: ! 1061: ! 1062: /* KIF misc code */ ! 1063: ! 1064: void ! 1065: kif_sys_start(struct kif_proto *p UNUSED) ! 1066: { ! 1067: } ! 1068: ! 1069: void ! 1070: kif_sys_shutdown(struct kif_proto *p) ! 1071: { ! 1072: krt_buffer_release(&p->p); ! 1073: } ! 1074: ! 1075: ! 1076: struct ifa * ! 1077: kif_get_primary_ip(struct iface *i UNUSED6) ! 1078: { ! 1079: #ifndef IPV6 ! 1080: static int fd = -1; ! 1081: ! 1082: if (fd < 0) ! 1083: fd = socket(AF_INET, SOCK_DGRAM, 0); ! 1084: ! 1085: struct ifreq ifr; ! 1086: memset(&ifr, 0, sizeof(ifr)); ! 1087: strncpy(ifr.ifr_name, i->name, IFNAMSIZ); ! 1088: ! 1089: int rv = ioctl(fd, SIOCGIFADDR, (char *) &ifr); ! 1090: if (rv < 0) ! 1091: return NULL; ! 1092: ! 1093: ip_addr addr; ! 1094: struct sockaddr_in *sin = (struct sockaddr_in *) &ifr.ifr_addr; ! 1095: memcpy(&addr, &sin->sin_addr.s_addr, sizeof(ip_addr)); ! 1096: ipa_ntoh(addr); ! 1097: ! 1098: struct ifa *a; ! 1099: WALK_LIST(a, i->addrs) ! 1100: { ! 1101: if (ipa_equal(a->ip, addr)) ! 1102: return a; ! 1103: } ! 1104: #endif ! 1105: ! 1106: return NULL; ! 1107: }