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 @@ void if_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 list and didn't find */
^
a random typo appears!
> + 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 && (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.
> +
> +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) {
> + switch (karp_compare(ka, pivot)) {
> + case 0: /* collision */
> + return (-1);
> + case -1: /* ka < pivot */
> + TAILQ_INSERT_BEFORE(pivot, ka, entry);
> + return (0);
> + }
> + }
> + /* ka is larger than any other element on the list */
> + TAILQ_INSERT_TAIL(&kn->arps, ka, entry);
> + return (0);
> +}
> +
> +int
> +karp_remove(struct kif_node *kn, struct kif_arp *ka)
> +{
> + if (ka->if_index == 0)
> + return (-1);
> + if (!kn && (kn = kif_find(ka->if_index)) == NULL)
> + return (-1);
> + TAILQ_REMOVE(&kn->arps, ka, entry);
> + free(ka);
> + return (0);
> +}
> +
> +struct kif_arp *
> +karp_first(u_short ifindex)
> +{
> + struct kif_node *kn;
> +
> + if ((kn = kif_find(ifindex)) == NULL)
> + return (NULL);
> + return (TAILQ_FIRST(&kn->arps));
> +}
> +
> +struct kif_arp *
> +karp_getaddr(struct sockaddr *sa, u_short ifindex, int next)
> +{
> + struct kif_arp *ka;
> +
> + if ((ka = karp_find(sa, ifindex)) == NULL)
> + return (NULL);
> + return (next ? TAILQ_NEXT(ka, entry) : ka);
> +}
> +
> struct kif_node *
> kif_find(u_short if_index)
> {
> struct kif_node s;
>
> @@ -570,10 +698,11 @@ kif_insert(u_short if_index)
> if ((kif = calloc(1, sizeof(struct kif_node))) == NULL)
> return (NULL);
>
> kif->k.if_index = if_index;
> TAILQ_INIT(&kif->addrs);
> + TAILQ_INIT(&kif->arps);
>
> if (RB_INSERT(kif_tree, &kit, kif) != NULL)
> fatalx("kif_insert: RB_INSERT");
>
> kr_state.ks_nkif++;
> @@ -584,20 +713,24 @@ kif_insert(u_short if_index)
>
> int
> kif_remove(struct kif_node *kif)
> {
> struct kif_addr *ka;
> + struct kif_arp *kr;
>
> if (RB_REMOVE(kif_tree, &kit, kif) == NULL) {
> log_warnx("RB_REMOVE(kif_tree, &kit, kif)");
> return (-1);
> }
>
> while ((ka = TAILQ_FIRST(&kif->addrs)) != NULL) {
> TAILQ_REMOVE(&kif->addrs, ka, entry);
> ka_remove(ka);
> }
> + while ((kr = TAILQ_FIRST(&kif->arps)) != NULL) {
> + karp_remove(kif, kr);
> + }
> free(kif);
>
> kr_state.ks_nkif--;
> kr_state.ks_iflastchange = smi_getticks();
>
> @@ -895,10 +1028,12 @@ 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;
> @@ -974,10 +1109,49 @@ fetchifs(u_short if_index)
> free(buf);
>
> return (rv);
> }
>
> +int
> +fetcharp(void)
> +{
> + size_t len;
> + int mib[7];
> + char *buf;
> + int rv;
> +
> + mib[0] = CTL_NET;
> + mib[1] = AF_ROUTE;
> + mib[2] = 0;
> + mib[3] = AF_INET;
> + mib[4] = NET_RT_FLAGS;
> + mib[5] = RTF_LLINFO;
> + mib[6] = snmpd_rtableid;
> +
> + if (sysctl(mib, 7, NULL, &len, NULL, 0) == -1) {
> + log_warn("sysctl");
> + return (-1);
> + }
> + /* Empty table? */
> + if (len == 0)
> + return (0);
> + if ((buf = malloc(len)) == NULL) {
> + log_warn("fetcharp");
> + return (-1);
> + }
> + if (sysctl(mib, 7, buf, &len, NULL, 0) == -1) {
> + log_warn("sysctl");
> + free(buf);
> + return (-1);
> + }
> +
> + rv = rtmsg_process(buf, len);
> + free(buf);
> +
> + return (rv);
> +}
> +
> /* ARGSUSED */
> void
> dispatch_rtmsg(int fd, short event, void *arg)
> {
> char buf[RT_BUF_SIZE];
> @@ -1018,14 +1192,13 @@ rtmsg_process(char *buf, int len)
> switch (rtm->rtm_type) {
> case RTM_ADD:
> case RTM_GET:
> case RTM_CHANGE:
> case RTM_DELETE:
> + case RTM_RESOLVE:
> if (rtm->rtm_errno) /* failed attempts */
> continue;
> - if (rtm->rtm_flags & RTF_LLINFO) /* arp cache */
> - continue;
>
> if (dispatch_rtmsg_addr(rtm, rti_info) == -1)
> return (-1);
> break;
> case RTM_IFINFO:
> @@ -1067,12 +1240,14 @@ int
> dispatch_rtmsg_addr(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;
> struct kroute_node *kr;
> struct kroute6_node *kr6;
> + struct kif_arp *ka;
> int flags, mpath = 0;
> u_int16_t ifindex;
> u_int8_t prefixlen;
> u_int8_t prio;
>
> @@ -1129,16 +1304,33 @@ dispatch_rtmsg_addr(struct rt_msghdr *rtm, struct
> sockaddr *rti_info[RTAX_MAX])
> if ((sa = rti_info[RTAX_GATEWAY]) != NULL)
> switch (sa->sa_family) {
> case AF_LINK:
> flags |= F_CONNECTED;
> ifindex = rtm->rtm_index;
> - sa = NULL;
> mpath = 0; /* link local stuff can't be mpath */
> break;
> }
>
> if (rtm->rtm_type == RTM_DELETE) {
> + if (sa != NULL && sa->sa_family == AF_LINK &&
> + (rtm->rtm_flags & RTF_HOST) &&
> + psa->sa_family == AF_INET) {
> + if (rtm->rtm_tableid != snmpd_rtableid)
> + return (0);
> + if ((ka = karp_find(psa, ifindex)) == NULL)
> + return (0);
> + if (karp_remove(NULL, ka) == -1)
> + return (-1);
> + return (0);
> + } else if (sa == NULL && (rtm->rtm_flags & RTF_HOST) &&
> + psa->sa_family == AF_INET) {
> + if (rtm->rtm_tableid != snmpd_rtableid)
> + return (0);
> + if ((ka = karp_find(psa, ifindex)) != NULL)
> + karp_remove(NULL, ka);
> + /* 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,
> prefixlen, prio)) == NULL)
> @@ -1178,10 +1370,52 @@ dispatch_rtmsg_addr(struct rt_msghdr *rtm, struct
> sockaddr *rti_info[RTAX_MAX])
> }
>
> if (sa == NULL && !(flags & F_CONNECTED))
> return (0);
>
> + /* Add or update an ARP entry */
> + if ((rtm->rtm_flags & RTF_LLINFO) && (rtm->rtm_flags & RTF_HOST) &&
> + sa != NULL && sa->sa_family == AF_LINK &&
> + psa->sa_family == AF_INET) {
> + if (rtm->rtm_tableid != snmpd_rtableid)
> + return (0);
> + sa_dl = (struct sockaddr_dl *)sa;
> + /* ignore incomplete entries */
> + if (!sa_dl->sdl_alen)
> + return (0);
> + /* ignore entries that do not specify an interface */
> + if (ifindex == 0)
> + return (0);
> + if ((ka = karp_find(psa, ifindex)) != NULL) {
> + memcpy(&ka->target.sdl, sa_dl, sa_dl->sdl_len);
> + /* save static/permanent flags */
> + if (rtm->rtm_rmx.rmx_expire == 0 ||
> + rtm->rtm_flags & RTF_PERMANENT_ARP)
> + flags |= F_STATIC;
> + ka->flags = flags;
> + } else {
> + if ((ka = calloc(1, sizeof(struct kif_arp))) == NULL) {
> + log_warn("dispatch_rtmsg");
> + return (-1);
> + }
> + memcpy(&ka->addr.sa, psa, psa->sa_len);
> + memcpy(&ka->target.sdl, sa_dl, sa_dl->sdl_len);
> + /* save static/permanent flags */
> + if (rtm->rtm_rmx.rmx_expire == 0 ||
> + rtm->rtm_flags & RTF_PERMANENT_ARP)
> + flags |= F_STATIC;
> + ka->flags = flags;
> + ka->if_index = ifindex;
> + if (karp_insert(NULL, ka)) {
> + free(ka);
> + log_warnx("dispatch_rtmsg: failed to insert");
> + return (-1);
> + }
> + }
> + return (0);
> + }
> +
> switch (psa->sa_family) {
> case AF_INET:
> sa_in = (struct sockaddr_in *)sa;
> if ((kr = kroute_find(psa_in->sin_addr.s_addr, prefixlen,
> prio)) != NULL) {
> diff --git usr.sbin/snmpd/mib.c usr.sbin/snmpd/mib.c
> index e124a03..c284472 100644
> --- usr.sbin/snmpd/mib.c
> +++ usr.sbin/snmpd/mib.c
> @@ -2928,10 +2928,13 @@ int mib_ipfragfails(struct oid *, struct ber_oid *,
> struct ber_element **);
> int mib_iproutingdiscards(struct oid *, struct ber_oid *,
> struct ber_element **);
> int mib_ipaddr(struct oid *, struct ber_oid *, struct ber_element **);
> struct ber_oid *
> mib_ipaddrtable(struct oid *, struct ber_oid *, struct ber_oid *);
> +int mib_physaddr(struct oid *, struct ber_oid *, struct ber_element **);
> +struct ber_oid *
> + mib_physaddrtable(struct oid *, struct ber_oid *, struct ber_oid *);
>
> static struct oid ip_mib[] = {
> { MIB(ipMIB), OID_MIB },
> { MIB(ipForwarding), OID_RD, mib_ipforwarding },
> { MIB(ipDefaultTTL), OID_RD, mib_ipdefaultttl },
> @@ -2963,15 +2966,19 @@ static struct oid ip_mib[] = {
> mib_ipaddrtable },
> { MIB(ipAdEntBcastAddr), OID_TRD, mib_ipaddr, NULL,
> mib_ipaddrtable },
> { MIB(ipAdEntReasmMaxSize), OID_TRD, mib_ipaddr, NULL,
> mib_ipaddrtable },
> + { MIB(ipNetToMediaIfIndex), OID_TRD, mib_physaddr, NULL,
> + mib_physaddrtable },
> + { MIB(ipNetToMediaPhysAddress), OID_TRD, mib_physaddr, NULL,
> + mib_physaddrtable },
> + { MIB(ipNetToMediaNetAddress), OID_TRD, mib_physaddr, NULL,
> + mib_physaddrtable },
> + { MIB(ipNetToMediaType), OID_TRD, mib_physaddr, NULL,
> + mib_physaddrtable },
> #ifdef notyet
> - { MIB(ipNetToMediaIfIndex) },
> - { MIB(ipNetToMediaPhysAddress) },
> - { MIB(ipNetToMediaNetAddress) },
> - { MIB(ipNetToMediaType) },
> { MIB(ipRoutingDiscards) },
> #endif
> { MIBEND }
> };
>
> @@ -3256,10 +3263,153 @@ mib_ipaddr(struct oid *oid, struct ber_oid *o,
> struct ber_element **elm)
> }
>
> return (0);
> }
>
> +struct ber_oid *
> +mib_physaddrtable(struct oid *oid, struct ber_oid *o, struct ber_oid *no)
> +{
> + struct sockaddr_in addr;
> + struct oid a, b;
> + struct kif *kif;
> + struct kif_arp *ka;
> + u_int32_t id, idx = 0;
> +
> + bcopy(&oid->o_id, no, sizeof(*no));
> + id = oid->o_oidlen - 1;
> +
> + if (o->bo_n >= oid->o_oidlen) {
> + /*
> + * Compare the requested and the matched OID to see
> + * if we have to iterate to the next element.
> + */
> + bzero(&a, sizeof(a));
> + bcopy(o, &a.o_id, sizeof(struct ber_oid));
> + bzero(&b, sizeof(b));
> + bcopy(&oid->o_id, &b.o_id, sizeof(struct ber_oid));
> + b.o_oidlen--;
> + b.o_flags |= OID_TABLE;
> + if (smi_oid_cmp(&a, &b) == 0) {
> + o->bo_id[id] = oid->o_oid[id];
> + bcopy(o, no, sizeof(*no));
> + }
> + }
> +
> + if (o->bo_n > OIDIDX_ipNetToMedia + 1)
> + idx = o->bo_id[OIDIDX_ipNetToMedia + 1];
> +
> + bzero(&addr, sizeof(addr));
> + addr.sin_family = AF_INET;
> + addr.sin_len = sizeof(addr);
> + if (o->bo_n > OIDIDX_ipNetToMedia + 2)
> + mps_decodeinaddr(no, &addr.sin_addr, OIDIDX_ipNetToMedia + 2);
> +
> + if ((kif = kr_getif(idx)) == NULL) {
> + /* No configured interfaces */
> + if (idx == 0)
> + return (NULL);
> + /*
> + * It may happen that an interface with a specific index
> + * does not exist or has been removed. Jump to the next
> + * available interface.
> + */
> + kif = kr_getif(0);
> + nextif:
> + for (; kif != NULL; kif = kr_getnextif(kif->if_index))
> + if (kif->if_index > idx &&
> + (ka = karp_first(kif->if_index)) != NULL)
> + break;
> + if (kif == NULL) {
> + /* No more interfaces with addresses on them */
> + o->bo_id[OIDIDX_ipNetToMedia + 1] = 0;
> + mps_encodeinaddr(no, NULL, OIDIDX_ipNetToMedia + 2);
> + smi_oidlen(o);
> + return (NULL);
> + }
> + } else {
> + if (idx == 0 || addr.sin_addr.s_addr == 0)
> + ka = karp_first(kif->if_index);
> + else
> + ka = karp_getaddr((struct sockaddr *)&addr, idx, 1);
> + if (ka == NULL) {
> + /* Try next interface */
> + goto nextif;
> + }
> + }
> + idx = kif->if_index;
> +
> + no->bo_id[OIDIDX_ipNetToMedia + 1] = idx;
> + /* Encode real IPv4 address */
> + memcpy(&addr, &ka->addr.sin, ka->addr.sin.sin_len);
> + mps_encodeinaddr(no, &addr.sin_addr, OIDIDX_ipNetToMedia + 2);
> +
> + smi_oidlen(o);
> + return (no);
> +}
> +
> +int
> +mib_physaddr(struct oid *oid, struct ber_oid *o, struct ber_element **elm)
> +{
> + struct ber_element *ber = *elm;
> + struct sockaddr_in addr;
> + struct kif_arp *ka;
> + u_int32_t val, idx = 0;
> +
> + idx = o->bo_id[OIDIDX_ipNetToMedia + 1];
> + if (idx == 0) {
> + /* Strip invalid interface index and fail */
> + o->bo_n = OIDIDX_ipNetToMedia + 1;
> + return (1);
> + }
> +
> + /* Get the IP address */
> + bzero(&addr, sizeof(addr));
> + addr.sin_family = AF_INET;
> + addr.sin_len = sizeof(addr);
> +
> + if (mps_decodeinaddr(o, &addr.sin_addr,
> + OIDIDX_ipNetToMedia + 2) == -1) {
> + /* Strip invalid address and fail */
> + o->bo_n = OIDIDX_ipNetToMedia + 2;
> + return (1);
> + }
> + if ((ka = karp_getaddr((struct sockaddr *)&addr, idx, 0)) == NULL)
> + return (1);
> +
> + /* write OID */
> + ber = ber_add_oid(ber, o);
> +
> + switch (o->bo_id[OIDIDX_ipNetToMedia]) {
> + case 1: /* ipNetToMediaIfIndex */
> + ber = ber_add_integer(ber, ka->if_index);
> + break;
> + case 2: /* ipNetToMediaPhysAddress */
> + if (bcmp(LLADDR(&ka->target.sdl), ether_zeroaddr,
> + sizeof(ether_zeroaddr)) == 0)
> + ber = ber_add_nstring(ber, ether_zeroaddr,
> + sizeof(ether_zeroaddr));
> + else
> + ber = ber_add_nstring(ber, LLADDR(&ka->target.sdl),
> + ka->target.sdl.sdl_alen);
> + break;
> + case 3: /* ipNetToMediaNetAddress */
> + val = addr.sin_addr.s_addr;
> + ber = ber_add_nstring(ber, (char *)&val, sizeof(u_int32_t));
> + ber_set_header(ber, BER_CLASS_APPLICATION, SNMP_T_IPADDR);
> + break;
> + case 4: /* ipNetToMediaType */
> + if (ka->flags & F_STATIC)
> + ber = ber_add_integer(ber, 4); /* static */
> + else
> + ber = ber_add_integer(ber, 3); /* dynamic */
> + break;
> + default:
> + return (-1);
> + }
> + return (0);
> +}
> +
> /*
> * Defined in IP-FORWARD-MIB.txt (rfc4292)
> */
>
> int mib_ipfnroutes(struct oid *, struct ber_oid *, struct ber_element **);
> diff --git usr.sbin/snmpd/mib.h usr.sbin/snmpd/mib.h
> index 2551adb..4017508 100644
> --- usr.sbin/snmpd/mib.h
> +++ usr.sbin/snmpd/mib.h
> @@ -324,10 +324,11 @@
> #define MIB_ipAdEntNetMask MIB_ipAddrEntry, 3
> #define MIB_ipAdEntBcastAddr MIB_ipAddrEntry, 4
> #define MIB_ipAdEntReasmMaxSize MIB_ipAddrEntry, 5
> #define MIB_ipNetToMediaTable MIB_ipMIB, 22
> #define MIB_ipNetToMediaEntry MIB_ipNetToMediaTable, 1
> +#define OIDIDX_ipNetToMedia 9
> #define MIB_ipNetToMediaIfIndex MIB_ipNetToMediaEntry, 1
> #define MIB_ipNetToMediaPhysAddress MIB_ipNetToMediaEntry, 2
> #define MIB_ipNetToMediaNetAddress MIB_ipNetToMediaEntry, 3
> #define MIB_ipNetToMediaType MIB_ipNetToMediaEntry, 4
> #define MIB_ipRoutingDiscards MIB_ipMIB, 23
> @@ -1283,11 +1284,10 @@
> { MIBDECL(ipNetToMediaEntry) }, \
> { MIBDECL(ipNetToMediaIfIndex) }, \
> { MIBDECL(ipNetToMediaPhysAddress) }, \
> { MIBDECL(ipNetToMediaNetAddress) }, \
> { MIBDECL(ipNetToMediaType) }, \
> - { MIBDECL(ipNetToMediaType) }, \
> \
> { MIBDECL(ipfMIB) }, \
> { MIBDECL(ipfInetCidrRouteNumber) }, \
> { MIBDECL(ipfInetCidrRouteTable) }, \
> { MIBDECL(ipfInetCidrRouteEntry) }, \
> diff --git usr.sbin/snmpd/snmpd.h usr.sbin/snmpd/snmpd.h
> index 95244c4..bd5eae9 100644
> --- usr.sbin/snmpd/snmpd.h
> +++ usr.sbin/snmpd/snmpd.h
> @@ -20,10 +20,11 @@
> #ifndef _SNMPD_H
> #define _SNMPD_H
>
> #include <netinet/in.h>
> #include <netinet/if_ether.h>
> +#include <net/if_dl.h>
> #include <net/pfvar.h>
> #include <net/route.h>
>
> #include "ber.h"
> #include <snmp.h>
> @@ -173,10 +174,11 @@ extern struct ctl_connlist ctl_conns;
>
> union kaddr {
> struct sockaddr sa;
> struct sockaddr_in sin;
> struct sockaddr_in6 sin6;
> + struct sockaddr_dl sdl;
> char pad[32];
> };
>
> struct kroute {
> struct in_addr prefix;
> @@ -206,10 +208,19 @@ struct kif_addr {
>
> TAILQ_ENTRY(kif_addr) entry;
> RB_ENTRY(kif_addr) node;
> };
>
> +struct kif_arp {
> + u_short flags;
> + u_short if_index;
> + union kaddr addr;
> + union kaddr target;
> +
> + TAILQ_ENTRY(kif_arp) entry;
> +};
> +
> struct kif {
> char if_name[IF_NAMESIZE];
> char if_descr[IFDESCRSIZE];
> u_int8_t if_lladdr[ETHER_ADDR_LEN];
> struct if_data if_data;
> @@ -538,10 +549,13 @@ struct kif_addr *kr_getaddr(struct sockaddr *);
> struct kif_addr *kr_getnextaddr(struct sockaddr *);
>
> struct kroute *kroute_first(void);
> struct kroute *kroute_getaddr(in_addr_t, u_int8_t, u_int8_t, int);
>
> +struct kif_arp *karp_first(u_short);
> +struct kif_arp *karp_getaddr(struct sockaddr *, u_short, int);
> +
> /* snmpe.c */
> pid_t snmpe(struct privsep *, struct privsep_proc *);
> void snmpe_shutdown(struct privsep *, struct privsep_proc *);
>
> /* trap.c */
>