diff options
author | Claudio Jeker <claudio@cvs.openbsd.org> | 2005-05-27 17:52:12 +0000 |
---|---|---|
committer | Claudio Jeker <claudio@cvs.openbsd.org> | 2005-05-27 17:52:12 +0000 |
commit | f8a7bd2f4960c3af166fe20b59da361bd1084394 (patch) | |
tree | 0d9671db1f88942c742f5dd9bd8643865e2669d2 /usr.sbin/bgpd/kroute.c | |
parent | 5d05f9ab43992c83f5ea86e6f65ba7b0d82ceeac (diff) |
kroute6 support, at least partially. Get it in so that Henning can clean it
up more. OK henning@
Diffstat (limited to 'usr.sbin/bgpd/kroute.c')
-rw-r--r-- | usr.sbin/bgpd/kroute.c | 1041 |
1 files changed, 904 insertions, 137 deletions
diff --git a/usr.sbin/bgpd/kroute.c b/usr.sbin/bgpd/kroute.c index 0037414f2ac..776d8af9cd3 100644 --- a/usr.sbin/bgpd/kroute.c +++ b/usr.sbin/bgpd/kroute.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kroute.c,v 1.126 2005/05/13 08:34:33 claudio Exp $ */ +/* $OpenBSD: kroute.c,v 1.127 2005/05/27 17:52:11 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> @@ -56,7 +56,7 @@ struct kroute6_node { struct knexthop_node { RB_ENTRY(knexthop_node) entry; struct bgpd_addr nexthop; - struct kroute_node *kroute; + void *kroute; }; struct kif_kr { @@ -64,15 +64,23 @@ struct kif_kr { struct kroute_node *kr; }; +struct kif_kr6 { + LIST_ENTRY(kif_kr6) entry; + struct kroute6_node *kr; +}; + LIST_HEAD(kif_kr_head, kif_kr); +LIST_HEAD(kif_kr6_head, kif_kr6); struct kif_node { RB_ENTRY(kif_node) entry; struct kif k; struct kif_kr_head kroute_l; + struct kif_kr6_head kroute6_l; }; int kr_redistribute(int, struct kroute *); +int kr_redistribute6(int, struct kroute6 *); int kroute_compare(struct kroute_node *, struct kroute_node *); int kroute6_compare(struct kroute6_node *, struct kroute6_node *); int knexthop_compare(struct knexthop_node *, struct knexthop_node *); @@ -83,6 +91,11 @@ int kroute_insert(struct kroute_node *); int kroute_remove(struct kroute_node *); void kroute_clear(void); +struct kroute6_node *kroute6_find(const struct in6_addr *, u_int8_t); +int kroute6_insert(struct kroute6_node *); +int kroute6_remove(struct kroute6_node *); +void kroute6_clear(void); + struct knexthop_node *knexthop_find(struct bgpd_addr *); int knexthop_insert(struct knexthop_node *); int knexthop_remove(struct knexthop_node *); @@ -96,12 +109,15 @@ void kif_clear(void); int kif_kr_insert(struct kroute_node *); int kif_kr_remove(struct kroute_node *); +int kif_kr6_insert(struct kroute6_node *); +int kif_kr6_remove(struct kroute6_node *); + int kif_validate(struct kif *); int kroute_validate(struct kroute *); +int kroute6_validate(struct kroute6 *); void knexthop_validate(struct knexthop_node *); struct kroute_node *kroute_match(in_addr_t); -void kroute_attach_nexthop(struct knexthop_node *, - struct kroute_node *); +struct kroute6_node *kroute6_match(struct in6_addr *); void kroute_detach_nexthop(struct knexthop_node *); int protect_lo(void); @@ -113,9 +129,13 @@ void if_change(u_short, int, struct if_data *); void if_announce(void *); int send_rtmsg(int, int, struct kroute *); +int send_rt6msg(int, int, struct kroute6 *); int dispatch_rtmsg(void); int fetchtable(void); +int fetchtable6(void); int fetchifs(int); +int dispatch_rtmsg_addr(struct rt_msghdr *, + struct sockaddr *[RTAX_MAX]); RB_HEAD(kroute_tree, kroute_node) krt; RB_PROTOTYPE(kroute_tree, kroute_node, entry, kroute_compare) @@ -172,6 +192,7 @@ kr_init(int fs) kr_state.rtseq = 1; RB_INIT(&krt); + RB_INIT(&krt6); RB_INIT(&knt); RB_INIT(&kit); @@ -181,6 +202,9 @@ kr_init(int fs) if (fetchtable() == -1) return (-1); + if (fetchtable6() == -1) + return (-1); + if (protect_lo() == -1) return (-1); @@ -262,12 +286,87 @@ kr_delete(struct kroute *kroute) return (0); } +int +kr6_change(struct kroute6 *kroute) +{ + struct kroute6_node *kr6; + int action = RTM_ADD; + + if ((kr6 = kroute6_find(&kroute->prefix, kroute->prefixlen)) != NULL) { + if (kr6->r.flags & F_BGPD_INSERTED) + action = RTM_CHANGE; + else /* a non-bgp route already exists. not a problem */ + return (0); + } + + /* nexthop to loopback -> ignore silently */ + if (IN6_IS_ADDR_LOOPBACK(&kroute->nexthop)) + return (0); + + if (send_rt6msg(kr_state.fd, action, kroute) == -1) + return (-1); + + if (action == RTM_ADD) { + if ((kr6 = calloc(1, sizeof(struct kroute_node))) == NULL) { + log_warn("kr_change"); + return (-1); + } + memcpy(&kr6->r.prefix, &kroute->prefix, + sizeof(struct in6_addr)); + kr6->r.prefixlen = kroute->prefixlen; + memcpy(&kr6->r.nexthop, &kroute->nexthop, + sizeof(struct in6_addr)); + kr6->r.flags = kroute->flags | F_BGPD_INSERTED; + + if (kroute6_insert(kr6) == -1) + free(kr6); + } else { + memcpy(&kr6->r.nexthop, &kroute->nexthop, + sizeof(struct in6_addr)); + if (kroute->flags & F_BLACKHOLE) + kr6->r.flags |= F_BLACKHOLE; + else + kr6->r.flags &= ~F_BLACKHOLE; + if (kroute->flags & F_REJECT) + kr6->r.flags |= F_REJECT; + else + kr6->r.flags &= ~F_REJECT; + } + + return (0); +} + +int +kr6_delete(struct kroute6 *kroute) +{ + struct kroute6_node *kr6; + + if ((kr6 = kroute6_find(&kroute->prefix, kroute->prefixlen)) == NULL) + return (0); + + if (!(kr6->r.flags & F_BGPD_INSERTED)) + return (0); + + /* nexthop to loopback -> ignore silently */ + if (IN6_IS_ADDR_LOOPBACK(&kroute->nexthop)) + return (0); + + if (send_rt6msg(kr_state.fd, RTM_DELETE, kroute) == -1) + return (-1); + + if (kroute6_remove(kr6) == -1) + return (-1); + + return (0); +} + void kr_shutdown(void) { kr_fib_decouple(); knexthop_clear(); kroute_clear(); + kroute6_clear(); kif_clear(); } @@ -275,6 +374,7 @@ void kr_fib_couple(void) { struct kroute_node *kr; + struct kroute6_node *kr6; if (kr_state.fib_sync == 1) /* already coupled */ return; @@ -284,6 +384,9 @@ kr_fib_couple(void) RB_FOREACH(kr, kroute_tree, &krt) if ((kr->r.flags & F_BGPD_INSERTED)) send_rtmsg(kr_state.fd, RTM_ADD, &kr->r); + RB_FOREACH(kr6, kroute6_tree, &krt6) + if ((kr6->r.flags & F_BGPD_INSERTED)) + send_rt6msg(kr_state.fd, RTM_ADD, &kr6->r); log_info("kernel routing table coupled"); } @@ -292,6 +395,7 @@ void kr_fib_decouple(void) { struct kroute_node *kr; + struct kroute6_node *kr6; if (kr_state.fib_sync == 0) /* already decoupled */ return; @@ -299,6 +403,9 @@ kr_fib_decouple(void) RB_FOREACH(kr, kroute_tree, &krt) if ((kr->r.flags & F_BGPD_INSERTED)) send_rtmsg(kr_state.fd, RTM_DELETE, &kr->r); + RB_FOREACH(kr6, kroute6_tree, &krt6) + if ((kr6->r.flags & F_BGPD_INSERTED)) + send_rt6msg(kr_state.fd, RTM_DELETE, &kr6->r); kr_state.fib_sync = 0; @@ -317,21 +424,35 @@ kr_nexthop_add(struct bgpd_addr *addr) struct knexthop_node *h; if ((h = knexthop_find(addr)) != NULL) { - /* should not happen... this is really an error path */ + /* should not happen... this is acctually an error path */ struct kroute_nexthop nh; + struct kroute_node *k; + struct kroute6_node *k6; bzero(&nh, sizeof(nh)); memcpy(&nh.nexthop, addr, sizeof(nh.nexthop)); - if (h->kroute != NULL) { - nh.valid = 1; - nh.connected = h->kroute->r.flags & F_CONNECTED; - if (h->kroute->r.nexthop.s_addr != 0) { + nh.valid = 1; + if (h->kroute != NULL && addr->af == AF_INET) { + k = h->kroute; + nh.connected = k->r.flags & F_CONNECTED; + if (k->r.nexthop.s_addr != 0) { nh.gateway.af = AF_INET; nh.gateway.v4.s_addr = - h->kroute->r.nexthop.s_addr; + k->r.nexthop.s_addr; } - memcpy(&nh.kr.kr4, &h->kroute->r, sizeof(nh.kr.kr4)); + memcpy(&nh.kr.kr4, &k->r, sizeof(nh.kr.kr4)); + } else if (h->kroute != NULL && addr->af == AF_INET6) { + k6 = h->kroute; + nh.connected = k6->r.flags & F_CONNECTED; + if (memcmp(&k6->r.nexthop, &in6addr_any, + sizeof(struct in6_addr)) != 0) { + nh.gateway.af = AF_INET6; + memcpy(&nh.gateway.v4, &k6->r.nexthop, + sizeof(struct in6_addr)); + } + memcpy(&nh.kr.kr6, &k6->r, sizeof(nh.kr.kr6)); } + send_nexthop_update(&nh); } else { if ((h = calloc(1, sizeof(struct knexthop_node))) == NULL) { @@ -362,11 +483,13 @@ void kr_show_route(struct imsg *imsg) { struct kroute_node *kr; + struct kroute6_node *kr6; struct bgpd_addr *addr; int flags; struct ctl_show_nexthop snh; struct knexthop_node *h; struct kif_node *kif; + u_short ifindex = 0; switch (imsg->hdr.type) { case IMSG_CTL_KROUTE: @@ -380,6 +503,17 @@ kr_show_route(struct imsg *imsg) send_imsg_session(IMSG_CTL_KROUTE, imsg->hdr.pid, &kr->r, sizeof(kr->r)); break; + case IMSG_CTL_KROUTE6: + if (imsg->hdr.len != IMSG_HEADER_SIZE + sizeof(flags)) { + log_warnx("kr_show_route: wrong imsg len"); + return; + } + memcpy(&flags, imsg->data, sizeof(flags)); + RB_FOREACH(kr6, kroute6_tree, &krt6) + if (!flags || kr6->r.flags & flags) + send_imsg_session(IMSG_CTL_KROUTE6, + imsg->hdr.pid, &kr6->r, sizeof(kr6->r)); + break; case IMSG_CTL_KROUTE_ADDR: if (imsg->hdr.len != IMSG_HEADER_SIZE + sizeof(struct bgpd_addr)) { @@ -388,20 +522,39 @@ kr_show_route(struct imsg *imsg) } addr = imsg->data; kr = NULL; - if (addr->af == AF_INET) + switch(addr->af) { + case AF_INET: kr = kroute_match(addr->v4.s_addr); - if (kr != NULL) - send_imsg_session(IMSG_CTL_KROUTE, imsg->hdr.pid, - &kr->r, sizeof(kr->r)); + if (kr != NULL) + send_imsg_session(IMSG_CTL_KROUTE, + imsg->hdr.pid, &kr->r, sizeof(kr->r)); + break; + case AF_INET6: + kr6 = kroute6_match(&addr->v6); + if (kr6 != NULL) + send_imsg_session(IMSG_CTL_KROUTE6, + imsg->hdr.pid, &kr6->r, sizeof(kr6->r)); + break; + } break; case IMSG_CTL_SHOW_NEXTHOP: RB_FOREACH(h, knexthop_tree, &knt) { bzero(&snh, sizeof(snh)); memcpy(&snh.addr, &h->nexthop, sizeof(snh.addr)); if (h->kroute != NULL) { - snh.valid = kroute_validate(&h->kroute->r); - if ((kif = kif_find(h->kroute->r.ifindex)) != - NULL) + switch (h->nexthop.af) { + case AF_INET: + kr = h->kroute; + snh.valid = kroute_validate(&kr->r); + ifindex = kr->r.ifindex; + break; + case AF_INET6: + kr6 = h->kroute; + snh.valid = kroute6_validate(&kr6->r); + ifindex = kr6->r.ifindex; + break; + } + if ((kif = kif_find(ifindex)) != NULL) memcpy(&snh.kif, &kif->k, sizeof(snh.kif)); } @@ -437,8 +590,10 @@ kr_ifinfo(char *ifname) struct redist_node { LIST_ENTRY(redist_node) entry; struct kroute *kr; + struct kroute6 *kr6; }; + LIST_HEAD(, redist_node) redistlist; int @@ -506,7 +661,80 @@ kr_redistribute(int type, struct kroute *kr) return (-1); } - return (bgpd_redistribute(type, kr)); + return (bgpd_redistribute(type, kr, NULL)); +} + +int +kr_redistribute6(int type, struct kroute6 *kr6) +{ + struct redist_node *rn; + + if (!(kr6->flags & F_KERNEL)) + return (0); + + /* + * We consider unspecified, loopback, multicast, link- and site-local, + * IPv4 mapped and IPv4 compatible addresses as not redistributable. + */ + if (IN6_IS_ADDR_UNSPECIFIED(&kr6->prefix) || + IN6_IS_ADDR_LOOPBACK(&kr6->prefix) || + IN6_IS_ADDR_MULTICAST(&kr6->prefix) || + IN6_IS_ADDR_LINKLOCAL(&kr6->prefix) || + IN6_IS_ADDR_SITELOCAL(&kr6->prefix) || + IN6_IS_ADDR_V4MAPPED(&kr6->prefix) || + IN6_IS_ADDR_V4COMPAT(&kr6->prefix)) + return (0); + + /* + * Consider networks with nexthop loopback as not redistributable. + */ + if (IN6_IS_ADDR_LOOPBACK(&kr6->nexthop)) + return (0); + + /* + * never allow ::/0 the default route can only be redistributed + * with announce default. + */ + if (memcmp(&kr6->prefix, &in6addr_any, sizeof(struct in6_addr)) == 0 && + kr6->prefixlen == 0) + return (0); + + /* Add or delete kr from list ... + * using a linear list to store the redistributed networks will hurt + * as soon as redistribute ospf comes but until then keep it simple. + */ + LIST_FOREACH(rn, &redistlist, entry) + if (rn->kr6 == kr6) + break; + + switch (type) { + case IMSG_NETWORK_ADD: + log_debug("kr_redistribute: ADD %s/%d fl %x", + log_in6addr(&kr6->prefix), kr6->prefixlen, kr6->flags); + if (rn == NULL) { + if ((rn = calloc(1, sizeof(struct redist_node))) == + NULL) { + log_warn("kr_redistribute"); + return (-1); + } + rn->kr6 = kr6; + LIST_INSERT_HEAD(&redistlist, rn, entry); + } + break; + case IMSG_NETWORK_REMOVE: + log_debug("kr_redistribute: DEL %s/%d fl %x", + log_in6addr(&kr6->prefix), kr6->prefixlen, kr6->flags); + if (rn != NULL) { + LIST_REMOVE(rn, entry); + free(rn); + } + break; + default: + errno = EINVAL; + return (-1); + } + + return (bgpd_redistribute(type, NULL, kr6)); } int @@ -515,7 +743,7 @@ kr_redist_reload(void) struct redist_node *rn; LIST_FOREACH(rn, &redistlist, entry) - if (bgpd_redistribute(IMSG_NETWORK_ADD, rn->kr) == -1) + if (bgpd_redistribute(IMSG_NETWORK_ADD, rn->kr, rn->kr6) == -1) return (-1); return (0); } @@ -676,6 +904,89 @@ kroute_clear(void) kroute_remove(kr); } +struct kroute6_node * +kroute6_find(const struct in6_addr *prefix, u_int8_t prefixlen) +{ + struct kroute6_node s; + + memcpy(&s.r.prefix, prefix, sizeof(struct in6_addr)); + s.r.prefixlen = prefixlen; + + return (RB_FIND(kroute6_tree, &krt6, &s)); +} + +int +kroute6_insert(struct kroute6_node *kr) +{ + struct knexthop_node *h; + struct in6_addr ina, inb; + + if (RB_INSERT(kroute6_tree, &krt6, kr) != NULL) { + log_warnx("kroute_tree insert failed for %s/%u", + log_in6addr(&kr->r.prefix), kr->r.prefixlen); + free(kr); + return (-1); + } + + if (kr->r.flags & F_KERNEL) { + inet6applymask(&ina, &kr->r.prefix, kr->r.prefixlen); + RB_FOREACH(h, knexthop_tree, &knt) + if (h->nexthop.af == AF_INET6) { + inet6applymask(&inb, &h->nexthop.v6, + kr->r.prefixlen); + if (memcmp(&ina, &inb, sizeof(ina)) == 0) + knexthop_validate(h); + } + + if (kr->r.flags & F_CONNECTED) + if (kif_kr6_insert(kr) == -1) + return (-1); + + kr_redistribute6(IMSG_NETWORK_ADD, &kr->r); + } + + return (0); +} + +int +kroute6_remove(struct kroute6_node *kr) +{ + struct knexthop_node *s; + + if (RB_REMOVE(kroute6_tree, &krt6, kr) == NULL) { + log_warnx("kroute_remove failed for %s/%u", + log_in6addr(&kr->r.prefix), kr->r.prefixlen); + return (-1); + } + + /* check wether a nexthop depends on this kroute */ + if ((kr->r.flags & F_KERNEL) && (kr->r.flags & F_NEXTHOP)) + RB_FOREACH(s, knexthop_tree, &knt) + if (s->kroute == kr) + knexthop_validate(s); + + if (kr->r.flags & F_KERNEL) + kr_redistribute6(IMSG_NETWORK_REMOVE, &kr->r); + + if (kr->r.flags & F_CONNECTED) + if (kif_kr6_remove(kr) == -1) { + free(kr); + return (-1); + } + + free(kr); + return (0); +} + +void +kroute6_clear(void) +{ + struct kroute6_node *kr; + + while ((kr = RB_MIN(kroute6_tree, &krt6)) != NULL) + kroute6_remove(kr); +} + struct knexthop_node * knexthop_find(struct bgpd_addr *addr) { @@ -740,6 +1051,7 @@ int kif_insert(struct kif_node *kif) { LIST_INIT(&kif->kroute_l); + LIST_INIT(&kif->kroute6_l); if (RB_INSERT(kif_tree, &kit, kif) != NULL) { log_warnx("RB_INSERT(kif_tree, &kit, kif)"); @@ -754,6 +1066,7 @@ int kif_remove(struct kif_node *kif) { struct kif_kr *kkr; + struct kif_kr6 *kkr6; if (RB_REMOVE(kif_tree, &kit, kif) == NULL) { log_warnx("RB_REMOVE(kif_tree, &kit, kif)"); @@ -767,6 +1080,13 @@ kif_remove(struct kif_node *kif) free(kkr); } + while ((kkr6 = LIST_FIRST(&kif->kroute6_l)) != NULL) { + LIST_REMOVE(kkr6, entry); + kkr6->kr->r.flags &= ~F_NEXTHOP; + kroute6_remove(kkr6->kr); + free(kkr6); + } + free(kif); return (0); } @@ -839,6 +1159,65 @@ kif_kr_remove(struct kroute_node *kr) return (0); } +int +kif_kr6_insert(struct kroute6_node *kr) +{ + struct kif_node *kif; + struct kif_kr6 *kkr6; + + if ((kif = kif_find(kr->r.ifindex)) == NULL) { + if (kr->r.ifindex) + log_warnx("interface with index %u not found", + kr->r.ifindex); + return (0); + } + + if (kif->k.nh_reachable) + kr->r.flags &= ~F_DOWN; + else + kr->r.flags |= F_DOWN; + + if ((kkr6 = calloc(1, sizeof(struct kif_kr6))) == NULL) { + log_warn("kif_kr6_insert"); + return (-1); + } + + kkr6->kr = kr; + + LIST_INSERT_HEAD(&kif->kroute6_l, kkr6, entry); + + return (0); +} + +int +kif_kr6_remove(struct kroute6_node *kr) +{ + struct kif_node *kif; + struct kif_kr6 *kkr6; + + if ((kif = kif_find(kr->r.ifindex)) == NULL) { + if (kr->r.ifindex) + log_warnx("interface with index %u not found", + kr->r.ifindex); + return (0); + } + + for (kkr6 = LIST_FIRST(&kif->kroute6_l); kkr6 != NULL && kkr6->kr != kr; + kkr6 = LIST_NEXT(kkr6, entry)) + ; /* nothing */ + + if (kkr6 == NULL) { + log_warnx("can't remove connected route from interface " + "with index %u: not found", kr->r.ifindex); + return (-1); + } + + LIST_REMOVE(kkr6, entry); + free(kkr6); + + return (0); +} + /* * nexthop validation */ @@ -878,15 +1257,35 @@ kroute_validate(struct kroute *kr) return (kif->k.nh_reachable); } +int +kroute6_validate(struct kroute6 *kr) +{ + struct kif_node *kif; + + if ((kif = kif_find(kr->ifindex)) == NULL) { + if (kr->ifindex) + log_warnx("interface with index %d not found, " + "referenced from route for %s/%u", + kr->ifindex, log_in6addr(&kr->prefix), + kr->prefixlen); + return (1); + } + + return (kif->k.nh_reachable); +} + void knexthop_validate(struct knexthop_node *kn) { struct kroute_node *kr; + struct kroute6_node *kr6; struct kroute_nexthop n; int was_valid = 0; - if (kn->kroute != NULL) - was_valid = kroute_validate(&kn->kroute->r); + if (kn->nexthop.af == AF_INET && (kr = kn->kroute) != NULL) + was_valid = kroute_validate(&kr->r); + if (kn->nexthop.af == AF_INET6 && (kr6 = kn->kroute) != NULL) + was_valid = kroute6_validate(&kr6->r); bzero(&n, sizeof(n)); memcpy(&n.nexthop, &kn->nexthop, sizeof(n.nexthop)); @@ -904,13 +1303,38 @@ knexthop_validate(struct knexthop_node *kn) if ((n.gateway.v4.s_addr = kr->r.nexthop.s_addr) != 0) n.gateway.af = AF_INET; - memcpy(&n.kr, &kr->r, sizeof(n.kr)); + memcpy(&n.kr.kr4, &kr->r, sizeof(n.kr.kr4)); send_nexthop_update(&n); } else /* down */ if (was_valid) send_nexthop_update(&n); - kroute_attach_nexthop(kn, kr); + kn->kroute = kr; + kr->r.flags |= F_NEXTHOP; + } + break; + case AF_INET6: + if ((kr6 = kroute6_match(&kn->nexthop.v6)) == NULL) { + if (was_valid) + send_nexthop_update(&n); + } else { /* match */ + if (kroute6_validate(&kr6->r)) { /* valid */ + n.valid = 1; + n.connected = kr6->r.flags & F_CONNECTED; + if (memcmp(&kr6->r.nexthop, &in6addr_any, + sizeof(struct in6_addr)) != 0) { + n.gateway.af = AF_INET6; + memcpy(&n.gateway.v4, &kr6->r.nexthop, + sizeof(struct in6_addr)); + } + memcpy(&n.kr.kr6, &kr6->r, sizeof(n.kr.kr6)); + send_nexthop_update(&n); + } else /* down */ + if (was_valid) + send_nexthop_update(&n); + + kn->kroute = kr6; + kr6->r.flags |= F_NEXTHOP; } break; } @@ -938,17 +1362,33 @@ kroute_match(in_addr_t key) return (NULL); } -void -kroute_attach_nexthop(struct knexthop_node *kn, struct kroute_node *kr) +struct kroute6_node * +kroute6_match(struct in6_addr *key) { - kn->kroute = kr; - kr->r.flags |= F_NEXTHOP; + int i; + struct kroute6_node *kr6; + struct in6_addr ina; + + /* we will never match the default route */ + for (i = 128; i > 0; i--) { + inet6applymask(&ina, key, i); + if ((kr6 = kroute6_find(&ina, i)) != NULL) + return (kr6); + } + + /* if we don't have a match yet, try to find a default route */ + if ((kr6 = kroute6_find(&in6addr_any, 0)) != NULL) + return (kr6); + + return (NULL); } void kroute_detach_nexthop(struct knexthop_node *kn) { struct knexthop_node *s; + struct kroute_node *k; + struct kroute6_node *k6; /* * check wether there's another nexthop depending on this kroute @@ -962,8 +1402,18 @@ kroute_detach_nexthop(struct knexthop_node *kn) s->kroute != kn->kroute; s = RB_NEXT(knexthop_tree, &knt, s)) ; /* nothing */ - if (s == NULL) - kn->kroute->r.flags &= ~F_NEXTHOP; + if (s == NULL) { + switch (kn->nexthop.af) { + case AF_INET: + k = kn->kroute; + k->r.flags &= ~F_NEXTHOP; + break; + case AF_INET6: + k6 = kn->kroute; + k6->r.flags &= ~F_NEXTHOP; + break; + } + } kn->kroute = NULL; } @@ -977,6 +1427,7 @@ int protect_lo(void) { struct kroute_node *kr; + struct kroute6_node *kr6; /* special protection for 127/8 */ if ((kr = calloc(1, sizeof(struct kroute_node))) == NULL) { @@ -990,6 +1441,18 @@ protect_lo(void) if (RB_INSERT(kroute_tree, &krt, kr) != NULL) free(kr); /* kernel route already there, no problem */ + /* special protection for loopback */ + if ((kr6 = calloc(1, sizeof(struct kroute6_node))) == NULL) { + log_warn("protect_lo"); + return (-1); + } + memcpy(&kr6->r.prefix, &in6addr_loopback, sizeof(kr6->r.prefix)); + kr6->r.prefixlen = 128; + kr->r.flags = F_KERNEL|F_CONNECTED; + + if (RB_INSERT(kroute6_tree, &krt6, kr6) != NULL) + free(kr6); /* kernel route already there, no problem */ + return (0); } @@ -1099,6 +1562,7 @@ if_change(u_short ifindex, int flags, struct if_data *ifd) { struct kif_node *kif; struct kif_kr *kkr; + struct kif_kr6 *kkr6; struct kroute_nexthop nh; struct knexthop_node *n; u_int8_t reachable; @@ -1132,14 +1596,43 @@ if_change(u_short ifindex, int flags, struct if_data *ifd) bzero(&nh, sizeof(nh)); memcpy(&nh.nexthop, &n->nexthop, sizeof(nh.nexthop)); - if (kroute_validate(&n->kroute->r)) { + if (kroute_validate(&kkr->kr->r)) { nh.valid = 1; nh.connected = 1; if ((nh.gateway.v4.s_addr = kkr->kr->r.nexthop.s_addr) != 0) nh.gateway.af = AF_INET; } - memcpy(&nh.kr, &kkr->kr->r, sizeof(nh.kr)); + memcpy(&nh.kr.kr4, &kkr->kr->r, + sizeof(nh.kr.kr4)); + send_nexthop_update(&nh); + } + } + LIST_FOREACH(kkr6, &kif->kroute6_l, entry) { + if (reachable) + kkr6->kr->r.flags &= ~F_DOWN; + else + kkr6->kr->r.flags |= F_DOWN; + + RB_FOREACH(n, knexthop_tree, &knt) + if (n->kroute == kkr6->kr) { + bzero(&nh, sizeof(nh)); + memcpy(&nh.nexthop, &n->nexthop, + sizeof(nh.nexthop)); + if (kroute6_validate(&kkr6->kr->r)) { + nh.valid = 1; + nh.connected = 1; + if (memcmp(&kkr6->kr->r.nexthop, + &in6addr_any, sizeof(struct + in6_addr))) { + nh.gateway.af = AF_INET6; + memcpy(&nh.gateway.v6, + &kkr6->kr->r.nexthop, + sizeof(struct in6_addr)); + } + } + memcpy(&nh.kr.kr6, &kkr6->kr->r, + sizeof(nh.kr.kr6)); send_nexthop_update(&nh); } } @@ -1245,6 +1738,77 @@ retry: } int +send_rt6msg(int fd, int action, struct kroute6 *kroute) +{ + struct { + struct rt_msghdr hdr; + struct sockaddr_in6 prefix; + struct sockaddr_in6 nexthop; + struct sockaddr_in6 mask; + } r; + + if (kr_state.fib_sync == 0) + return (0); + + bzero(&r, sizeof(r)); + r.hdr.rtm_msglen = sizeof(r); + r.hdr.rtm_version = RTM_VERSION; + r.hdr.rtm_type = action; + r.hdr.rtm_flags = RTF_PROTO1; + if (kroute->flags & F_BLACKHOLE) + r.hdr.rtm_flags |= RTF_BLACKHOLE; + if (kroute->flags & F_REJECT) + r.hdr.rtm_flags |= RTF_REJECT; + r.hdr.rtm_seq = kr_state.rtseq++; /* overflow doesn't matter */ + r.hdr.rtm_addrs = RTA_DST|RTA_GATEWAY|RTA_NETMASK; + r.prefix.sin6_len = sizeof(r.prefix); + r.prefix.sin6_family = AF_INET6; + memcpy(&r.prefix.sin6_addr, &kroute->prefix, sizeof(struct in6_addr)); + /* XXX scope does not matter or? */ + + r.nexthop.sin6_len = sizeof(r.nexthop); + r.nexthop.sin6_family = AF_INET6; + memcpy(&r.nexthop.sin6_addr, &kroute->nexthop, sizeof(struct in6_addr)); + if (memcmp(&kroute->nexthop, &in6addr_any, sizeof(struct in6_addr))) + r.hdr.rtm_flags |= RTF_GATEWAY; + + r.mask.sin6_len = sizeof(r.mask); + r.mask.sin6_family = AF_INET6; + memcpy(&r.mask.sin6_addr, prefixlen2mask6(kroute->prefixlen), + sizeof(struct in6_addr)); + +retry: + if (write(fd, &r, sizeof(r)) == -1) { + switch (errno) { + case ESRCH: + if (r.hdr.rtm_type == RTM_CHANGE) { + r.hdr.rtm_type = RTM_ADD; + goto retry; + } else if (r.hdr.rtm_type == RTM_DELETE) { + log_info("route %s/%u vanished before delete", + log_in6addr(&kroute->prefix), + kroute->prefixlen); + return (0); + } else { + log_warnx("send_rtmsg: action %u, " + "prefix %s/%u: %s", r.hdr.rtm_type, + log_in6addr(&kroute->prefix), + kroute->prefixlen, strerror(errno)); + return (0); + } + break; + default: + log_warnx("send_rtmsg: action %u, prefix %s/%u: %s", + r.hdr.rtm_type, log_in6addr(&kroute->prefix), + kroute->prefixlen, strerror(errno)); + return (0); + } + } + + return (0); +} + +int fetchtable(void) { size_t len; @@ -1344,6 +1908,107 @@ fetchtable(void) } int +fetchtable6(void) +{ + size_t len; + int mib[6]; + char *buf, *next, *lim; + struct rt_msghdr *rtm; + struct sockaddr *sa, *rti_info[RTAX_MAX]; + struct sockaddr_in6 *sa_in6; + struct kroute6_node *kr; + + mib[0] = CTL_NET; + mib[1] = AF_ROUTE; + mib[2] = 0; + mib[3] = AF_INET6; + mib[4] = NET_RT_DUMP; + mib[5] = 0; + + if (sysctl(mib, 6, NULL, &len, NULL, 0) == -1) { + log_warn("sysctl"); + return (-1); + } + if ((buf = malloc(len)) == NULL) { + log_warn("fetchtable"); + return (-1); + } + if (sysctl(mib, 6, buf, &len, NULL, 0) == -1) { + log_warn("sysctl"); + free(buf); + return (-1); + } + + lim = buf + len; + for (next = buf; next < lim; next += rtm->rtm_msglen) { + rtm = (struct rt_msghdr *)next; + sa = (struct sockaddr *)(rtm + 1); + get_rtaddrs(rtm->rtm_addrs, sa, rti_info); + + if ((sa = rti_info[RTAX_DST]) == NULL) + continue; + + if (rtm->rtm_flags & RTF_LLINFO) /* arp cache */ + continue; + +#ifdef RTF_MPATH + if (rtm->rtm_flags & RTF_MPATH) /* multipath */ + continue; +#endif + + if ((kr = calloc(1, sizeof(struct kroute6_node))) == NULL) { + log_warn("fetchtable"); + free(buf); + return (-1); + } + + kr->r.flags = F_KERNEL; + kr->r.ifindex = rtm->rtm_index; + + switch (sa->sa_family) { + case AF_INET6: + memcpy(&kr->r.prefix, + &((struct sockaddr_in6 *)sa)->sin6_addr, + sizeof(kr->r.prefix)); + + sa_in6 = (struct sockaddr_in6 *)rti_info[RTAX_NETMASK]; + if (rtm->rtm_flags & RTF_STATIC) + kr->r.flags |= F_STATIC; + if (sa_in6 != NULL) { + if (sa_in6->sin6_len == 0) + break; + kr->r.prefixlen = + mask2prefixlen6(&sa_in6->sin6_addr); + } else if (rtm->rtm_flags & RTF_HOST) + kr->r.prefixlen = 128; + else + fatalx("INET6 route without netmask"); + break; + default: + free(kr); + continue; + } + + if ((sa = rti_info[RTAX_GATEWAY]) != NULL) + switch (sa->sa_family) { + case AF_INET6: + memcpy(&kr->r.nexthop, + &((struct sockaddr_in6 *)sa)->sin6_addr, + sizeof(kr->r.nexthop)); + break; + case AF_LINK: + kr->r.flags |= F_CONNECTED; + break; + } + + kroute6_insert(kr); + + } + free(buf); + return (0); +} + +int fetchifs(int ifindex) { size_t len; @@ -1423,12 +2088,6 @@ dispatch_rtmsg(void) struct rt_msghdr *rtm; struct if_msghdr ifm; struct sockaddr *sa, *rti_info[RTAX_MAX]; - struct sockaddr_in *sa_in; - struct kroute_node *kr; - struct in_addr prefix, nexthop; - u_int8_t prefixlen; - int flags, oflags; - u_short ifindex; if ((n = read(kr_state.fd, &buf, sizeof(buf))) == -1) { log_warn("dispatch_rtmsg: read error"); @@ -1446,116 +2105,19 @@ dispatch_rtmsg(void) sa = (struct sockaddr *)(rtm + 1); get_rtaddrs(rtm->rtm_addrs, sa, rti_info); - prefix.s_addr = 0; - prefixlen = 0; - flags = F_KERNEL; - nexthop.s_addr = 0; - ifindex = 0; - if (rtm->rtm_pid == kr_state.pid) /* cause by us */ continue; if (rtm->rtm_errno) /* failed attempts... */ continue; - if (rtm->rtm_type == RTM_ADD || rtm->rtm_type == RTM_CHANGE || - rtm->rtm_type == RTM_DELETE) { - if (rtm->rtm_flags & RTF_LLINFO) /* arp cache */ - continue; - switch (sa->sa_family) { - case AF_INET: - prefix.s_addr = - ((struct sockaddr_in *)sa)->sin_addr.s_addr; - sa_in = (struct sockaddr_in *) - rti_info[RTAX_NETMASK]; - if (rtm->rtm_flags & RTF_STATIC) - flags |= F_STATIC; - if (sa_in != NULL) { - if (sa_in->sin_len != 0) - prefixlen = mask2prefixlen( - sa_in->sin_addr.s_addr); - } else if (rtm->rtm_flags & RTF_HOST) - prefixlen = 32; - else - prefixlen = - prefixlen_classful(prefix.s_addr); - break; - default: - continue; - } - } - - if ((sa = rti_info[RTAX_GATEWAY]) != NULL) - switch (sa->sa_family) { - case AF_INET: - nexthop.s_addr = - ((struct sockaddr_in *)sa)->sin_addr.s_addr; - break; - case AF_LINK: - flags |= F_CONNECTED; - ifindex = rtm->rtm_index; - break; - } - switch (rtm->rtm_type) { case RTM_ADD: case RTM_CHANGE: - if (nexthop.s_addr == 0 && !(flags & F_CONNECTED)) { - log_warnx("dispatch_rtmsg no nexthop for %s/%u", - inet_ntoa(prefix), prefixlen); - continue; - } - - if ((kr = kroute_find(prefix.s_addr, prefixlen)) != - NULL) { - if (kr->r.flags & F_KERNEL) { - kr->r.nexthop.s_addr = nexthop.s_addr; - if (kr->r.flags & F_NEXTHOP) - flags |= F_NEXTHOP; - oflags = kr->r.flags; - kr->r.flags = flags; - if ((oflags & F_CONNECTED) && - !(flags & F_CONNECTED)) { - kif_kr_remove(kr); - kr_redistribute( - IMSG_NETWORK_REMOVE, - &kr->r); - } - if ((flags & F_CONNECTED) && - !(oflags & F_CONNECTED)) { - kif_kr_insert(kr); - kr_redistribute( - IMSG_NETWORK_ADD, - &kr->r); - } - } - } else if (rtm->rtm_type == RTM_CHANGE) { - log_warnx("change req for %s/%u: not " - "in table", inet_ntoa(prefix), - prefixlen); - continue; - } else { - if ((kr = calloc(1, - sizeof(struct kroute_node))) == NULL) { - log_warn("dispatch_rtmsg"); - return (-1); - } - kr->r.prefix.s_addr = prefix.s_addr; - kr->r.prefixlen = prefixlen; - kr->r.nexthop.s_addr = nexthop.s_addr; - kr->r.flags = flags; - kr->r.ifindex = ifindex; - - kroute_insert(kr); - } - break; case RTM_DELETE: - if ((kr = kroute_find(prefix.s_addr, prefixlen)) == - NULL) - continue; - if (!(kr->r.flags & F_KERNEL)) + if (rtm->rtm_flags & RTF_LLINFO) /* arp cache */ continue; - if (kroute_remove(kr) == -1) + if (dispatch_rtmsg_addr(rtm, rti_info) == -1) return (-1); break; case RTM_IFINFO: @@ -1573,3 +2135,208 @@ dispatch_rtmsg(void) } return (0); } + +int +dispatch_rtmsg_addr(struct rt_msghdr *rtm, struct sockaddr *rti_info[RTAX_MAX]) +{ + struct sockaddr *sa; + struct sockaddr_in *sa_in; + struct sockaddr_in6 *sa_in6; + struct kroute_node *kr; + struct kroute6_node *kr6; + struct bgpd_addr prefix; + int flags, oflags; + u_int16_t ifindex; + u_int8_t prefixlen; + + flags = F_KERNEL; + ifindex = 0; + prefixlen = 0; + bzero(&prefix, sizeof(prefix)); + + if ((sa = rti_info[RTAX_DST]) == NULL) + return (-1); + + prefix.af = sa->sa_family; + switch (prefix.af) { + case AF_INET: + prefix.v4.s_addr = ((struct sockaddr_in *)sa)->sin_addr.s_addr; + sa_in = (struct sockaddr_in *)rti_info[RTAX_NETMASK]; + if (rtm->rtm_flags & RTF_STATIC) + flags |= F_STATIC; + if (sa_in != NULL) { + if (sa_in->sin_len != 0) + prefixlen = mask2prefixlen( + sa_in->sin_addr.s_addr); + } else if (rtm->rtm_flags & RTF_HOST) + prefixlen = 32; + else + prefixlen = + prefixlen_classful(prefix.v4.s_addr); + break; + case AF_INET6: + memcpy(&prefix.v6, &((struct sockaddr_in6 *)sa)->sin6_addr, + sizeof(struct in6_addr)); + sa_in6 = (struct sockaddr_in6 *)rti_info[RTAX_NETMASK]; + if (rtm->rtm_flags & RTF_STATIC) + flags |= F_STATIC; + if (sa_in6 != NULL) { + if (sa_in6->sin6_len != 0) + prefixlen = mask2prefixlen6(&sa_in6->sin6_addr); + } else if (rtm->rtm_flags & RTF_HOST) + prefixlen = 128; + else + fatalx("in6 net addr without netmask"); + break; + default: + return (0); + } + + if (rtm->rtm_type == RTM_DELETE) { + switch (prefix.af) { + case AF_INET: + if ((kr = kroute_find(prefix.v4.s_addr, + prefixlen)) == NULL) + return (0); + if (!(kr->r.flags & F_KERNEL)) + return (0); + if (kroute_remove(kr) == -1) + return (-1); + break; + case AF_INET6: + if ((kr6 = kroute6_find(&prefix.v6, prefixlen)) == NULL) + return (0); + if (!(kr6->r.flags & F_KERNEL)) + return (0); + if (kroute6_remove(kr6) == -1) + return (-1); + break; + } + return (0); + } + + if ((sa = rti_info[RTAX_GATEWAY]) != NULL) + switch (sa->sa_family) { + case AF_LINK: + flags |= F_CONNECTED; + ifindex = rtm->rtm_index; + sa = NULL; + break; + } + + if (sa == NULL && !(flags & F_CONNECTED)) { + log_warnx("dispatch_rtmsg no nexthop for %s/%u", + log_addr(&prefix), prefixlen); + return (0); + } + + switch (prefix.af) { + case AF_INET: + sa_in = (struct sockaddr_in *)sa; + if ((kr = kroute_find(prefix.v4.s_addr, prefixlen)) != NULL) { + if (kr->r.flags & F_KERNEL) { + if (sa_in != NULL) + kr->r.nexthop.s_addr = + sa_in->sin_addr.s_addr; + else + kr->r.nexthop.s_addr = 0; + + if (kr->r.flags & F_NEXTHOP) + flags |= F_NEXTHOP; + oflags = kr->r.flags; + kr->r.flags = flags; + if ((oflags & F_CONNECTED) && + !(flags & F_CONNECTED)) { + kif_kr_remove(kr); + kr_redistribute(IMSG_NETWORK_REMOVE, + &kr->r); + } + if ((flags & F_CONNECTED) && + !(oflags & F_CONNECTED)) { + kif_kr_insert(kr); + kr_redistribute(IMSG_NETWORK_ADD, + &kr->r); + } + } + } else if (rtm->rtm_type == RTM_CHANGE) { + log_warnx("change req for %s/%u: not in table", + log_addr(&prefix), prefixlen); + return (0); + } else { + if ((kr = calloc(1, + sizeof(struct kroute_node))) == NULL) { + log_warn("dispatch_rtmsg"); + return (-1); + } + kr->r.prefix.s_addr = prefix.v4.s_addr; + kr->r.prefixlen = prefixlen; + if (sa_in != NULL) + kr->r.nexthop.s_addr = sa_in->sin_addr.s_addr; + else + kr->r.nexthop.s_addr = 0; + kr->r.flags = flags; + kr->r.ifindex = ifindex; + + kroute_insert(kr); + } + break; + case AF_INET6: + sa_in6 = (struct sockaddr_in6 *)sa; + if ((kr6 = kroute6_find(&prefix.v6, prefixlen)) != NULL) { + if (kr6->r.flags & F_KERNEL) { + if (sa_in6 != NULL) + memcpy(&kr6->r.nexthop, + &sa_in6->sin6_addr, + sizeof(struct in6_addr)); + else + memcpy(&kr6->r.nexthop, + &in6addr_any, + sizeof(struct in6_addr)); + + if (kr6->r.flags & F_NEXTHOP) + flags |= F_NEXTHOP; + oflags = kr6->r.flags; + kr6->r.flags = flags; + if ((oflags & F_CONNECTED) && + !(flags & F_CONNECTED)) { + kif_kr6_remove(kr6); + kr_redistribute6(IMSG_NETWORK_REMOVE, + &kr6->r); + } + if ((flags & F_CONNECTED) && + !(oflags & F_CONNECTED)) { + kif_kr6_insert(kr6); + kr_redistribute6(IMSG_NETWORK_ADD, + &kr6->r); + } + } + } else if (rtm->rtm_type == RTM_CHANGE) { + log_warnx("change req for %s/%u: not in table", + log_addr(&prefix), prefixlen); + return (0); + } else { + if ((kr6 = calloc(1, + sizeof(struct kroute6_node))) == NULL) { + log_warn("dispatch_rtmsg"); + return (-1); + } + memcpy(&kr6->r.prefix, &prefix.v6, + sizeof(struct in6_addr)); + kr6->r.prefixlen = prefixlen; + if (sa_in6 != NULL) + memcpy(&kr6->r.nexthop, &sa_in6->sin6_addr, + sizeof(struct in6_addr)); + else + memcpy(&kr6->r.nexthop, &in6addr_any, + sizeof(struct in6_addr)); + kr6->r.flags = flags; + kr6->r.ifindex = ifindex; + + kroute6_insert(kr6); + } + break; + } + + return (0); +} + |