/*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Copyright (c) 1987 Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation,
* advertising materials, and other materials related to such
* distribution and use acknowledge that the software was developed
* by the University of California, Berkeley. The name of the
* University may not be used to endorse or promote products derived
* from this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <stdio.h>
#include <errno.h>
#include <signal.h>
#include <netinet/in_systm.h>
#include <netdb.h>
#include <fcntl.h>
#include <strings.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#ifdef lint
#else
#endif
#ifdef SYSV
#endif
/* For router advertisement */
struct icmp_ra {
short icmp_lifetime;
};
struct icmp_ra_addr {
};
/* Router constants */
/* Host constants */
/*
* A doubly linked list of all physical interfaces that each contain a
* doubly linked list of logical interfaces aka IP addresses.
*/
struct phyint {
};
struct logint {
int li_preference;
};
/*
* State bits
*/
/* Function prototypes */
static void flush_unreachable_routers(void);
static int support_multicast(void);
struct sockaddr_in *sin);
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);
struct sockaddr_in *joinaddr);
struct sockaddr_in *joinaddr);
struct sockaddr_in *joinaddr);
char *pr_type(int t);
static void initlog(void);
/* Local variables */
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; /* Socket file descriptor */
/* Common variables */
int verbose = 0;
int debug = 0;
int trace = 0;
int responder;
int ntransmitted = 0;
int nreceived = 0;
/* first response. */
/* Router variables */
int min_adv_int;
int lifetime;
/* Host variables */
/* best preference in the kernel. Not set */
/* puts all routes in the kernel. */
static void
prusage()
{
exit(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
if (t >= 0) {
(void) close(t);
}
#else
(void) setpgrp();
#endif
initlog();
}
/*
* M A I N
*/
int
{
#ifndef SYSV
#endif
while (*++av[0])
switch (*av[0]) {
case 'd':
debug = 1;
break;
case 't':
trace = 1;
break;
case 'v':
verbose++;
break;
case 's':
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':
if (argc != 0) {
"Bad Max Advertisement Interval\n");
exit(1);
}
max_adv_int = val;
} else {
prusage();
/* NOTREACHED */
}
goto next;
case 'p':
if (argc != 0) {
g_preference = val;
} else {
prusage();
/* NOTREACHED */
}
goto next;
default:
prusage();
/* NOTREACHED */
}
next:
}
if (argc < 1) {
if (support_multicast()) {
if (responder)
else
} else
sendaddress = "255.255.255.255";
} else {
sendaddress = av[0];
argc--;
}
if (argc < 1) {
if (support_multicast()) {
if (responder)
else
} else
recvaddress = "255.255.255.255";
} else {
recvaddress = av[0];
argc--;
}
if (argc != 0) {
prusage();
/* NOTREACHED */
}
prusage();
/* NOTREACHED */
}
do_fork();
}
exit(1);
}
exit(1);
}
if (responder) {
#ifdef SYSV
#else
#endif
}
logperror("socket");
exit(5);
}
#ifdef SYSV
#else
#endif
logerr("Failed initializing interfaces\n");
exit(2);
}
/*
* If there are no usable interfaces and we are soliciting
* waiting for to return an exit code (i.e. forever isn't set)
* give up immediately.
*/
logerr("in.rdisc: No interfaces up\n");
exit(5);
}
#ifdef SYSV
#else
/*
* Make sure that this signal actually interrupts (rather than
* restarts) the recvfrom call below.
*/
#endif
timer(); /* start things going */
for (;;) {
int cc;
&fromlen)) < 0) {
continue;
logperror("recvfrom");
continue;
}
/* Block all signals while processing */
(void) sigfillset(&newmask);
}
/* NOTREACHED */
}
static void
report(void)
{
}
static int left_until_advertise;
/* Called every TIMER_INTERVAL */
static void
timer(void)
{
static int time;
static int left_until_getifconf;
static int left_until_solicit;
time += TIMER_INTERVAL;
if (left_until_getifconf < 0) {
}
if (responder && left_until_advertise <= 0) {
ntransmitted++;
else
((max_adv_int - min_adv_int) *
} else if (solicit && left_until_solicit <= 0) {
if (ntransmitted < max_solicitations) {
ntransmitted++;
} else {
solicit = 0;
exit(5);
}
}
(void) alarm(TIMER_INTERVAL);
}
/*
* S O L I C I T O R
*
* Compose and transmit an ICMP ROUTER SOLICITATION REQUEST packet.
* The IP packet will be added on by the kernel.
*/
static void
{
int packetlen, i;
if (verbose) {
logtrace("Sending solicitation to %s\n",
}
icp->icmp_cksum = 0;
packetlen = 8;
/* Compute ICMP checksum here */
if (isbroadcast(sin))
else if (ismulticast(sin))
else {
if (verbose) {
logtrace("Suppressing sending %s on %s "
"(no route exchange on interface)\n",
}
return;
} else {
}
}
if (i < 0 || i != packetlen) {
if (i < 0) {
logperror("sendto");
}
logerr("wrote %s %d chars, ret=%d\n",
sendaddress, packetlen, i);
}
}
/*
* A D V E R T I S E
*
* Compose and transmit an ICMP ROUTER ADVERTISEMENT packet.
* The IP packet will be added on by the kernel.
*/
static void
{
if (verbose) {
logtrace("Sending advertisement to %s\n",
}
rap->icmp_cksum = 0;
rap->icmp_num_addrs = 0;
continue;
/*
* XXX Just truncate the list of addresses.
* Should probably send multiple packets.
*/
if (debug)
logdebug("full packet: %d addresses\n",
break;
}
rap->icmp_num_addrs++;
}
if (rap->icmp_num_addrs == 0)
continue;
/* Compute ICMP checksum here */
if (isbroadcast(sin))
else if (ismulticast(sin))
else {
/*
* Verify that the physical interface matches the
* destination address.
*/
pi);
continue;
if (verbose) {
logtrace("Suppressing sending %s on %s "
"(no route exchange on "
"interface)\n",
}
continue;
}
if (debug) {
logdebug("Unicast to %s ",
}
}
if (cc < 0) {
logperror("sendto");
} else {
logerr("wrote %s %d chars, ret=%d\n",
}
}
}
}
/*
* P R _ T Y P E
*
* Convert an ICMP "type" field to a printable string.
*/
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]);
}
/*
* P R _ N A M E
*
* Return a string name for the given IP address.
*/
char *
{
return (buf);
}
/*
* P R _ P A C K
*
* Print out the packet, if it came from us. This logic is necessary
* because ALL readers of the ICMP socket get a copy of ALL ICMP packets
* which arrive ('tis only fair). This permits multiple copies of this
* program to be run without having intermingled output (or statistics!).
*/
static void
{
register int i;
int hlen;
if (verbose)
return;
}
/*
* Let's check if IFF_NORTEXCH flag is set on the interface which
* recevied this packet.
* TODO: this code can be re-written using one socket per interface
* to determine which interface the packet is recevied.
*/
if (verbose) {
logtrace("Ignoring received %s on %s "
"(no route exchange on interface)",
}
return;
}
/*
* Assume that we are running on a pre-4.3BSD system
* such as SunOS before 4.0
*/
}
case ICMP_ROUTERADVERT: {
if (responder)
break;
/* TBD verify that the link is multicast or broadcast */
/* XXX Find out the link it came in over? */
#ifdef notdef
if (debug) {
logdebug("ROUTER_ADVERTISEMENT: \n");
}
#endif /* notdef */
if (verbose)
logtrace("ICMP %s from %s: Bad checksum\n",
return;
}
if (verbose)
logtrace("ICMP %s from %s: Code = %d\n",
return;
}
if (verbose)
logtrace("ICMP %s from %s: No addresses\n",
return;
}
if (verbose)
return;
}
if ((unsigned)cc <
if (verbose)
logtrace("ICMP %s from %s: Too short %d, %d\n",
cc,
return;
}
if (verbose)
logtrace("ICMP %s from %s: Invalid lifetime %d\n",
rap->icmp_lifetime);
return;
}
if (verbose)
logtrace("ICMP %s from %s, lifetime %d\n",
rap->icmp_lifetime);
/*
* Check that at least one router address is a neighbor
* on the arriving link.
*/
for (i = 0; (unsigned)i < rap->icmp_num_addrs; i++) {
ap = (struct icmp_ra_addr *)
if (verbose)
logtrace("\taddress %s, preference 0x%x\n",
ap->preference);
if (!responder) {
NULL) {
(long)ap->preference,
rap->icmp_lifetime);
}
}
}
nreceived++;
if (!forever) {
(void) alarm(0);
do_fork();
forever = 1;
(void) alarm(TIMER_INTERVAL);
}
break;
}
case ICMP_ROUTERSOLICIT: {
if (!responder)
break;
/* TBD verify that the link is multicast or broadcast */
/* XXX Find out the link it came in over? */
#ifdef notdef
if (debug) {
logdebug("ROUTER_SOLICITATION: \n");
}
#endif /* notdef */
if (verbose)
logtrace("ICMP %s from %s: Bad checksum\n",
return;
}
if (verbose)
logtrace("ICMP %s from %s: Code = %d\n",
return;
}
if (cc < ICMP_MINLEN) {
if (verbose)
logtrace("ICMP %s from %s: Too short %d, %d\n",
cc,
return;
}
if (verbose)
logtrace("ICMP %s from %s\n",
if (!responder)
break;
/*
* Check that ip_src is either a neighbor
* on the arriving link or 0.
*/
/*
* If it was sent to the broadcast address we respond
* to the broadcast address.
*/
} else
/* Restart the timer when we broadcast */
((max_adv_int - min_adv_int)
} else {
if (verbose)
logtrace("ICMP %s from %s: %s\n",
"source not directly connected");
break;
}
}
nreceived++;
ntransmitted++;
break;
}
}
}
/*
* I N _ C K S U M
*
* Checksum routine for Internet Protocol family headers (C Version)
*
*/
int
{
register int sum = 0;
/*
* Our algorithm is simple, using a 32 bit accumulator (sum),
* we add sequential 16 bit words to it, and at the end, fold
* back all the carry bits from the top 16 bits into the lower
* 16 bits.
*/
while (nleft > 1) {
sum += *w++;
nleft -= 2;
}
/* mop up an odd byte, if necessary */
if (nleft == 1) {
}
/*
* add back carry outs from top 16 bits to low 16 bits
*/
return (answer);
}
/*
* F I N I S H
*
* Print out statistics, and give up.
* Heavily buffered stdio is used here, so that all the statistics
* will be written with 1 sys-write call. This is nice when more
* than one copy of the program is running on a terminal; it prevents
* the statistics output from becoming intermingled.
*/
static void
finish(void)
{
if (responder) {
/*
* Send out a packet with a preference so that all
* hosts will know that we are dead.
*/
logerr("terminated\n");
ntransmitted++;
}
if (verbose) {
logtrace("\n");
}
exit(0);
}
#include <ctype.h>
#ifdef notdef
int
{
while (len) {
register int i;
for (i = 0; i < 16; i++) {
/*
* output the bytes one at a time,
* not going past "len" bytes
*/
if (len) {
charstring[i] = ch;
len--;
} else
if (i == 7)
}
}
}
#endif /* notdef */
static int
{
}
static int
{
}
/* From libc/rpc/pmap_rmt.c */
/* Only send once per physical interface */
static int
{
int cc;
continue;
break;
}
}
if (!bcast)
continue;
return (cc);
}
}
return (packetlen);
}
static int
{
int cc;
if (verbose) {
logtrace("Suppressing sending %s on %s "
"(interface is not broadcast capable)\n",
}
return (packetlen);
}
if (verbose) {
logtrace("Suppressing sending %s on %s "
"(no route exchange on interface)\n",
}
return (packetlen);
}
if (debug)
logdebug("Broadcast to %s\n",
logperror("sendbcast: sendto");
logerr("Cannot send broadcast packet to %s\n",
}
return (cc);
}
static int
{
int cc;
continue;
break;
}
}
if (!mcast)
continue;
return (cc);
}
}
return (packetlen);
}
static int
{
int cc;
if (verbose) {
logtrace("Suppressing sending %s on %s "
"(interface is not multicast capable)\n",
}
return (packetlen);
}
if (verbose) {
logtrace("Suppressing sending %s on %s "
"(no route exchange on interface)\n",
}
return (packetlen);
}
if (debug)
logdebug("Multicast to interface %s\n",
logperror("setsockopt (IP_MULTICAST_IF)");
logerr("Cannot send multicast packet over interface %s\n",
return (-1);
}
logperror("sendmcast: sendto");
logerr("Cannot send multicast packet over interface %s\n",
}
return (cc);
}
static void
reinitifs(void)
{
}
static void
{
continue;
}
}
}
/*
* Returns -1 on failure.
*/
static int
{
int n;
char *buf;
int numifs;
unsigned bufsize;
int num_deletions;
char *cp;
/*
* Mark all interfaces so that we can determine which ones
* have gone away.
*/
}
}
if (sock < 0) {
if (sock < 0) {
logperror("initifs: socket");
return (-1);
}
}
#ifdef SIOCGIFNUM
logperror("initifs: SIOCGIFNUM");
return (-1);
}
#else
#endif
logerr("out of memory\n");
sock = -1;
return (-1);
}
logperror("initifs: ioctl (get interface configuration)");
sock = -1;
return (-1);
}
/*
* We need to use new interface ioctls to get 64-bit flags.
*/
logperror("initifs: ioctl (get interface flags)");
continue;
}
continue;
continue;
continue;
continue;
/* Create the physical name by truncating at the ':' */
*cp = '\0';
logerr("out of memory\n");
sock = -1;
return (-1);
}
}
/*
* Detect significant changes.
* We treat netmask changes as insignificant but all
* other changes cause a delete plus add of the
* logical interface.
* Note: if the flags and localaddr are unchanged
* then nothing but the netmask and the broadcast
* address could have changed since the other addresses
* are derived from the flags and the localaddr.
*/
continue;
}
/* Treat as an interface deletion + addition */
} else {
/*
* No significant changes.
* Just update the netmask, and broadcast.
*/
}
}
logerr("out of memory\n");
sock = -1;
return (-1);
}
/* init li */
continue;
}
}
}
/*
* Determine which interfaces have gone away.
* The deletion is done in three phases:
* 1. Mark ST_DELETED
* 2. Inform using the deleted_* function.
* 3. Unlink and free the actual memory.
* Note that for #3 the physical interface must be deleted after
* the logical ones.
* Also count the number of physical interfaces.
*/
num_deletions = 0;
}
}
}
}
if (num_deletions != 0) {
/*
* By deleting the physical interface pi, all of
* the corresponding logical interfaces will
* also be deleted so there is no need to delete
* them individually.
*/
} else {
joinaddr);
}
}
}
}
/* Do the actual linked list update + free */
}
}
}
/*
* When the set of available interfaces goes from zero to
* non-zero we restart solicitations if '-s' was specified.
*/
if (old_num_usable_interfaces == 0 && num_usable_interfaces > 0 &&
start_solicit && !solicit) {
if (debug)
logdebug("switching to solicitations: num if %d\n",
ntransmitted = 0;
ntransmitted++;
}
return (0);
}
static boolean_t
{
logperror("initifs: ioctl (get if index)");
/* Continue with 0; a safe value never used for interfaces */
} else {
}
if (if_flags & IFF_POINTOPOINT) {
logperror("initifs: ioctl (get dest addr)");
return (B_FALSE);
}
/* A pt-pt link is identified by the remote address */
/* Simulate broadcast for pt-pt */
} else {
/*
* Non pt-pt links are identified by the local
* address
*/
logperror("initifs: ioctl (get netmask)");
return (B_FALSE);
}
if (if_flags & IFF_BROADCAST) {
"initifs: ioctl (get broadcast address)");
return (B_FALSE);
}
}
}
return (B_TRUE);
}
static int
support_multicast(void)
{
int sock;
if (sock < 0) {
logperror("support_multicast: socket");
return (0);
}
return (0);
}
return (1);
}
/*
* For a given destination address, find the logical interface to use.
* If opi is NULL check all interfaces. Otherwise just match against
* the specified physical interface.
* Return logical interface if there's a match, NULL otherwise.
*/
static struct logint *
{
else
continue;
/* Check that the subnetwork numbers match */
return (li);
}
break;
}
return (NULL);
}
/*
* INTERFACES - physical and logical identified by name
*/
static void
report_interfaces(void)
{
logdebug("\nInterfaces:\n\n");
logdebug("Phyint %s state 0x%x\n",
logdebug("IF %s state 0x%x, flags 0x%x, addr %s\n",
logdebug("\tlocal %s pref 0x%x ",
logdebug("bcast %s\n",
logdebug("\tremote %s ",
logdebug("netmask %s\n",
}
}
}
static struct phyint *
{
return (pi);
}
return (NULL);
}
/* Assumes that the entry does not exist - caller must use find_* */
static struct phyint *
{
return (NULL);
/* Link into list */
return (pi);
}
static void
{
/* Delete first */
} else {
}
}
}
static struct logint *
{
return (li);
}
return (NULL);
}
/*
* Assumes that the entry does not exist - caller must use find_*
* Tail insertion.
*/
static struct logint *
{
return (NULL);
/* Link into list */
/* First one */
} else {
}
return (li);
}
static void
{
/* Delete first */
} else {
}
/* Delete last */
} else {
}
}
/* Tell all the logical interfaces that they are going away */
static void
struct sockaddr_in *joinaddr)
{
if (debug)
}
}
}
/*
* Join the multicast address if no other logical interface has done
* so for this physical interface.
*/
static void
struct sockaddr_in *joinaddr)
{
if (debug)
(!isbroadcast(joinaddr))) {
if (debug)
logperror("setsockopt (IP_ADD_MEMBERSHIP)");
} else {
}
}
}
/*
* Leave the multicast address if this logical interface joined it.
* Look for a replacement logical interface for the same physical interface.
* Remove any routes which are no longer reachable.
*
* If newli is non-NULL, then it is likely that the address of a logical
* interface has changed. In this case, the membership should be dropped using
* the new address of the interface in question.
*
* XXX When a physical interface is being deleted by deleted_phyint(), this
* routine will be called for each logical interface associated with the
* physical one. This should be made more efficient as there is no point in
* searching for an alternate logical interface to add group membership to as
* they all are marked ST_DELETED.
*/
static void
struct sockaddr_in *joinaddr)
{
if (debug)
else
if (debug)
/*
* EADDRNOTAVAIL will be returned if the interface has
* been unplumbed or if the interface no longer has
* IFF_MULTICAST set. The former is the common case
* while the latter is rare so don't log the error
* unless some other error was returned or if debug is
* set.
*/
if (errno != EADDRNOTAVAIL) {
logperror("setsockopt (IP_DROP_MEMBERSHIP)");
} else if (debug) {
logdebug("%s: %s\n",
"setsockopt (IP_DROP_MEMBERSHIP)",
}
}
/* Is there another interface that can join? */
continue;
if (debug)
logdebug("Joining MC on interface %s\n",
logperror("setsockopt (IP_ADD_MEMBERSHIP)");
} else {
break;
}
}
}
}
/*
* TABLES
*/
struct table {
int preference;
int remaining_time;
int in_kernel;
};
static void
report_routes(void)
{
logdebug("\nRoutes:\n\n");
while (tp) {
logdebug("Router %s, pref 0x%x, time %d, %s kernel\n",
}
}
static struct table *
{
while (tp) {
return (tp);
}
return (NULL);
}
static int
max_preference(void)
{
while (tp) {
}
return (max);
}
/* Note: this might leave the kernel with no default route for a short time. */
static void
{
int recalculate_max = 0;
if (tp->remaining_time <= 0) {
if (debug) {
logdebug("Timed out router %s\n",
}
if (best_preference &&
} else {
}
}
if (recalculate_max) {
if (max != IGNORE_PREFERENCE) {
while (tp) {
}
}
}
}
}
/*
* Remove any routes which are no longer directly connected.
*/
static void
{
int recalculate_max = 0;
if (debug) {
logdebug("Unreachable router %s\n",
}
if (best_preference &&
} else {
}
}
if (recalculate_max) {
if (max != IGNORE_PREFERENCE) {
while (tp) {
}
}
}
}
}
static void
{
if (debug)
logdebug("Recording %s, preference 0x%x\n",
if (tp) {
changed_down++;
changed_up++;
} else {
if (preference > old_max)
changed_up++;
logerr("Out of memory\n");
return;
}
}
}
}
if (best_preference && changed_down) {
/* Check if we should add routes */
if (new_max != IGNORE_PREFERENCE) {
while (tp) {
}
}
}
}
/* Check if we should remove routes already in the kernel */
while (tp) {
}
}
}
}
static void
{
if (debug)
}
static void
{
if (debug)
}
static void
{
int sock;
if (sock < 0) {
logperror("rtioctl: socket");
return;
}
}
}
/*
* LOGGER
*/
#include <syslog.h>
static int logging = 0;
static void
initlog(void)
{
logging++;
}
/* VARARGS1 */
void
char *fmt;
{
if (logging)
else
}
/* VARARGS1 */
void
char *fmt;
{
if (logging)
else
}
/* VARARGS1 */
void
char *fmt;
{
if (logging)
else
}
void
char *str;
{
if (logging)
else
}