-/* $OpenBSD: kroute.c,v 1.24 2013/10/30 17:24:35 deraadt Exp $ */
+/* $OpenBSD: kroute.c,v 1.25 2014/04/28 12:03:32 mikeb Exp $ */
/*
* Copyright (c) 2007, 2008 Reyk Floeter <reyk@openbsd.org>
struct kif_node {
RB_ENTRY(kif_node) entry;
TAILQ_HEAD(, kif_addr) addrs;
+ TAILQ_HEAD(, kif_arp) arps;
struct kif k;
};
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 *);
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 *,
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);
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) {
+ 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)
{
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");
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)");
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--;
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);
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] = 0;
+
+ 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)
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);
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;
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 ((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 ((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 (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) {
+ 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);
+ if (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);
+ if (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;
-/* $OpenBSD: mib.c,v 1.67 2014/04/08 14:04:11 mpi Exp $ */
+/* $OpenBSD: mib.c,v 1.68 2014/04/28 12:03:32 mikeb Exp $ */
/*
* Copyright (c) 2012 Joel Knight <joel@openbsd.org>
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_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 }
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)
*/
-/* $OpenBSD: mib.h,v 1.33 2013/10/01 19:24:46 reyk Exp $ */
+/* $OpenBSD: mib.h,v 1.34 2014/04/28 12:03:32 mikeb Exp $ */
/*
* Copyright (c) 2007, 2008 Reyk Floeter <reyk@openbsd.org>
#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
{ MIBDECL(ipNetToMediaIfIndex) }, \
{ MIBDECL(ipNetToMediaPhysAddress) }, \
{ MIBDECL(ipNetToMediaNetAddress) }, \
- { MIBDECL(ipNetToMediaType) }, \
{ MIBDECL(ipNetToMediaType) }, \
\
{ MIBDECL(ipfMIB) }, \
-/* $OpenBSD: snmpd.h,v 1.53 2014/04/28 08:25:05 blambert Exp $ */
+/* $OpenBSD: snmpd.h,v 1.54 2014/04/28 12:03:32 mikeb Exp $ */
/*
* Copyright (c) 2007, 2008, 2012 Reyk Floeter <reyk@openbsd.org>
#include <netinet/in.h>
#include <netinet/if_ether.h>
+#include <net/if_dl.h>
#include <net/pfvar.h>
#include <net/route.h>
struct sockaddr sa;
struct sockaddr_in sin;
struct sockaddr_in6 sin6;
+ struct sockaddr_dl sdl;
char pad[32];
};
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];
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(void);