Annotation of embedaddon/bird/sysdep/bsd/krt-sock.c, revision 1.1
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: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>