This adds ktable code from bgpd to fetch, store and perform lookups in multiple routing tables. Currently it doesn't do anything useful but it's a prerequisite for any future work in this direction.
OK to get this in? diff --git usr.sbin/snmpd/kroute.c usr.sbin/snmpd/kroute.c index e157b25..e19f924 100644 --- usr.sbin/snmpd/kroute.c +++ usr.sbin/snmpd/kroute.c @@ -45,10 +45,13 @@ #include "snmpd.h" extern struct snmpd *env; +struct ktable **krt; +u_int krt_size; + struct { struct event ks_ev; u_long ks_iflastchange; u_long ks_nroutes; /* 4 billions enough? */ int ks_fd; @@ -77,24 +80,32 @@ struct kif_node { int kroute_compare(struct kroute_node *, struct kroute_node *); int kroute6_compare(struct kroute6_node *, struct kroute6_node *); int kif_compare(struct kif_node *, struct kif_node *); -struct kroute_node *kroute_find(in_addr_t, u_int8_t, u_int8_t); +void ktable_init(void); +int ktable_new(u_int, u_int); +void ktable_free(u_int); +int ktable_exists(u_int, u_int *); +struct ktable *ktable_get(u_int); +int ktable_update(u_int); + +struct kroute_node *kroute_find(struct ktable *, in_addr_t, u_int8_t, + u_int8_t); struct kroute_node *kroute_matchgw(struct kroute_node *, struct sockaddr_in *); -int kroute_insert(struct kroute_node *); -int kroute_remove(struct kroute_node *); -void kroute_clear(void); +int kroute_insert(struct ktable *, struct kroute_node *); +int kroute_remove(struct ktable *, struct kroute_node *); +void kroute_clear(struct ktable *); -struct kroute6_node *kroute6_find(const struct in6_addr *, u_int8_t, - u_int8_t); +struct kroute6_node *kroute6_find(struct ktable *, const struct in6_addr *, + u_int8_t, u_int8_t); struct kroute6_node *kroute6_matchgw(struct kroute6_node *, struct sockaddr_in6 *); -int kroute6_insert(struct kroute6_node *); -int kroute6_remove(struct kroute6_node *); -void kroute6_clear(void); +int kroute6_insert(struct ktable *, struct kroute6_node *); +int kroute6_remove(struct ktable *, struct kroute6_node *); +void kroute6_clear(struct ktable *); struct kif_arp *karp_find(struct sockaddr *, u_short); int karp_insert(struct kif_node *, struct kif_arp *); int karp_remove(struct kif_node *, struct kif_arp *); @@ -121,23 +132,21 @@ void if_newaddr(u_short, struct sockaddr *, struct sockaddr *, struct sockaddr *); void if_deladdr(u_short, struct sockaddr *, struct sockaddr *, struct sockaddr *); void if_announce(void *); -int fetchtable(void); +int fetchtable(struct ktable *); int fetchifs(u_short); -int fetcharp(void); +int fetcharp(struct ktable *); void dispatch_rtmsg(int, short, void *); int rtmsg_process(char *, int); -int dispatch_rtmsg_addr(struct rt_msghdr *, +int dispatch_rtmsg_addr(struct ktable *, struct rt_msghdr *, struct sockaddr *[RTAX_MAX]); -RB_HEAD(kroute_tree, kroute_node) krt; RB_PROTOTYPE(kroute_tree, kroute_node, entry, kroute_compare) RB_GENERATE(kroute_tree, kroute_node, entry, kroute_compare) -RB_HEAD(kroute6_tree, kroute6_node) krt6; RB_PROTOTYPE(kroute6_tree, kroute6_node, entry, kroute6_compare) RB_GENERATE(kroute6_tree, kroute6_node, entry, kroute6_compare) RB_HEAD(kif_tree, kif_node) kit; RB_PROTOTYPE(kif_tree, kif_node, entry, kif_compare) @@ -149,10 +158,11 @@ RB_GENERATE(ka_tree, kif_addr, node, ka_compare) void kr_init(void) { int opt = 0, rcvbuf, default_rcvbuf; + unsigned int tid = RTABLE_ANY; socklen_t optlen; if ((kr_state.ks_ifd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) fatal("kr_init: ioctl socket"); @@ -179,31 +189,166 @@ kr_init(void) setsockopt(kr_state.ks_fd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf)) == -1 && errno == ENOBUFS; rcvbuf /= 2) ; /* nothing */ - RB_INIT(&krt); - RB_INIT(&krt6); + if (setsockopt(kr_state.ks_fd, AF_ROUTE, ROUTE_TABLEFILTER, &tid, + sizeof(tid)) == -1) + log_warn("kr_init: setsockopt AF_ROUTE ROUTE_TABLEFILTER"); + RB_INIT(&kit); RB_INIT(&kat); if (fetchifs(0) == -1) fatalx("kr_init fetchifs"); - if (fetchtable() == -1) - fatalx("kr_init fetchtable"); - if (fetcharp() == -1) - fatalx("kr_init fetcharp"); + + ktable_init(); event_set(&kr_state.ks_ev, kr_state.ks_fd, EV_READ | EV_PERSIST, dispatch_rtmsg, NULL); event_add(&kr_state.ks_ev, NULL); } void +ktable_init(void) +{ + u_int i; + + for (i = 0; i < RT_TABLEID_MAX; i++) + if (ktable_exists(i, NULL)) + ktable_update(i); +} + +int +ktable_new(u_int rtableid, u_int rdomid) +{ + struct ktable **xkrt; + struct ktable *kt; + size_t newsize, oldsize; + + /* resize index table if needed */ + if (rtableid >= krt_size) { + oldsize = sizeof(struct ktable *) * krt_size; + newsize = sizeof(struct ktable *) * (rtableid + 1); + if ((xkrt = realloc(krt, newsize)) == NULL) { + log_warn("ktable_new"); + return (-1); + } + krt = xkrt; + krt_size = rtableid + 1; + bzero((char *)krt + oldsize, newsize - oldsize); + } + + if (krt[rtableid]) + fatalx("ktable_new: table already exists."); + + /* allocate new element */ + kt = krt[rtableid] = calloc(1, sizeof(struct ktable)); + if (kt == NULL) { + log_warn("ktable_new"); + return (-1); + } + + /* initialize structure ... */ + RB_INIT(&kt->krt); + RB_INIT(&kt->krt6); + kt->rtableid = rtableid; + kt->rdomain = rdomid; + + /* ... and load it */ + if (fetchtable(kt) == -1) + return (-1); + /* load arp information */ + if (fetcharp(kt) == -1) + return (-1); + + log_debug("new ktable for rtableid %d", rtableid); + return (0); +} + +void +ktable_free(u_int rtableid) +{ + struct ktable *kt; + + if ((kt = ktable_get(rtableid)) == NULL) + return; + + log_debug("freeing ktable rtableid %u", kt->rtableid); + kroute_clear(kt); + kroute6_clear(kt); + + krt[kt->rtableid] = NULL; + free(kt); +} + +struct ktable * +ktable_get(u_int rtableid) +{ + if (rtableid >= krt_size) + return (NULL); + return (krt[rtableid]); +} + +int +ktable_update(u_int rtableid) +{ + struct ktable *kt; + u_int rdomid; + + if (!ktable_exists(rtableid, &rdomid)) + fatalx("King Bula lost a table"); /* may not happen */ + + if (rdomid != rtableid) { + if (ktable_get(rdomid) == NULL && + ktable_new(rdomid, rdomid) != 0) + return (-1); + } + + kt = ktable_get(rtableid); + if (kt == NULL) { + if (ktable_new(rtableid, rdomid)) + return (-1); + } + return (0); +} + +int +ktable_exists(u_int rtableid, u_int *rdomid) +{ + size_t len; + struct rt_tableinfo info; + int mib[6]; + + mib[0] = CTL_NET; + mib[1] = AF_ROUTE; + mib[2] = 0; + mib[3] = 0; + mib[4] = NET_RT_TABLE; + mib[5] = rtableid; + + len = sizeof(info); + if (sysctl(mib, 6, &info, &len, NULL, 0) == -1) { + if (errno == ENOENT) + /* table nonexistent */ + return (0); + log_warn("sysctl"); + /* must return 0 so that the table is considered non-existent */ + return (0); + } + if (rdomid) + *rdomid = info.rti_domainid; + return (1); +} + +void kr_shutdown(void) { - kroute_clear(); + u_int i; + + for (i = krt_size; i > 0; i--) + ktable_free(i - 1); kif_clear(); } u_int kr_ifnumber(void) @@ -301,28 +446,29 @@ ka_compare(struct kif_addr *a, struct kif_addr *b) return (memcmp(&a->addr.sa, &b->addr.sa, a->addr.sa.sa_len)); } /* tree management */ struct kroute_node * -kroute_find(in_addr_t prefix, u_int8_t prefixlen, u_int8_t prio) +kroute_find(struct ktable *kt, in_addr_t prefix, u_int8_t prefixlen, + u_int8_t prio) { struct kroute_node s; struct kroute_node *kn, *tmp; s.r.prefix.s_addr = prefix; s.r.prefixlen = prefixlen; s.r.priority = prio; - kn = RB_FIND(kroute_tree, &krt, &s); + kn = RB_FIND(kroute_tree, &kt->krt, &s); if (kn && prio == RTP_ANY) { - tmp = RB_PREV(kroute_tree, &krt, kn); + tmp = RB_PREV(kroute_tree, &kt->krt, kn); while (tmp) { if (kroute_compare(&s, tmp) == 0) kn = tmp; else break; - tmp = RB_PREV(kroute_tree, &krt, kn); + tmp = RB_PREV(kroute_tree, &kt->krt, kn); } } return (kn); } @@ -345,15 +491,15 @@ kroute_matchgw(struct kroute_node *kr, struct sockaddr_in *sa_in) return (NULL); } int -kroute_insert(struct kroute_node *kr) +kroute_insert(struct ktable *kt, struct kroute_node *kr) { struct kroute_node *krm; - if ((krm = RB_INSERT(kroute_tree, &krt, kr)) != NULL) { + if ((krm = RB_INSERT(kroute_tree, &kt->krt, kr)) != NULL) { /* multipath route, add at end of list */ while (krm->next != NULL) krm = krm->next; krm->next = kr; kr->next = NULL; /* to be sure */ @@ -362,29 +508,30 @@ kroute_insert(struct kroute_node *kr) kr_state.ks_nroutes++; return (0); } int -kroute_remove(struct kroute_node *kr) +kroute_remove(struct ktable *kt, struct kroute_node *kr) { struct kroute_node *krm; - if ((krm = RB_FIND(kroute_tree, &krt, kr)) == NULL) { + if ((krm = RB_FIND(kroute_tree, &kt->krt, kr)) == NULL) { log_warnx("kroute_remove failed to find %s/%u", inet_ntoa(kr->r.prefix), kr->r.prefixlen); return (-1); } if (krm == kr) { /* head element */ - if (RB_REMOVE(kroute_tree, &krt, kr) == NULL) { + if (RB_REMOVE(kroute_tree, &kt->krt, kr) == NULL) { log_warnx("kroute_remove failed for %s/%u", inet_ntoa(kr->r.prefix), kr->r.prefixlen); return (-1); } if (kr->next != NULL) { - if (RB_INSERT(kroute_tree, &krt, kr->next) != NULL) { + if (RB_INSERT(kroute_tree, &kt->krt, kr->next) + != NULL) { log_warnx("kroute_remove failed to add %s/%u", inet_ntoa(kr->r.prefix), kr->r.prefixlen); return (-1); } } @@ -405,37 +552,38 @@ kroute_remove(struct kroute_node *kr) free(kr); return (0); } void -kroute_clear(void) +kroute_clear(struct ktable *kt) { struct kroute_node *kr; - while ((kr = RB_MIN(kroute_tree, &krt)) != NULL) - kroute_remove(kr); + while ((kr = RB_MIN(kroute_tree, &kt->krt)) != NULL) + kroute_remove(kt, kr); } struct kroute6_node * -kroute6_find(const struct in6_addr *prefix, u_int8_t prefixlen, u_int8_t prio) +kroute6_find(struct ktable *kt, const struct in6_addr *prefix, + u_int8_t prefixlen, u_int8_t prio) { struct kroute6_node s; struct kroute6_node *kn6, *tmp; memcpy(&s.r.prefix, prefix, sizeof(struct in6_addr)); s.r.prefixlen = prefixlen; s.r.priority = prio; - kn6 = RB_FIND(kroute6_tree, &krt6, &s); + kn6 = RB_FIND(kroute6_tree, &kt->krt6, &s); if (kn6 && prio == RTP_ANY) { - tmp = RB_PREV(kroute6_tree, &krt6, kn6); + tmp = RB_PREV(kroute6_tree, &kt->krt6, kn6); while (tmp) { if (kroute6_compare(&s, tmp) == 0) kn6 = tmp; else break; - tmp = RB_PREV(kroute6_tree, &krt6, kn6); + tmp = RB_PREV(kroute6_tree, &kt->krt6, kn6); } } return (kn6); } @@ -458,15 +606,15 @@ kroute6_matchgw(struct kroute6_node *kr, struct sockaddr_in6 *sa_in6) return (NULL); } int -kroute6_insert(struct kroute6_node *kr) +kroute6_insert(struct ktable *kt, struct kroute6_node *kr) { struct kroute6_node *krm; - if ((krm = RB_INSERT(kroute6_tree, &krt6, kr)) != NULL) { + if ((krm = RB_INSERT(kroute6_tree, &kt->krt6, kr)) != NULL) { /* multipath route, add at end of list */ while (krm->next != NULL) krm = krm->next; krm->next = kr; kr->next = NULL; /* to be sure */ @@ -475,29 +623,30 @@ kroute6_insert(struct kroute6_node *kr) kr_state.ks_nroutes++; return (0); } int -kroute6_remove(struct kroute6_node *kr) +kroute6_remove(struct ktable *kt, struct kroute6_node *kr) { struct kroute6_node *krm; - if ((krm = RB_FIND(kroute6_tree, &krt6, kr)) == NULL) { + if ((krm = RB_FIND(kroute6_tree, &kt->krt6, kr)) == NULL) { log_warnx("kroute6_remove failed for %s/%u", log_in6addr(&kr->r.prefix), kr->r.prefixlen); return (-1); } if (krm == kr) { /* head element */ - if (RB_REMOVE(kroute6_tree, &krt6, kr) == NULL) { + if (RB_REMOVE(kroute6_tree, &kt->krt6, kr) == NULL) { log_warnx("kroute6_remove failed for %s/%u", log_in6addr(&kr->r.prefix), kr->r.prefixlen); return (-1); } if (kr->next != NULL) { - if (RB_INSERT(kroute6_tree, &krt6, kr->next) != NULL) { + if (RB_INSERT(kroute6_tree, &kt->krt6, kr->next) != + NULL) { log_warnx("kroute6_remove failed to add %s/%u", log_in6addr(&kr->r.prefix), kr->r.prefixlen); return (-1); } @@ -519,16 +668,16 @@ kroute6_remove(struct kroute6_node *kr) free(kr); return (0); } void -kroute6_clear(void) +kroute6_clear(struct ktable *kt) { struct kroute6_node *kr; - while ((kr = RB_MIN(kroute6_tree, &krt6)) != NULL) - kroute6_remove(kr); + while ((kr = RB_MIN(kroute6_tree, &kt->krt6)) != NULL) + kroute6_remove(kt, kr); } static inline int karp_compare(struct kif_arp *a, struct kif_arp *b) { @@ -1021,22 +1170,20 @@ if_announce(void *msg) switch (ifan->ifan_what) { case IFAN_ARRIVAL: kif = kif_insert(ifan->ifan_index); strlcpy(kif->k.if_name, ifan->ifan_name, sizeof(kif->k.if_name)); - /* Update the ARP table */ - fetcharp(); break; case IFAN_DEPARTURE: kif = kif_find(ifan->ifan_index); kif_remove(kif); break; } } int -fetchtable(void) +fetchtable(struct ktable *kt) { int mib[7]; size_t len; char *buf; int rv; @@ -1045,22 +1192,27 @@ fetchtable(void) mib[1] = AF_ROUTE; mib[2] = 0; mib[3] = AF_INET; mib[4] = NET_RT_DUMP; mib[5] = 0; - mib[6] = 0; /* rtableid */ + mib[6] = kt->rtableid; if (sysctl(mib, 7, NULL, &len, NULL, 0) == -1) { + if (kt->rtableid != 0 && errno == EINVAL) + /* table nonexistent */ + return (0); log_warn("sysctl"); return (-1); } + if (len == 0) + return (0); if ((buf = malloc(len)) == NULL) { log_warn("fetchtable"); return (-1); } if (sysctl(mib, 7, buf, &len, NULL, 0) == -1) { - log_warn("sysctl"); + log_warn("sysctl2"); free(buf); return (-1); } rv = rtmsg_process(buf, len); @@ -1103,11 +1255,11 @@ fetchifs(u_short if_index) return (rv); } int -fetcharp(void) +fetcharp(struct ktable *kt) { size_t len; int mib[7]; char *buf; int rv; @@ -1116,11 +1268,11 @@ fetcharp(void) mib[1] = AF_ROUTE; mib[2] = 0; mib[3] = AF_INET; mib[4] = NET_RT_FLAGS; mib[5] = RTF_LLINFO; - mib[6] = 0; + mib[6] = kt->rtableid; if (sysctl(mib, 7, NULL, &len, NULL, 0) == -1) { log_warn("sysctl"); return (-1); } @@ -1164,10 +1316,11 @@ dispatch_rtmsg(int fd, short event, void *arg) } int rtmsg_process(char *buf, int len) { + struct ktable *kt; struct rt_msghdr *rtm; struct if_msghdr ifm; struct ifa_msghdr *ifam; struct sockaddr *sa, *rti_info[RTAX_MAX]; int offset; @@ -1189,11 +1342,14 @@ rtmsg_process(char *buf, int len) case RTM_DELETE: case RTM_RESOLVE: if (rtm->rtm_errno) /* failed attempts */ continue; - if (dispatch_rtmsg_addr(rtm, rti_info) == -1) + if ((kt = ktable_get(rtm->rtm_tableid)) == NULL) + continue; + + if (dispatch_rtmsg_addr(kt, rtm, rti_info) == -1) return (-1); break; case RTM_IFINFO: memcpy(&ifm, next, sizeof(ifm)); if_change(ifm.ifm_index, ifm.ifm_flags, &ifm.ifm_data, @@ -1228,11 +1384,12 @@ rtmsg_process(char *buf, int len) return (offset); } int -dispatch_rtmsg_addr(struct rt_msghdr *rtm, struct sockaddr *rti_info[RTAX_MAX]) +dispatch_rtmsg_addr(struct ktable *kt, struct rt_msghdr *rtm, + struct sockaddr *rti_info[RTAX_MAX]) { struct sockaddr *sa, *psa; struct sockaddr_in *sa_in, *psa_in = NULL; struct sockaddr_in6 *sa_in6, *psa_in6 = NULL; struct sockaddr_dl *sa_dl; @@ -1319,11 +1476,11 @@ dispatch_rtmsg_addr(struct rt_msghdr *rtm, struct sockaddr *rti_info[RTAX_MAX]) /* Continue to the route section below */ } switch (psa->sa_family) { case AF_INET: sa_in = (struct sockaddr_in *)sa; - if ((kr = kroute_find(psa_in->sin_addr.s_addr, + if ((kr = kroute_find(kt, psa_in->sin_addr.s_addr, prefixlen, prio)) == NULL) return (0); if (mpath) /* get the correct route */ @@ -1331,17 +1488,17 @@ dispatch_rtmsg_addr(struct rt_msghdr *rtm, struct sockaddr *rti_info[RTAX_MAX]) log_warnx("dispatch_rtmsg[delete] " "mpath route not found"); return (0); } - if (kroute_remove(kr) == -1) + if (kroute_remove(kt, kr) == -1) return (-1); break; case AF_INET6: sa_in6 = (struct sockaddr_in6 *)sa; - if ((kr6 = kroute6_find(&psa_in6->sin6_addr, prefixlen, - prio)) == NULL) + if ((kr6 = kroute6_find(kt, &psa_in6->sin6_addr, + prefixlen, prio)) == NULL) return (0); if (mpath) /* get the correct route */ if ((kr6 = kroute6_matchgw(kr6, sa_in6)) == @@ -1349,11 +1506,11 @@ dispatch_rtmsg_addr(struct rt_msghdr *rtm, struct sockaddr *rti_info[RTAX_MAX]) log_warnx("dispatch_rtmsg[delete] " "IPv6 mpath route not found"); return (0); } - if (kroute6_remove(kr6) == -1) + if (kroute6_remove(kt, kr6) == -1) return (-1); break; } return (0); } @@ -1398,11 +1555,11 @@ dispatch_rtmsg_addr(struct rt_msghdr *rtm, struct sockaddr *rti_info[RTAX_MAX]) } switch (psa->sa_family) { case AF_INET: sa_in = (struct sockaddr_in *)sa; - if ((kr = kroute_find(psa_in->sin_addr.s_addr, prefixlen, + if ((kr = kroute_find(kt, psa_in->sin_addr.s_addr, prefixlen, prio)) != NULL) { /* get the correct route */ if (mpath && rtm->rtm_type == RTM_CHANGE && (kr = kroute_matchgw(kr, sa_in)) == NULL) { log_warnx("dispatch_rtmsg[change] " @@ -1435,16 +1592,16 @@ add4: kr->r.flags = flags; kr->r.if_index = ifindex; kr->r.ticks = smi_getticks(); kr->r.priority = prio; - kroute_insert(kr); + kroute_insert(kt, kr); } break; case AF_INET6: sa_in6 = (struct sockaddr_in6 *)sa; - if ((kr6 = kroute6_find(&psa_in6->sin6_addr, prefixlen, + if ((kr6 = kroute6_find(kt, &psa_in6->sin6_addr, prefixlen, prio)) != NULL) { /* get the correct route */ if (mpath && rtm->rtm_type == RTM_CHANGE && (kr6 = kroute6_matchgw(kr6, sa_in6)) == NULL) { @@ -1485,11 +1642,11 @@ add6: kr6->r.flags = flags; kr6->r.if_index = ifindex; kr6->r.ticks = smi_getticks(); kr6->r.priority = prio; - kroute6_insert(kr6); + kroute6_insert(kt, kr6); } break; } return (0); @@ -1497,23 +1654,29 @@ add6: struct kroute * kroute_first(void) { struct kroute_node *kn; + struct ktable *kt; - kn = RB_MIN(kroute_tree, &krt); + if ((kt = ktable_get(0)) == NULL) + return (NULL); + kn = RB_MIN(kroute_tree, &kt->krt); return (&kn->r); } struct kroute * kroute_getaddr(in_addr_t prefix, u_int8_t prefixlen, u_int8_t prio, int next) { struct kroute_node *kn; + struct ktable *kt; - kn = kroute_find(prefix, prefixlen, prio); + if ((kt = ktable_get(0)) == NULL) + return (NULL); + kn = kroute_find(kt, prefix, prefixlen, prio); if (kn != NULL && next) - kn = RB_NEXT(kroute_tree, &krt, kn); + kn = RB_NEXT(kroute_tree, &kt->krt, kn); if (kn != NULL) return (&kn->r); else return (NULL); } diff --git usr.sbin/snmpd/snmpd.h usr.sbin/snmpd/snmpd.h index 048ef79..61bc749 100644 --- usr.sbin/snmpd/snmpd.h +++ usr.sbin/snmpd/snmpd.h @@ -186,10 +186,22 @@ extern struct ctl_connlist ctl_conns; /* * kroute */ +struct kroute_node; +struct kroute6_node; +RB_HEAD(kroute_tree, kroute_node); +RB_HEAD(kroute6_tree, kroute6_node); + +struct ktable { + struct kroute_tree krt; + struct kroute6_tree krt6; + u_int rtableid; + u_int rdomain; +}; + union kaddr { struct sockaddr sa; struct sockaddr_in sin; struct sockaddr_in6 sin6; struct sockaddr_dl sdl;