snmpd: support for ipNetToMediaTable (ARP table exporting)
Hi, With some help from blambert@ and thorough testing by sthen@, I was able to get this working fairly well. There are some rough edges however, namely the rdomain kludge and the need for an RTM_DELETE notification, but apart from that it seems to be doing the right thing. The ARP part of the kroute builds sorted ARP lists hanging off of the interface nodes in the interface R/B tree. There's fetcharp() that fetches and updates ARP entries on all interfaces within an rdomain and kernel notifications arriving at the routing socket that add or remove entries. When interface goes away its ARP list gets purged. The MIB part iterates over all interfaces and all ARP entries and calls mib_physaddr for each one of them that completes filling all node's fields. OK? diff --git usr.sbin/snmpd/kroute.c usr.sbin/snmpd/kroute.c index 1ed4d17..7c0989b 100644 --- usr.sbin/snmpd/kroute.c +++ usr.sbin/snmpd/kroute.c @@ -45,10 +45,12 @@ #include "snmpd.h" extern struct snmpd*env; +u_short snmpd_rtableid; + struct { struct event ks_ev; u_long ks_iflastchange; u_long ks_nroutes;/* 4 billions enough? */ int ks_fd; @@ -69,10 +71,11 @@ struct kroute6_node { }; struct kif_node { RB_ENTRY(kif_node) entry; TAILQ_HEAD(, kif_addr) addrs; + TAILQ_HEAD(, kif_arp)arps; struct kif k; }; intkroute_compare(struct kroute_node *, struct kroute_node *); intkroute6_compare(struct kroute6_node *, struct kroute6_node *); @@ -91,10 +94,14 @@ struct kroute6_node *kroute6_matchgw(struct kroute6_node *, struct sockaddr_in6 *); int kroute6_insert(struct kroute6_node *); int kroute6_remove(struct kroute6_node *); voidkroute6_clear(void); +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 *); + struct kif_node*kif_find(u_short); struct kif_node*kif_insert(u_short); int kif_remove(struct kif_node *); voidkif_clear(void); struct kif *kif_update(u_short, int, struct if_data *, @@ -118,10 +125,11 @@ void if_deladdr(u_short, struct sockaddr *, struct sockaddr *, struct sockaddr *); void if_announce(void *); intfetchtable(void); intfetchifs(u_short); +intfetcharp(void); void dispatch_rtmsg(int, short, void *); intrtmsg_process(char *, int); intdispatch_rtmsg_addr(struct rt_msghdr *, struct sockaddr *[RTAX_MAX]); @@ -145,10 +153,12 @@ void kr_init(void) { int opt = 0, rcvbuf, default_rcvbuf; socklen_t optlen; + snmpd_rtableid = getrtable(); + if ((kr_state.ks_ifd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) fatal("kr_init: ioctl socket"); if ((kr_state.ks_fd = socket(AF_ROUTE, SOCK_RAW, 0)) == -1) fatal("kr_init: route socket"); @@ -182,10 +192,12 @@ kr_init(void) if (fetchifs(0) == -1) fatalx("kr_init fetchifs"); if (fetchtable() == -1) fatalx("kr_init fetchtable"); + if (fetcharp() == -1) + fatalx("kr_init fetcharp"); event_set(&kr_state.ks_ev, kr_state.ks_fd, EV_READ | EV_PERSIST, dispatch_rtmsg, NULL); event_add(&kr_state.ks_ev, NULL); } @@ -519,10 +531,126 @@ kroute6_clear(void) while ((kr = RB_MIN(kroute6_tree, &krt6)) != NULL) kroute6_remove(kr); } +static inline int +karp_compare(struct kif_arp *a, struct kif_arp *b) +{ + /* Interface indices are assumed equal */ + if (ntohl(a->addr.sin.sin_addr.s_addr) > + ntohl(b->addr.sin.sin_addr.s_addr)) + return (1); + if (ntohl(a->addr.sin.sin_addr.s_addr) < + ntohl(b->addr.sin.sin_addr.s_addr)) + return (-1); + return (0); +} + +static inline struct kif_arp * +karp_search(struct kif_arp *s, struct kif_node *kn) +{ + struct kif_arp *ka; + + TAILQ_FOREACH(ka, &kn->arps, entry) { + switch (karp_compare(s, ka)) { + case 0: /* found */ + return (ka); + case -1: /* s < ka, end the search */ + return (NULL); + } + } + /* looped throught the whole list and didn't find */ + return (NULL); +} + +struct kif_arp * +karp_find(struct sockaddr *sa, u_short ifindex) +{ + struct kif_node *kn; + struct kif_arp *ka = NULL, s; + + memcpy(&s.addr.sa, sa, sa->sa_len); + + if
Re: snmpd: support for ipNetToMediaTable (ARP table exporting)
On Mon, Apr 07, 2014 at 04:04:24PM +0200, Mike Belopuhov wrote: > Hi, > > With some help from blambert@ and thorough testing by sthen@, I was > able to get this working fairly well. There are some rough edges > however, namely the rdomain kludge and the need for an RTM_DELETE > notification, but apart from that it seems to be doing the right > thing. > > The ARP part of the kroute builds sorted ARP lists hanging off of > the interface nodes in the interface R/B tree. There's fetcharp() > that fetches and updates ARP entries on all interfaces within an > rdomain and kernel notifications arriving at the routing socket > that add or remove entries. When interface goes away its ARP list > gets purged. > > The MIB part iterates over all interfaces and all ARP entries and > calls mib_physaddr for each one of them that completes filling all > node's fields. > > OK? One nit, one possible improvement inline below. > > diff --git usr.sbin/snmpd/kroute.c usr.sbin/snmpd/kroute.c > index 1ed4d17..7c0989b 100644 > --- usr.sbin/snmpd/kroute.c > +++ usr.sbin/snmpd/kroute.c > @@ -45,10 +45,12 @@ > > #include "snmpd.h" > > extern struct snmpd *env; > > +u_short snmpd_rtableid; > + > struct { > struct event ks_ev; > u_long ks_iflastchange; > u_long ks_nroutes;/* 4 billions enough? */ > int ks_fd; > @@ -69,10 +71,11 @@ struct kroute6_node { > }; > > struct kif_node { > RB_ENTRY(kif_node) entry; > TAILQ_HEAD(, kif_addr) addrs; > + TAILQ_HEAD(, kif_arp)arps; > struct kif k; > }; > > int kroute_compare(struct kroute_node *, struct kroute_node *); > int kroute6_compare(struct kroute6_node *, struct kroute6_node *); > @@ -91,10 +94,14 @@ 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); > > +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 *); > + > struct kif_node *kif_find(u_short); > struct kif_node *kif_insert(u_short); > int kif_remove(struct kif_node *); > void kif_clear(void); > struct kif *kif_update(u_short, int, struct if_data *, > @@ -118,10 +125,11 @@ voidif_deladdr(u_short, struct sockaddr *, > struct sockaddr *, > struct sockaddr *); > void if_announce(void *); > > int fetchtable(void); > int fetchifs(u_short); > +int fetcharp(void); > void dispatch_rtmsg(int, short, void *); > int rtmsg_process(char *, int); > int dispatch_rtmsg_addr(struct rt_msghdr *, > struct sockaddr *[RTAX_MAX]); > > @@ -145,10 +153,12 @@ void > kr_init(void) > { > int opt = 0, rcvbuf, default_rcvbuf; > socklen_t optlen; > > + snmpd_rtableid = getrtable(); > + > if ((kr_state.ks_ifd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) > fatal("kr_init: ioctl socket"); > > if ((kr_state.ks_fd = socket(AF_ROUTE, SOCK_RAW, 0)) == -1) > fatal("kr_init: route socket"); > @@ -182,10 +192,12 @@ kr_init(void) > > if (fetchifs(0) == -1) > fatalx("kr_init fetchifs"); > if (fetchtable() == -1) > fatalx("kr_init fetchtable"); > + if (fetcharp() == -1) > + fatalx("kr_init fetcharp"); > > event_set(&kr_state.ks_ev, kr_state.ks_fd, EV_READ | EV_PERSIST, > dispatch_rtmsg, NULL); > event_add(&kr_state.ks_ev, NULL); > } > @@ -519,10 +531,126 @@ kroute6_clear(void) > > while ((kr = RB_MIN(kroute6_tree, &krt6)) != NULL) > kroute6_remove(kr); > } > > +static inline int > +karp_compare(struct kif_arp *a, struct kif_arp *b) > +{ > + /* Interface indices are assumed equal */ > + if (ntohl(a->addr.sin.sin_addr.s_addr) > > + ntohl(b->addr.sin.sin_addr.s_addr)) > + return (1); > + if (ntohl(a->addr.sin.sin_addr.s_addr) < > + ntohl(b->addr.sin.sin_addr.s_addr)) > + return (-1); > + return (0); > +} > + > +static inline struct kif_arp * > +karp_search(struct kif_arp *s, struct kif_node *kn) > +{ > + struct kif_arp *ka; > + > + TAILQ_FOREACH(ka, &kn->arps, entry) { > + switch (karp_compare(s, ka)) { > + case 0: /* found */ > + return (ka); > + case -1: /* s < ka, end the search */ > + return (NULL); > + } > + } > + /* looped throught the whole
Re: snmpd: support for ipNetToMediaTable (ARP table exporting)
On Mon, Apr 07, 2014 at 16:21 +0200, Bret Lambert wrote: > On Mon, Apr 07, 2014 at 04:04:24PM +0200, Mike Belopuhov wrote: > > +struct kif_arp * > > +karp_find(struct sockaddr *sa, u_short ifindex) > > +{ > > + struct kif_node *kn; > > + struct kif_arp *ka = NULL, s; > > + > > + memcpy(&s.addr.sa, sa, sa->sa_len); > > + > > + if (ifindex > 0 && (kn = kif_find(ifindex)) == NULL) > > + return (NULL); > > + > > + if (ifindex == 0) { > > + /* > > +* We do this manually, because we want to handle zero > > +* ifindex special case differently from kif_find, in > > +* particular it means that we have to look for the > > +* address on all available interfaces. > > +*/ > > + RB_FOREACH(kn, kif_tree, &kit) { > > + if ((ka = karp_search(&s, kn)) != NULL) > > + break; > > + } > > + } else { > > + if ((kn = kif_find(ifindex)) == NULL) > > + return (NULL); > > + ka = karp_search(&s, kn); > > + } > > + return (ka); > > +} > > If I'm following the logic correctly here (ifindex, being unsigned, > is either 0 or >0), you check for a non-zero ifindex, perform a > lookup via kif_find, and then immediately check for a non-zero > ifindex and perform a lookup via kif_find again. > > If I'm missing something here, please point it out. > > Other than these, looks good to me. > Apparently it's just a leftover that I didn't spot. I'm also sitting on a fence regarding the rdomain handling, but I guess it won't matter much in the long run as we'll hopefully get some better rdomain handling soon. diff --git usr.sbin/snmpd/kroute.c usr.sbin/snmpd/kroute.c index 1ed4d17..d6c3cab 100644 --- usr.sbin/snmpd/kroute.c +++ usr.sbin/snmpd/kroute.c @@ -45,10 +45,12 @@ #include "snmpd.h" extern struct snmpd*env; +u_short snmpd_rtableid; + struct { struct event ks_ev; u_long ks_iflastchange; u_long ks_nroutes;/* 4 billions enough? */ int ks_fd; @@ -69,10 +71,11 @@ struct kroute6_node { }; struct kif_node { RB_ENTRY(kif_node) entry; TAILQ_HEAD(, kif_addr) addrs; + TAILQ_HEAD(, kif_arp)arps; struct kif k; }; intkroute_compare(struct kroute_node *, struct kroute_node *); intkroute6_compare(struct kroute6_node *, struct kroute6_node *); @@ -91,10 +94,14 @@ struct kroute6_node *kroute6_matchgw(struct kroute6_node *, struct sockaddr_in6 *); int kroute6_insert(struct kroute6_node *); int kroute6_remove(struct kroute6_node *); voidkroute6_clear(void); +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 *); + struct kif_node*kif_find(u_short); struct kif_node*kif_insert(u_short); int kif_remove(struct kif_node *); voidkif_clear(void); struct kif *kif_update(u_short, int, struct if_data *, @@ -118,10 +125,11 @@ void if_deladdr(u_short, struct sockaddr *, struct sockaddr *, struct sockaddr *); void if_announce(void *); intfetchtable(void); intfetchifs(u_short); +intfetcharp(void); void dispatch_rtmsg(int, short, void *); intrtmsg_process(char *, int); intdispatch_rtmsg_addr(struct rt_msghdr *, struct sockaddr *[RTAX_MAX]); @@ -145,10 +153,12 @@ void kr_init(void) { int opt = 0, rcvbuf, default_rcvbuf; socklen_t optlen; + snmpd_rtableid = getrtable(); + if ((kr_state.ks_ifd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) fatal("kr_init: ioctl socket"); if ((kr_state.ks_fd = socket(AF_ROUTE, SOCK_RAW, 0)) == -1) fatal("kr_init: route socket"); @@ -182,10 +192,12 @@ kr_init(void) if (fetchifs(0) == -1) fatalx("kr_init fetchifs"); if (fetchtable() == -1) fatalx("kr_init fetchtable"); + if (fetcharp() == -1) + fatalx("kr_init fetcharp"); event_set(&kr_state.ks_ev, kr_state.ks_fd, EV_READ | EV_PERSIST, dispatch_rtmsg, NULL); event_add(&kr_state.ks_ev, NULL); } @@ -519,10 +531,123 @@ kroute6_clear(void) while ((kr = RB_MIN(kroute6_tree, &krt6)) != NULL) kroute6_remove(kr); } +static inline int +karp_compare(struct kif_arp *a, struct kif_arp *b) +{ + /* Interface indices are assumed equal */ + if (ntohl(a->addr.sin.sin_addr.s_addr) > +
Re: snmpd: support for ipNetToMediaTable (ARP table exporting)
a bit of an update, mainly style changes. one functional change: don't rely on rtm_rmx.rmx_expire to set the F_STATIC flag as rt_getmetrics is not called consistenly (only with RTM_GETs) and besides RTF_STATIC flag is already present for static ARP entries. http://www.vantronix.net/~mike/snmpd-arp.diff
Re: snmpd: support for ipNetToMediaTable (ARP table exporting)
On Mon, Apr 07, 2014 at 17:03 +0200, Mike Belopuhov wrote: > a bit of an update, mainly style changes. one functional change: > don't rely on rtm_rmx.rmx_expire to set the F_STATIC flag as > rt_getmetrics is not called consistenly (only with RTM_GETs) and > besides RTF_STATIC flag is already present for static ARP entries. > > http://www.vantronix.net/~mike/snmpd-arp.diff I've ditched rdomain kludges to simplify the diff and because actual rdomain support doesn't need any of those. OK? diff --git usr.sbin/snmpd/kroute.c usr.sbin/snmpd/kroute.c index 1ed4d17..e157b25 100644 --- usr.sbin/snmpd/kroute.c +++ usr.sbin/snmpd/kroute.c @@ -69,10 +69,11 @@ struct kroute6_node { }; struct kif_node { RB_ENTRY(kif_node) entry; TAILQ_HEAD(, kif_addr) addrs; + TAILQ_HEAD(, kif_arp)arps; struct kif k; }; intkroute_compare(struct kroute_node *, struct kroute_node *); intkroute6_compare(struct kroute6_node *, struct kroute6_node *); @@ -91,10 +92,14 @@ struct kroute6_node *kroute6_matchgw(struct kroute6_node *, struct sockaddr_in6 *); int kroute6_insert(struct kroute6_node *); int kroute6_remove(struct kroute6_node *); voidkroute6_clear(void); +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 *); + struct kif_node*kif_find(u_short); struct kif_node*kif_insert(u_short); int kif_remove(struct kif_node *); voidkif_clear(void); struct kif *kif_update(u_short, int, struct if_data *, @@ -118,10 +123,11 @@ void if_deladdr(u_short, struct sockaddr *, struct sockaddr *, struct sockaddr *); void if_announce(void *); intfetchtable(void); intfetchifs(u_short); +intfetcharp(void); void dispatch_rtmsg(int, short, void *); intrtmsg_process(char *, int); intdispatch_rtmsg_addr(struct rt_msghdr *, struct sockaddr *[RTAX_MAX]); @@ -182,10 +188,12 @@ kr_init(void) if (fetchifs(0) == -1) fatalx("kr_init fetchifs"); if (fetchtable() == -1) fatalx("kr_init fetchtable"); + if (fetcharp() == -1) + fatalx("kr_init fetcharp"); event_set(&kr_state.ks_ev, kr_state.ks_fd, EV_READ | EV_PERSIST, dispatch_rtmsg, NULL); event_add(&kr_state.ks_ev, NULL); } @@ -519,10 +527,123 @@ kroute6_clear(void) while ((kr = RB_MIN(kroute6_tree, &krt6)) != NULL) kroute6_remove(kr); } +static inline int +karp_compare(struct kif_arp *a, struct kif_arp *b) +{ + /* Interface indices are assumed equal */ + if (ntohl(a->addr.sin.sin_addr.s_addr) > + ntohl(b->addr.sin.sin_addr.s_addr)) + return (1); + if (ntohl(a->addr.sin.sin_addr.s_addr) < + ntohl(b->addr.sin.sin_addr.s_addr)) + return (-1); + return (0); +} + +static inline struct kif_arp * +karp_search(struct kif_node *kn, struct kif_arp *ka) +{ + struct kif_arp *pivot; + + TAILQ_FOREACH(pivot, &kn->arps, entry) { + switch (karp_compare(ka, pivot)) { + case 0: /* found */ + return (pivot); + case -1: /* ka < pivot, end the search */ + return (NULL); + } + } + /* looped through the whole list and didn't find */ + return (NULL); +} + +struct kif_arp * +karp_find(struct sockaddr *sa, u_short ifindex) +{ + struct kif_node *kn; + struct kif_arp *ka = NULL, s; + + memcpy(&s.addr.sa, sa, sa->sa_len); + + if (ifindex == 0) { + /* +* We iterate manually to handle zero ifindex special +* case differently from kif_find, in particular we +* want to look for the address on all available +* interfaces. +*/ + RB_FOREACH(kn, kif_tree, &kit) { + if ((ka = karp_search(kn, &s)) != NULL) + break; + } + } else { + if ((kn = kif_find(ifindex)) == NULL) + return (NULL); + ka = karp_search(kn, &s); + } + return (ka); +} + +int +karp_insert(struct kif_node *kn, struct kif_arp *ka) +{ + struct kif_arp *pivot; + + if (ka->if_index == 0) + return (-1); + if (!kn && (kn = kif_find(ka->if_index)) == NULL) + return (-1); + /* Put entry on the list in the ascending lexical order */ + TAILQ_FOREACH(pivot, &kn->arps, entry) {