#include <stdint.h>
#include "defs.h"
#include "pathnames.h"
struct ifhead ifnet = LIST_HEAD_INITIALIZER(ifnet);
struct ifhead remote_if = LIST_HEAD_INITIALIZER(remote_if);
#define AHASH_LEN 211
#define AHASH(a) &ahash_tbl[(a)%AHASH_LEN]
static struct interface *ahash_tbl[AHASH_LEN];
#define BHASH_LEN 211
#define BHASH(a) &bhash_tbl[(a)%BHASH_LEN]
static struct interface *bhash_tbl[BHASH_LEN];
#define NHASH_LEN 97
static struct interface *nhash_tbl[NHASH_LEN];
int tot_interfaces;
int rip_interfaces;
static int foundloopback;
naddr loopaddr;
static struct rt_spare loop_rts;
struct timeval ifinit_timer;
static struct timeval last_ifinit;
#define IF_RESCAN_DELAY() (last_ifinit.tv_sec == now.tv_sec \
&& last_ifinit.tv_usec == now.tv_usec \
&& timercmp(&ifinit_timer, &now, >))
int have_ripv1_out;
static int have_ripv1_in;
static void if_bad(struct interface *);
static int addrouteforif(struct interface *);
static struct interface**
nhash(char *p)
{
u_int i;
for (i = 0; *p != '\0'; p++) {
i = ((i<<1) & 0x7fffffff) | ((i>>31) & 1);
i ^= *p;
}
return &nhash_tbl[i % NHASH_LEN];
}
void
if_link(struct interface *ifp)
{
struct interface **hifp;
LIST_INSERT_HEAD(&ifnet, ifp, int_list);
hifp = AHASH(ifp->int_addr);
ifp->int_ahash_prev = hifp;
if ((ifp->int_ahash = *hifp) != NULL)
(*hifp)->int_ahash_prev = &ifp->int_ahash;
*hifp = ifp;
if (ifp->int_if_flags & IFF_BROADCAST) {
hifp = BHASH(ifp->int_brdaddr);
ifp->int_bhash_prev = hifp;
if ((ifp->int_bhash = *hifp) != NULL)
(*hifp)->int_bhash_prev = &ifp->int_bhash;
*hifp = ifp;
}
if (ifp->int_state & IS_REMOTE)
LIST_INSERT_HEAD(&remote_if, ifp, remote_list);
hifp = nhash(ifp->int_name);
if (ifp->int_state & IS_ALIAS) {
while (*hifp != NULL)
hifp = &(*hifp)->int_nhash;
}
ifp->int_nhash_prev = hifp;
if ((ifp->int_nhash = *hifp) != NULL)
(*hifp)->int_nhash_prev = &ifp->int_nhash;
*hifp = ifp;
}
struct interface *
ifwithaddr(naddr addr,
int bcast,
int remote)
{
struct interface *ifp, *possible = NULL;
remote = (remote == 0) ? IS_REMOTE : 0;
for (ifp = *AHASH(addr); ifp; ifp = ifp->int_ahash) {
if (ifp->int_addr != addr)
continue;
if ((ifp->int_state & remote) != 0)
continue;
if ((ifp->int_state & (IS_BROKE | IS_PASSIVE)) == 0)
return ifp;
possible = ifp;
}
if (possible || !bcast)
return possible;
for (ifp = *BHASH(addr); ifp; ifp = ifp->int_bhash) {
if (ifp->int_brdaddr != addr)
continue;
if ((ifp->int_state & remote) != 0)
continue;
if ((ifp->int_state & (IS_BROKE | IS_PASSIVE)) == 0)
return ifp;
possible = ifp;
}
return possible;
}
static struct interface *
ifwithname(char *name,
naddr addr)
{
struct interface *ifp;
for (;;) {
for (ifp = *nhash(name); ifp != NULL; ifp = ifp->int_nhash) {
if (!strcmp(ifp->int_name, name)
&& ((addr == 0 && !(ifp->int_state & IS_ALIAS))
|| (ifp->int_addr == addr)))
return ifp;
}
if (IF_RESCAN_DELAY())
return 0;
ifinit();
}
}
struct interface *
ifwithindex(u_short ifindex,
int rescan_ok)
{
struct interface *ifp;
for (;;) {
LIST_FOREACH(ifp, &ifnet, int_list) {
if (ifp->int_index == ifindex)
return ifp;
}
if (!rescan_ok
|| IF_RESCAN_DELAY())
return 0;
ifinit();
}
}
struct interface *
iflookup(naddr addr)
{
struct interface *ifp, *maybe;
int once = 0;
maybe = NULL;
for (;;) {
LIST_FOREACH(ifp, &ifnet, int_list) {
if (ifp->int_if_flags & IFF_POINTOPOINT) {
if (ifp->int_dstaddr == addr)
return ifp;
} else {
if (ifp->int_addr == addr)
return ifp;
if (on_net(addr, ifp->int_net, ifp->int_mask)
&& (maybe == NULL
|| ifp->int_mask > maybe->int_mask))
maybe = ifp;
}
}
if (maybe != NULL || once || IF_RESCAN_DELAY())
return maybe;
once = 1;
ifinit();
}
}
naddr
std_mask(naddr addr)
{
addr = ntohl(addr);
if (addr == 0)
return 0;
if (IN_CLASSA(addr))
return IN_CLASSA_NET;
if (IN_CLASSB(addr))
return IN_CLASSB_NET;
return IN_CLASSC_NET;
}
naddr
ripv1_mask_net(naddr addr,
struct interface *ifp)
{
struct r1net *r1p;
naddr mask = 0;
if (addr == 0)
return mask;
if (ifp != NULL && ifp->int_ripv1_mask != HOST_MASK) {
if (on_net(addr, ifp->int_net, ifp->int_std_mask))
mask = ifp->int_ripv1_mask;
} else {
LIST_FOREACH(ifp, &ifnet, int_list) {
if (on_net(addr, ifp->int_std_net, ifp->int_std_mask)
&& ifp->int_ripv1_mask > mask
&& ifp->int_ripv1_mask != HOST_MASK)
mask = ifp->int_ripv1_mask;
}
}
if (mask == 0) {
for (r1p = r1nets; r1p != NULL; r1p = r1p->r1net_next) {
if (on_net(addr, r1p->r1net_net, r1p->r1net_match)
&& r1p->r1net_mask > mask)
mask = r1p->r1net_mask;
}
if (mask == 0)
mask = std_mask(addr);
}
return mask;
}
naddr
ripv1_mask_host(naddr addr,
struct interface *ifp)
{
naddr mask = ripv1_mask_net(addr, ifp);
if ((ntohl(addr) & ~mask) != 0)
mask = HOST_MASK;
return mask;
}
int
check_dst(naddr addr)
{
addr = ntohl(addr);
if (IN_CLASSA(addr)) {
if (addr == 0)
return 1;
addr >>= IN_CLASSA_NSHIFT;
return (addr != 0 && addr != IN_LOOPBACKNET);
}
return (IN_CLASSB(addr) || IN_CLASSC(addr));
}
struct interface *
check_dup(naddr addr,
naddr dstaddr,
naddr mask,
int if_flags)
{
struct interface *ifp;
LIST_FOREACH(ifp, &ifnet, int_list) {
if (ifp->int_mask != mask)
continue;
if (!iff_up(ifp->int_if_flags))
continue;
if ((!(ifp->int_state & IS_REMOTE) || !(if_flags & IS_REMOTE))
&& ifp->int_addr == addr
&& (((if_flags|ifp->int_if_flags) & IFF_POINTOPOINT) == 0))
return ifp;
if (on_net(ifp->int_dstaddr, ntohl(dstaddr),mask))
return ifp;
}
return 0;
}
int
check_remote(struct interface *ifp)
{
struct rt_entry *rt;
if (!(ifp->int_state & IS_REMOTE))
return 1;
rt = rtfind(ifp->int_addr);
if (rt != NULL
&& rt->rt_ifp != 0
&&on_net(ifp->int_addr,
rt->rt_ifp->int_net, rt->rt_ifp->int_mask))
return 1;
if (!(ifp->int_state & IS_BROKE)) {
msglog("unreachable gateway %s in "_PATH_GATEWAYS,
naddr_ntoa(ifp->int_addr));
if_bad(ifp);
}
return 0;
}
static void
ifdel(struct interface *ifp)
{
struct interface *ifp1;
trace_if("Del", ifp);
ifp->int_state |= IS_BROKE;
LIST_REMOVE(ifp, int_list);
*ifp->int_ahash_prev = ifp->int_ahash;
if (ifp->int_ahash != 0)
ifp->int_ahash->int_ahash_prev = ifp->int_ahash_prev;
*ifp->int_nhash_prev = ifp->int_nhash;
if (ifp->int_nhash != 0)
ifp->int_nhash->int_nhash_prev = ifp->int_nhash_prev;
if (ifp->int_if_flags & IFF_BROADCAST) {
*ifp->int_bhash_prev = ifp->int_bhash;
if (ifp->int_bhash != 0)
ifp->int_bhash->int_bhash_prev = ifp->int_bhash_prev;
}
if (ifp->int_state & IS_REMOTE)
LIST_REMOVE(ifp, remote_list);
if (!(ifp->int_state & IS_ALIAS)) {
LIST_FOREACH(ifp1, &ifnet, int_list) {
if (ifp1 != ifp
&& !strcmp(ifp->int_name, ifp1->int_name))
ifdel(ifp1);
}
if ((ifp->int_if_flags & IFF_MULTICAST) && rip_sock >= 0) {
struct group_req gr;
struct sockaddr_in *sin;
memset(&gr, 0, sizeof(gr));
gr.gr_interface = ifp->int_index;
sin = (struct sockaddr_in *)&gr.gr_group;
sin->sin_family = AF_INET;
#ifdef _HAVE_SIN_LEN
sin->sin_len = sizeof(struct sockaddr_in);
#endif
sin->sin_addr.s_addr = htonl(INADDR_RIP_GROUP);
if (setsockopt(rip_sock, IPPROTO_IP, MCAST_LEAVE_GROUP,
&gr, sizeof(gr)) < 0
&& errno != EADDRNOTAVAIL
&& !TRACEACTIONS)
LOGERR("setsockopt(MCAST_LEAVE_GROUP RIP)");
if (rip_sock_mcast == ifp)
rip_sock_mcast = NULL;
}
if (ifp->int_rip_sock >= 0) {
(void)close(ifp->int_rip_sock);
ifp->int_rip_sock = -1;
fix_select();
}
tot_interfaces--;
if (!IS_RIP_OFF(ifp->int_state))
rip_interfaces--;
(void)rn_walktree(rhead, walk_bad, 0);
set_rdisc_mg(ifp, 0);
if_bad_rdisc(ifp);
}
free(ifp);
}
void
if_sick(struct interface *ifp)
{
if (0 == (ifp->int_state & (IS_SICK | IS_BROKE))) {
ifp->int_state |= IS_SICK;
ifp->int_act_time = NEVER;
trace_if("Chg", ifp);
LIM_SEC(ifinit_timer, now.tv_sec+CHECK_BAD_INTERVAL);
}
}
static void
if_bad(struct interface *ifp)
{
struct interface *ifp1;
if (ifp->int_state & IS_BROKE)
return;
LIM_SEC(ifinit_timer, now.tv_sec+CHECK_BAD_INTERVAL);
ifp->int_state |= (IS_BROKE | IS_SICK);
ifp->int_act_time = NEVER;
ifp->int_query_time = NEVER;
ifp->int_data.ts = now.tv_sec;
trace_if("Chg", ifp);
if (!(ifp->int_state & IS_ALIAS)) {
LIST_FOREACH(ifp1, &ifnet, int_list) {
if (ifp1 != ifp
&& !strcmp(ifp->int_name, ifp1->int_name))
if_bad(ifp1);
}
(void)rn_walktree(rhead, walk_bad, 0);
if_bad_rdisc(ifp);
}
}
int
if_ok(struct interface *ifp,
const char *type)
{
struct interface *ifp1;
if (!(ifp->int_state & IS_BROKE)) {
if (ifp->int_state & IS_SICK) {
trace_act("%sinterface %s to %s working better",
type,
ifp->int_name, naddr_ntoa(ifp->int_dstaddr));
ifp->int_state &= ~IS_SICK;
}
return 0;
}
msglog("%sinterface %s to %s restored",
type, ifp->int_name, naddr_ntoa(ifp->int_dstaddr));
ifp->int_state &= ~(IS_BROKE | IS_SICK);
ifp->int_data.ts = 0;
if (!(ifp->int_state & IS_ALIAS)) {
LIST_FOREACH(ifp1, &ifnet, int_list) {
if (ifp1 != ifp
&& !strcmp(ifp->int_name, ifp1->int_name))
if_ok(ifp1, type);
}
if_ok_rdisc(ifp);
}
if (ifp->int_state & IS_REMOTE) {
if (!addrouteforif(ifp))
return 0;
}
return 1;
}
void
rt_xaddrs(struct rt_addrinfo *info,
struct sockaddr *sa,
struct sockaddr *lim,
int addrs)
{
int i;
#ifdef _HAVE_SA_LEN
static struct sockaddr sa_zero;
#endif
memset(info, 0, sizeof(*info));
info->rti_addrs = addrs;
for (i = 0; i < RTAX_MAX && sa < lim; i++) {
if ((addrs & (1 << i)) == 0)
continue;
info->rti_info[i] = (sa->sa_len != 0) ? sa : &sa_zero;
sa = (struct sockaddr *)((char*)(sa) + SA_SIZE(sa));
}
}
void
ifinit(void)
{
static struct ifa_msghdr *sysctl_buf;
static size_t sysctl_buf_size = 0;
uint complaints = 0;
static u_int prev_complaints = 0;
# define COMP_NOT_INET 0x001
# define COMP_NOADDR 0x002
# define COMP_BADADDR 0x004
# define COMP_NODST 0x008
# define COMP_NOBADR 0x010
# define COMP_NOMASK 0x020
# define COMP_DUP 0x040
# define COMP_BAD_METRIC 0x080
# define COMP_NETMASK 0x100
struct interface ifs, ifs0, *ifp, *ifp1;
struct rt_entry *rt;
size_t needed;
int mib[6];
struct if_msghdr *ifm;
void *ifam_lim;
struct ifa_msghdr *ifam, *ifam2;
int in, ierr, out, oerr;
struct intnet *intnetp;
struct rt_addrinfo info;
#ifdef SIOCGIFMETRIC
struct ifreq ifr;
#endif
last_ifinit = now;
ifinit_timer.tv_sec = now.tv_sec + (supplier
? CHECK_ACT_INTERVAL
: CHECK_QUIET_INTERVAL);
LIST_FOREACH(ifp, &ifnet, int_list)
ifp->int_state &= ~(IS_CHECKED | IS_DUP);
mib[0] = CTL_NET;
mib[1] = PF_ROUTE;
mib[2] = 0;
mib[3] = AF_INET;
mib[4] = NET_RT_IFLIST;
mib[5] = 0;
for (;;) {
if ((needed = sysctl_buf_size) != 0) {
if (sysctl(mib, 6, sysctl_buf,&needed, 0, 0) >= 0)
break;
if (errno != ENOMEM && errno != EFAULT)
BADERR(1, "ifinit: sysctl(RT_IFLIST)");
free(sysctl_buf);
needed = 0;
}
if (sysctl(mib, 6, 0, &needed, 0, 0) < 0)
BADERR(1,"ifinit: sysctl(RT_IFLIST) estimate");
sysctl_buf = rtmalloc(sysctl_buf_size = needed,
"ifinit sysctl");
}
ifam_lim = (char *)sysctl_buf + needed;
for (ifam = sysctl_buf; (void *)ifam < ifam_lim; ifam = ifam2) {
ifam2 = (struct ifa_msghdr*)((char*)ifam + ifam->ifam_msglen);
#ifdef RTM_OIFINFO
if (ifam->ifam_type == RTM_OIFINFO)
continue;
#endif
if (ifam->ifam_type == RTM_IFINFO) {
struct sockaddr_dl *sdl;
ifm = (struct if_msghdr *)ifam;
memset(&ifs0, 0, sizeof(ifs0));
ifs0.int_rip_sock = -1;
ifs0.int_index = ifm->ifm_index;
ifs0.int_if_flags = ifm->ifm_flags;
ifs0.int_state = IS_CHECKED;
ifs0.int_query_time = NEVER;
ifs0.int_act_time = now.tv_sec;
ifs0.int_data.ts = now.tv_sec;
ifs0.int_data.ipackets = ifm->ifm_data.ifi_ipackets;
ifs0.int_data.ierrors = ifm->ifm_data.ifi_ierrors;
ifs0.int_data.opackets = ifm->ifm_data.ifi_opackets;
ifs0.int_data.oerrors = ifm->ifm_data.ifi_oerrors;
sdl = (struct sockaddr_dl *)(ifm + 1);
sdl->sdl_data[sdl->sdl_nlen] = 0;
strncpy(ifs0.int_name, sdl->sdl_data,
MIN(sizeof(ifs0.int_name), sdl->sdl_nlen));
continue;
}
if (ifam->ifam_type != RTM_NEWADDR) {
logbad(1,"ifinit: out of sync");
continue;
}
rt_xaddrs(&info, (struct sockaddr *)(ifam+1),
(struct sockaddr *)ifam2,
ifam->ifam_addrs);
memcpy(&ifs, &ifs0, sizeof(ifs));
ifs0.int_state |= (IS_ALIAS | IS_NO_RIP_OUT | IS_NO_RDISC);
if (INFO_IFA(&info) == 0) {
if (iff_up(ifs.int_if_flags)) {
if (!(prev_complaints & COMP_NOADDR))
msglog("%s has no address",
ifs.int_name);
complaints |= COMP_NOADDR;
}
continue;
}
if (INFO_IFA(&info)->sa_family != AF_INET) {
if (iff_up(ifs.int_if_flags)) {
if (!(prev_complaints & COMP_NOT_INET))
trace_act("%s: not AF_INET",
ifs.int_name);
complaints |= COMP_NOT_INET;
}
continue;
}
ifs.int_addr = S_ADDR(INFO_IFA(&info));
if (ntohl(ifs.int_addr)>>24 == 0
|| ntohl(ifs.int_addr)>>24 == 0xff) {
if (iff_up(ifs.int_if_flags)) {
if (!(prev_complaints & COMP_BADADDR))
msglog("%s has a bad address",
ifs.int_name);
complaints |= COMP_BADADDR;
}
continue;
}
if (ifs.int_if_flags & IFF_LOOPBACK) {
ifs.int_state |= IS_NO_RIP | IS_NO_RDISC;
if (ifs.int_addr == htonl(INADDR_LOOPBACK))
ifs.int_state |= IS_PASSIVE;
ifs.int_dstaddr = ifs.int_addr;
ifs.int_mask = HOST_MASK;
ifs.int_ripv1_mask = HOST_MASK;
ifs.int_std_mask = std_mask(ifs.int_dstaddr);
ifs.int_net = ntohl(ifs.int_dstaddr);
if (!foundloopback) {
foundloopback = 1;
loopaddr = ifs.int_addr;
loop_rts.rts_gate = loopaddr;
loop_rts.rts_router = loopaddr;
}
} else if (ifs.int_if_flags & IFF_POINTOPOINT) {
if (INFO_BRD(&info) == 0
|| INFO_BRD(&info)->sa_family != AF_INET) {
if (iff_up(ifs.int_if_flags)) {
if (!(prev_complaints & COMP_NODST))
msglog("%s has a bad"
" destination address",
ifs.int_name);
complaints |= COMP_NODST;
}
continue;
}
ifs.int_dstaddr = S_ADDR(INFO_BRD(&info));
if (ntohl(ifs.int_dstaddr)>>24 == 0
|| ntohl(ifs.int_dstaddr)>>24 == 0xff) {
if (iff_up(ifs.int_if_flags)) {
if (!(prev_complaints & COMP_NODST))
msglog("%s has a bad"
" destination address",
ifs.int_name);
complaints |= COMP_NODST;
}
continue;
}
ifs.int_mask = HOST_MASK;
ifs.int_ripv1_mask = ntohl(S_ADDR(INFO_MASK(&info)));
ifs.int_std_mask = std_mask(ifs.int_dstaddr);
ifs.int_net = ntohl(ifs.int_dstaddr);
} else {
if (INFO_MASK(&info) == 0) {
if (iff_up(ifs.int_if_flags)) {
if (!(prev_complaints & COMP_NOMASK))
msglog("%s has no netmask",
ifs.int_name);
complaints |= COMP_NOMASK;
}
continue;
}
ifs.int_dstaddr = ifs.int_addr;
ifs.int_mask = ntohl(S_ADDR(INFO_MASK(&info)));
ifs.int_ripv1_mask = ifs.int_mask;
ifs.int_std_mask = std_mask(ifs.int_addr);
ifs.int_net = ntohl(ifs.int_addr) & ifs.int_mask;
if (ifs.int_mask != ifs.int_std_mask)
ifs.int_state |= IS_SUBNET;
if (ifs.int_if_flags & IFF_BROADCAST) {
if (INFO_BRD(&info) == 0) {
if (iff_up(ifs.int_if_flags)) {
if (!(prev_complaints
& COMP_NOBADR))
msglog("%s has"
"no broadcast address",
ifs.int_name);
complaints |= COMP_NOBADR;
}
continue;
}
ifs.int_brdaddr = S_ADDR(INFO_BRD(&info));
}
}
ifs.int_std_net = ifs.int_net & ifs.int_std_mask;
ifs.int_std_addr = htonl(ifs.int_std_net);
#ifdef SIOCGIFMETRIC
strncpy(ifr.ifr_name, ifs.int_name, sizeof(ifr.ifr_name));
if (ioctl(rt_sock, SIOCGIFMETRIC, &ifr) < 0) {
DBGERR(1, "ioctl(SIOCGIFMETRIC)");
ifs.int_metric = 0;
} else {
ifs.int_metric = ifr.ifr_metric;
}
#else
ifs.int_metric = ifam->ifam_metric;
#endif
if (ifs.int_metric > HOPCNT_INFINITY) {
ifs.int_metric = 0;
if (!(prev_complaints & COMP_BAD_METRIC)
&& iff_up(ifs.int_if_flags)) {
complaints |= COMP_BAD_METRIC;
msglog("%s has a metric of %d",
ifs.int_name, ifs.int_metric);
}
}
ifp = ifwithname(ifs.int_name, ((ifs.int_state & IS_ALIAS)
? ifs.int_addr
: 0));
if (ifp != NULL) {
ifp->int_state |= IS_CHECKED;
if (0 != ((ifp->int_if_flags ^ ifs.int_if_flags)
& (IFF_BROADCAST
| IFF_LOOPBACK
| IFF_POINTOPOINT
| IFF_MULTICAST))
|| 0 != ((ifp->int_state ^ ifs.int_state)
& IS_ALIAS)
|| ifp->int_addr != ifs.int_addr
|| ifp->int_brdaddr != ifs.int_brdaddr
|| ifp->int_dstaddr != ifs.int_dstaddr
|| ifp->int_mask != ifs.int_mask
|| ifp->int_metric != ifs.int_metric) {
trace_act("interface %s has changed",
ifp->int_name);
ifdel(ifp);
ifp = NULL;
}
}
if (ifp != NULL) {
if (ifp->int_state & IS_ALIAS)
continue;
if (!iff_up(ifs.int_if_flags)) {
if (iff_up(ifp->int_if_flags)) {
msglog("interface %s to %s turned off",
ifp->int_name,
naddr_ntoa(ifp->int_dstaddr));
if_bad(ifp);
ifp->int_if_flags &= ~IFF_UP;
} else if (now.tv_sec>(ifp->int_data.ts
+ CHECK_BAD_INTERVAL)) {
trace_act("interface %s has been off"
" %jd seconds; forget it",
ifp->int_name,
(intmax_t)now.tv_sec -
ifp->int_data.ts);
ifdel(ifp);
ifp = NULL;
}
continue;
}
if (!iff_up(ifp->int_if_flags)) {
ifp->int_if_flags |= IFF_UP;
(void)if_ok(ifp, "");
}
if (now.tv_sec < ifp->int_data.ts+CHECK_BAD_INTERVAL)
continue;
in = ifs.int_data.ipackets - ifp->int_data.ipackets;
ierr = ifs.int_data.ierrors - ifp->int_data.ierrors;
out = ifs.int_data.opackets - ifp->int_data.opackets;
oerr = ifs.int_data.oerrors - ifp->int_data.oerrors;
if (ifp->int_data.ts == 0) {
ifp->int_data = ifs.int_data;
continue;
}
ifp->int_data = ifs.int_data;
if (ierr < 0 || in < 0 || oerr < 0 || out < 0) {
LIM_SEC(ifinit_timer,
now.tv_sec+CHECK_BAD_INTERVAL);
continue;
}
if (in == 0 && out == 0 && ierr == 0 && oerr == 0)
continue;
if ((in <= ierr && ierr > 0)
|| (out <= oerr && oerr > 0)) {
if (!(ifp->int_state & IS_SICK)) {
trace_act("interface %s to %s"
" sick: in=%d ierr=%d"
" out=%d oerr=%d",
ifp->int_name,
naddr_ntoa(ifp->int_dstaddr),
in, ierr, out, oerr);
if_sick(ifp);
continue;
}
if (!(ifp->int_state & IS_BROKE)) {
msglog("interface %s to %s broken:"
" in=%d ierr=%d out=%d oerr=%d",
ifp->int_name,
naddr_ntoa(ifp->int_dstaddr),
in, ierr, out, oerr);
if_bad(ifp);
}
continue;
}
ifp->int_act_time = now.tv_sec;
(void)if_ok(ifp, "");
continue;
}
if (!iff_up(ifs.int_if_flags))
continue;
ifp = check_dup(ifs.int_addr,ifs.int_dstaddr,ifs.int_mask,
ifs.int_if_flags);
if (ifp != NULL) {
if (!strcmp(ifp->int_name, ifs.int_name))
continue;
if (!(prev_complaints & COMP_DUP)) {
complaints |= COMP_DUP;
msglog("%s (%s%s%s) is duplicated by"
" %s (%s%s%s)",
ifs.int_name,
addrname(ifs.int_addr,ifs.int_mask,1),
((ifs.int_if_flags & IFF_POINTOPOINT)
? "-->" : ""),
((ifs.int_if_flags & IFF_POINTOPOINT)
? naddr_ntoa(ifs.int_dstaddr) : ""),
ifp->int_name,
addrname(ifp->int_addr,ifp->int_mask,1),
((ifp->int_if_flags & IFF_POINTOPOINT)
? "-->" : ""),
((ifp->int_if_flags & IFF_POINTOPOINT)
? naddr_ntoa(ifp->int_dstaddr) : ""));
}
ifp->int_state |= IS_DUP;
continue;
}
if (0 == (ifs.int_if_flags & (IFF_POINTOPOINT | IFF_BROADCAST | IFF_LOOPBACK))) {
trace_act("%s is neither broadcast, point-to-point,"
" nor loopback",
ifs.int_name);
if (!(ifs.int_state & IFF_MULTICAST))
ifs.int_state |= IS_NO_RDISC;
}
ifp = (struct interface *)rtmalloc(sizeof(*ifp), "ifinit ifp");
memcpy(ifp, &ifs, sizeof(*ifp));
get_parms(ifp);
if_link(ifp);
trace_if("Add", ifp);
if (!(prev_complaints & COMP_NETMASK)
&& !(ifp->int_if_flags & IFF_POINTOPOINT)
&& ifp->int_addr != RIP_DEFAULT) {
LIST_FOREACH(ifp1, &ifnet, int_list) {
if (ifp1->int_mask == ifp->int_mask)
continue;
if (ifp1->int_if_flags & IFF_POINTOPOINT)
continue;
if (ifp1->int_dstaddr == RIP_DEFAULT)
continue;
if (!strcmp(ifp->int_name, ifp1->int_name))
continue;
if (on_net(ifp->int_dstaddr,
ifp1->int_net, ifp1->int_mask)
|| on_net(ifp1->int_dstaddr,
ifp->int_net, ifp->int_mask)) {
msglog("possible netmask problem"
" between %s:%s and %s:%s",
ifp->int_name,
addrname(htonl(ifp->int_net),
ifp->int_mask, 1),
ifp1->int_name,
addrname(htonl(ifp1->int_net),
ifp1->int_mask, 1));
complaints |= COMP_NETMASK;
}
}
}
if (!(ifp->int_state & IS_ALIAS)) {
if (!(ifp->int_if_flags & IFF_LOOPBACK))
tot_interfaces++;
if (!IS_RIP_OFF(ifp->int_state))
rip_interfaces++;
if_ok_rdisc(ifp);
rip_on(ifp);
}
}
if (!supplier_set && rip_interfaces > 1)
set_supplier();
if ((advertise_mhome && ifp)
|| (tot_interfaces > 1
&& mhome
&& (ifp = ifwithaddr(myaddr, 0, 0)) != NULL
&& foundloopback)) {
advertise_mhome = 1;
rt = rtget(myaddr, HOST_MASK);
if (rt != NULL) {
if (rt->rt_ifp != ifp
|| rt->rt_router != loopaddr) {
rtdelete(rt);
rt = NULL;
} else {
loop_rts.rts_ifp = ifp;
loop_rts.rts_metric = 0;
loop_rts.rts_time = rt->rt_time;
rtchange(rt, rt->rt_state | RS_MHOME,
&loop_rts, 0);
}
}
if (rt == NULL) {
loop_rts.rts_ifp = ifp;
loop_rts.rts_metric = 0;
rtadd(myaddr, HOST_MASK, RS_MHOME, &loop_rts);
}
}
LIST_FOREACH_SAFE(ifp, &ifnet, int_list, ifp1) {
if (!(ifp->int_state & (IS_CHECKED | IS_REMOTE))) {
trace_act("interface %s has disappeared",
ifp->int_name);
ifdel(ifp);
continue;
}
if ((ifp->int_state & IS_BROKE)
&& !(ifp->int_state & IS_PASSIVE))
LIM_SEC(ifinit_timer, now.tv_sec+CHECK_BAD_INTERVAL);
if (!(ifp->int_state & IS_NO_RIPV1_OUT) && supplier)
have_ripv1_out = 1;
if (!(ifp->int_state & IS_NO_RIPV1_IN))
have_ripv1_in = 1;
}
LIST_FOREACH(ifp, &ifnet, int_list) {
if (!addrouteforif(ifp))
continue;
if ((ifp->int_if_flags & IFF_POINTOPOINT)
&& !(ifp->int_state & IS_REMOTE)
&& foundloopback) {
del_static(ifp->int_addr, HOST_MASK, 0, 0);
rt = rtget(ifp->int_addr, HOST_MASK);
if (rt != NULL && rt->rt_router != loopaddr) {
rtdelete(rt);
rt = NULL;
}
if (rt != NULL) {
if (!(rt->rt_state & RS_LOCAL)
|| rt->rt_metric > ifp->int_metric) {
ifp1 = ifp;
} else {
ifp1 = rt->rt_ifp;
}
loop_rts.rts_ifp = ifp1;
loop_rts.rts_metric = 0;
loop_rts.rts_time = rt->rt_time;
rtchange(rt, ((rt->rt_state & ~RS_NET_SYN)
| (RS_IF|RS_LOCAL)),
&loop_rts, 0);
} else {
loop_rts.rts_ifp = ifp;
loop_rts.rts_metric = 0;
rtadd(ifp->int_addr, HOST_MASK,
(RS_IF | RS_LOCAL), &loop_rts);
}
}
}
for (intnetp = intnets; intnetp != NULL;
intnetp = intnetp->intnet_next) {
rt = rtget(intnetp->intnet_addr, intnetp->intnet_mask);
if (rt != NULL
&& !(rt->rt_state & RS_NO_NET_SYN)
&& !(rt->rt_state & RS_NET_INT)) {
rtdelete(rt);
rt = NULL;
}
if (rt == NULL) {
loop_rts.rts_ifp = NULL;
loop_rts.rts_metric = intnetp->intnet_metric-1;
rtadd(intnetp->intnet_addr, intnetp->intnet_mask,
RS_NET_SYN | RS_NET_INT, &loop_rts);
}
}
prev_complaints = complaints;
}
static void
check_net_syn(struct interface *ifp)
{
struct rt_entry *rt;
static struct rt_spare new;
if (have_ripv1_out || have_ripv1_in) {
ifp->int_state |= IS_NEED_NET_SYN;
rt = rtget(ifp->int_std_addr, ifp->int_std_mask);
if (rt != NULL
&& 0 == (rt->rt_state & RS_NO_NET_SYN)
&& (!(rt->rt_state & RS_NET_SYN)
|| rt->rt_metric > ifp->int_metric)) {
rtdelete(rt);
rt = NULL;
}
if (rt == NULL) {
new.rts_ifp = ifp;
new.rts_gate = ifp->int_addr;
new.rts_router = ifp->int_addr;
new.rts_metric = ifp->int_metric;
rtadd(ifp->int_std_addr, ifp->int_std_mask,
RS_NET_SYN, &new);
}
} else {
ifp->int_state &= ~IS_NEED_NET_SYN;
rt = rtget(ifp->int_std_addr,
ifp->int_std_mask);
if (rt != NULL
&& (rt->rt_state & RS_NET_SYN)
&& rt->rt_ifp == ifp)
rtbad_sub(rt);
}
}
static int
addrouteforif(struct interface *ifp)
{
struct rt_entry *rt;
static struct rt_spare new;
naddr dst;
if (ifp->int_state & IS_BROKE)
return 0;
if (ifp->int_state & IS_SUBNET)
check_net_syn(ifp);
dst = (0 != (ifp->int_if_flags & (IFF_POINTOPOINT | IFF_LOOPBACK))
? ifp->int_dstaddr
: htonl(ifp->int_net));
new.rts_ifp = ifp;
new.rts_router = ifp->int_addr;
new.rts_gate = ifp->int_addr;
new.rts_metric = ifp->int_metric;
new.rts_time = now.tv_sec;
if ((ifp->int_state & IS_REMOTE)
&& !(ifp->int_state & IS_EXTERNAL)
&& !check_remote(ifp))
return 0;
del_static(dst, ifp->int_mask, 0, 0);
rt = rtget(dst, ifp->int_mask);
if (rt != NULL) {
if ((rt->rt_ifp != ifp
|| rt->rt_router != ifp->int_addr)
&& (!(ifp->int_state & IS_DUP)
|| rt->rt_ifp == 0
|| (rt->rt_ifp->int_state & IS_BROKE))) {
rtdelete(rt);
rt = NULL;
} else {
rtchange(rt, ((rt->rt_state | RS_IF)
& ~(RS_NET_SYN | RS_LOCAL)),
&new, 0);
}
}
if (rt == NULL) {
if (ifp->int_transitions++ > 0)
trace_act("re-install interface %s",
ifp->int_name);
rtadd(dst, ifp->int_mask, RS_IF, &new);
}
return 1;
}