#include <stdio.h>
#include <errno.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/file.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <netinet/in_systm.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <strings.h>
#include <stdarg.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#define ALIGN(ptr) (ptr)
#ifdef SYSV
#define signal(s, f) sigset(s, (void (*)(int))f)
#define random() rand()
#endif
#define ALL_HOSTS_ADDRESS "224.0.0.1"
#define ALL_ROUTERS_ADDRESS "224.0.0.2"
#define MAXIFS 256
struct icmp_ra {
uchar_t icmp_type;
uchar_t icmp_code;
ushort_t icmp_cksum;
uchar_t icmp_num_addrs;
uchar_t icmp_wpa;
short icmp_lifetime;
};
struct icmp_ra_addr {
ulong_t addr;
ulong_t preference;
};
#define MAX_INITIAL_ADVERT_INTERVAL 16
#define MAX_INITIAL_ADVERTISEMENTS 3
#define MAX_RESPONSE_DELAY 2
#define MAX_SOLICITATIONS 3
#define SOLICITATION_INTERVAL 3
#define MAX_SOLICITATION_DELAY 1
#define IGNORE_PREFERENCE 0x80000000
#define MAX_ADV_INT 600
struct phyint {
char pi_name[IFNAMSIZ];
int pi_state;
struct logint *pi_logical_first;
struct logint *pi_logical_last;
struct phyint *pi_next;
struct phyint *pi_prev;
};
struct logint {
char li_name[IFNAMSIZ];
int li_state;
struct in_addr li_address;
struct in_addr li_localaddr;
int li_preference;
int li_index;
uint64_t li_flags;
struct in_addr li_bcastaddr;
struct in_addr li_remoteaddr;
struct in_addr li_netmask;
struct logint *li_next;
struct logint *li_prev;
struct phyint *li_physical;
};
struct phyint *phyint;
int num_usable_interfaces;
#define ST_MARKED 0x01
#define ST_JOINED 0x02
#define ST_DELETED 0x04
static void solicitor(struct sockaddr_in *sin);
static void advertise(struct sockaddr_in *sin);
static void age_table(int time);
static void flush_unreachable_routers(void);
static void record_router(struct in_addr router, long preference, int ttl);
static void add_route(struct in_addr addr);
static void del_route(struct in_addr addr);
static void rtioctl(struct in_addr addr, int op);
static int support_multicast(void);
static int sendbcast(int s, char *packet, int packetlen);
static int sendbcastif(int s, char *packet, int packetlen,
struct logint *li);
static int sendmcast(int s, char *packet, int packetlen,
struct sockaddr_in *sin);
static int sendmcastif(int s, char *packet, int packetlen,
struct sockaddr_in *sin, struct logint *li);
static int ismulticast(struct sockaddr_in *sin);
static int isbroadcast(struct sockaddr_in *sin);
int in_cksum(ushort_t *addr, int len);
static struct logint *find_directly_connected_logint(struct in_addr in,
struct phyint *pi);
static void force_preference(int preference);
static void timer(void);
static void finish(void);
static void report(void);
static void report_interfaces(void);
static void report_routes(void);
static void reinitifs(void);
static struct phyint *find_phyint(char *name);
static struct phyint *add_phyint(char *name);
static void free_phyint(struct phyint *pi);
static struct logint *find_logint(struct phyint *pi, char *name);
static struct logint *add_logint(struct phyint *pi, char *name);
static void free_logint(struct logint *li);
static void deleted_phyint(struct phyint *pi, int s,
struct sockaddr_in *joinaddr);
static void added_logint(struct logint *li, int s,
struct sockaddr_in *joinaddr);
static void deleted_logint(struct logint *li, struct logint *newli, int s,
struct sockaddr_in *joinaddr);
static int initifs(int s, struct sockaddr_in *joinaddr, int preference);
static boolean_t getconfig(int sock, uint64_t if_flags, struct sockaddr *addr,
struct ifreq *ifr, struct logint *li);
static void pr_pack(char *buf, int cc, struct sockaddr_in *from);
char *pr_name(struct in_addr addr);
char *pr_type(int t);
static void initlog(void);
static void logerr(char *, ...);
static void logtrace(char *, ...);
static void logdebug(char *, ...);
static void logperror(char *);
#define MAXPACKET 4096
uchar_t packet[MAXPACKET];
char usage[] =
"Usage: rdisc [-s] [-v] [-f] [-a] [send_address] [receive_address]\n"
" rdisc -r [-v] [-p <preference>] [-T <secs>] \n"
" [send_address] [receive_address]\n";
int s;
struct sockaddr_in whereto;
struct sockaddr_in g_joinaddr;
char *sendaddress, *recvaddress;
int verbose = 0;
int debug = 0;
int trace = 0;
int start_solicit = 0;
int solicit = 0;
int responder;
int ntransmitted = 0;
int nreceived = 0;
int forever = 0;
int max_adv_int = MAX_ADV_INT;
int min_adv_int;
int lifetime;
int initial_advert_interval = MAX_INITIAL_ADVERT_INTERVAL;
int initial_advertisements = MAX_INITIAL_ADVERTISEMENTS;
ulong_t g_preference = 0;
int max_solicitations = MAX_SOLICITATIONS;
unsigned int solicitation_interval = SOLICITATION_INTERVAL;
int best_preference = 1;
static void
prusage()
{
(void) fprintf(stderr, usage);
exit(1);
}
static int sock = -1;
static void
do_fork()
{
int t;
if (trace)
return;
if (fork())
exit(0);
for (t = 0; t < 20; t++)
if (t != s)
(void) close(t);
sock = -1;
(void) open("/", 0);
(void) dup2(0, 1);
(void) dup2(0, 2);
#ifndef SYSV
t = open("/dev/tty", 2);
if (t >= 0) {
(void) ioctl(t, TIOCNOTTY, (char *)0);
(void) close(t);
}
#else
(void) setpgrp();
#endif
initlog();
}
int
main(int argc, char *argv[])
{
#ifndef SYSV
struct sigvec sv;
#endif
struct sockaddr_in from;
char **av = argv;
struct sockaddr_in *to = &whereto;
ulong_t val;
min_adv_int = (max_adv_int * 3 / 4);
lifetime = (3*max_adv_int);
argc--, av++;
while (argc > 0 && *av[0] == '-') {
while (*++av[0])
switch (*av[0]) {
case 'd':
debug = 1;
break;
case 't':
trace = 1;
break;
case 'v':
verbose++;
break;
case 's':
start_solicit = solicit = 1;
break;
case 'r':
responder = 1;
break;
case 'a':
best_preference = 0;
break;
case 'b':
best_preference = 1;
break;
case 'f':
forever = 1;
break;
case 'T':
argc--, av++;
if (argc != 0) {
val = strtol(av[0], (char **)NULL, 0);
if (val < 4 || val > 1800) {
(void) fprintf(stderr,
"Bad Max Advertisement Interval\n");
exit(1);
}
max_adv_int = val;
min_adv_int = (max_adv_int * 3 / 4);
lifetime = (3*max_adv_int);
} else {
prusage();
}
goto next;
case 'p':
argc--, av++;
if (argc != 0) {
val = strtoul(av[0], (char **)NULL, 0);
g_preference = val;
} else {
prusage();
}
goto next;
default:
prusage();
}
next:
argc--, av++;
}
if (argc < 1) {
if (support_multicast()) {
if (responder)
sendaddress = ALL_HOSTS_ADDRESS;
else
sendaddress = ALL_ROUTERS_ADDRESS;
} else
sendaddress = "255.255.255.255";
} else {
sendaddress = av[0];
argc--;
}
if (argc < 1) {
if (support_multicast()) {
if (responder)
recvaddress = ALL_ROUTERS_ADDRESS;
else
recvaddress = ALL_HOSTS_ADDRESS;
} else
recvaddress = "255.255.255.255";
} else {
recvaddress = av[0];
argc--;
}
if (argc != 0) {
(void) fprintf(stderr, "Extra paramaters\n");
prusage();
}
if (solicit && responder) {
prusage();
}
if (!(solicit && !forever)) {
do_fork();
}
bzero((char *)&whereto, sizeof (struct sockaddr_in));
to->sin_family = AF_INET;
to->sin_addr.s_addr = inet_addr(sendaddress);
if (to->sin_addr.s_addr == (unsigned long)-1) {
logerr("in.rdisc: bad address %s\n", sendaddress);
exit(1);
}
bzero((char *)&g_joinaddr, sizeof (struct sockaddr_in));
g_joinaddr.sin_family = AF_INET;
g_joinaddr.sin_addr.s_addr = inet_addr(recvaddress);
if (g_joinaddr.sin_addr.s_addr == (unsigned long)-1) {
logerr("in.rdisc: bad address %s\n", recvaddress);
exit(1);
}
if (responder) {
#ifdef SYSV
srand((int)gethostid());
#else
srandom((int)gethostid());
#endif
}
if ((s = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0) {
logperror("socket");
exit(5);
}
#ifdef SYSV
setvbuf(stdout, NULL, _IOLBF, 0);
#else
setlinebuf(stdout);
#endif
(void) signal(SIGINT, finish);
(void) signal(SIGTERM, finish);
(void) signal(SIGHUP, reinitifs);
(void) signal(SIGUSR1, report);
if (initifs(s, &g_joinaddr, g_preference) < 0) {
logerr("Failed initializing interfaces\n");
exit(2);
}
if (num_usable_interfaces == 0 && solicit && !forever) {
logerr("in.rdisc: No interfaces up\n");
exit(5);
}
#ifdef SYSV
(void) signal(SIGALRM, timer);
#else
sv.sv_handler = timer;
sv.sv_mask = 0;
sv.sv_flags = SV_INTERRUPT;
(void) sigvec(SIGALRM, &sv, (struct sigvec *)NULL);
#endif
timer();
for (;;) {
int len = sizeof (packet);
socklen_t fromlen = (socklen_t)sizeof (from);
int cc;
sigset_t newmask, oldmask;
if ((cc = recvfrom(s, (char *)packet, len, 0,
(struct sockaddr *)&from,
&fromlen)) < 0) {
if (errno == EINTR)
continue;
logperror("recvfrom");
continue;
}
(void) sigfillset(&newmask);
(void) sigprocmask(SIG_SETMASK, &newmask, &oldmask);
pr_pack((char *)packet, cc, &from);
(void) sigprocmask(SIG_SETMASK, &oldmask, NULL);
}
}
static void
report(void)
{
report_interfaces();
report_routes();
}
#define TIMER_INTERVAL 6
#define GETIFCONF_TIMER 30
static int left_until_advertise;
static void
timer(void)
{
static int time;
static int left_until_getifconf;
static int left_until_solicit;
time += TIMER_INTERVAL;
left_until_getifconf -= TIMER_INTERVAL;
left_until_advertise -= TIMER_INTERVAL;
left_until_solicit -= TIMER_INTERVAL;
if (left_until_getifconf < 0) {
(void) initifs(s, &g_joinaddr, g_preference);
left_until_getifconf = GETIFCONF_TIMER;
}
if (responder && left_until_advertise <= 0) {
ntransmitted++;
advertise(&whereto);
if (ntransmitted < initial_advertisements)
left_until_advertise = initial_advert_interval;
else
left_until_advertise = min_adv_int +
((max_adv_int - min_adv_int) *
(random() % 1000)/1000);
} else if (solicit && left_until_solicit <= 0) {
if (ntransmitted < max_solicitations) {
ntransmitted++;
solicitor(&whereto);
left_until_solicit = solicitation_interval;
} else {
solicit = 0;
if (!forever && nreceived == 0)
exit(5);
}
}
age_table(TIMER_INTERVAL);
(void) alarm(TIMER_INTERVAL);
}
static void
solicitor(struct sockaddr_in *sin)
{
static uchar_t outpack[MAXPACKET];
register struct icmp *icp = (struct icmp *)ALIGN(outpack);
int packetlen, i;
if (verbose) {
logtrace("Sending solicitation to %s\n",
pr_name(sin->sin_addr));
}
icp->icmp_type = ICMP_ROUTERSOLICIT;
icp->icmp_code = 0;
icp->icmp_cksum = 0;
icp->icmp_void = 0;
packetlen = 8;
icp->icmp_cksum = in_cksum((ushort_t *)icp, packetlen);
if (isbroadcast(sin))
i = sendbcast(s, (char *)outpack, packetlen);
else if (ismulticast(sin))
i = sendmcast(s, (char *)outpack, packetlen, sin);
else {
struct logint *li;
li = find_directly_connected_logint(sin->sin_addr, NULL);
if (li != NULL && (li->li_flags & IFF_NORTEXCH)) {
if (verbose) {
logtrace("Suppressing sending %s on %s "
"(no route exchange on interface)\n",
pr_type((int)icp->icmp_type), li->li_name);
}
return;
} else {
i = sendto(s, (char *)outpack, packetlen, 0,
(struct sockaddr *)sin, sizeof (struct sockaddr));
}
}
if (i < 0 || i != packetlen) {
if (i < 0) {
logperror("sendto");
}
logerr("wrote %s %d chars, ret=%d\n",
sendaddress, packetlen, i);
}
}
static void
advertise(struct sockaddr_in *sin)
{
struct phyint *pi;
struct logint *li, *li_tmp;
static uchar_t outpack[MAXPACKET];
register struct icmp_ra *rap = (struct icmp_ra *)ALIGN(outpack);
struct icmp_ra_addr *ap;
int packetlen, cc;
if (verbose) {
logtrace("Sending advertisement to %s\n",
pr_name(sin->sin_addr));
}
for (pi = phyint; pi != NULL; pi = pi->pi_next) {
rap->icmp_type = ICMP_ROUTERADVERT;
rap->icmp_code = 0;
rap->icmp_cksum = 0;
rap->icmp_num_addrs = 0;
rap->icmp_wpa = 2;
rap->icmp_lifetime = htons(lifetime);
packetlen = ICMP_MINLEN;
for (li = pi->pi_logical_first; li != NULL; li = li->li_next) {
if (li->li_state & ST_DELETED)
continue;
if (packetlen + rap->icmp_wpa * 4 > sizeof (outpack)) {
if (debug)
logdebug("full packet: %d addresses\n",
rap->icmp_num_addrs);
break;
}
ap = (struct icmp_ra_addr *)ALIGN(outpack + packetlen);
ap->addr = li->li_localaddr.s_addr;
ap->preference = htonl(li->li_preference);
packetlen += rap->icmp_wpa * 4;
rap->icmp_num_addrs++;
}
if (rap->icmp_num_addrs == 0)
continue;
rap->icmp_cksum = in_cksum((ushort_t *)rap, packetlen);
if (isbroadcast(sin))
cc = sendbcastif(s, (char *)outpack, packetlen,
pi->pi_logical_first);
else if (ismulticast(sin))
cc = sendmcastif(s, (char *)outpack, packetlen, sin,
pi->pi_logical_first);
else {
li_tmp = find_directly_connected_logint(sin->sin_addr,
pi);
if (li_tmp == NULL)
continue;
if (li_tmp->li_flags & IFF_NORTEXCH) {
if (verbose) {
logtrace("Suppressing sending %s on %s "
"(no route exchange on "
"interface)\n",
pr_type((int)rap->icmp_type),
li_tmp->li_name);
}
continue;
}
if (debug) {
logdebug("Unicast to %s ",
pr_name(sin->sin_addr));
logdebug("on interface %s\n", pi->pi_name);
}
cc = sendto(s, (char *)outpack, packetlen, 0,
(struct sockaddr *)sin, sizeof (struct sockaddr));
}
if (cc < 0 || cc != packetlen) {
if (cc < 0) {
logperror("sendto");
} else {
logerr("wrote %s %d chars, ret=%d\n",
sendaddress, packetlen, cc);
}
}
}
}
char *
pr_type(int t)
{
static char *ttab[] = {
"Echo Reply",
"ICMP 1",
"ICMP 2",
"Dest Unreachable",
"Source Quench",
"Redirect",
"ICMP 6",
"ICMP 7",
"Echo",
"Router Advertise",
"Router Solicitation",
"Time Exceeded",
"Parameter Problem",
"Timestamp",
"Timestamp Reply",
"Info Request",
"Info Reply",
"Netmask Request",
"Netmask Reply"
};
if (t < 0 || t > 16)
return ("OUT-OF-RANGE");
return (ttab[t]);
}
char *
pr_name(struct in_addr addr)
{
struct hostent *phe;
static char buf[256];
phe = gethostbyaddr((char *)&addr.s_addr, 4, AF_INET);
if (phe == NULL)
return (inet_ntoa(addr));
(void) sprintf(buf, "%s (%s)", phe->h_name, inet_ntoa(addr));
return (buf);
}
static void
pr_pack(char *buf, int cc, struct sockaddr_in *from)
{
struct ip *ip;
register struct icmp *icp;
register int i;
int hlen;
struct logint *li;
ip = (struct ip *)ALIGN(buf);
hlen = ip->ip_hl << 2;
if (cc < hlen + ICMP_MINLEN) {
if (verbose)
logtrace("packet too short (%d bytes) from %s\n", cc,
pr_name(from->sin_addr));
return;
}
cc -= hlen;
icp = (struct icmp *)ALIGN(buf + hlen);
li = find_directly_connected_logint(ip->ip_src, NULL);
if (li != NULL && (li->li_flags & IFF_NORTEXCH)) {
if (verbose) {
logtrace("Ignoring received %s on %s "
"(no route exchange on interface)",
pr_type((int)icp->icmp_type), li->li_name);
}
return;
}
if (ip->ip_p == 0) {
icp = (struct icmp *)ALIGN(buf);
}
switch (icp->icmp_type) {
case ICMP_ROUTERADVERT: {
struct icmp_ra *rap = (struct icmp_ra *)ALIGN(icp);
struct icmp_ra_addr *ap;
if (responder)
break;
#ifdef notdef
if (debug) {
logdebug("ROUTER_ADVERTISEMENT: \n");
pr_hex(buf+hlen, cc);
}
#endif
if (in_cksum((ushort_t *)ALIGN(buf+hlen), cc)) {
if (verbose)
logtrace("ICMP %s from %s: Bad checksum\n",
pr_type((int)rap->icmp_type),
pr_name(from->sin_addr));
return;
}
if (rap->icmp_code != 0) {
if (verbose)
logtrace("ICMP %s from %s: Code = %d\n",
pr_type((int)rap->icmp_type),
pr_name(from->sin_addr),
rap->icmp_code);
return;
}
if (rap->icmp_num_addrs < 1) {
if (verbose)
logtrace("ICMP %s from %s: No addresses\n",
pr_type((int)rap->icmp_type),
pr_name(from->sin_addr));
return;
}
if (rap->icmp_wpa < 2) {
if (verbose)
logtrace("ICMP %s from %s: Words/addr = %d\n",
pr_type((int)rap->icmp_type),
pr_name(from->sin_addr),
rap->icmp_wpa);
return;
}
if ((unsigned)cc <
ICMP_MINLEN + rap->icmp_num_addrs * rap->icmp_wpa * 4) {
if (verbose)
logtrace("ICMP %s from %s: Too short %d, %d\n",
pr_type((int)rap->icmp_type),
pr_name(from->sin_addr),
cc,
ICMP_MINLEN +
rap->icmp_num_addrs *
rap->icmp_wpa * 4);
return;
}
rap->icmp_lifetime = ntohs(rap->icmp_lifetime);
if ((rap->icmp_lifetime < 4 && rap->icmp_lifetime != 0) ||
rap->icmp_lifetime > 9000) {
if (verbose)
logtrace("ICMP %s from %s: Invalid lifetime %d\n",
pr_type((int)rap->icmp_type),
pr_name(from->sin_addr),
rap->icmp_lifetime);
return;
}
if (verbose)
logtrace("ICMP %s from %s, lifetime %d\n",
pr_type((int)rap->icmp_type),
pr_name(from->sin_addr),
rap->icmp_lifetime);
for (i = 0; (unsigned)i < rap->icmp_num_addrs; i++) {
struct in_addr ina;
ap = (struct icmp_ra_addr *)
ALIGN(buf + hlen + ICMP_MINLEN +
i * rap->icmp_wpa * 4);
ap->preference = ntohl(ap->preference);
ina.s_addr = ap->addr;
if (verbose)
logtrace("\taddress %s, preference 0x%x\n",
pr_name(ina),
ap->preference);
if (!responder) {
if (find_directly_connected_logint(ina, NULL) !=
NULL) {
record_router(ina,
(long)ap->preference,
rap->icmp_lifetime);
}
}
}
nreceived++;
if (!forever) {
(void) alarm(0);
do_fork();
forever = 1;
(void) alarm(TIMER_INTERVAL);
}
break;
}
case ICMP_ROUTERSOLICIT: {
struct sockaddr_in sin;
if (!responder)
break;
#ifdef notdef
if (debug) {
logdebug("ROUTER_SOLICITATION: \n");
pr_hex(buf+hlen, cc);
}
#endif
if (in_cksum((ushort_t *)ALIGN(buf+hlen), cc)) {
if (verbose)
logtrace("ICMP %s from %s: Bad checksum\n",
pr_type((int)icp->icmp_type),
pr_name(from->sin_addr));
return;
}
if (icp->icmp_code != 0) {
if (verbose)
logtrace("ICMP %s from %s: Code = %d\n",
pr_type((int)icp->icmp_type),
pr_name(from->sin_addr),
icp->icmp_code);
return;
}
if (cc < ICMP_MINLEN) {
if (verbose)
logtrace("ICMP %s from %s: Too short %d, %d\n",
pr_type((int)icp->icmp_type),
pr_name(from->sin_addr),
cc,
ICMP_MINLEN);
return;
}
if (verbose)
logtrace("ICMP %s from %s\n",
pr_type((int)icp->icmp_type),
pr_name(from->sin_addr));
if (!responder)
break;
sin.sin_family = AF_INET;
if (ip->ip_src.s_addr == 0) {
if (IN_CLASSD(ntohl(ip->ip_dst.s_addr))) {
sin.sin_addr.s_addr =
htonl(INADDR_ALLHOSTS_GROUP);
} else
sin.sin_addr.s_addr = htonl(INADDR_BROADCAST);
left_until_advertise = min_adv_int +
((max_adv_int - min_adv_int)
* (random() % 1000)/1000);
} else {
if (li == NULL) {
if (verbose)
logtrace("ICMP %s from %s: %s\n",
pr_type((int)icp->icmp_type),
pr_name(from->sin_addr),
"source not directly connected");
break;
}
sin.sin_addr.s_addr = ip->ip_src.s_addr;
}
nreceived++;
ntransmitted++;
advertise(&sin);
break;
}
}
}
int
in_cksum(ushort_t *addr, int len)
{
register int nleft = len;
register ushort_t *w = addr;
register ushort_t answer;
ushort_t odd_byte = 0;
register int sum = 0;
while (nleft > 1) {
sum += *w++;
nleft -= 2;
}
if (nleft == 1) {
*(uchar_t *)(&odd_byte) = *(uchar_t *)w;
sum += odd_byte;
}
sum = (sum >> 16) + (sum & 0xffff);
sum += (sum >> 16);
answer = ~sum;
return (answer);
}
static void
finish(void)
{
if (responder) {
logerr("terminated\n");
force_preference(IGNORE_PREFERENCE);
ntransmitted++;
advertise(&whereto);
}
if (verbose) {
logtrace("\n----%s rdisc Statistics----\n", sendaddress);
logtrace("%d packets transmitted, ", ntransmitted);
logtrace("%d packets received, ", nreceived);
logtrace("\n");
}
(void) fflush(stdout);
exit(0);
}
#include <ctype.h>
#ifdef notdef
int
pr_hex(unsigned char *data, int len)
{
FILE *out;
out = stdout;
while (len) {
register int i;
char charstring[17];
(void) strcpy(charstring, " ");
for (i = 0; i < 16; i++) {
if (len) {
char ch = *data & 0x7f;
if (!isprint((uchar_t)ch))
ch = ' ';
charstring[i] = ch;
(void) fprintf(out, "%02x ", *data++);
len--;
} else
(void) fprintf(out, " ");
if (i == 7)
(void) fprintf(out, " ");
}
(void) fprintf(out, " *%s*\n", charstring);
}
}
#endif
static int
isbroadcast(struct sockaddr_in *sin)
{
return (sin->sin_addr.s_addr == htonl(INADDR_BROADCAST));
}
static int
ismulticast(struct sockaddr_in *sin)
{
return (IN_CLASSD(ntohl(sin->sin_addr.s_addr)));
}
static int
sendbcast(int s, char *packet, int packetlen)
{
struct phyint *pi;
struct logint *li;
boolean_t bcast;
int cc;
for (pi = phyint; pi != NULL; pi = pi->pi_next) {
bcast = B_FALSE;
for (li = pi->pi_logical_first; li != NULL; li = li->li_next) {
if (li->li_state & ST_DELETED)
continue;
if (li->li_flags & IFF_BROADCAST) {
bcast = B_TRUE;
break;
}
}
if (!bcast)
continue;
cc = sendbcastif(s, packet, packetlen, li);
if (cc != packetlen) {
return (cc);
}
}
return (packetlen);
}
static int
sendbcastif(int s, char *packet, int packetlen, struct logint *li)
{
int cc;
struct sockaddr_in baddr;
struct icmp *icp = (struct icmp *)ALIGN(packet);
baddr.sin_family = AF_INET;
if ((li->li_flags & IFF_BROADCAST) == 0) {
if (verbose) {
logtrace("Suppressing sending %s on %s "
"(interface is not broadcast capable)\n",
pr_type((int)icp->icmp_type), li->li_name);
}
return (packetlen);
}
if (li->li_flags & IFF_NORTEXCH) {
if (verbose) {
logtrace("Suppressing sending %s on %s "
"(no route exchange on interface)\n",
pr_type((int)icp->icmp_type), li->li_name);
}
return (packetlen);
}
baddr.sin_addr = li->li_bcastaddr;
if (debug)
logdebug("Broadcast to %s\n",
pr_name(baddr.sin_addr));
cc = sendto(s, packet, packetlen, 0,
(struct sockaddr *)&baddr, sizeof (struct sockaddr));
if (cc != packetlen) {
logperror("sendbcast: sendto");
logerr("Cannot send broadcast packet to %s\n",
pr_name(baddr.sin_addr));
}
return (cc);
}
static int
sendmcast(int s, char *packet, int packetlen, struct sockaddr_in *sin)
{
struct phyint *pi;
struct logint *li;
boolean_t mcast;
int cc;
for (pi = phyint; pi != NULL; pi = pi->pi_next) {
mcast = B_FALSE;
for (li = pi->pi_logical_first; li != NULL; li = li->li_next) {
if (li->li_state & ST_DELETED)
continue;
if (li->li_flags & IFF_MULTICAST) {
mcast = B_TRUE;
break;
}
}
if (!mcast)
continue;
cc = sendmcastif(s, packet, packetlen, sin, li);
if (cc != packetlen) {
return (cc);
}
}
return (packetlen);
}
static int
sendmcastif(int s, char *packet, int packetlen, struct sockaddr_in *sin,
struct logint *li)
{
int cc;
struct sockaddr_in ifaddr;
struct icmp *icp = (struct icmp *)ALIGN(packet);
ifaddr.sin_family = AF_INET;
if ((li->li_flags & IFF_MULTICAST) == 0) {
if (verbose) {
logtrace("Suppressing sending %s on %s "
"(interface is not multicast capable)\n",
pr_type((int)icp->icmp_type), li->li_name);
}
return (packetlen);
}
if (li->li_flags & IFF_NORTEXCH) {
if (verbose) {
logtrace("Suppressing sending %s on %s "
"(no route exchange on interface)\n",
pr_type((int)icp->icmp_type), li->li_name);
}
return (packetlen);
}
ifaddr.sin_addr = li->li_address;
if (debug)
logdebug("Multicast to interface %s\n",
pr_name(ifaddr.sin_addr));
if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF,
(char *)&ifaddr.sin_addr,
sizeof (ifaddr.sin_addr)) < 0) {
logperror("setsockopt (IP_MULTICAST_IF)");
logerr("Cannot send multicast packet over interface %s\n",
pr_name(ifaddr.sin_addr));
return (-1);
}
cc = sendto(s, packet, packetlen, 0,
(struct sockaddr *)sin, sizeof (struct sockaddr));
if (cc != packetlen) {
logperror("sendmcast: sendto");
logerr("Cannot send multicast packet over interface %s\n",
pr_name(ifaddr.sin_addr));
}
return (cc);
}
static void
reinitifs(void)
{
(void) initifs(s, &g_joinaddr, g_preference);
}
static void
force_preference(int preference)
{
struct phyint *pi;
struct logint *li;
for (pi = phyint; pi != NULL; pi = pi->pi_next) {
for (li = pi->pi_logical_first; li != NULL; li = li->li_next) {
if (li->li_state & ST_DELETED)
continue;
li->li_preference = preference;
}
}
}
static int
initifs(int s, struct sockaddr_in *joinaddr, int preference)
{
struct ifconf ifc;
struct ifreq ifreq, *ifr;
struct lifreq lifreq;
int n;
char *buf;
int numifs;
unsigned bufsize;
struct phyint *pi;
struct logint *li;
int num_deletions;
char phyintname[IFNAMSIZ];
char *cp;
int old_num_usable_interfaces = num_usable_interfaces;
for (pi = phyint; pi != NULL; pi = pi->pi_next) {
pi->pi_state |= ST_MARKED;
for (li = pi->pi_logical_first; li != NULL; li = li->li_next) {
li->li_state |= ST_MARKED;
}
}
if (sock < 0) {
sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock < 0) {
logperror("initifs: socket");
return (-1);
}
}
#ifdef SIOCGIFNUM
if (ioctl(sock, SIOCGIFNUM, (char *)&numifs) < 0) {
logperror("initifs: SIOCGIFNUM");
return (-1);
}
#else
numifs = MAXIFS;
#endif
bufsize = numifs * sizeof (struct ifreq);
buf = (char *)malloc(bufsize);
if (buf == NULL) {
logerr("out of memory\n");
(void) close(sock);
sock = -1;
return (-1);
}
ifc.ifc_len = bufsize;
ifc.ifc_buf = buf;
if (ioctl(sock, SIOCGIFCONF, (char *)&ifc) < 0) {
logperror("initifs: ioctl (get interface configuration)");
(void) close(sock);
sock = -1;
(void) free(buf);
return (-1);
}
ifr = ifc.ifc_req;
for (n = ifc.ifc_len/sizeof (struct ifreq); n > 0; n--, ifr++) {
ifreq = *ifr;
(void) strncpy(lifreq.lifr_name, ifr->ifr_name,
sizeof (ifr->ifr_name));
if (ioctl(sock, SIOCGLIFFLAGS, (char *)&lifreq) < 0) {
logperror("initifs: ioctl (get interface flags)");
continue;
}
if (ifr->ifr_addr.sa_family != AF_INET)
continue;
if ((lifreq.lifr_flags & IFF_UP) == 0)
continue;
if (lifreq.lifr_flags & IFF_LOOPBACK)
continue;
if ((lifreq.lifr_flags & (IFF_MULTICAST | IFF_BROADCAST)) == 0)
continue;
strncpy(phyintname, ifreq.ifr_name, sizeof (phyintname));
if ((cp = strchr(phyintname, ':')) != NULL)
*cp = '\0';
pi = find_phyint(phyintname);
if (pi == NULL) {
pi = add_phyint(phyintname);
if (pi == NULL) {
logerr("out of memory\n");
(void) close(sock);
sock = -1;
(void) free(buf);
return (-1);
}
}
pi->pi_state &= ~ST_MARKED;
li = find_logint(pi, ifreq.ifr_name);
if (li != NULL) {
struct logint newli;
if (!getconfig(sock, lifreq.lifr_flags, &ifr->ifr_addr,
&ifreq, &newli)) {
free_logint(li);
continue;
}
if (newli.li_flags != li->li_flags ||
newli.li_localaddr.s_addr !=
li->li_localaddr.s_addr || newli.li_index !=
li->li_index) {
li->li_state |= ST_DELETED;
deleted_logint(li, &newli, s, joinaddr);
free_logint(li);
li = NULL;
} else {
li->li_netmask = newli.li_netmask;
li->li_bcastaddr = newli.li_bcastaddr;
}
}
if (li == NULL) {
li = add_logint(pi, ifreq.ifr_name);
if (li == NULL) {
logerr("out of memory\n");
(void) close(sock);
sock = -1;
(void) free(buf);
return (-1);
}
if (!getconfig(sock, lifreq.lifr_flags, &ifr->ifr_addr,
&ifreq, li)) {
free_logint(li);
continue;
}
li->li_preference = preference;
added_logint(li, s, joinaddr);
}
li->li_state &= ~ST_MARKED;
}
(void) free(buf);
num_usable_interfaces = 0;
num_deletions = 0;
for (pi = phyint; pi != NULL; pi = pi->pi_next) {
if (pi->pi_state & ST_MARKED) {
num_deletions++;
pi->pi_state |= ST_DELETED;
}
for (li = pi->pi_logical_first; li != NULL; li = li->li_next) {
if (li->li_state & ST_MARKED) {
num_deletions++;
li->li_state |= ST_DELETED;
}
}
if (!(pi->pi_state & ST_DELETED))
num_usable_interfaces++;
}
if (num_deletions != 0) {
struct phyint *nextpi;
struct logint *nextli;
for (pi = phyint; pi != NULL; pi = pi->pi_next) {
if (pi->pi_state & ST_DELETED) {
deleted_phyint(pi, s, joinaddr);
} else {
for (li = pi->pi_logical_first; li != NULL;
li = li->li_next) {
if (li->li_state & ST_DELETED) {
deleted_logint(li, NULL, s,
joinaddr);
}
}
}
}
for (pi = phyint; pi != NULL; pi = nextpi) {
nextpi = pi->pi_next;
for (li = pi->pi_logical_first; li != NULL;
li = nextli) {
nextli = li->li_next;
if (li->li_state & ST_DELETED)
free_logint(li);
}
if (pi->pi_state & ST_DELETED)
free_phyint(pi);
}
}
if (old_num_usable_interfaces == 0 && num_usable_interfaces > 0 &&
start_solicit && !solicit) {
if (debug)
logdebug("switching to solicitations: num if %d\n",
num_usable_interfaces);
solicit = start_solicit;
ntransmitted = 0;
ntransmitted++;
solicitor(&whereto);
}
return (0);
}
static boolean_t
getconfig(int sock, uint64_t if_flags, struct sockaddr *addr,
struct ifreq *ifr, struct logint *li)
{
struct ifreq ifreq;
struct sockaddr_in *sin;
struct lifreq lifreq;
ifreq = *ifr;
li->li_flags = if_flags;
sin = (struct sockaddr_in *)ALIGN(addr);
li->li_localaddr = sin->sin_addr;
(void) strlcpy(lifreq.lifr_name, ifr->ifr_name,
sizeof (lifreq.lifr_name));
if (ioctl(sock, SIOCGLIFINDEX, &lifreq) < 0) {
logperror("initifs: ioctl (get if index)");
li->li_index = 0;
} else {
li->li_index = lifreq.lifr_index;
}
if (if_flags & IFF_POINTOPOINT) {
li->li_netmask.s_addr = (unsigned long)0xffffffff;
if (ioctl(sock, SIOCGIFDSTADDR, (char *)&ifreq) < 0) {
logperror("initifs: ioctl (get dest addr)");
return (B_FALSE);
}
sin = (struct sockaddr_in *)ALIGN(&ifreq.ifr_addr);
li->li_address = sin->sin_addr;
li->li_remoteaddr = sin->sin_addr;
li->li_bcastaddr = sin->sin_addr;
li->li_flags |= IFF_BROADCAST;
} else {
li->li_address = li->li_localaddr;
li->li_remoteaddr = li->li_address;
if (ioctl(sock, SIOCGIFNETMASK, (char *)&ifreq) < 0) {
logperror("initifs: ioctl (get netmask)");
return (B_FALSE);
}
sin = (struct sockaddr_in *)ALIGN(&ifreq.ifr_addr);
li->li_netmask = sin->sin_addr;
if (if_flags & IFF_BROADCAST) {
if (ioctl(sock, SIOCGIFBRDADDR, (char *)&ifreq) < 0) {
logperror(
"initifs: ioctl (get broadcast address)");
return (B_FALSE);
}
sin = (struct sockaddr_in *)ALIGN(&ifreq.ifr_addr);
li->li_bcastaddr = sin->sin_addr;
}
}
return (B_TRUE);
}
static int
support_multicast(void)
{
int sock;
uchar_t ttl = 1;
sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (sock < 0) {
logperror("support_multicast: socket");
return (0);
}
if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL,
(char *)&ttl, sizeof (ttl)) < 0) {
(void) close(sock);
return (0);
}
(void) close(sock);
return (1);
}
static struct logint *
find_directly_connected_logint(struct in_addr in, struct phyint *opi)
{
struct phyint *pi;
struct logint *li;
if (opi == NULL)
pi = phyint;
else
pi = opi;
for (; pi != NULL; pi = pi->pi_next) {
for (li = pi->pi_logical_first; li != NULL; li = li->li_next) {
if (li->li_state & ST_DELETED)
continue;
if ((in.s_addr & li->li_netmask.s_addr) ==
(li->li_remoteaddr.s_addr &
li->li_netmask.s_addr))
return (li);
}
if (opi != NULL)
break;
}
return (NULL);
}
static void
report_interfaces(void)
{
struct phyint *pi;
struct logint *li;
logdebug("\nInterfaces:\n\n");
for (pi = phyint; pi != NULL; pi = pi->pi_next) {
logdebug("Phyint %s state 0x%x\n",
pi->pi_name, pi->pi_state);
for (li = pi->pi_logical_first; li != NULL; li = li->li_next) {
logdebug("IF %s state 0x%x, flags 0x%x, addr %s\n",
li->li_name, li->li_state, li->li_flags,
pr_name(li->li_address));
logdebug("\tlocal %s pref 0x%x ",
pr_name(li->li_localaddr), li->li_preference);
logdebug("bcast %s\n",
pr_name(li->li_bcastaddr));
logdebug("\tremote %s ",
pr_name(li->li_remoteaddr));
logdebug("netmask %s\n",
pr_name(li->li_netmask));
}
}
}
static struct phyint *
find_phyint(char *name)
{
struct phyint *pi;
for (pi = phyint; pi != NULL; pi = pi->pi_next) {
if (strcmp(pi->pi_name, name) == 0)
return (pi);
}
return (NULL);
}
static struct phyint *
add_phyint(char *name)
{
struct phyint *pi;
pi = malloc(sizeof (*pi));
if (pi == NULL)
return (NULL);
bzero((char *)pi, sizeof (*pi));
strncpy(pi->pi_name, name, sizeof (pi->pi_name));
pi->pi_next = phyint;
pi->pi_prev = NULL;
if (phyint != NULL)
phyint->pi_prev = pi;
phyint = pi;
return (pi);
}
static void
free_phyint(struct phyint *pi)
{
assert(pi->pi_logical_first == NULL);
assert(pi->pi_logical_last == NULL);
if (pi->pi_prev == NULL) {
assert(phyint == pi);
phyint = pi->pi_next;
} else {
assert(pi->pi_prev->pi_next == pi);
pi->pi_prev->pi_next = pi->pi_next;
}
if (pi->pi_next != NULL) {
assert(pi->pi_next->pi_prev == pi);
pi->pi_next->pi_prev = pi->pi_prev;
}
free(pi);
}
static struct logint *
find_logint(struct phyint *pi, char *name)
{
struct logint *li;
for (li = pi->pi_logical_first; li != NULL; li = li->li_next) {
if (strcmp(li->li_name, name) == 0)
return (li);
}
return (NULL);
}
static struct logint *
add_logint(struct phyint *pi, char *name)
{
struct logint *li;
li = malloc(sizeof (*li));
if (li == NULL)
return (NULL);
bzero((char *)li, sizeof (*li));
strncpy(li->li_name, name, sizeof (li->li_name));
li->li_prev = pi->pi_logical_last;
if (pi->pi_logical_last == NULL) {
assert(pi->pi_logical_first == NULL);
pi->pi_logical_first = li;
} else {
pi->pi_logical_last->li_next = li;
}
li->li_next = NULL;
li->li_physical = pi;
pi->pi_logical_last = li;
return (li);
}
static void
free_logint(struct logint *li)
{
struct phyint *pi;
pi = li->li_physical;
if (li->li_prev == NULL) {
assert(pi->pi_logical_first == li);
pi->pi_logical_first = li->li_next;
} else {
assert(li->li_prev->li_next == li);
li->li_prev->li_next = li->li_next;
}
if (li->li_next == NULL) {
assert(pi->pi_logical_last == li);
pi->pi_logical_last = li->li_prev;
} else {
assert(li->li_next->li_prev == li);
li->li_next->li_prev = li->li_prev;
}
free(li);
}
static void
deleted_phyint(struct phyint *pi, int s,
struct sockaddr_in *joinaddr)
{
struct logint *li;
if (debug)
logdebug("Deleting physical interface %s\n", pi->pi_name);
for (li = pi->pi_logical_first; li != NULL; li = li->li_next) {
li->li_state |= ST_DELETED;
}
for (li = pi->pi_logical_first; li != NULL; li = li->li_next) {
deleted_logint(li, NULL, s, joinaddr);
}
}
static void
added_logint(struct logint *li, int s,
struct sockaddr_in *joinaddr)
{
if (debug)
logdebug("Adding logical interface %s\n", li->li_name);
if ((!(li->li_physical->pi_state & ST_JOINED)) &&
(!isbroadcast(joinaddr))) {
struct ip_mreq mreq;
mreq.imr_multiaddr = joinaddr->sin_addr;
mreq.imr_interface = li->li_address;
if (debug)
logdebug("Joining MC on interface %s\n", li->li_name);
if (setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP,
(char *)&mreq, sizeof (mreq)) < 0) {
logperror("setsockopt (IP_ADD_MEMBERSHIP)");
} else {
li->li_physical->pi_state |= ST_JOINED;
li->li_state |= ST_JOINED;
}
}
}
static void
deleted_logint(struct logint *li, struct logint *newli, int s,
struct sockaddr_in *joinaddr)
{
struct phyint *pi;
struct logint *oli;
if (debug)
logdebug("Deleting logical interface %s\n", li->li_name);
assert(li->li_state & ST_DELETED);
if (li->li_state & ST_JOINED) {
struct ip_mreq mreq;
pi = li->li_physical;
assert(pi->pi_state & ST_JOINED);
assert(!isbroadcast(joinaddr));
mreq.imr_multiaddr = joinaddr->sin_addr;
if (newli != NULL)
mreq.imr_interface = newli->li_address;
else
mreq.imr_interface = li->li_address;
if (debug)
logdebug("Leaving MC on interface %s\n", li->li_name);
if (setsockopt(s, IPPROTO_IP, IP_DROP_MEMBERSHIP,
(char *)&mreq, sizeof (mreq)) < 0) {
if (errno != EADDRNOTAVAIL) {
logperror("setsockopt (IP_DROP_MEMBERSHIP)");
} else if (debug) {
logdebug("%s: %s\n",
"setsockopt (IP_DROP_MEMBERSHIP)",
strerror(errno));
}
}
li->li_physical->pi_state &= ~ST_JOINED;
li->li_state &= ~ST_JOINED;
for (oli = pi->pi_logical_first; oli != NULL;
oli = oli->li_next) {
if (oli->li_state & ST_DELETED)
continue;
mreq.imr_multiaddr = joinaddr->sin_addr;
mreq.imr_interface = oli->li_address;
if (debug)
logdebug("Joining MC on interface %s\n",
oli->li_name);
if (setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP,
(char *)&mreq, sizeof (mreq)) < 0) {
logperror("setsockopt (IP_ADD_MEMBERSHIP)");
} else {
pi->pi_state |= ST_JOINED;
oli->li_state |= ST_JOINED;
break;
}
}
}
flush_unreachable_routers();
}
struct table {
struct in_addr router;
int preference;
int remaining_time;
int in_kernel;
struct table *next;
};
struct table *table;
static void
report_routes(void)
{
struct table *tp;
logdebug("\nRoutes:\n\n");
tp = table;
while (tp) {
logdebug("Router %s, pref 0x%x, time %d, %s kernel\n",
pr_name(tp->router), tp->preference,
tp->remaining_time,
(tp->in_kernel ? "in" : "not in"));
tp = tp->next;
}
}
static struct table *
find_router(struct in_addr addr)
{
struct table *tp;
tp = table;
while (tp) {
if (tp->router.s_addr == addr.s_addr)
return (tp);
tp = tp->next;
}
return (NULL);
}
static int
max_preference(void)
{
struct table *tp;
int max = (int)IGNORE_PREFERENCE;
tp = table;
while (tp) {
if (tp->preference > max)
max = tp->preference;
tp = tp->next;
}
return (max);
}
static void
age_table(int time)
{
struct table **tpp, *tp;
int recalculate_max = 0;
int max = max_preference();
tpp = &table;
while (*tpp != NULL) {
tp = *tpp;
tp->remaining_time -= time;
if (tp->remaining_time <= 0) {
*tpp = tp->next;
if (debug) {
logdebug("Timed out router %s\n",
pr_name(tp->router));
}
if (tp->in_kernel)
del_route(tp->router);
if (best_preference &&
tp->preference == max)
recalculate_max++;
free((char *)tp);
} else {
tpp = &tp->next;
}
}
if (recalculate_max) {
int max = max_preference();
if (max != IGNORE_PREFERENCE) {
tp = table;
while (tp) {
if (tp->preference == max && !tp->in_kernel) {
add_route(tp->router);
tp->in_kernel++;
}
tp = tp->next;
}
}
}
}
static void
flush_unreachable_routers(void)
{
struct table **tpp, *tp;
int recalculate_max = 0;
int max = max_preference();
tpp = &table;
while (*tpp != NULL) {
tp = *tpp;
if (find_directly_connected_logint(tp->router, NULL) == NULL) {
*tpp = tp->next;
if (debug) {
logdebug("Unreachable router %s\n",
pr_name(tp->router));
}
if (tp->in_kernel)
del_route(tp->router);
if (best_preference &&
tp->preference == max)
recalculate_max++;
free((char *)tp);
} else {
tpp = &tp->next;
}
}
if (recalculate_max) {
int max = max_preference();
if (max != IGNORE_PREFERENCE) {
tp = table;
while (tp) {
if (tp->preference == max && !tp->in_kernel) {
add_route(tp->router);
tp->in_kernel++;
}
tp = tp->next;
}
}
}
}
static void
record_router(struct in_addr router, long preference, int ttl)
{
struct table *tp;
int old_max = max_preference();
int changed_up = 0;
int changed_down = 0;
if (debug)
logdebug("Recording %s, preference 0x%x\n",
pr_name(router),
preference);
tp = find_router(router);
if (tp) {
if (tp->preference > preference &&
tp->preference == old_max)
changed_down++;
else if (preference > tp->preference)
changed_up++;
tp->preference = preference;
tp->remaining_time = ttl;
} else {
if (preference > old_max)
changed_up++;
tp = (struct table *)ALIGN(malloc(sizeof (struct table)));
if (tp == NULL) {
logerr("Out of memory\n");
return;
}
tp->router = router;
tp->preference = preference;
tp->remaining_time = ttl;
tp->in_kernel = 0;
tp->next = table;
table = tp;
}
if (!tp->in_kernel &&
(!best_preference || tp->preference == max_preference()) &&
tp->preference != IGNORE_PREFERENCE) {
add_route(tp->router);
tp->in_kernel++;
}
if (tp->preference == IGNORE_PREFERENCE && tp->in_kernel) {
del_route(tp->router);
tp->in_kernel = 0;
}
if (best_preference && changed_down) {
int new_max = max_preference();
if (new_max != IGNORE_PREFERENCE) {
tp = table;
while (tp) {
if (tp->preference == new_max &&
!tp->in_kernel) {
add_route(tp->router);
tp->in_kernel++;
}
tp = tp->next;
}
}
}
if (best_preference && (changed_up || changed_down)) {
int new_max = max_preference();
tp = table;
while (tp) {
if (tp->preference < new_max && tp->in_kernel) {
del_route(tp->router);
tp->in_kernel = 0;
}
tp = tp->next;
}
}
}
#include <net/route.h>
static void
add_route(struct in_addr addr)
{
if (debug)
logdebug("Add default route to %s\n", pr_name(addr));
rtioctl(addr, SIOCADDRT);
}
static void
del_route(struct in_addr addr)
{
if (debug)
logdebug("Delete default route to %s\n", pr_name(addr));
rtioctl(addr, SIOCDELRT);
}
static void
rtioctl(struct in_addr addr, int op)
{
int sock;
struct rtentry rt;
struct sockaddr_in *sin;
bzero((char *)&rt, sizeof (struct rtentry));
rt.rt_dst.sa_family = AF_INET;
rt.rt_gateway.sa_family = AF_INET;
sin = (struct sockaddr_in *)ALIGN(&rt.rt_gateway);
sin->sin_addr = addr;
rt.rt_flags = RTF_UP | RTF_GATEWAY;
sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (sock < 0) {
logperror("rtioctl: socket");
return;
}
if (ioctl(sock, op, (char *)&rt) < 0) {
if (!(op == SIOCADDRT && errno == EEXIST))
logperror("ioctl (add/delete route)");
}
(void) close(sock);
}
#include <syslog.h>
static int logging = 0;
static void
initlog(void)
{
logging++;
openlog("in.rdisc", LOG_PID | LOG_CONS, LOG_DAEMON);
}
static void
logimpl(int pri, char *fmt, va_list ap)
{
FILE *log;
if (pri == LOG_ERR)
log = stderr;
else
log = stdout;
if (logging)
vsyslog(pri, fmt, ap);
else
(void) vfprintf(log, fmt, ap);
}
static void
logerr(char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
logimpl(LOG_ERR, fmt, ap);
va_end(ap);
}
static void
logtrace(char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
logimpl(LOG_INFO, fmt, ap);
va_end(ap);
}
static void
logdebug(char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
logimpl(LOG_DEBUG, fmt, ap);
va_end(ap);
}
static void
logperror(char *str)
{
if (logging)
syslog(LOG_ERR, "%s: %s\n", str, strerror(errno));
else
(void) fprintf(stderr, "%s: %s\n", str, strerror(errno));
}