diff options
author | Henning Brauer <henning@cvs.openbsd.org> | 2004-01-08 16:07:16 +0000 |
---|---|---|
committer | Henning Brauer <henning@cvs.openbsd.org> | 2004-01-08 16:07:16 +0000 |
commit | 3e38e576775f41701e85f4227019edef30a1e9b7 (patch) | |
tree | 6d584a3d87c1d1e39b14d7922a2777cbe68cbf0b | |
parent | 58b037aa2a117fe4c474549ee14e4c6bfbba585d (diff) |
big reorganization.
much nicer now.
ok claudio@
-rw-r--r-- | usr.sbin/bgpd/kroute.c | 967 |
1 files changed, 536 insertions, 431 deletions
diff --git a/usr.sbin/bgpd/kroute.c b/usr.sbin/bgpd/kroute.c index 153f93d1e51..c7877e0f589 100644 --- a/usr.sbin/bgpd/kroute.c +++ b/usr.sbin/bgpd/kroute.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kroute.c,v 1.51 2004/01/08 11:40:03 henning Exp $ */ +/* $OpenBSD: kroute.c,v 1.52 2004/01/08 16:07:15 henning Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> @@ -69,26 +69,40 @@ struct kif_node { struct kif_kr_head kroute_l; }; +int kroute_compare(struct kroute_node *, struct kroute_node *); +int knexthop_compare(struct knexthop_node *, struct knexthop_node *); +int kif_compare(struct kif_node *, struct kif_node *); + +struct kroute_node *kroute_tree_find(in_addr_t, u_int8_t); +int kroute_tree_insert(struct kroute_node *); +int kroute_tree_remove(struct kroute_node *); + +struct knexthop_node *knexthop_tree_find(in_addr_t); +int knexthop_tree_insert(struct knexthop_node *); +int knexthop_tree_remove(struct knexthop_node *); + +struct kif_node *kif_tree_find(int); +int kif_tree_insert(struct kif_node *kif); + +int kif_kr_insert(struct kroute_node *); +int kif_kr_remove(struct kroute_node *); + +void knexthop_validate(struct knexthop_node *); +struct kroute_node *kroute_match(in_addr_t); +void kroute_attach_nexthop(struct knexthop_node *, + struct kroute_node *); +void kroute_detach_nexthop(struct knexthop_node *); + int kroute_protect_lo(void); -int kroute_msg(int, int, struct kroute *); -int kroute_insert(struct kroute_node *); -int kroute_insert_connected(struct kroute_node *); -int kroute_remove_connected(struct kroute_node *); -int kroute_compare(struct kroute_node *, struct kroute_node *); -void get_rtaddrs(int, struct sockaddr *, struct sockaddr **); u_int8_t prefixlen_classful(in_addr_t); u_int8_t mask2prefixlen(in_addr_t); -int kroute_fetchtable(void); -int kroute_fetchifs(int); -int kroute_remove(struct kroute_node *); -int kroute_nexthop_insert(in_addr_t, struct kroute_nexthop *); -int kroute_nexthop_checkmatch(in_addr_t, struct knexthop_node *); -int knexthop_compare(struct knexthop_node *, - struct knexthop_node *); -int kif_compare(struct kif_node *, struct kif_node *); +void get_rtaddrs(int, struct sockaddr *, struct sockaddr **); void kroute_interface_statuschange(u_short, int); -struct kroute_node *kroute_match(in_addr_t); +int kroute_send_rtmsg(int, int, struct kroute *); +int kroute_get_rtmsg(void); +int kroute_fetchtable(void); +int kroute_fetchifs(int); RB_HEAD(kroute_tree, kroute_node) kroute_tree, krt; RB_PROTOTYPE(kroute_tree, kroute_node, entry, kroute_compare); @@ -108,6 +122,11 @@ RB_GENERATE(kif_tree, kif_node, entry, kif_compare); #define F_NEXTHOP 0x0008 #define F_DOWN 0x0010 + +/* + * exported functions + */ + int kroute_init(int fs) { @@ -145,129 +164,197 @@ kroute_init(int fs) } int -kroute_protect_lo(void) +kroute_change(struct kroute *kroute) { struct kroute_node *kr; + int action = RTM_ADD; - /* special protection for 127/8 */ - if ((kr = calloc(1, sizeof(struct kroute_node))) == NULL) { - log_err("kroute_protect_lo"); - return (-1); + if ((kr = kroute_tree_find(kroute->prefix, kroute->prefixlen)) != + NULL) { + if (kr->flags & F_BGPD_INSERTED) + action = RTM_CHANGE; + else /* a non-bgp route already exists. not a problem */ + return (0); } - kr->r.prefix = inet_addr("127.0.0.1"); - kr->r.prefixlen = 8; - kr->r.nexthop = 0; - kr->flags = F_KERNEL|F_CONNECTED; - if (RB_INSERT(kroute_tree, &krt, kr) != NULL) - free(kr); /* kernel route already there, no problem */ + if (kroute_send_rtmsg(kr_state.fd, action, kroute) == -1) + return (-1); + + if (action == RTM_ADD) { + if ((kr = calloc(1, sizeof(struct kroute_node))) == NULL) { + log_err("kroute_change"); + return (-1); + } + kr->r.prefix = kroute->prefix; + kr->r.prefixlen = kroute->prefixlen; + kr->r.nexthop = kroute->nexthop; + kr->flags = F_BGPD_INSERTED; + + if (kroute_tree_insert(kr) == -1) + free(kr); + } else + kr->r.nexthop = kroute->nexthop; return (0); } int -kroute_msg(int fd, int action, struct kroute *kroute) +kroute_delete(struct kroute *kroute) { - struct { - struct rt_msghdr hdr; - struct sockaddr_in prefix; - struct sockaddr_in nexthop; - struct sockaddr_in mask; - } r; - ssize_t n; + struct kroute_node *kr; - if (kr_state.fib_sync == 0) + if ((kr = kroute_tree_find(kroute->prefix, kroute->prefixlen)) == NULL) 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_GATEWAY|RTF_PROTO1; - r.hdr.rtm_seq = kr_state.rtseq++; /* overflow doesn't matter */ - r.hdr.rtm_addrs = RTA_DST|RTA_GATEWAY|RTA_NETMASK; - r.prefix.sin_len = sizeof(r.prefix); - r.prefix.sin_family = AF_INET; - r.prefix.sin_addr.s_addr = kroute->prefix; - r.nexthop.sin_len = sizeof(r.nexthop); - r.nexthop.sin_family = AF_INET; - r.nexthop.sin_addr.s_addr = kroute->nexthop; - r.mask.sin_len = sizeof(r.mask); - r.mask.sin_family = AF_INET; - r.mask.sin_addr.s_addr = htonl(0xffffffff << (32 - kroute->prefixlen)); + if (!(kr->flags & F_BGPD_INSERTED)) + return (0); -retry: - if ((n = 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) { - logit(LOG_INFO, - "route %s/%u vanished before delete", - log_ntoa(kroute->prefix), - kroute->prefixlen); - return (0); - } else { - logit(LOG_CRIT, - "kroute_msg: action %u, prefix %s/%u: %s", - r.hdr.rtm_type, log_ntoa(kroute->prefix), - kroute->prefixlen, strerror(errno)); - return (0); - } - break; - default: - logit(LOG_CRIT, - "kroute_msg: action %u, prefix %s/%u: %s", - r.hdr.rtm_type, log_ntoa(kroute->prefix), - kroute->prefixlen, strerror(errno)); - return (0); - } - } + if (kroute_send_rtmsg(kr_state.fd, RTM_DELETE, kroute) == -1) + return (-1); + + if (kroute_tree_remove(kr) == -1) + return (-1); return (0); } -int -kroute_change(struct kroute *kroute) +void +kroute_shutdown(void) { - struct kroute_node *kr, s; - int action = RTM_ADD; + kroute_fib_decouple(); +} - s.r.prefix = kroute->prefix; - s.r.prefixlen = kroute->prefixlen; +void +kroute_fib_couple(void) +{ + struct kroute_node *kr; - if ((kr = RB_FIND(kroute_tree, &krt, &s)) != NULL) { - if (kr->flags & F_BGPD_INSERTED) - action = RTM_CHANGE; - else /* a non-bgp route already exists. not a problem */ - return (0); - } + if (kr_state.fib_sync == 1) /* already coupled */ + return; - if (kroute_msg(kr_state.fd, action, kroute) == -1) - return (-1); + kr_state.fib_sync = 1; - if (action == RTM_ADD) { - if ((kr = calloc(1, sizeof(struct kroute_node))) == NULL) { - log_err("kroute_change"); + RB_FOREACH(kr, kroute_tree, &krt) + if ((kr->flags & F_BGPD_INSERTED)) + kroute_send_rtmsg(kr_state.fd, RTM_ADD, &kr->r); + + logit(LOG_INFO, "kernel routing table coupled"); +} + +void +kroute_fib_decouple(void) +{ + struct kroute_node *kr; + + if (kr_state.fib_sync == 0) /* already decoupled */ + return; + + RB_FOREACH(kr, kroute_tree, &krt) + if ((kr->flags & F_BGPD_INSERTED)) + kroute_send_rtmsg(kr_state.fd, RTM_DELETE, &kr->r); + + kr_state.fib_sync = 0; + + logit(LOG_INFO, "kernel routing table decoupled"); +} + +int +kroute_dispatch_msg(void) +{ + return (kroute_get_rtmsg()); +} + +int +kroute_nexthop_add(in_addr_t key) +{ + struct knexthop_node *h; + + if ((h = knexthop_tree_find(key)) != NULL) { + /* should not happen... this is really an error path */ + struct kroute_nexthop nh; + + bzero(&nh, sizeof(nh)); + nh.nexthop = key; + if (h->kroute != NULL) { + nh.valid = 1; + nh.connected = h->kroute->flags & F_CONNECTED; + nh.gateway = h->kroute->r.nexthop; + } + send_nexthop_update(&nh); + } else { + if ((h = calloc(1, sizeof(struct knexthop_node))) == NULL) { + log_err("kroute_nexthop_add"); return (-1); } - kr->r.prefix = kroute->prefix; - kr->r.prefixlen = kroute->prefixlen; - kr->r.nexthop = kroute->nexthop; - kr->flags = F_BGPD_INSERTED; + h->nexthop = key; - if (kroute_insert(kr) == -1) - free(kr); - } else - kr->r.nexthop = kroute->nexthop; + if (knexthop_tree_insert(h) == -1) + return (-1); + } return (0); } +void +kroute_nexthop_delete(in_addr_t key) +{ + struct knexthop_node *kn; + + if ((kn = knexthop_tree_find(key)) == NULL) + return; + + knexthop_tree_remove(kn); +} + + +/* + * RB-tree compare functions + */ + +int +kroute_compare(struct kroute_node *a, struct kroute_node *b) +{ + if (a->r.prefix < b->r.prefix) + return (-1); + if (a->r.prefix > b->r.prefix) + return (1); + if (a->r.prefixlen < b->r.prefixlen) + return (-1); + if (a->r.prefixlen > b->r.prefixlen) + return (1); + return (0); +} + +int +knexthop_compare(struct knexthop_node *a, struct knexthop_node *b) +{ + return (b->nexthop - a->nexthop); +} + int -kroute_insert(struct kroute_node *kr) +kif_compare(struct kif_node *a, struct kif_node *b) +{ + return (b->ifindex - a->ifindex); +} + + +/* + * tree management functions + */ + +struct kroute_node * +kroute_tree_find(in_addr_t prefix, u_int8_t prefixlen) +{ + struct kroute_node s; + + s.r.prefix = prefix; + s.r.prefixlen = prefixlen; + + return (RB_FIND(kroute_tree, &krt, &s)); +} + +int +kroute_tree_insert(struct kroute_node *kr) { struct knexthop_node *h; struct kroute_nexthop nh; @@ -276,6 +363,7 @@ kroute_insert(struct kroute_node *kr) if (RB_INSERT(kroute_tree, &krt, kr) != NULL) { logit(LOG_CRIT, "kroute_tree insert failed for %s/%u", log_ntoa(kr->r.prefix), kr->r.prefixlen); + free(kr); return (-1); } @@ -297,163 +385,271 @@ kroute_insert(struct kroute_node *kr) } } if (kr->flags & F_CONNECTED) - if (kroute_insert_connected(kr) == -1) + if (kif_kr_insert(kr) == -1) return (-1); } return (0); } int -kroute_insert_connected(struct kroute_node *kr) +kroute_tree_remove(struct kroute_node *kr) { - struct kif_node *kif, s; - struct kif_kr *kkr; + struct knexthop_node *s; + + if (RB_REMOVE(kroute_tree, &krt, kr) == NULL) { + logit(LOG_CRIT, "kroute_tree_remove failed for %s/%u", + log_ntoa(kr->r.prefix), kr->r.prefixlen); + return (-1); + } + + /* check wether a nexthop depends on this kroute */ + if ((kr->flags & F_KERNEL) && (kr->flags & F_NEXTHOP)) + RB_FOREACH(s, knexthop_tree, &knt) + if (s->kroute == kr) + knexthop_validate(s); + + if (kr->flags & F_CONNECTED) + if (kif_kr_remove(kr) == -1) { + free(kr); + return (-1); + } + + free(kr); + return (0); +} + +struct knexthop_node * +knexthop_tree_find(in_addr_t key) +{ + struct knexthop_node s; bzero(&s, sizeof(s)); - s.ifindex = kr->ifindex; + s.nexthop = key; - if ((kif = RB_FIND(kif_tree, &kit, &s)) == NULL) { - logit(LOG_CRIT, "interface with index %u not found", - kr->ifindex); - return (0); - } + return (RB_FIND(knexthop_tree, &knt, &s)); +} - if ((kkr = calloc(1, sizeof(struct kif_kr))) == NULL) { - log_err("kroute_insert_connected"); +int +knexthop_tree_insert(struct knexthop_node *kn) +{ + if (RB_INSERT(knexthop_tree, &knt, kn) != NULL) { + logit(LOG_CRIT, "knexthop_tree insert failed for %s", + log_ntoa(kn->nexthop)); + free(kn); return (-1); } - kkr->kr = kr; - - LIST_INSERT_HEAD(&kif->kroute_l, kkr, entry); + knexthop_validate(kn); return (0); } int -kroute_remove_connected(struct kroute_node *kr) +knexthop_tree_remove(struct knexthop_node *kn) +{ + kroute_detach_nexthop(kn); + + if (RB_REMOVE(knexthop_tree, &knt, kn) == NULL) { + logit(LOG_CRIT, "knexthop_tree_remove failed for %s", + log_ntoa(kn->nexthop)); + return (-1); + } + + free(kn); + return (0); +} + +struct kif_node * +kif_tree_find(int ifindex) { struct kif_node *kif, s; - struct kif_kr *a; bzero(&s, sizeof(s)); - s.ifindex = kr->ifindex; + s.ifindex = ifindex; - if ((kif = RB_FIND(kif_tree, &kit, &s)) == NULL) { - logit(LOG_CRIT, "interface with index %u not found", - kr->ifindex); - return (0); - } + if ((kif = RB_FIND(kif_tree, &kit, &s)) != NULL) + return (kif); - for (a = LIST_FIRST(&kif->kroute_l); a != NULL && a->kr != kr; - a = LIST_NEXT(a, entry)) - ; /* nothing */ + /* check wether the interface showed up now */ + kroute_fetchifs(ifindex); + return (RB_FIND(kif_tree, &kit, &s)); +} - if (a == NULL) { - logit(LOG_CRIT, "can't remove connected route from interface " - "with index %u: not found", kr->ifindex); +int +kif_tree_insert(struct kif_node *kif) +{ + if (RB_INSERT(kif_tree, &kit, kif) != NULL) { + logit(LOG_CRIT, "RB_INSERT(kif_tree, &kit, kif)"); + free(kif); return (-1); } - LIST_REMOVE(a, entry); - free(a); - return (0); } + int -kroute_delete(struct kroute *kroute) +kif_kr_insert(struct kroute_node *kr) { - struct kroute_node *kr, s; - - s.r.prefix = kroute->prefix; - s.r.prefixlen = kroute->prefixlen; - - if ((kr = RB_FIND(kroute_tree, &krt, &s)) == NULL) - return (0); + struct kif_node *kif; + struct kif_kr *kkr; - if (!(kr->flags & F_BGPD_INSERTED)) + if ((kif = kif_tree_find(kr->ifindex)) == NULL) { + logit(LOG_CRIT, "interface with index %u not found", + kr->ifindex); return (0); + } - if (kroute_msg(kr_state.fd, RTM_DELETE, kroute) == -1) + if ((kkr = calloc(1, sizeof(struct kif_kr))) == NULL) { + log_err("kif_kr_insert"); return (-1); + } - RB_REMOVE(kroute_tree, &krt, kr); - free(kr); + kkr->kr = kr; + + LIST_INSERT_HEAD(&kif->kroute_l, kkr, entry); return (0); } int -kroute_compare(struct kroute_node *a, struct kroute_node *b) +kif_kr_remove(struct kroute_node *kr) { - if (a->r.prefix < b->r.prefix) - return (-1); - if (a->r.prefix > b->r.prefix) - return (1); - if (a->r.prefixlen < b->r.prefixlen) + struct kif_node *kif; + struct kif_kr *kkr; + + if ((kif = kif_tree_find(kr->ifindex)) == NULL) { + logit(LOG_CRIT, "interface with index %u not found", + kr->ifindex); + return (0); + } + + for (kkr = LIST_FIRST(&kif->kroute_l); kkr != NULL && kkr->kr != kr; + kkr = LIST_NEXT(kkr, entry)) + ; /* nothing */ + + if (kkr == NULL) { + logit(LOG_CRIT, "can't remove connected route from interface " + "with index %u: not found", kr->ifindex); return (-1); - if (a->r.prefixlen > b->r.prefixlen) - return (1); + } + + LIST_REMOVE(kkr, entry); + free(kkr); + return (0); } +/* + * nexthop validation + */ + void -kroute_shutdown(void) +knexthop_validate(struct knexthop_node *kn) { - kroute_fib_decouple(); + struct kroute_node *kr; + struct kroute_nexthop n; + int was_valid = 0; + + if (kn->kroute != NULL && (!(kn->kroute->flags & F_DOWN))) + was_valid = 1; + + bzero(&n, sizeof(n)); + n.nexthop = kn->nexthop; + + if ((kr = kroute_match(kn->nexthop)) == NULL) { /* no match */ + if (was_valid) + send_nexthop_update(&n); + kroute_detach_nexthop(kn); + } else { /* found match */ + if (kr->flags & F_DOWN) { /* but is down */ + if (was_valid) + send_nexthop_update(&n); + } else { /* valid route */ + if (!was_valid) { + n.valid = 1; + n.connected = kr->flags & F_CONNECTED; + n.gateway = kr->r.nexthop; + send_nexthop_update(&n); + } + } + kroute_attach_nexthop(kn, kr); + } } -void -kroute_fib_couple(void) +struct kroute_node * +kroute_match(in_addr_t key) { + int i; struct kroute_node *kr; + in_addr_t ina; - if (kr_state.fib_sync == 1) /* already coupled */ - return; + ina = ntohl(key); - kr_state.fib_sync = 1; + /* we will never match the default route */ + for (i = 32; i > 0; i--) + if ((kr = kroute_tree_find( + htonl(ina & (0xffffffff << (32 - i))), i)) != NULL) + return (kr); - RB_FOREACH(kr, kroute_tree, &krt) - if ((kr->flags & F_BGPD_INSERTED)) - kroute_msg(kr_state.fd, RTM_ADD, &kr->r); + /* if we don't have a match yet, try to find a default route */ + if ((kr = kroute_tree_find(0, 0)) != NULL) + return (kr); - logit(LOG_INFO, "kernel routing table coupled"); + return (NULL); } void -kroute_fib_decouple(void) +kroute_attach_nexthop(struct knexthop_node *kn, struct kroute_node *kr) { - struct kroute_node *kr; + kn->kroute = kr; + kr->flags |= F_NEXTHOP; +} - if (kr_state.fib_sync == 0) /* already decoupled */ - return; +void +kroute_detach_nexthop(struct knexthop_node *kn) +{ + struct knexthop_node *s; - RB_FOREACH(kr, kroute_tree, &krt) - if ((kr->flags & F_BGPD_INSERTED)) - kroute_msg(kr_state.fd, RTM_DELETE, &kr->r); + /* + * check wether there's another nexthop depending on this kroute + * if not remove the flag + */ - kr_state.fib_sync = 0; + for (s = RB_MIN(knexthop_tree, &knt); s != NULL && + s->kroute != s->kroute; s = RB_NEXT(knexthop_tree, &knt, s)) + ; /* nothing */ - logit(LOG_INFO, "kernel routing table decoupled"); + if (s == NULL) + kn->kroute->flags &= ~F_NEXTHOP; + + kn->kroute = NULL; } -#define ROUNDUP(a, size) \ - (((a) & ((size) - 1)) ? (1 + ((a) | ((size) - 1))) : (a)) -void -get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info) +/* + * misc helpers + */ + +int +kroute_protect_lo(void) { - int i; + struct kroute_node *kr; - for (i = 0; i < RTAX_MAX; i++) { - if (addrs & (1 << i)) { - rti_info[i] = sa; - sa = (struct sockaddr *)((char *)(sa) + - ROUNDUP(sa->sa_len, sizeof(long))); - } else - rti_info[i] = NULL; + /* special protection for 127/8 */ + if ((kr = calloc(1, sizeof(struct kroute_node))) == NULL) { + log_err("kroute_protect_lo"); + return (-1); } + kr->r.prefix = inet_addr("127.0.0.1"); + kr->r.prefixlen = 8; + kr->r.nexthop = 0; + kr->flags = F_KERNEL|F_CONNECTED; + + if (RB_INSERT(kroute_tree, &krt, kr) != NULL) + free(kr); /* kernel route already there, no problem */ + + return (0); } u_int8_t @@ -482,6 +678,128 @@ mask2prefixlen(in_addr_t ina) return (33 - ffs(ntohl(ina))); } +#define ROUNDUP(a, size) \ + (((a) & ((size) - 1)) ? (1 + ((a) | ((size) - 1))) : (a)) + +void +get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info) +{ + int i; + + for (i = 0; i < RTAX_MAX; i++) { + if (addrs & (1 << i)) { + rti_info[i] = sa; + sa = (struct sockaddr *)((char *)(sa) + + ROUNDUP(sa->sa_len, sizeof(long))); + } else + rti_info[i] = NULL; + } +} + +void +kroute_interface_statuschange(u_short ifindex, int flags) +{ + struct kif_node *kif; + struct kif_kr *kkr; + struct kroute_nexthop nh; + struct knexthop_node *n; + + if ((kif = kif_tree_find(ifindex)) == NULL) { + logit(LOG_CRIT, "interface with index %u not found", + ifindex); + return; + } + + LIST_FOREACH(kkr, &kif->kroute_l, entry) { + if (flags & IFF_UP) + kkr->kr->flags &= ~F_DOWN; + else + kkr->kr->flags |= F_DOWN; + + RB_FOREACH(n, knexthop_tree, &knt) + if (n->kroute == kkr->kr) { + bzero(&nh, sizeof(nh)); + nh.nexthop = n->nexthop; + if (!(kkr->kr->flags & F_DOWN)) { + nh.valid = 1; + nh.connected = 1; + nh.gateway = kkr->kr->r.nexthop; + } + send_nexthop_update(&nh); + } + } +} + + +/* + * rtsock related functions + */ + +int +kroute_send_rtmsg(int fd, int action, struct kroute *kroute) +{ + struct { + struct rt_msghdr hdr; + struct sockaddr_in prefix; + struct sockaddr_in nexthop; + struct sockaddr_in mask; + } r; + ssize_t n; + + 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_GATEWAY|RTF_PROTO1; + r.hdr.rtm_seq = kr_state.rtseq++; /* overflow doesn't matter */ + r.hdr.rtm_addrs = RTA_DST|RTA_GATEWAY|RTA_NETMASK; + r.prefix.sin_len = sizeof(r.prefix); + r.prefix.sin_family = AF_INET; + r.prefix.sin_addr.s_addr = kroute->prefix; + r.nexthop.sin_len = sizeof(r.nexthop); + r.nexthop.sin_family = AF_INET; + r.nexthop.sin_addr.s_addr = kroute->nexthop; + r.mask.sin_len = sizeof(r.mask); + r.mask.sin_family = AF_INET; + r.mask.sin_addr.s_addr = htonl(0xffffffff << (32 - kroute->prefixlen)); + +retry: + if ((n = 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) { + logit(LOG_INFO, + "route %s/%u vanished before delete", + log_ntoa(kroute->prefix), + kroute->prefixlen); + return (0); + } else { + logit(LOG_CRIT, + "kroute_send_rtmsg: action %u, " + "prefix %s/%u: %s", r.hdr.rtm_type, + log_ntoa(kroute->prefix), kroute->prefixlen, + strerror(errno)); + return (0); + } + break; + default: + logit(LOG_CRIT, + "kroute_send_rtmsg: action %u, prefix %s/%u: %s", + r.hdr.rtm_type, log_ntoa(kroute->prefix), + kroute->prefixlen, strerror(errno)); + return (0); + } + } + + return (0); +} + int kroute_fetchtable(void) { @@ -565,8 +883,8 @@ kroute_fetchtable(void) break; } - if (kroute_insert(kr) == -1) - free(kr); + kroute_tree_insert(kr); + } free(buf); return (0); @@ -615,16 +933,13 @@ kroute_fetchifs(int ifindex) LIST_INIT(&kif->kroute_l); kif->ifindex = ifm->ifm_index; kif->flags = ifm->ifm_flags; - if (RB_INSERT(kif_tree, &kit, kif) != NULL) { - logit(LOG_CRIT, "RB_INSERT(kif_tree, &kit, kif)"); - free(kif); - } + kif_tree_insert(kif); } return (0); } int -kroute_dispatch_msg(void) +kroute_get_rtmsg(void) { char buf[RT_BUF_SIZE]; ssize_t n; @@ -633,13 +948,14 @@ kroute_dispatch_msg(void) struct if_msghdr *ifm; struct sockaddr *sa, *rti_info[RTAX_MAX]; struct sockaddr_in *sa_in; - struct kroute_node *kr, s; - in_addr_t nexthop; + struct kroute_node *kr; + in_addr_t prefix, nexthop; + u_int8_t prefixlen; int flags; u_short ifindex; if ((n = read(kr_state.fd, &buf, sizeof(buf))) == -1) { - log_err("kroute_dispatch_msg: read error"); + log_err("kroute_get_rtmsg: read error"); return (-1); } @@ -654,7 +970,8 @@ kroute_dispatch_msg(void) sa = (struct sockaddr *)(rtm + 1); get_rtaddrs(rtm->rtm_addrs, sa, rti_info); - bzero(&s, sizeof(s)); + prefix = 0; + prefixlen = 0; flags = F_KERNEL; nexthop = 0; ifindex = 0; @@ -673,18 +990,17 @@ kroute_dispatch_msg(void) switch (sa->sa_family) { case AF_INET: - s.r.prefix = - ((struct sockaddr_in *)sa)->sin_addr.s_addr; + prefix = ((struct sockaddr_in *)sa)->sin_addr.s_addr; sa_in = (struct sockaddr_in *)rti_info[RTAX_NETMASK]; if (sa_in != NULL) { if (sa_in->sin_family != AF_INET) continue; - s.r.prefixlen = + prefixlen = mask2prefixlen(sa_in->sin_addr.s_addr); } else if (rtm->rtm_flags & RTF_HOST) - s.r.prefixlen = 32; + prefixlen = 32; else - s.r.prefixlen = prefixlen_classful(s.r.prefix); + prefixlen = prefixlen_classful(prefix); break; default: if (rtm->rtm_type != RTM_IFINFO) @@ -709,45 +1025,45 @@ kroute_dispatch_msg(void) case RTM_CHANGE: if (nexthop == 0 && !(flags & F_CONNECTED)) { logit(LOG_CRIT, - "kroute_dispatch_msg: no nexthop for %s/%u", - log_ntoa(s.r.prefix), s.r.prefixlen); + "kroute_get_rtmsg: no nexthop for %s/%u", + log_ntoa(prefix), prefixlen); continue; } - if ((kr = RB_FIND(kroute_tree, &krt, &s)) != NULL) { + if ((kr = kroute_tree_find(prefix, prefixlen)) != + NULL) { if (kr->flags & F_KERNEL) { kr->r.nexthop = nexthop; - if (kr->flags & F_NEXTHOP) /* XXX */ + if (kr->flags & F_NEXTHOP) flags |= F_NEXTHOP; if ((kr->flags & F_CONNECTED) && !(flags & F_CONNECTED)) - kroute_remove_connected(kr); + kif_kr_remove(kr); if ((flags & F_CONNECTED) && !(kr->flags & F_CONNECTED)) - kroute_insert_connected(kr); + kif_kr_insert(kr); kr->flags = flags; } } else { if ((kr = calloc(1, sizeof(struct kroute_node))) == NULL) { - log_err("kroute_dispatch_msg"); + log_err("kroute_get_rtmsg"); return (-1); } - kr->r.prefix = s.r.prefix; - kr->r.prefixlen = s.r.prefixlen; + kr->r.prefix = prefix; + kr->r.prefixlen = prefixlen; kr->r.nexthop = nexthop; kr->flags = flags; - if (kroute_insert(kr) == -1) - free(kr); + kroute_tree_insert(kr); } break; case RTM_DELETE: - if ((kr = RB_FIND(kroute_tree, &krt, &s)) == NULL) + if ((kr = kroute_tree_find(prefix, prefixlen)) == NULL) continue; if (!(kr->flags & F_KERNEL)) continue; - if (kroute_remove(kr) == -1) + if (kroute_tree_remove(kr) == -1) return (-1); break; case RTM_IFINFO: @@ -762,214 +1078,3 @@ kroute_dispatch_msg(void) } return (0); } - -int -kroute_remove(struct kroute_node *kr) -{ - struct knexthop_node *s; - struct kroute_nexthop h; - - /* - * the foreach is suboptimal, but: - * -routes disappering that have nexthops attached should not - * happen often; except for some ibgp and multihop that should - * be connected routes anyway - * -maintaining an SLIST per kroute_node is probably worse - * -it is easy to change if needed ;-) - */ - - RB_REMOVE(kroute_tree, &krt, kr); - - if ((kr->flags & F_KERNEL) && (kr->flags & F_NEXTHOP)) - RB_FOREACH(s, knexthop_tree, &knt) - if (s->kroute == kr) { - /* - * XXX check again wether there's another - * non-bgp route. if not, notify RDE - * that this nexthop is now invalid - */ - if (!kroute_nexthop_checkmatch(s->nexthop, - s)) { - s->kroute = NULL; - bzero(&h, sizeof(h)); - h.nexthop = s->nexthop; - send_nexthop_update(&h); - } - } - - if (kr->flags & F_CONNECTED) - if (kroute_remove_connected(kr) == -1) { - free(kr); - return (-1); - } - - free(kr); - return (0); -} - -struct kroute_node * -kroute_match(in_addr_t key) -{ - int i; - struct kroute_node s, *kr; - in_addr_t ina; - - ina = ntohl(key); - - /* we will never match the default route */ - for (i = 32; i > 0; i--) { - s.r.prefix = htonl(ina & (0xffffffff << (32 - i))); - s.r.prefixlen = i; - if ((kr = RB_FIND(kroute_tree, &krt, &s)) != NULL) - return (kr); - } - - /* if we don't have a match yet, try to find a default route */ - s.r.prefix = 0; - s.r.prefixlen = 0; - if ((kr = RB_FIND(kroute_tree, &krt, &s)) != NULL) - return (kr); - - return (NULL); -} - -int -kroute_nexthop_add(in_addr_t key) -{ - struct kroute_nexthop nh; - struct knexthop_node *h, s; - - s.nexthop = key; - - bzero(&nh, sizeof(nh)); - nh.nexthop = key; - - if ((h = RB_FIND(knexthop_tree, &knt, &s)) != NULL) { - if (h->kroute != NULL) { - nh.valid = 1; - nh.connected = h->kroute->flags & F_CONNECTED; - nh.gateway = h->kroute->r.nexthop; - } - } else - if (kroute_nexthop_insert(key, &nh) == -1) - return (-1); - - send_nexthop_update(&nh); - - return (0); -} - -void -kroute_nexthop_delete(in_addr_t key) -{ - struct knexthop_node *a, *b, s; - - s.nexthop = key; - - if ((a = RB_FIND(knexthop_tree, &knt, &s)) == NULL) - return; - - /* - * check wether there's another nexthop depending on this kroute - * if not remove the flag - */ - - for (b = RB_MIN(knexthop_tree, &knt); b != NULL && - b->kroute != a->kroute; b = RB_NEXT(knexthop_tree, &knt, b)) - ; /* nothing */ - - if (b == NULL) - a->kroute->flags &= ~F_NEXTHOP; - - RB_REMOVE(knexthop_tree, &knt, a); -} - -int -kroute_nexthop_insert(in_addr_t key, struct kroute_nexthop *nh) -{ - struct knexthop_node *h; - - if ((h = calloc(1, sizeof(struct knexthop_node))) == NULL) { - log_err("kroute_nexthop_insert"); - return (-1); - } - - h->nexthop = nh->nexthop = key; - - if (kroute_nexthop_checkmatch(key, h) == 1) { - nh->valid = 1; - nh->connected = h->kroute->flags & F_CONNECTED; - nh->gateway = h->kroute->r.nexthop; - } - - if (RB_INSERT(knexthop_tree, &knt, h) != NULL) { - logit(LOG_CRIT, "knexthop_tree insert failed for %s", - log_ntoa(h->nexthop)); - free(h); - } - return (0); -} - -int -kroute_nexthop_checkmatch(in_addr_t key, struct knexthop_node *h) -{ - struct kroute_node *kr; - - if ((kr = kroute_match(key)) != NULL) - if (!(kr->flags & F_DOWN)) { /* may be bgp, bad idea tho */ - h->kroute = kr; - kr->flags |= F_NEXTHOP; - return (1); - } - - return (0); -} - -int -knexthop_compare(struct knexthop_node *a, struct knexthop_node *b) -{ - return (b->nexthop - a->nexthop); -} - -int -kif_compare(struct kif_node *a, struct kif_node *b) -{ - return (b->ifindex - a->ifindex); -} - -void -kroute_interface_statuschange(u_short ifindex, int flags) -{ - struct kif_node *kif, s; - struct kif_kr *kkr; - struct kroute_nexthop nh; - struct knexthop_node *n; - - bzero(&s, sizeof(s)); - s.ifindex = ifindex; - - if ((kif = RB_FIND(kif_tree, &kit, &s)) == NULL) { - logit(LOG_CRIT, "interface with index %u not found", - ifindex); - return; - } - - LIST_FOREACH(kkr, &kif->kroute_l, entry) { - if (flags & IFF_UP) - kkr->kr->flags &= ~F_DOWN; - else - kkr->kr->flags |= F_DOWN; - - RB_FOREACH(n, knexthop_tree, &knt) - if (n->kroute == kkr->kr) { - bzero(&nh, sizeof(nh)); - nh.nexthop = n->nexthop; - if (!(kkr->kr->flags & F_DOWN)) { - nh.valid = 1; - nh.connected = 1; - nh.gateway = kkr->kr->r.nexthop; - } - send_nexthop_update(&nh); - } - } -} |