snmpd: support for ipNetToMediaTable (ARP table exporting)

2014-04-07 Thread Mike Belopuhov
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)

2014-04-07 Thread Bret Lambert
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)

2014-04-07 Thread Mike Belopuhov
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)

2014-04-07 Thread Mike Belopuhov
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)

2014-04-10 Thread Mike Belopuhov
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) {