ce42ce038e
as otherwise platforms with strict alignment would break. It's unclear to me if there's also a problem with access to the address list following the structure. However we never copied the address list after the structure and thus are pointing at random memory. For now just use a pointer to the original memory for accessing the address list making it at least work on platforms with weak memory access. PR: 195445 Reported by: wolfgang lyxys.ka.sub.org Tested by: wolfgang lyxys.ka.sub.org (x86) MFC after: 3 days
1819 lines
41 KiB
C
1819 lines
41 KiB
C
/*
|
|
* Copyright (c) 2001-2003
|
|
* Fraunhofer Institute for Open Communication Systems (FhG Fokus).
|
|
* All rights reserved.
|
|
*
|
|
* Author: Harti Brandt <harti@freebsd.org>
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*
|
|
* $Begemot: mibII.c 516 2006-10-27 15:54:02Z brandt_h $
|
|
*
|
|
* Implementation of the standard interfaces and ip MIB.
|
|
*/
|
|
#include "mibII.h"
|
|
#include "mibII_oid.h"
|
|
#include <net/if.h>
|
|
#include <net/if_types.h>
|
|
|
|
|
|
/*****************************/
|
|
|
|
/* our module */
|
|
static struct lmodule *module;
|
|
|
|
/* routing socket */
|
|
static int route;
|
|
static void *route_fd;
|
|
|
|
/* if-index allocator */
|
|
static uint32_t next_if_index = 1;
|
|
|
|
/* currently fetching the arp table */
|
|
static int in_update_arp;
|
|
|
|
/* OR registrations */
|
|
static u_int ifmib_reg;
|
|
static u_int ipmib_reg;
|
|
static u_int tcpmib_reg;
|
|
static u_int udpmib_reg;
|
|
static u_int ipForward_reg;
|
|
|
|
/*****************************/
|
|
|
|
/* list of all IP addresses */
|
|
struct mibifa_list mibifa_list = TAILQ_HEAD_INITIALIZER(mibifa_list);
|
|
|
|
/* list of all interfaces */
|
|
struct mibif_list mibif_list = TAILQ_HEAD_INITIALIZER(mibif_list);
|
|
|
|
/* list of dynamic interface names */
|
|
struct mibdynif_list mibdynif_list = SLIST_HEAD_INITIALIZER(mibdynif_list);
|
|
|
|
/* list of all interface index mappings */
|
|
struct mibindexmap_list mibindexmap_list = STAILQ_HEAD_INITIALIZER(mibindexmap_list);
|
|
|
|
/* list of all stacking entries */
|
|
struct mibifstack_list mibifstack_list = TAILQ_HEAD_INITIALIZER(mibifstack_list);
|
|
|
|
/* list of all receive addresses */
|
|
struct mibrcvaddr_list mibrcvaddr_list = TAILQ_HEAD_INITIALIZER(mibrcvaddr_list);
|
|
|
|
/* list of all NetToMedia entries */
|
|
struct mibarp_list mibarp_list = TAILQ_HEAD_INITIALIZER(mibarp_list);
|
|
|
|
/* number of interfaces */
|
|
int32_t mib_if_number;
|
|
|
|
/* last change of table */
|
|
uint64_t mib_iftable_last_change;
|
|
|
|
/* last change of stack table */
|
|
uint64_t mib_ifstack_last_change;
|
|
|
|
/* if this is set, one of our lists may be bad. refresh them when idle */
|
|
int mib_iflist_bad;
|
|
|
|
/* network socket */
|
|
int mib_netsock;
|
|
|
|
/* last time refreshed */
|
|
uint64_t mibarpticks;
|
|
|
|
/* info on system clocks */
|
|
struct clockinfo clockinfo;
|
|
|
|
/* list of all New if registrations */
|
|
static struct newifreg_list newifreg_list = TAILQ_HEAD_INITIALIZER(newifreg_list);
|
|
|
|
/* baud rate of fastest interface */
|
|
uint64_t mibif_maxspeed;
|
|
|
|
/* user-forced update interval */
|
|
u_int mibif_force_hc_update_interval;
|
|
|
|
/* current update interval */
|
|
u_int mibif_hc_update_interval;
|
|
|
|
/* HC update timer handle */
|
|
static void *hc_update_timer;
|
|
|
|
/* Idle poll timer */
|
|
static void *mibII_poll_timer;
|
|
|
|
/* interfaces' data poll interval */
|
|
u_int mibII_poll_ticks;
|
|
|
|
/* Idle poll hook */
|
|
static void mibII_idle(void *arg __unused);
|
|
|
|
/*****************************/
|
|
|
|
static const struct asn_oid oid_ifMIB = OIDX_ifMIB;
|
|
static const struct asn_oid oid_ipMIB = OIDX_ipMIB;
|
|
static const struct asn_oid oid_tcpMIB = OIDX_tcpMIB;
|
|
static const struct asn_oid oid_udpMIB = OIDX_udpMIB;
|
|
static const struct asn_oid oid_ipForward = OIDX_ipForward;
|
|
static const struct asn_oid oid_linkDown = OIDX_linkDown;
|
|
static const struct asn_oid oid_linkUp = OIDX_linkUp;
|
|
static const struct asn_oid oid_ifIndex = OIDX_ifIndex;
|
|
|
|
/*****************************/
|
|
|
|
/*
|
|
* Find an interface
|
|
*/
|
|
struct mibif *
|
|
mib_find_if(u_int idx)
|
|
{
|
|
struct mibif *ifp;
|
|
|
|
TAILQ_FOREACH(ifp, &mibif_list, link)
|
|
if (ifp->index == idx)
|
|
return (ifp);
|
|
return (NULL);
|
|
}
|
|
|
|
struct mibif *
|
|
mib_find_if_sys(u_int sysindex)
|
|
{
|
|
struct mibif *ifp;
|
|
|
|
TAILQ_FOREACH(ifp, &mibif_list, link)
|
|
if (ifp->sysindex == sysindex)
|
|
return (ifp);
|
|
return (NULL);
|
|
}
|
|
|
|
struct mibif *
|
|
mib_find_if_name(const char *name)
|
|
{
|
|
struct mibif *ifp;
|
|
|
|
TAILQ_FOREACH(ifp, &mibif_list, link)
|
|
if (strcmp(ifp->name, name) == 0)
|
|
return (ifp);
|
|
return (NULL);
|
|
}
|
|
|
|
/*
|
|
* Check whether an interface is dynamic. The argument may include the
|
|
* unit number. This assumes, that the name part does NOT contain digits.
|
|
*/
|
|
int
|
|
mib_if_is_dyn(const char *name)
|
|
{
|
|
size_t len;
|
|
struct mibdynif *d;
|
|
|
|
for (len = 0; name[len] != '\0' && isalpha(name[len]) ; len++)
|
|
;
|
|
SLIST_FOREACH(d, &mibdynif_list, link)
|
|
if (strlen(d->name) == len && strncmp(d->name, name, len) == 0)
|
|
return (1);
|
|
return (0);
|
|
}
|
|
|
|
/* set an interface name to dynamic mode */
|
|
void
|
|
mib_if_set_dyn(const char *name)
|
|
{
|
|
struct mibdynif *d;
|
|
|
|
SLIST_FOREACH(d, &mibdynif_list, link)
|
|
if (strcmp(name, d->name) == 0)
|
|
return;
|
|
if ((d = malloc(sizeof(*d))) == NULL)
|
|
err(1, NULL);
|
|
strcpy(d->name, name);
|
|
SLIST_INSERT_HEAD(&mibdynif_list, d, link);
|
|
}
|
|
|
|
/*
|
|
* register for interface creations
|
|
*/
|
|
int
|
|
mib_register_newif(int (*func)(struct mibif *), const struct lmodule *mod)
|
|
{
|
|
struct newifreg *reg;
|
|
|
|
TAILQ_FOREACH(reg, &newifreg_list, link)
|
|
if (reg->mod == mod) {
|
|
reg->func = func;
|
|
return (0);
|
|
}
|
|
if ((reg = malloc(sizeof(*reg))) == NULL) {
|
|
syslog(LOG_ERR, "newifreg: %m");
|
|
return (-1);
|
|
}
|
|
reg->mod = mod;
|
|
reg->func = func;
|
|
TAILQ_INSERT_TAIL(&newifreg_list, reg, link);
|
|
|
|
return (0);
|
|
}
|
|
|
|
void
|
|
mib_unregister_newif(const struct lmodule *mod)
|
|
{
|
|
struct newifreg *reg;
|
|
|
|
TAILQ_FOREACH(reg, &newifreg_list, link)
|
|
if (reg->mod == mod) {
|
|
TAILQ_REMOVE(&newifreg_list, reg, link);
|
|
free(reg);
|
|
return;
|
|
}
|
|
|
|
}
|
|
|
|
struct mibif *
|
|
mib_first_if(void)
|
|
{
|
|
return (TAILQ_FIRST(&mibif_list));
|
|
}
|
|
struct mibif *
|
|
mib_next_if(const struct mibif *ifp)
|
|
{
|
|
return (TAILQ_NEXT(ifp, link));
|
|
}
|
|
|
|
/*
|
|
* Change the admin status of an interface
|
|
*/
|
|
int
|
|
mib_if_admin(struct mibif *ifp, int up)
|
|
{
|
|
struct ifreq ifr;
|
|
|
|
strncpy(ifr.ifr_name, ifp->name, sizeof(ifr.ifr_name));
|
|
if (ioctl(mib_netsock, SIOCGIFFLAGS, &ifr) == -1) {
|
|
syslog(LOG_ERR, "SIOCGIFFLAGS(%s): %m", ifp->name);
|
|
return (-1);
|
|
}
|
|
if (up)
|
|
ifr.ifr_flags |= IFF_UP;
|
|
else
|
|
ifr.ifr_flags &= ~IFF_UP;
|
|
if (ioctl(mib_netsock, SIOCSIFFLAGS, &ifr) == -1) {
|
|
syslog(LOG_ERR, "SIOCSIFFLAGS(%s): %m", ifp->name);
|
|
return (-1);
|
|
}
|
|
|
|
(void)mib_fetch_ifmib(ifp);
|
|
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Generate a link up/down trap
|
|
*/
|
|
static void
|
|
link_trap(struct mibif *ifp, int up)
|
|
{
|
|
struct snmp_value ifindex;
|
|
|
|
ifindex.var = oid_ifIndex;
|
|
ifindex.var.subs[ifindex.var.len++] = ifp->index;
|
|
ifindex.syntax = SNMP_SYNTAX_INTEGER;
|
|
ifindex.v.integer = ifp->index;
|
|
|
|
snmp_send_trap(up ? &oid_linkUp : &oid_linkDown, &ifindex,
|
|
(struct snmp_value *)NULL);
|
|
}
|
|
|
|
/**
|
|
* Fetch the GENERIC IFMIB and update the HC counters
|
|
*/
|
|
static int
|
|
fetch_generic_mib(struct mibif *ifp, const struct ifmibdata *old)
|
|
{
|
|
int name[6];
|
|
size_t len;
|
|
struct mibif_private *p = ifp->private;
|
|
|
|
name[0] = CTL_NET;
|
|
name[1] = PF_LINK;
|
|
name[2] = NETLINK_GENERIC;
|
|
name[3] = IFMIB_IFDATA;
|
|
name[4] = ifp->sysindex;
|
|
name[5] = IFDATA_GENERAL;
|
|
|
|
len = sizeof(ifp->mib);
|
|
if (sysctl(name, 6, &ifp->mib, &len, NULL, 0) == -1) {
|
|
if (errno != ENOENT)
|
|
syslog(LOG_WARNING, "sysctl(ifmib, %s) failed %m",
|
|
ifp->name);
|
|
return (-1);
|
|
}
|
|
|
|
/*
|
|
* Assume that one of the two following compounds is optimized away
|
|
*/
|
|
if (ULONG_MAX >= 0xffffffffffffffffULL) {
|
|
p->hc_inoctets = ifp->mib.ifmd_data.ifi_ibytes;
|
|
p->hc_outoctets = ifp->mib.ifmd_data.ifi_obytes;
|
|
p->hc_omcasts = ifp->mib.ifmd_data.ifi_omcasts;
|
|
p->hc_opackets = ifp->mib.ifmd_data.ifi_opackets;
|
|
p->hc_imcasts = ifp->mib.ifmd_data.ifi_imcasts;
|
|
p->hc_ipackets = ifp->mib.ifmd_data.ifi_ipackets;
|
|
|
|
} else if (ULONG_MAX >= 0xffffffff) {
|
|
|
|
#define UPDATE(HC, MIB) \
|
|
if (old->ifmd_data.MIB > ifp->mib.ifmd_data.MIB) \
|
|
p->HC += (0x100000000ULL + \
|
|
ifp->mib.ifmd_data.MIB) - \
|
|
old->ifmd_data.MIB; \
|
|
else \
|
|
p->HC += ifp->mib.ifmd_data.MIB - \
|
|
old->ifmd_data.MIB;
|
|
|
|
UPDATE(hc_inoctets, ifi_ibytes)
|
|
UPDATE(hc_outoctets, ifi_obytes)
|
|
UPDATE(hc_omcasts, ifi_omcasts)
|
|
UPDATE(hc_opackets, ifi_opackets)
|
|
UPDATE(hc_imcasts, ifi_imcasts)
|
|
UPDATE(hc_ipackets, ifi_ipackets)
|
|
|
|
#undef UPDATE
|
|
} else
|
|
abort();
|
|
return (0);
|
|
}
|
|
|
|
/**
|
|
* Update the 64-bit interface counters
|
|
*/
|
|
static void
|
|
update_hc_counters(void *arg __unused)
|
|
{
|
|
struct mibif *ifp;
|
|
struct ifmibdata oldmib;
|
|
|
|
TAILQ_FOREACH(ifp, &mibif_list, link) {
|
|
oldmib = ifp->mib;
|
|
(void)fetch_generic_mib(ifp, &oldmib);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Recompute the poll timer for the HC counters
|
|
*/
|
|
void
|
|
mibif_reset_hc_timer(void)
|
|
{
|
|
u_int ticks;
|
|
|
|
if ((ticks = mibif_force_hc_update_interval) == 0) {
|
|
if (mibif_maxspeed <= IF_Mbps(10)) {
|
|
/* at 10Mbps overflow needs 3436 seconds */
|
|
ticks = 3000 * 100; /* 50 minutes */
|
|
} else if (mibif_maxspeed <= IF_Mbps(100)) {
|
|
/* at 100Mbps overflow needs 343 seconds */
|
|
ticks = 300 * 100; /* 5 minutes */
|
|
} else if (mibif_maxspeed < IF_Mbps(622)) {
|
|
/* at 622Mbps overflow needs 53 seconds */
|
|
ticks = 40 * 100; /* 40 seconds */
|
|
} else if (mibif_maxspeed <= IF_Mbps(1000)) {
|
|
/* at 1Gbps overflow needs 34 seconds */
|
|
ticks = 20 * 100; /* 20 seconds */
|
|
} else {
|
|
/* at 10Gbps overflow needs 3.4 seconds */
|
|
ticks = 100; /* 1 seconds */
|
|
}
|
|
}
|
|
|
|
if (ticks == mibif_hc_update_interval)
|
|
return;
|
|
|
|
if (hc_update_timer != NULL) {
|
|
timer_stop(hc_update_timer);
|
|
hc_update_timer = NULL;
|
|
}
|
|
update_hc_counters(NULL);
|
|
if ((hc_update_timer = timer_start_repeat(ticks * 10, ticks * 10,
|
|
update_hc_counters, NULL, module)) == NULL) {
|
|
syslog(LOG_ERR, "timer_start(%u): %m", ticks);
|
|
return;
|
|
}
|
|
mibif_hc_update_interval = ticks;
|
|
}
|
|
|
|
/**
|
|
* Restart the idle poll timer.
|
|
*/
|
|
void
|
|
mibif_restart_mibII_poll_timer(void)
|
|
{
|
|
if (mibII_poll_timer != NULL)
|
|
timer_stop(mibII_poll_timer);
|
|
|
|
if ((mibII_poll_timer = timer_start_repeat(mibII_poll_ticks * 10,
|
|
mibII_poll_ticks * 10, mibII_idle, NULL, module)) == NULL)
|
|
syslog(LOG_ERR, "timer_start(%u): %m", mibII_poll_ticks);
|
|
}
|
|
|
|
/*
|
|
* Fetch new MIB data.
|
|
*/
|
|
int
|
|
mib_fetch_ifmib(struct mibif *ifp)
|
|
{
|
|
int name[6];
|
|
size_t len;
|
|
void *newmib;
|
|
struct ifmibdata oldmib = ifp->mib;
|
|
struct ifreq irr;
|
|
|
|
if (fetch_generic_mib(ifp, &oldmib) == -1)
|
|
return (-1);
|
|
|
|
/*
|
|
* Quoting RFC2863, 3.1.15: "... LinkUp and linkDown traps are
|
|
* generated just after ifOperStatus leaves, or just before it
|
|
* enters, the down state, respectively;"
|
|
*/
|
|
if (ifp->trap_enable && ifp->mib.ifmd_data.ifi_link_state !=
|
|
oldmib.ifmd_data.ifi_link_state &&
|
|
(ifp->mib.ifmd_data.ifi_link_state == LINK_STATE_DOWN ||
|
|
oldmib.ifmd_data.ifi_link_state == LINK_STATE_DOWN))
|
|
link_trap(ifp, ifp->mib.ifmd_data.ifi_link_state ==
|
|
LINK_STATE_UP ? 1 : 0);
|
|
|
|
ifp->flags &= ~(MIBIF_HIGHSPEED | MIBIF_VERYHIGHSPEED);
|
|
if (ifp->mib.ifmd_data.ifi_baudrate > 20000000) {
|
|
ifp->flags |= MIBIF_HIGHSPEED;
|
|
if (ifp->mib.ifmd_data.ifi_baudrate > 650000000)
|
|
ifp->flags |= MIBIF_VERYHIGHSPEED;
|
|
}
|
|
if (ifp->mib.ifmd_data.ifi_baudrate > mibif_maxspeed) {
|
|
mibif_maxspeed = ifp->mib.ifmd_data.ifi_baudrate;
|
|
mibif_reset_hc_timer();
|
|
}
|
|
|
|
/*
|
|
* linkspecific MIB
|
|
*/
|
|
name[0] = CTL_NET;
|
|
name[1] = PF_LINK;
|
|
name[2] = NETLINK_GENERIC;
|
|
name[3] = IFMIB_IFDATA;
|
|
name[4] = ifp->sysindex;
|
|
name[5] = IFDATA_LINKSPECIFIC;
|
|
if (sysctl(name, 6, NULL, &len, NULL, 0) == -1) {
|
|
syslog(LOG_WARNING, "sysctl linkmib estimate (%s): %m",
|
|
ifp->name);
|
|
if (ifp->specmib != NULL) {
|
|
ifp->specmib = NULL;
|
|
ifp->specmiblen = 0;
|
|
}
|
|
goto out;
|
|
}
|
|
if (len == 0) {
|
|
if (ifp->specmib != NULL) {
|
|
ifp->specmib = NULL;
|
|
ifp->specmiblen = 0;
|
|
}
|
|
goto out;
|
|
}
|
|
|
|
if (ifp->specmiblen != len) {
|
|
if ((newmib = realloc(ifp->specmib, len)) == NULL) {
|
|
ifp->specmib = NULL;
|
|
ifp->specmiblen = 0;
|
|
goto out;
|
|
}
|
|
ifp->specmib = newmib;
|
|
ifp->specmiblen = len;
|
|
}
|
|
if (sysctl(name, 6, ifp->specmib, &len, NULL, 0) == -1) {
|
|
syslog(LOG_WARNING, "sysctl linkmib (%s): %m", ifp->name);
|
|
if (ifp->specmib != NULL) {
|
|
ifp->specmib = NULL;
|
|
ifp->specmiblen = 0;
|
|
}
|
|
}
|
|
|
|
out:
|
|
strncpy(irr.ifr_name, ifp->name, sizeof(irr.ifr_name));
|
|
irr.ifr_buffer.buffer = MIBIF_PRIV(ifp)->alias;
|
|
irr.ifr_buffer.length = sizeof(MIBIF_PRIV(ifp)->alias);
|
|
if (ioctl(mib_netsock, SIOCGIFDESCR, &irr) == -1) {
|
|
MIBIF_PRIV(ifp)->alias[0] = 0;
|
|
if (errno != ENOMSG)
|
|
syslog(LOG_WARNING, "SIOCGIFDESCR (%s): %m", ifp->name);
|
|
} else if (irr.ifr_buffer.buffer == NULL) {
|
|
MIBIF_PRIV(ifp)->alias[0] = 0;
|
|
syslog(LOG_WARNING, "SIOCGIFDESCR (%s): too long (%zu)",
|
|
ifp->name, irr.ifr_buffer.length);
|
|
}
|
|
ifp->mibtick = get_ticks();
|
|
return (0);
|
|
}
|
|
|
|
/* find first/next address for a given interface */
|
|
struct mibifa *
|
|
mib_first_ififa(const struct mibif *ifp)
|
|
{
|
|
struct mibifa *ifa;
|
|
|
|
TAILQ_FOREACH(ifa, &mibifa_list, link)
|
|
if (ifp->index == ifa->ifindex)
|
|
return (ifa);
|
|
return (NULL);
|
|
}
|
|
|
|
struct mibifa *
|
|
mib_next_ififa(struct mibifa *ifa0)
|
|
{
|
|
struct mibifa *ifa;
|
|
|
|
ifa = ifa0;
|
|
while ((ifa = TAILQ_NEXT(ifa, link)) != NULL)
|
|
if (ifa->ifindex == ifa0->ifindex)
|
|
return (ifa);
|
|
return (NULL);
|
|
}
|
|
|
|
/*
|
|
* Allocate a new IFA
|
|
*/
|
|
static struct mibifa *
|
|
alloc_ifa(u_int ifindex, struct in_addr addr)
|
|
{
|
|
struct mibifa *ifa;
|
|
uint32_t ha;
|
|
|
|
if ((ifa = malloc(sizeof(struct mibifa))) == NULL) {
|
|
syslog(LOG_ERR, "ifa: %m");
|
|
return (NULL);
|
|
}
|
|
ifa->inaddr = addr;
|
|
ifa->ifindex = ifindex;
|
|
|
|
ha = ntohl(ifa->inaddr.s_addr);
|
|
ifa->index.len = 4;
|
|
ifa->index.subs[0] = (ha >> 24) & 0xff;
|
|
ifa->index.subs[1] = (ha >> 16) & 0xff;
|
|
ifa->index.subs[2] = (ha >> 8) & 0xff;
|
|
ifa->index.subs[3] = (ha >> 0) & 0xff;
|
|
|
|
ifa->flags = 0;
|
|
ifa->inbcast.s_addr = 0;
|
|
ifa->inmask.s_addr = 0xffffffff;
|
|
|
|
INSERT_OBJECT_OID(ifa, &mibifa_list);
|
|
|
|
return (ifa);
|
|
}
|
|
|
|
/*
|
|
* Delete an interface address
|
|
*/
|
|
static void
|
|
destroy_ifa(struct mibifa *ifa)
|
|
{
|
|
TAILQ_REMOVE(&mibifa_list, ifa, link);
|
|
free(ifa);
|
|
}
|
|
|
|
|
|
/*
|
|
* Helper routine to extract the sockaddr structures from a routing
|
|
* socket message.
|
|
*/
|
|
void
|
|
mib_extract_addrs(int addrs, u_char *info, struct sockaddr **out)
|
|
{
|
|
u_int i;
|
|
|
|
for (i = 0; i < RTAX_MAX; i++) {
|
|
if ((addrs & (1 << i)) != 0) {
|
|
*out = (struct sockaddr *)(void *)info;
|
|
info += roundup((*out)->sa_len, sizeof(long));
|
|
} else
|
|
*out = NULL;
|
|
out++;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* save the phys address of an interface. Handle receive address entries here.
|
|
*/
|
|
static void
|
|
get_physaddr(struct mibif *ifp, struct sockaddr_dl *sdl, u_char *ptr)
|
|
{
|
|
u_char *np;
|
|
struct mibrcvaddr *rcv;
|
|
|
|
if (sdl->sdl_alen == 0) {
|
|
/* no address */
|
|
if (ifp->physaddrlen != 0) {
|
|
if ((rcv = mib_find_rcvaddr(ifp->index, ifp->physaddr,
|
|
ifp->physaddrlen)) != NULL)
|
|
mib_rcvaddr_delete(rcv);
|
|
free(ifp->physaddr);
|
|
ifp->physaddr = NULL;
|
|
ifp->physaddrlen = 0;
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (ifp->physaddrlen != sdl->sdl_alen) {
|
|
/* length changed */
|
|
if (ifp->physaddrlen) {
|
|
/* delete olf receive address */
|
|
if ((rcv = mib_find_rcvaddr(ifp->index, ifp->physaddr,
|
|
ifp->physaddrlen)) != NULL)
|
|
mib_rcvaddr_delete(rcv);
|
|
}
|
|
if ((np = realloc(ifp->physaddr, sdl->sdl_alen)) == NULL) {
|
|
free(ifp->physaddr);
|
|
ifp->physaddr = NULL;
|
|
ifp->physaddrlen = 0;
|
|
return;
|
|
}
|
|
ifp->physaddr = np;
|
|
ifp->physaddrlen = sdl->sdl_alen;
|
|
|
|
} else if (memcmp(ifp->physaddr, ptr, ifp->physaddrlen) == 0) {
|
|
/* no change */
|
|
return;
|
|
|
|
} else {
|
|
/* address changed */
|
|
|
|
/* delete olf receive address */
|
|
if ((rcv = mib_find_rcvaddr(ifp->index, ifp->physaddr,
|
|
ifp->physaddrlen)) != NULL)
|
|
mib_rcvaddr_delete(rcv);
|
|
}
|
|
|
|
memcpy(ifp->physaddr, ptr, ifp->physaddrlen);
|
|
|
|
/* make new receive address */
|
|
if ((rcv = mib_rcvaddr_create(ifp, ifp->physaddr, ifp->physaddrlen)) != NULL)
|
|
rcv->flags |= MIBRCVADDR_HW;
|
|
}
|
|
|
|
/*
|
|
* Free an interface
|
|
*/
|
|
static void
|
|
mibif_free(struct mibif *ifp)
|
|
{
|
|
struct mibif *ifp1;
|
|
struct mibindexmap *map;
|
|
struct mibifa *ifa, *ifa1;
|
|
struct mibrcvaddr *rcv, *rcv1;
|
|
struct mibarp *at, *at1;
|
|
|
|
if (ifp->xnotify != NULL)
|
|
(*ifp->xnotify)(ifp, MIBIF_NOTIFY_DESTROY, ifp->xnotify_data);
|
|
|
|
(void)mib_ifstack_delete(ifp, NULL);
|
|
(void)mib_ifstack_delete(NULL, ifp);
|
|
|
|
TAILQ_REMOVE(&mibif_list, ifp, link);
|
|
|
|
/* if this was the fastest interface - recompute this */
|
|
if (ifp->mib.ifmd_data.ifi_baudrate == mibif_maxspeed) {
|
|
mibif_maxspeed = ifp->mib.ifmd_data.ifi_baudrate;
|
|
TAILQ_FOREACH(ifp1, &mibif_list, link)
|
|
if (ifp1->mib.ifmd_data.ifi_baudrate > mibif_maxspeed)
|
|
mibif_maxspeed =
|
|
ifp1->mib.ifmd_data.ifi_baudrate;
|
|
mibif_reset_hc_timer();
|
|
}
|
|
|
|
free(ifp->private);
|
|
if (ifp->physaddr != NULL)
|
|
free(ifp->physaddr);
|
|
if (ifp->specmib != NULL)
|
|
free(ifp->specmib);
|
|
|
|
STAILQ_FOREACH(map, &mibindexmap_list, link)
|
|
if (map->mibif == ifp) {
|
|
map->mibif = NULL;
|
|
break;
|
|
}
|
|
|
|
/* purge interface addresses */
|
|
ifa = TAILQ_FIRST(&mibifa_list);
|
|
while (ifa != NULL) {
|
|
ifa1 = TAILQ_NEXT(ifa, link);
|
|
if (ifa->ifindex == ifp->index)
|
|
destroy_ifa(ifa);
|
|
ifa = ifa1;
|
|
}
|
|
|
|
/* purge receive addresses */
|
|
rcv = TAILQ_FIRST(&mibrcvaddr_list);
|
|
while (rcv != NULL) {
|
|
rcv1 = TAILQ_NEXT(rcv, link);
|
|
if (rcv->ifindex == ifp->index)
|
|
mib_rcvaddr_delete(rcv);
|
|
rcv = rcv1;
|
|
}
|
|
|
|
/* purge ARP entries */
|
|
at = TAILQ_FIRST(&mibarp_list);
|
|
while (at != NULL) {
|
|
at1 = TAILQ_NEXT(at, link);
|
|
if (at->index.subs[0] == ifp->index)
|
|
mib_arp_delete(at);
|
|
at = at1;
|
|
}
|
|
|
|
|
|
free(ifp);
|
|
mib_if_number--;
|
|
mib_iftable_last_change = this_tick;
|
|
}
|
|
|
|
/*
|
|
* Create a new interface
|
|
*/
|
|
static struct mibif *
|
|
mibif_create(u_int sysindex, const char *name)
|
|
{
|
|
struct mibif *ifp;
|
|
struct mibindexmap *map;
|
|
|
|
if ((ifp = malloc(sizeof(*ifp))) == NULL) {
|
|
syslog(LOG_WARNING, "%s: %m", __func__);
|
|
return (NULL);
|
|
}
|
|
memset(ifp, 0, sizeof(*ifp));
|
|
if ((ifp->private = malloc(sizeof(struct mibif_private))) == NULL) {
|
|
syslog(LOG_WARNING, "%s: %m", __func__);
|
|
free(ifp);
|
|
return (NULL);
|
|
}
|
|
memset(ifp->private, 0, sizeof(struct mibif_private));
|
|
|
|
ifp->sysindex = sysindex;
|
|
strcpy(ifp->name, name);
|
|
strcpy(ifp->descr, name);
|
|
ifp->spec_oid = oid_zeroDotZero;
|
|
|
|
map = NULL;
|
|
if (!mib_if_is_dyn(ifp->name)) {
|
|
/* non-dynamic. look whether we know the interface */
|
|
STAILQ_FOREACH(map, &mibindexmap_list, link)
|
|
if (strcmp(map->name, ifp->name) == 0) {
|
|
ifp->index = map->ifindex;
|
|
map->mibif = ifp;
|
|
break;
|
|
}
|
|
/* assume it has a connector if it is not dynamic */
|
|
ifp->has_connector = 1;
|
|
ifp->trap_enable = 1;
|
|
}
|
|
if (map == NULL) {
|
|
/* new interface - get new index */
|
|
if (next_if_index > 0x7fffffff)
|
|
errx(1, "ifindex wrap");
|
|
|
|
if ((map = malloc(sizeof(*map))) == NULL) {
|
|
syslog(LOG_ERR, "ifmap: %m");
|
|
free(ifp);
|
|
return (NULL);
|
|
}
|
|
map->ifindex = next_if_index++;
|
|
map->sysindex = ifp->sysindex;
|
|
strcpy(map->name, ifp->name);
|
|
map->mibif = ifp;
|
|
STAILQ_INSERT_TAIL(&mibindexmap_list, map, link);
|
|
} else {
|
|
/* re-instantiate. Introduce a counter discontinuity */
|
|
ifp->counter_disc = get_ticks();
|
|
}
|
|
ifp->index = map->ifindex;
|
|
ifp->mib.ifmd_data.ifi_link_state = LINK_STATE_UNKNOWN;
|
|
|
|
INSERT_OBJECT_INT(ifp, &mibif_list);
|
|
mib_if_number++;
|
|
mib_iftable_last_change = this_tick;
|
|
|
|
/* instantiate default ifStack entries */
|
|
(void)mib_ifstack_create(ifp, NULL);
|
|
(void)mib_ifstack_create(NULL, ifp);
|
|
|
|
return (ifp);
|
|
}
|
|
|
|
/*
|
|
* Inform all interested parties about a new interface
|
|
*/
|
|
static void
|
|
notify_newif(struct mibif *ifp)
|
|
{
|
|
struct newifreg *reg;
|
|
|
|
TAILQ_FOREACH(reg, &newifreg_list, link)
|
|
if ((*reg->func)(ifp))
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* This is called for new interfaces after we have fetched the interface
|
|
* MIB. If this is a broadcast interface try to guess the broadcast address
|
|
* depending on the interface type.
|
|
*/
|
|
static void
|
|
check_llbcast(struct mibif *ifp)
|
|
{
|
|
static u_char ether_bcast[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
|
|
static u_char arcnet_bcast = 0;
|
|
struct mibrcvaddr *rcv;
|
|
|
|
if (!(ifp->mib.ifmd_flags & IFF_BROADCAST))
|
|
return;
|
|
|
|
switch (ifp->mib.ifmd_data.ifi_type) {
|
|
|
|
case IFT_ETHER:
|
|
case IFT_FDDI:
|
|
case IFT_ISO88025:
|
|
case IFT_L2VLAN:
|
|
if (mib_find_rcvaddr(ifp->index, ether_bcast, 6) == NULL &&
|
|
(rcv = mib_rcvaddr_create(ifp, ether_bcast, 6)) != NULL)
|
|
rcv->flags |= MIBRCVADDR_BCAST;
|
|
break;
|
|
|
|
case IFT_ARCNET:
|
|
if (mib_find_rcvaddr(ifp->index, &arcnet_bcast, 1) == NULL &&
|
|
(rcv = mib_rcvaddr_create(ifp, &arcnet_bcast, 1)) != NULL)
|
|
rcv->flags |= MIBRCVADDR_BCAST;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Retrieve the current interface list from the system.
|
|
*/
|
|
void
|
|
mib_refresh_iflist(void)
|
|
{
|
|
struct mibif *ifp, *ifp1;
|
|
size_t len;
|
|
u_short idx;
|
|
int name[6];
|
|
int count;
|
|
struct ifmibdata mib;
|
|
|
|
TAILQ_FOREACH(ifp, &mibif_list, link)
|
|
ifp->flags &= ~MIBIF_FOUND;
|
|
|
|
len = sizeof(count);
|
|
if (sysctlbyname("net.link.generic.system.ifcount", &count, &len,
|
|
NULL, 0) == -1) {
|
|
syslog(LOG_ERR, "ifcount: %m");
|
|
return;
|
|
}
|
|
name[0] = CTL_NET;
|
|
name[1] = PF_LINK;
|
|
name[2] = NETLINK_GENERIC;
|
|
name[3] = IFMIB_IFDATA;
|
|
name[5] = IFDATA_GENERAL;
|
|
for (idx = 1; idx <= count; idx++) {
|
|
name[4] = idx;
|
|
len = sizeof(mib);
|
|
if (sysctl(name, 6, &mib, &len, NULL, 0) == -1) {
|
|
if (errno == ENOENT)
|
|
continue;
|
|
syslog(LOG_ERR, "ifmib(%u): %m", idx);
|
|
return;
|
|
}
|
|
if ((ifp = mib_find_if_sys(idx)) != NULL) {
|
|
ifp->flags |= MIBIF_FOUND;
|
|
continue;
|
|
}
|
|
/* Unknown interface - create */
|
|
if ((ifp = mibif_create(idx, mib.ifmd_name)) != NULL) {
|
|
ifp->flags |= MIBIF_FOUND;
|
|
(void)mib_fetch_ifmib(ifp);
|
|
check_llbcast(ifp);
|
|
notify_newif(ifp);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Purge interfaces that disappeared
|
|
*/
|
|
ifp = TAILQ_FIRST(&mibif_list);
|
|
while (ifp != NULL) {
|
|
ifp1 = TAILQ_NEXT(ifp, link);
|
|
if (!(ifp->flags & MIBIF_FOUND))
|
|
mibif_free(ifp);
|
|
ifp = ifp1;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Find an interface address
|
|
*/
|
|
struct mibifa *
|
|
mib_find_ifa(struct in_addr addr)
|
|
{
|
|
struct mibifa *ifa;
|
|
|
|
TAILQ_FOREACH(ifa, &mibifa_list, link)
|
|
if (ifa->inaddr.s_addr == addr.s_addr)
|
|
return (ifa);
|
|
return (NULL);
|
|
}
|
|
|
|
/*
|
|
* Process a new ARP entry
|
|
*/
|
|
static void
|
|
process_arp(const struct rt_msghdr *rtm, const struct sockaddr_dl *sdl,
|
|
const struct sockaddr_in *sa)
|
|
{
|
|
struct mibif *ifp;
|
|
struct mibarp *at;
|
|
|
|
/* IP arp table entry */
|
|
if (sdl->sdl_alen == 0)
|
|
return;
|
|
if ((ifp = mib_find_if_sys(sdl->sdl_index)) == NULL)
|
|
return;
|
|
/* have a valid entry */
|
|
if ((at = mib_find_arp(ifp, sa->sin_addr)) == NULL &&
|
|
(at = mib_arp_create(ifp, sa->sin_addr,
|
|
sdl->sdl_data + sdl->sdl_nlen, sdl->sdl_alen)) == NULL)
|
|
return;
|
|
|
|
if (rtm->rtm_rmx.rmx_expire == 0)
|
|
at->flags |= MIBARP_PERM;
|
|
else
|
|
at->flags &= ~MIBARP_PERM;
|
|
at->flags |= MIBARP_FOUND;
|
|
}
|
|
|
|
/*
|
|
* Handle a routing socket message.
|
|
*/
|
|
static void
|
|
handle_rtmsg(struct rt_msghdr *rtm)
|
|
{
|
|
struct sockaddr *addrs[RTAX_MAX];
|
|
struct if_msghdr *ifm;
|
|
struct ifa_msghdr ifam, *ifamp;
|
|
struct ifma_msghdr *ifmam;
|
|
#ifdef RTM_IFANNOUNCE
|
|
struct if_announcemsghdr *ifan;
|
|
#endif
|
|
struct mibif *ifp;
|
|
struct sockaddr_dl *sdl;
|
|
struct sockaddr_in *sa;
|
|
struct mibifa *ifa;
|
|
struct mibrcvaddr *rcv;
|
|
u_char *ptr;
|
|
|
|
if (rtm->rtm_version != RTM_VERSION) {
|
|
syslog(LOG_ERR, "Bogus RTM version %u", rtm->rtm_version);
|
|
return;
|
|
}
|
|
|
|
switch (rtm->rtm_type) {
|
|
|
|
case RTM_NEWADDR:
|
|
ifamp = (struct ifa_msghdr *)rtm;
|
|
memcpy(&ifam, ifamp, sizeof(ifam));
|
|
mib_extract_addrs(ifam.ifam_addrs, (u_char *)(ifamp + 1), addrs);
|
|
if (addrs[RTAX_IFA] == NULL || addrs[RTAX_NETMASK] == NULL)
|
|
break;
|
|
|
|
sa = (struct sockaddr_in *)(void *)addrs[RTAX_IFA];
|
|
if ((ifa = mib_find_ifa(sa->sin_addr)) == NULL) {
|
|
/* unknown address */
|
|
if ((ifp = mib_find_if_sys(ifam.ifam_index)) == NULL) {
|
|
syslog(LOG_WARNING, "RTM_NEWADDR for unknown "
|
|
"interface %u", ifam.ifam_index);
|
|
break;
|
|
}
|
|
if ((ifa = alloc_ifa(ifp->index, sa->sin_addr)) == NULL)
|
|
break;
|
|
}
|
|
sa = (struct sockaddr_in *)(void *)addrs[RTAX_NETMASK];
|
|
ifa->inmask = sa->sin_addr;
|
|
|
|
if (addrs[RTAX_BRD] != NULL) {
|
|
sa = (struct sockaddr_in *)(void *)addrs[RTAX_BRD];
|
|
ifa->inbcast = sa->sin_addr;
|
|
}
|
|
ifa->flags |= MIBIFA_FOUND;
|
|
break;
|
|
|
|
case RTM_DELADDR:
|
|
ifamp = (struct ifa_msghdr *)rtm;
|
|
memcpy(&ifam, ifamp, sizeof(ifam));
|
|
mib_extract_addrs(ifam.ifam_addrs, (u_char *)(ifamp + 1), addrs);
|
|
if (addrs[RTAX_IFA] == NULL)
|
|
break;
|
|
|
|
sa = (struct sockaddr_in *)(void *)addrs[RTAX_IFA];
|
|
if ((ifa = mib_find_ifa(sa->sin_addr)) != NULL) {
|
|
ifa->flags |= MIBIFA_FOUND;
|
|
if (!(ifa->flags & MIBIFA_DESTROYED))
|
|
destroy_ifa(ifa);
|
|
}
|
|
break;
|
|
|
|
case RTM_NEWMADDR:
|
|
ifmam = (struct ifma_msghdr *)rtm;
|
|
mib_extract_addrs(ifmam->ifmam_addrs, (u_char *)(ifmam + 1), addrs);
|
|
if (addrs[RTAX_IFA] == NULL ||
|
|
addrs[RTAX_IFA]->sa_family != AF_LINK)
|
|
break;
|
|
sdl = (struct sockaddr_dl *)(void *)addrs[RTAX_IFA];
|
|
if ((rcv = mib_find_rcvaddr(sdl->sdl_index,
|
|
sdl->sdl_data + sdl->sdl_nlen, sdl->sdl_alen)) == NULL) {
|
|
/* unknown address */
|
|
if ((ifp = mib_find_if_sys(sdl->sdl_index)) == NULL) {
|
|
syslog(LOG_WARNING, "RTM_NEWMADDR for unknown "
|
|
"interface %u", sdl->sdl_index);
|
|
break;
|
|
}
|
|
if ((rcv = mib_rcvaddr_create(ifp,
|
|
sdl->sdl_data + sdl->sdl_nlen, sdl->sdl_alen)) == NULL)
|
|
break;
|
|
rcv->flags |= MIBRCVADDR_VOLATILE;
|
|
}
|
|
rcv->flags |= MIBRCVADDR_FOUND;
|
|
break;
|
|
|
|
case RTM_DELMADDR:
|
|
ifmam = (struct ifma_msghdr *)rtm;
|
|
mib_extract_addrs(ifmam->ifmam_addrs, (u_char *)(ifmam + 1), addrs);
|
|
if (addrs[RTAX_IFA] == NULL ||
|
|
addrs[RTAX_IFA]->sa_family != AF_LINK)
|
|
break;
|
|
sdl = (struct sockaddr_dl *)(void *)addrs[RTAX_IFA];
|
|
if ((rcv = mib_find_rcvaddr(sdl->sdl_index,
|
|
sdl->sdl_data + sdl->sdl_nlen, sdl->sdl_alen)) != NULL)
|
|
mib_rcvaddr_delete(rcv);
|
|
break;
|
|
|
|
case RTM_IFINFO:
|
|
ifm = (struct if_msghdr *)(void *)rtm;
|
|
mib_extract_addrs(ifm->ifm_addrs, (u_char *)(ifm + 1), addrs);
|
|
if ((ifp = mib_find_if_sys(ifm->ifm_index)) == NULL)
|
|
break;
|
|
if (addrs[RTAX_IFP] != NULL &&
|
|
addrs[RTAX_IFP]->sa_family == AF_LINK) {
|
|
sdl = (struct sockaddr_dl *)(void *)addrs[RTAX_IFP];
|
|
ptr = sdl->sdl_data + sdl->sdl_nlen;
|
|
get_physaddr(ifp, sdl, ptr);
|
|
}
|
|
(void)mib_fetch_ifmib(ifp);
|
|
break;
|
|
|
|
#ifdef RTM_IFANNOUNCE
|
|
case RTM_IFANNOUNCE:
|
|
ifan = (struct if_announcemsghdr *)rtm;
|
|
ifp = mib_find_if_sys(ifan->ifan_index);
|
|
|
|
switch (ifan->ifan_what) {
|
|
|
|
case IFAN_ARRIVAL:
|
|
if (ifp == NULL && (ifp = mibif_create(ifan->ifan_index,
|
|
ifan->ifan_name)) != NULL) {
|
|
(void)mib_fetch_ifmib(ifp);
|
|
check_llbcast(ifp);
|
|
notify_newif(ifp);
|
|
}
|
|
break;
|
|
|
|
case IFAN_DEPARTURE:
|
|
if (ifp != NULL)
|
|
mibif_free(ifp);
|
|
break;
|
|
}
|
|
break;
|
|
#endif
|
|
case RTM_GET:
|
|
case RTM_ADD:
|
|
mib_extract_addrs(rtm->rtm_addrs, (u_char *)(rtm + 1), addrs);
|
|
if (rtm->rtm_flags & RTF_LLINFO) {
|
|
if (addrs[RTAX_DST] == NULL ||
|
|
addrs[RTAX_GATEWAY] == NULL ||
|
|
addrs[RTAX_DST]->sa_family != AF_INET ||
|
|
addrs[RTAX_GATEWAY]->sa_family != AF_LINK)
|
|
break;
|
|
process_arp(rtm,
|
|
(struct sockaddr_dl *)(void *)addrs[RTAX_GATEWAY],
|
|
(struct sockaddr_in *)(void *)addrs[RTAX_DST]);
|
|
} else {
|
|
if (rtm->rtm_errno == 0 && (rtm->rtm_flags & RTF_UP))
|
|
mib_sroute_process(rtm, addrs[RTAX_GATEWAY],
|
|
addrs[RTAX_DST], addrs[RTAX_NETMASK]);
|
|
}
|
|
break;
|
|
|
|
case RTM_DELETE:
|
|
mib_extract_addrs(rtm->rtm_addrs, (u_char *)(rtm + 1), addrs);
|
|
|
|
if (rtm->rtm_errno == 0 && (rtm->rtm_flags & RTF_UP))
|
|
mib_sroute_process(rtm, addrs[RTAX_GATEWAY],
|
|
addrs[RTAX_DST], addrs[RTAX_NETMASK]);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* send a routing message
|
|
*/
|
|
void
|
|
mib_send_rtmsg(struct rt_msghdr *rtm, struct sockaddr *gw,
|
|
struct sockaddr *dst, struct sockaddr *mask)
|
|
{
|
|
size_t len;
|
|
struct rt_msghdr *msg;
|
|
char *cp;
|
|
ssize_t sent;
|
|
|
|
len = sizeof(*rtm) + SA_SIZE(gw) + SA_SIZE(dst) + SA_SIZE(mask);
|
|
if ((msg = malloc(len)) == NULL) {
|
|
syslog(LOG_ERR, "%s: %m", __func__);
|
|
return;
|
|
}
|
|
cp = (char *)(msg + 1);
|
|
|
|
memset(msg, 0, sizeof(*msg));
|
|
msg->rtm_flags = 0;
|
|
msg->rtm_version = RTM_VERSION;
|
|
msg->rtm_addrs = RTA_DST | RTA_GATEWAY;
|
|
|
|
memcpy(cp, dst, SA_SIZE(dst));
|
|
cp += SA_SIZE(dst);
|
|
memcpy(cp, gw, SA_SIZE(gw));
|
|
cp += SA_SIZE(gw);
|
|
if (mask != NULL) {
|
|
memcpy(cp, mask, SA_SIZE(mask));
|
|
cp += SA_SIZE(mask);
|
|
msg->rtm_addrs |= RTA_NETMASK;
|
|
}
|
|
msg->rtm_msglen = cp - (char *)msg;
|
|
msg->rtm_type = RTM_GET;
|
|
if ((sent = write(route, msg, msg->rtm_msglen)) == -1) {
|
|
syslog(LOG_ERR, "%s: write: %m", __func__);
|
|
free(msg);
|
|
return;
|
|
}
|
|
if (sent != msg->rtm_msglen) {
|
|
syslog(LOG_ERR, "%s: short write", __func__);
|
|
free(msg);
|
|
return;
|
|
}
|
|
free(msg);
|
|
}
|
|
|
|
/*
|
|
* Fetch the routing table via sysctl
|
|
*/
|
|
u_char *
|
|
mib_fetch_rtab(int af, int info, int arg, size_t *lenp)
|
|
{
|
|
int name[6];
|
|
u_char *buf, *newbuf;
|
|
|
|
name[0] = CTL_NET;
|
|
name[1] = PF_ROUTE;
|
|
name[2] = 0;
|
|
name[3] = af;
|
|
name[4] = info;
|
|
name[5] = arg;
|
|
|
|
*lenp = 0;
|
|
|
|
/* initial estimate */
|
|
if (sysctl(name, 6, NULL, lenp, NULL, 0) == -1) {
|
|
syslog(LOG_ERR, "sysctl estimate (%d,%d,%d,%d,%d,%d): %m",
|
|
name[0], name[1], name[2], name[3], name[4], name[5]);
|
|
return (NULL);
|
|
}
|
|
if (*lenp == 0)
|
|
return (NULL);
|
|
|
|
buf = NULL;
|
|
for (;;) {
|
|
if ((newbuf = realloc(buf, *lenp)) == NULL) {
|
|
syslog(LOG_ERR, "sysctl buffer: %m");
|
|
free(buf);
|
|
return (NULL);
|
|
}
|
|
buf = newbuf;
|
|
|
|
if (sysctl(name, 6, buf, lenp, NULL, 0) == 0)
|
|
break;
|
|
|
|
if (errno != ENOMEM) {
|
|
syslog(LOG_ERR, "sysctl get: %m");
|
|
free(buf);
|
|
return (NULL);
|
|
}
|
|
*lenp += *lenp / 8 + 1;
|
|
}
|
|
|
|
return (buf);
|
|
}
|
|
|
|
/*
|
|
* Update the following info: interface, interface addresses, interface
|
|
* receive addresses, arp-table.
|
|
* This does not change the interface list itself.
|
|
*/
|
|
static void
|
|
update_ifa_info(void)
|
|
{
|
|
u_char *buf, *next;
|
|
struct rt_msghdr *rtm;
|
|
struct mibifa *ifa, *ifa1;
|
|
struct mibrcvaddr *rcv, *rcv1;
|
|
size_t needed;
|
|
static const int infos[][3] = {
|
|
{ 0, NET_RT_IFLIST, 0 },
|
|
#ifdef NET_RT_IFMALIST
|
|
{ AF_LINK, NET_RT_IFMALIST, 0 },
|
|
#endif
|
|
};
|
|
u_int i;
|
|
|
|
TAILQ_FOREACH(ifa, &mibifa_list, link)
|
|
ifa->flags &= ~MIBIFA_FOUND;
|
|
TAILQ_FOREACH(rcv, &mibrcvaddr_list, link)
|
|
rcv->flags &= ~MIBRCVADDR_FOUND;
|
|
|
|
for (i = 0; i < sizeof(infos) / sizeof(infos[0]); i++) {
|
|
if ((buf = mib_fetch_rtab(infos[i][0], infos[i][1], infos[i][2],
|
|
&needed)) == NULL)
|
|
continue;
|
|
|
|
next = buf;
|
|
while (next < buf + needed) {
|
|
rtm = (struct rt_msghdr *)(void *)next;
|
|
next += rtm->rtm_msglen;
|
|
handle_rtmsg(rtm);
|
|
}
|
|
free(buf);
|
|
}
|
|
|
|
/*
|
|
* Purge the address list of unused entries. These may happen for
|
|
* interface aliases that are on the same subnet. We don't receive
|
|
* routing socket messages for them.
|
|
*/
|
|
ifa = TAILQ_FIRST(&mibifa_list);
|
|
while (ifa != NULL) {
|
|
ifa1 = TAILQ_NEXT(ifa, link);
|
|
if (!(ifa->flags & MIBIFA_FOUND))
|
|
destroy_ifa(ifa);
|
|
ifa = ifa1;
|
|
}
|
|
|
|
rcv = TAILQ_FIRST(&mibrcvaddr_list);
|
|
while (rcv != NULL) {
|
|
rcv1 = TAILQ_NEXT(rcv, link);
|
|
if (!(rcv->flags & (MIBRCVADDR_FOUND | MIBRCVADDR_BCAST |
|
|
MIBRCVADDR_HW)))
|
|
mib_rcvaddr_delete(rcv);
|
|
rcv = rcv1;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Update arp table
|
|
*
|
|
*/
|
|
void
|
|
mib_arp_update(void)
|
|
{
|
|
struct mibarp *at, *at1;
|
|
size_t needed;
|
|
u_char *buf, *next;
|
|
struct rt_msghdr *rtm;
|
|
|
|
if (in_update_arp)
|
|
return; /* Aaargh */
|
|
in_update_arp = 1;
|
|
|
|
TAILQ_FOREACH(at, &mibarp_list, link)
|
|
at->flags &= ~MIBARP_FOUND;
|
|
|
|
if ((buf = mib_fetch_rtab(AF_INET, NET_RT_FLAGS, 0, &needed)) == NULL) {
|
|
in_update_arp = 0;
|
|
return;
|
|
}
|
|
|
|
next = buf;
|
|
while (next < buf + needed) {
|
|
rtm = (struct rt_msghdr *)(void *)next;
|
|
next += rtm->rtm_msglen;
|
|
handle_rtmsg(rtm);
|
|
}
|
|
free(buf);
|
|
|
|
at = TAILQ_FIRST(&mibarp_list);
|
|
while (at != NULL) {
|
|
at1 = TAILQ_NEXT(at, link);
|
|
if (!(at->flags & MIBARP_FOUND))
|
|
mib_arp_delete(at);
|
|
at = at1;
|
|
}
|
|
mibarpticks = get_ticks();
|
|
in_update_arp = 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* Intput on the routing socket.
|
|
*/
|
|
static void
|
|
route_input(int fd, void *udata __unused)
|
|
{
|
|
u_char buf[1024 * 16];
|
|
ssize_t n;
|
|
struct rt_msghdr *rtm;
|
|
|
|
if ((n = read(fd, buf, sizeof(buf))) == -1)
|
|
err(1, "read(rt_socket)");
|
|
|
|
if (n == 0)
|
|
errx(1, "EOF on rt_socket");
|
|
|
|
rtm = (struct rt_msghdr *)(void *)buf;
|
|
if ((size_t)n != rtm->rtm_msglen)
|
|
errx(1, "n=%zu, rtm_msglen=%u", (size_t)n, rtm->rtm_msglen);
|
|
|
|
handle_rtmsg(rtm);
|
|
}
|
|
|
|
/*
|
|
* execute and SIOCAIFADDR
|
|
*/
|
|
static int
|
|
siocaifaddr(char *ifname, struct in_addr addr, struct in_addr mask,
|
|
struct in_addr bcast)
|
|
{
|
|
struct ifaliasreq addreq;
|
|
struct sockaddr_in *sa;
|
|
|
|
memset(&addreq, 0, sizeof(addreq));
|
|
strncpy(addreq.ifra_name, ifname, sizeof(addreq.ifra_name));
|
|
|
|
sa = (struct sockaddr_in *)(void *)&addreq.ifra_addr;
|
|
sa->sin_family = AF_INET;
|
|
sa->sin_len = sizeof(*sa);
|
|
sa->sin_addr = addr;
|
|
|
|
sa = (struct sockaddr_in *)(void *)&addreq.ifra_mask;
|
|
sa->sin_family = AF_INET;
|
|
sa->sin_len = sizeof(*sa);
|
|
sa->sin_addr = mask;
|
|
|
|
sa = (struct sockaddr_in *)(void *)&addreq.ifra_broadaddr;
|
|
sa->sin_family = AF_INET;
|
|
sa->sin_len = sizeof(*sa);
|
|
sa->sin_addr = bcast;
|
|
|
|
return (ioctl(mib_netsock, SIOCAIFADDR, &addreq));
|
|
}
|
|
|
|
/*
|
|
* Exececute a SIOCDIFADDR
|
|
*/
|
|
static int
|
|
siocdifaddr(const char *ifname, struct in_addr addr)
|
|
{
|
|
struct ifreq delreq;
|
|
struct sockaddr_in *sa;
|
|
|
|
memset(&delreq, 0, sizeof(delreq));
|
|
strncpy(delreq.ifr_name, ifname, sizeof(delreq.ifr_name));
|
|
sa = (struct sockaddr_in *)(void *)&delreq.ifr_addr;
|
|
sa->sin_family = AF_INET;
|
|
sa->sin_len = sizeof(*sa);
|
|
sa->sin_addr = addr;
|
|
|
|
return (ioctl(mib_netsock, SIOCDIFADDR, &delreq));
|
|
}
|
|
|
|
/*
|
|
* Verify an interface address without fetching the entire list
|
|
*/
|
|
static int
|
|
verify_ifa(const char *name, struct mibifa *ifa)
|
|
{
|
|
struct ifreq req;
|
|
struct sockaddr_in *sa;
|
|
|
|
memset(&req, 0, sizeof(req));
|
|
strncpy(req.ifr_name, name, sizeof(req.ifr_name));
|
|
sa = (struct sockaddr_in *)(void *)&req.ifr_addr;
|
|
sa->sin_family = AF_INET;
|
|
sa->sin_len = sizeof(*sa);
|
|
sa->sin_addr = ifa->inaddr;
|
|
|
|
if (ioctl(mib_netsock, SIOCGIFADDR, &req) == -1)
|
|
return (-1);
|
|
if (ifa->inaddr.s_addr != sa->sin_addr.s_addr) {
|
|
syslog(LOG_ERR, "%s: address mismatch", __func__);
|
|
return (-1);
|
|
}
|
|
|
|
if (ioctl(mib_netsock, SIOCGIFNETMASK, &req) == -1)
|
|
return (-1);
|
|
if (ifa->inmask.s_addr != sa->sin_addr.s_addr) {
|
|
syslog(LOG_ERR, "%s: netmask mismatch", __func__);
|
|
return (-1);
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Restore a deleted interface address. Don't wait for the routing socket
|
|
* to update us.
|
|
*/
|
|
void
|
|
mib_undestroy_ifa(struct mibifa *ifa)
|
|
{
|
|
struct mibif *ifp;
|
|
|
|
if ((ifp = mib_find_if(ifa->ifindex)) == NULL)
|
|
/* keep it destroyed */
|
|
return;
|
|
|
|
if (siocaifaddr(ifp->name, ifa->inaddr, ifa->inmask, ifa->inbcast))
|
|
/* keep it destroyed */
|
|
return;
|
|
|
|
ifa->flags &= ~MIBIFA_DESTROYED;
|
|
}
|
|
|
|
/*
|
|
* Destroy an interface address
|
|
*/
|
|
int
|
|
mib_destroy_ifa(struct mibifa *ifa)
|
|
{
|
|
struct mibif *ifp;
|
|
|
|
if ((ifp = mib_find_if(ifa->ifindex)) == NULL) {
|
|
/* ups. */
|
|
mib_iflist_bad = 1;
|
|
return (-1);
|
|
}
|
|
if (siocdifaddr(ifp->name, ifa->inaddr)) {
|
|
/* ups. */
|
|
syslog(LOG_ERR, "SIOCDIFADDR: %m");
|
|
mib_iflist_bad = 1;
|
|
return (-1);
|
|
}
|
|
ifa->flags |= MIBIFA_DESTROYED;
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Rollback the modification of an address. Don't bother to wait for
|
|
* the routing socket.
|
|
*/
|
|
void
|
|
mib_unmodify_ifa(struct mibifa *ifa)
|
|
{
|
|
struct mibif *ifp;
|
|
|
|
if ((ifp = mib_find_if(ifa->ifindex)) == NULL) {
|
|
/* ups. */
|
|
mib_iflist_bad = 1;
|
|
return;
|
|
}
|
|
|
|
if (siocaifaddr(ifp->name, ifa->inaddr, ifa->inmask, ifa->inbcast)) {
|
|
/* ups. */
|
|
mib_iflist_bad = 1;
|
|
return;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Modify an IFA.
|
|
*/
|
|
int
|
|
mib_modify_ifa(struct mibifa *ifa)
|
|
{
|
|
struct mibif *ifp;
|
|
|
|
if ((ifp = mib_find_if(ifa->ifindex)) == NULL) {
|
|
/* ups. */
|
|
mib_iflist_bad = 1;
|
|
return (-1);
|
|
}
|
|
|
|
if (siocaifaddr(ifp->name, ifa->inaddr, ifa->inmask, ifa->inbcast)) {
|
|
/* ups. */
|
|
mib_iflist_bad = 1;
|
|
return (-1);
|
|
}
|
|
|
|
if (verify_ifa(ifp->name, ifa)) {
|
|
/* ups. */
|
|
mib_iflist_bad = 1;
|
|
return (-1);
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Destroy a freshly created interface address. Don't bother to wait for
|
|
* the routing socket.
|
|
*/
|
|
void
|
|
mib_uncreate_ifa(struct mibifa *ifa)
|
|
{
|
|
struct mibif *ifp;
|
|
|
|
if ((ifp = mib_find_if(ifa->ifindex)) == NULL) {
|
|
/* ups. */
|
|
mib_iflist_bad = 1;
|
|
return;
|
|
}
|
|
if (siocdifaddr(ifp->name, ifa->inaddr)) {
|
|
/* ups. */
|
|
mib_iflist_bad = 1;
|
|
return;
|
|
}
|
|
|
|
destroy_ifa(ifa);
|
|
}
|
|
|
|
/*
|
|
* Create a new ifa and verify it
|
|
*/
|
|
struct mibifa *
|
|
mib_create_ifa(u_int ifindex, struct in_addr addr, struct in_addr mask,
|
|
struct in_addr bcast)
|
|
{
|
|
struct mibif *ifp;
|
|
struct mibifa *ifa;
|
|
|
|
if ((ifp = mib_find_if(ifindex)) == NULL)
|
|
return (NULL);
|
|
if ((ifa = alloc_ifa(ifindex, addr)) == NULL)
|
|
return (NULL);
|
|
ifa->inmask = mask;
|
|
ifa->inbcast = bcast;
|
|
|
|
if (siocaifaddr(ifp->name, ifa->inaddr, ifa->inmask, ifa->inbcast)) {
|
|
syslog(LOG_ERR, "%s: %m", __func__);
|
|
destroy_ifa(ifa);
|
|
return (NULL);
|
|
}
|
|
if (verify_ifa(ifp->name, ifa)) {
|
|
destroy_ifa(ifa);
|
|
return (NULL);
|
|
}
|
|
return (ifa);
|
|
}
|
|
|
|
/*
|
|
* Get all cloning interfaces and make them dynamic.
|
|
* Hah! Whe should probably do this on a periodic basis (XXX).
|
|
*/
|
|
static void
|
|
get_cloners(void)
|
|
{
|
|
struct if_clonereq req;
|
|
char *buf, *cp;
|
|
int i;
|
|
|
|
memset(&req, 0, sizeof(req));
|
|
if (ioctl(mib_netsock, SIOCIFGCLONERS, &req) == -1) {
|
|
syslog(LOG_ERR, "get cloners: %m");
|
|
return;
|
|
}
|
|
if ((buf = malloc(req.ifcr_total * IFNAMSIZ)) == NULL) {
|
|
syslog(LOG_ERR, "%m");
|
|
return;
|
|
}
|
|
req.ifcr_count = req.ifcr_total;
|
|
req.ifcr_buffer = buf;
|
|
if (ioctl(mib_netsock, SIOCIFGCLONERS, &req) == -1) {
|
|
syslog(LOG_ERR, "get cloners: %m");
|
|
free(buf);
|
|
return;
|
|
}
|
|
for (cp = buf, i = 0; i < req.ifcr_total; i++, cp += IFNAMSIZ)
|
|
mib_if_set_dyn(cp);
|
|
free(buf);
|
|
}
|
|
|
|
/*
|
|
* Idle function
|
|
*/
|
|
static void
|
|
mibII_idle(void *arg __unused)
|
|
{
|
|
struct mibifa *ifa;
|
|
|
|
if (mib_iflist_bad) {
|
|
TAILQ_FOREACH(ifa, &mibifa_list, link)
|
|
ifa->flags &= ~MIBIFA_DESTROYED;
|
|
|
|
/* assume, that all cloning interfaces are dynamic */
|
|
get_cloners();
|
|
|
|
mib_refresh_iflist();
|
|
update_ifa_info();
|
|
mib_arp_update();
|
|
mib_iflist_bad = 0;
|
|
}
|
|
|
|
mib_arp_update();
|
|
}
|
|
|
|
|
|
/*
|
|
* Start the module
|
|
*/
|
|
static void
|
|
mibII_start(void)
|
|
{
|
|
if ((route_fd = fd_select(route, route_input, NULL, module)) == NULL) {
|
|
syslog(LOG_ERR, "fd_select(route): %m");
|
|
return;
|
|
}
|
|
mib_refresh_iflist();
|
|
update_ifa_info();
|
|
mib_arp_update();
|
|
(void)mib_fetch_route();
|
|
mib_iftable_last_change = 0;
|
|
mib_ifstack_last_change = 0;
|
|
|
|
ifmib_reg = or_register(&oid_ifMIB,
|
|
"The MIB module to describe generic objects for network interface"
|
|
" sub-layers.", module);
|
|
|
|
ipmib_reg = or_register(&oid_ipMIB,
|
|
"The MIB module for managing IP and ICMP implementations, but "
|
|
"excluding their management of IP routes.", module);
|
|
|
|
tcpmib_reg = or_register(&oid_tcpMIB,
|
|
"The MIB module for managing TCP implementations.", module);
|
|
|
|
udpmib_reg = or_register(&oid_udpMIB,
|
|
"The MIB module for managing UDP implementations.", module);
|
|
|
|
ipForward_reg = or_register(&oid_ipForward,
|
|
"The MIB module for the display of CIDR multipath IP Routes.",
|
|
module);
|
|
|
|
mibII_poll_timer = NULL;
|
|
mibII_poll_ticks = MIBII_POLL_TICKS;
|
|
mibif_restart_mibII_poll_timer();
|
|
}
|
|
|
|
/*
|
|
* Initialize the module
|
|
*/
|
|
static int
|
|
mibII_init(struct lmodule *mod, int argc __unused, char *argv[] __unused)
|
|
{
|
|
size_t len;
|
|
|
|
module = mod;
|
|
|
|
len = sizeof(clockinfo);
|
|
if (sysctlbyname("kern.clockrate", &clockinfo, &len, NULL, 0) == -1) {
|
|
syslog(LOG_ERR, "kern.clockrate: %m");
|
|
return (-1);
|
|
}
|
|
if (len != sizeof(clockinfo)) {
|
|
syslog(LOG_ERR, "kern.clockrate: wrong size");
|
|
return (-1);
|
|
}
|
|
|
|
if ((route = socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC)) == -1) {
|
|
syslog(LOG_ERR, "PF_ROUTE: %m");
|
|
return (-1);
|
|
}
|
|
|
|
if ((mib_netsock = socket(PF_INET, SOCK_DGRAM, 0)) == -1) {
|
|
syslog(LOG_ERR, "PF_INET: %m");
|
|
(void)close(route);
|
|
return (-1);
|
|
}
|
|
(void)shutdown(mib_netsock, SHUT_RDWR);
|
|
|
|
/* assume, that all cloning interfaces are dynamic */
|
|
get_cloners();
|
|
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
mibII_fini(void)
|
|
{
|
|
if (mibII_poll_timer != NULL ) {
|
|
timer_stop(mibII_poll_timer);
|
|
mibII_poll_timer = NULL;
|
|
}
|
|
|
|
if (route_fd != NULL)
|
|
fd_deselect(route_fd);
|
|
if (route != -1)
|
|
(void)close(route);
|
|
if (mib_netsock != -1)
|
|
(void)close(mib_netsock);
|
|
/* XXX free memory */
|
|
|
|
or_unregister(ipForward_reg);
|
|
or_unregister(udpmib_reg);
|
|
or_unregister(tcpmib_reg);
|
|
or_unregister(ipmib_reg);
|
|
or_unregister(ifmib_reg);
|
|
|
|
return (0);
|
|
}
|
|
|
|
static void
|
|
mibII_loading(const struct lmodule *mod, int loaded)
|
|
{
|
|
struct mibif *ifp;
|
|
|
|
if (loaded == 1)
|
|
return;
|
|
|
|
TAILQ_FOREACH(ifp, &mibif_list, link)
|
|
if (ifp->xnotify_mod == mod) {
|
|
ifp->xnotify_mod = NULL;
|
|
ifp->xnotify_data = NULL;
|
|
ifp->xnotify = NULL;
|
|
}
|
|
|
|
mib_unregister_newif(mod);
|
|
}
|
|
|
|
const struct snmp_module config = {
|
|
"This module implements the interface and ip groups.",
|
|
mibII_init,
|
|
mibII_fini,
|
|
NULL, /* idle */
|
|
NULL, /* dump */
|
|
NULL, /* config */
|
|
mibII_start,
|
|
NULL,
|
|
mibII_ctree,
|
|
mibII_CTREE_SIZE,
|
|
mibII_loading
|
|
};
|
|
|
|
/*
|
|
* Should have a list of these attached to each interface.
|
|
*/
|
|
void *
|
|
mibif_notify(struct mibif *ifp, const struct lmodule *mod,
|
|
mibif_notify_f func, void *data)
|
|
{
|
|
ifp->xnotify = func;
|
|
ifp->xnotify_data = data;
|
|
ifp->xnotify_mod = mod;
|
|
|
|
return (ifp);
|
|
}
|
|
|
|
void
|
|
mibif_unnotify(void *arg)
|
|
{
|
|
struct mibif *ifp = arg;
|
|
|
|
ifp->xnotify = NULL;
|
|
ifp->xnotify_data = NULL;
|
|
ifp->xnotify_mod = NULL;
|
|
}
|