Copy SGI routed onto head.

This commit is contained in:
Garrett Wollman 1996-05-30 16:31:46 +00:00
parent 703f599354
commit 892201b43f
9 changed files with 4529 additions and 1320 deletions

View file

@ -1,12 +1,8 @@
# @(#)Makefile 8.1 (Berkeley) 6/19/93
# $Id$
PROG= routed
SRCS= af.c if.c input.c main.c output.c startup.c tables.c timer.c \
trace.c inet.c
SRCS= if.c input.c main.c output.c parms.c radix.c rdisc.c table.c trace.c
MAN8= routed.8
#SUBDIR= query trace
DPADD= ${LIBCOMPAT}
LDADD= -lcompat
SUBDIR= rtquery rttrace
.include <bsd.prog.mk>

View file

@ -33,71 +33,510 @@
* @(#)defs.h 8.1 (Berkeley) 6/5/93
*/
/*
* Internal data structure definitions for
* user routing process. Based on Xerox NS
* protocol specs with mods relevant to more
* general addressing scheme.
*/
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/time.h>
#ident "$Revision: 1.1.3.1 $"
#include <net/route.h>
#include <netinet/in.h>
#include <protocols/routed.h>
/* Definitions for RIPv2 routing process.
*
* This code is based on the 4.4BSD `routed` daemon, with extensions to
* support:
* RIPv2, including variable length subnet masks.
* Router Discovery
* aggregate routes in the kernel tables.
* aggregate advertised routes.
* maintain spare routes for faster selection of another gateway
* when the current gateway dies.
* timers on routes with second granularity so that selection
* of a new route does not wait 30-60 seconds.
* tolerance of static routes.
* tell the kernel hop counts
* do not advertise if ipforwarding=0
*
* The vestigual support for other protocols has been removed. There
* is no likelihood that IETF RIPv1 or RIPv2 will ever be used with
* other protocols. The result is far smaller, faster, cleaner, and
* perhaps understandable.
*
* The accumulation of special flags and kludges added over the many
* years have been simplified and integrated.
*/
#include <stdio.h>
#include <netdb.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#ifdef sgi
#include <strings.h>
#include <bstring.h>
#endif
#include <stdarg.h>
#include <syslog.h>
#include <time.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/ioctl.h>
#include <sys/sysctl.h>
#include <sys/socket.h>
#include <net/if.h>
#include <net/route.h>
#include <net/radix.h>
#ifndef sgi
struct walkarg;
#endif
#include <net/if_dl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define RIPVERSION RIPv2
#include <protocols/routed.h>
#include "trace.h"
#include "interface.h"
#include "table.h"
#include "af.h"
/*
* When we find any interfaces marked down we rescan the
* kernel every CHECK_INTERVAL seconds to see if they've
* come up.
/* Type of an IP address.
* Some systems do not like to pass structures, so do not use in_addr.
* Some systems think a long has 64 bits, which would be a gross waste.
* So define it here so it can be changed for the target system.
* It should be defined somewhere netinet/in.h, but it is not.
*/
#define CHECK_INTERVAL (1*60)
#ifdef sgi
#define naddr __uint32_t
#else
#define naddr u_long
#define _HAVE_SA_LEN
#define _HAVE_SIN_LEN
#endif
#define equal(a1, a2) \
(bcmp((caddr_t)(a1), (caddr_t)(a2), sizeof (struct sockaddr)) == 0)
#ifdef sgi
/* Turn on if IP_DROP_MEMBERSHIP and IP_ADD_MEMBERSHIP do not look at
* the dstaddr of point-to-point interfaces.
*/
#define MCAST_PPP_BUG
#endif
struct sockaddr_in addr; /* address of daemon's socket */
#define NEVER (24*60*60) /* a long time */
#define EPOCH NEVER /* bias time by this to avoid <0 */
int s; /* source and sink of all data */
int r; /* routing socket */
pid_t pid; /* process id for identifying messages */
uid_t uid; /* user id for identifying messages */
int seqno; /* sequence number for identifying messages */
int kmem;
int supplier; /* process should supply updates */
int install; /* if 1 call kernel */
int lookforinterfaces; /* if 1 probe kernel for new up interfaces */
int performnlist; /* if 1 check if /kernel has changed */
int externalinterfaces; /* # of remote and local interfaces */
struct timeval now; /* current idea of time */
struct timeval lastbcast; /* last time all/changes broadcast */
struct timeval lastfullupdate; /* last time full table broadcast */
struct timeval nextbcast; /* time to wait before changes broadcast */
int needupdate; /* true if we need update at nextbcast */
/* Scan the kernel regularly to see if any interfaces have appeared or been
* turned off. These must be less than STALE_TIME.
*/
#define CHECK_BAD_INTERVAL 5 /* when an interface is known bad */
#define CHECK_ACT_INTERVAL 30 /* when advertising */
#define CHECK_QUIET_INTERVAL 300 /* when not */
char packet[MAXPACKETSIZE+1];
struct rip *msg;
char **argv0;
struct servent *sp;
/* set times to this to continue poisoning a route */
#define POISON_SECS (GARBAGE_TIME - POISON_TIME)
struct in_addr inet_makeaddr();
int inet_addr();
int inet_maskof();
int sndmsg();
int supply();
int cleanup();
#define NET_S_METRIC 1 /* metric used on synthetic routes */
int rtioctl();
#define ADD 1
#define DELETE 2
#define CHANGE 3
#define LIM_SEC(s,l) ((s).tv_sec = MIN((s).tv_sec, (l)))
/* Router Discovery parameters */
#ifndef sgi
#define INADDR_ALLROUTERS_GROUP 0xe0000002 /* 224.0.0.2 */
#endif
#define MaxMaxAdvertiseInterval 1800
#define MinMaxAdvertiseInterval 4
#define DefMaxAdvertiseInterval 600
#define DEF_PreferenceLevel 0
#define MIN_PreferenceLevel 0x80000000
#define MAX_INITIAL_ADVERT_INTERVAL 16
#define MAX_INITIAL_ADVERTS 3
#define MAX_RESPONSE_DELAY 2
#define MAX_SOLICITATION_DELAY 1
#define SOLICITATION_INTERVAL 3
#define MAX_SOLICITATIONS 3
/* typical packet buffers */
union pkt_buf {
char packet[MAXPACKETSIZE+1];
struct rip rip;
};
/* Main, daemon routing table structure
*/
struct rt_entry {
struct radix_node rt_nodes[2]; /* radix tree glue */
u_int rt_state;
# define RS_IF 0x001 /* for network interface */
# define RS_NET_SUB 0x002 /* fake net route for subnet */
# define RS_NET_HOST 0x004 /* fake net route for host */
# define RS_NET_INT 0x008 /* authority route */
# define RS_NET_S (RS_NET_SUB | RS_NET_HOST | RS_NET_INT)
# define RS_SUBNET 0x010 /* subnet route from any source */
# define RS_LOCAL 0x020 /* loopback for pt-to-pt */
# define RS_MHOME 0x040 /* from -m */
# define RS_GW 0x080 /* from -g */
# define RS_STATIC 0x100 /* from the kernel */
# define RS_RDISC 0x200 /* from router discovery */
struct sockaddr_in rt_dst_sock;
naddr rt_mask;
struct rt_spare {
struct interface *rts_ifp;
naddr rts_gate; /* forward packets here */
naddr rts_router; /* on the authority of this router */
char rts_metric;
u_short rts_tag;
time_t rts_time; /* timer to junk stale routes */
#define NUM_SPARES 4
} rt_spares[NUM_SPARES];
u_int rt_seqno; /* when last changed */
char rt_hold_metric;
time_t rt_hold_down;
};
#define rt_dst rt_dst_sock.sin_addr.s_addr
#define rt_ifp rt_spares[0].rts_ifp
#define rt_gate rt_spares[0].rts_gate
#define rt_router rt_spares[0].rts_router
#define rt_metric rt_spares[0].rts_metric
#define rt_tag rt_spares[0].rts_tag
#define rt_time rt_spares[0].rts_time
#define HOST_MASK 0xffffffff
#define RT_ISHOST(rt) ((rt)->rt_mask == HOST_MASK)
/* age all routes that
* are not from -g, -m, or static routes from the kernel
* not unbroken interface routes
* but not broken interfaces
* nor non-passive, remote interfaces that are not aliases
* (i.e. remote & metric=0)
*/
#define AGE_RT(rt,ifp) (0 == ((rt)->rt_state & (RS_GW | RS_MHOME | RS_STATIC \
| RS_NET_SUB | RS_NET_HOST \
| RS_RDISC)) \
&& (!((rt)->rt_state & RS_IF) \
|| (ifp) == 0 \
|| (((ifp)->int_state & IS_REMOTE) \
&& !((ifp)->int_state & IS_PASSIVE))))
/* true if A is better than B
* Better if
* - A is not a poisoned route
* - and A is not stale
* - and A has a shorter path
* - or is the router speaking for itself
* - or the current route is equal but stale
*/
#define BETTER_LINK(A, B) ((A)->rts_metric != HOPCNT_INFINITY \
&& now_stale <= (A)->rts_time \
&& ((A)->rts_metric < (B)->rts_metric \
|| ((A)->rts_gate == (A)->rts_router \
&& (B)->rts_gate != (B)->rts_router) \
|| ((A)->rts_metric == (B)->rts_metric \
&& now_stale > (B)->rts_time)))
/* An "interface" is similar to a kernel ifnet structure, except it also
* handles "logical" or "IS_REMOTE" interfaces (remote gateways).
*/
struct interface {
struct interface *int_next, *int_prev;
char int_name[IFNAMSIZ+15+1]; /* big enough for IS_REMOTE */
u_short int_index;
naddr int_addr; /* address on this host (net order) */
naddr int_brdaddr; /* broadcast address (n) */
naddr int_dstaddr; /* other end of pt-to-pt link (n) */
naddr int_net; /* working network # (host order)*/
naddr int_mask; /* working net mask (host order) */
naddr int_std_addr; /* class A/B/C address (n) */
naddr int_std_net; /* class A/B/C network (h) */
naddr int_std_mask; /* class A/B/C netmask (h) */
naddr int_host_addr; /* RIPv1 net for pt-to-pt link (h) */
naddr int_host_mask; /* RIPv1 mask for pt-to-pt (h) */
int int_rip_sock; /* for queries */
int int_if_flags; /* copied from kernel */
u_int int_state;
time_t int_act_time; /* last thought healthy */
time_t int_quiet_time; /* last inactive */
u_short int_transitions; /* times gone up-down */
char int_metric;
char int_d_metric; /* for faked default route */
u_int int_data_ipackets; /* previous network stats */
u_int int_data_ierrors;
u_int int_data_opackets;
u_int int_data_oerrors;
#ifdef sgi
u_int int_data_odrops;
#endif
time_t int_data_ts; /* timestamp on network stats */
char int_passwd[RIP_AUTH_PW_LEN]; /* RIPv2 password */
int int_rdisc_pref; /* advertised rdisc preference */
int int_rdisc_int; /* MaxAdvertiseInterval */
int int_rdisc_cnt;
struct timeval int_rdisc_timer;
};
#define IS_ALIAS 0x0000001 /* interface alias */
#define IS_SUBNET 0x0000002 /* interface on subnetted network */
#define IS_REMOTE 0x0000004 /* interface is not on this machine */
#define IS_PASSIVE 0x0000008 /* remote and does not do RIP */
#define IS_EXTERNAL 0x0000010 /* handled by EGP or something */
#define IS_CHECKED 0x0000020 /* still exists */
#define IS_ALL_HOSTS 0x0000040 /* in INADDR_ALLHOSTS_GROUP */
#define IS_ALL_ROUTERS 0x0000080 /* in INADDR_ALLROUTERS_GROUP */
#define IS_RIP_QUERIED 0x0000100 /* query broadcast */
#define IS_BROKE 0x0000200 /* seems to be broken */
#define IS_ACTIVE 0x0000400 /* heard from it at least once */
#define IS_QUIET 0x0000800 /* have not heard from it recently */
#define IS_NEED_NET_SUB 0x0001000 /* need RS_NET_SUB route */
#define IS_NO_AG 0x0002000 /* do not aggregate subnets */
#define IS_NO_SUPER_AG 0x0004000 /* do not aggregate networks */
#define IS_NO_RIPV1_IN 0x0008000 /* no RIPv1 input at all */
#define IS_NO_RIPV2_IN 0x0010000 /* no RIPv2 input at all */
#define IS_NO_RIP_IN (IS_NO_RIPV2_IN | IS_NO_RIPV2_IN)
#define IS_NO_RIPV1_OUT 0x0020000 /* no RIPv1 output at all */
#define IS_NO_RIPV2_OUT 0x0040000 /* no RIPv2 output at all */
#define IS_NO_RIP_OUT (IS_NO_RIPV1_OUT | IS_NO_RIPV2_OUT)
#define IS_NO_ADV_IN 0x0080000
#define IS_NO_SOL_OUT 0x0100000 /* no solicitations */
#define IS_SOL_OUT 0x0200000 /* send solicitations */
#define GROUP_IS_SOL (IS_NO_ADV_IN|IS_NO_SOL_OUT)
#define IS_NO_ADV_OUT 0x0400000 /* do not advertise rdisc */
#define IS_ADV_OUT 0x0800000 /* advertise rdisc */
#define GROUP_IS_ADV (IS_NO_ADV_OUT|IS_ADV_OUT)
#define IS_BCAST_RDISC 0x1000000 /* broadcast instead of multicast */
#ifdef sgi
#define IFF_UP_RUNNING (IFF_RUNNING|IFF_UP)
#else
#define IFF_UP_RUNNING IFF_UP
#endif
#define iff_alive(f) (((f) & IFF_UP_RUNNING) == IFF_UP_RUNNING)
/* Information for aggregating routes */
#define NUM_AG_SLOTS 32
struct ag_info {
struct ag_info *ag_fine; /* slot with finer netmask */
struct ag_info *ag_cors; /* more coarse netmask */
naddr ag_dst_h; /* destination in host byte order */
naddr ag_mask;
naddr ag_gate;
char ag_metric; /* metric to be advertised */
char ag_pref; /* aggregate based on this */
u_int ag_seqno;
u_short ag_tag;
u_short ag_state;
#define AGS_SUPPRESS 0x01 /* combine with coaser mask */
#define AGS_PROMOTE 0x002 /* synthesize combined routes */
#define AGS_REDUN0 0x004 /* redundant, finer routes output */
#define AGS_REDUN1 0x008
#define AG_IS_REDUN(state) (((state) & (AGS_REDUN0 | AGS_REDUN1)) \
== (AGS_REDUN0 | AGS_REDUN1))
#define AGS_GATEWAY 0x010 /* tell kernel RTF_GATEWAY */
#define AGS_RIPV2 0x020 /* send only as RIPv2 */
#define AGS_DEAD 0x080 /* dead--ignore differing gate */
#define AGS_RDISC 0x100 /* suppresses most routes */
};
/* parameters for interfaces */
extern struct parm {
struct parm *parm_next;
char parm_name[IFNAMSIZ+1];
naddr parm_a_h;
naddr parm_m;
char parm_d_metric;
u_int parm_int_state;
int parm_rdisc_pref;
int parm_rdisc_int;
char parm_passwd[RIP_AUTH_PW_LEN+1];
} *parms;
/* authority for internal networks */
extern struct intnet {
struct intnet *intnet_next;
naddr intnet_addr;
naddr intnet_mask;
} *intnets;
extern pid_t mypid;
extern naddr myaddr; /* main address of this system */
extern int stopint; /* !=0 to stop */
extern int sock_max;
extern int rip_sock; /* RIP socket */
extern struct interface *rip_sock_mcast; /* current multicast interface */
extern int rt_sock; /* routing socket */
extern int rt_sock_seqno;
extern int rdisc_sock; /* router-discovery raw socket */
extern int seqno; /* sequence number for messages */
extern int supplier; /* process should supply updates */
extern int default_gateway; /* 1=advertise default */
extern int lookforinterfaces; /* 1=probe for new up interfaces */
extern int supplier_set; /* -s or -q requested */
extern int ridhosts; /* 1=reduce host routes */
extern int ppp_noage; /* 1=do not age quiet link routes */
extern int mhome; /* 1=want multi-homed host route */
extern int advertise_mhome; /* 1=must continue adverising it */
extern int auth_ok; /* 1=ignore auth if we do not care */
extern struct timeval epoch; /* when started */
extern struct timeval now; /* current idea of time */
extern time_t now_stale;
extern time_t now_garbage;
extern struct timeval next_bcast; /* next general broadcast */
extern struct timeval age_timer; /* next check of old routes */
extern struct timeval no_flash; /* inhibit flash update until then */
extern struct timeval rdisc_timer; /* next advert. or solicitation */
extern int rdisc_ok; /* using solicited route */
extern struct timeval ifinit_timer; /* time to check interfaces */
extern naddr loopaddr; /* our address on loopback */
extern int tot_interfaces; /* # of remote and local interfaces */
extern int rip_interfaces; /* # of interfaces doing RIP */
extern struct interface *ifnet; /* all interfaces */
extern int have_ripv1; /* have a RIPv1 interface */
extern int need_flash; /* flash update needed */
extern struct timeval need_kern; /* need to update kernel table */
extern int update_seqno; /* a route has changed */
extern u_int tracelevel, new_tracelevel;
#define MAX_TRACELEVEL 3
#define TRACEPACKETS (tracelevel >= 2) /* note packets */
#define TRACECONTENTS (tracelevel >= 3) /* display packet contents */
#define TRACEACTIONS (tracelevel != 0)
extern FILE *ftrace; /* output trace file */
extern struct radix_node_head *rhead;
#ifdef sgi
/* Fix conflicts */
#define dup2(x,y) BSDdup2(x,y)
#endif /* sgi */
extern void fix_sock(int, char *);
extern void fix_select(void);
extern void rip_off(void);
extern void rip_on(struct interface *);
enum output_type {OUT_QUERY, OUT_UNICAST, OUT_BROADCAST, OUT_MULTICAST};
extern int output(enum output_type, struct sockaddr_in *,
struct interface *, struct rip *, int);
extern void rip_query(void);
extern void rip_bcast(int);
extern void supply(struct sockaddr_in *, struct interface *,
enum output_type, int, int);
extern void msglog(char *, ...);
#define LOGERR(msg) msglog(msg ": %s", strerror(errno))
extern void logbad(int, char *, ...);
#define BADERR(dump,msg) logbad(dump,msg ": %s", strerror(errno))
#ifdef DEBUG
#define DBGERR(dump,msg) BADERR(dump,msg)
#else
#define DBGERR(dump,msg) LOGERR(msg)
#endif
#ifdef MCAST_PPP_BUG
extern void mcasterr(struct interface *, int, char *);
#define MCASTERR(ifp,dump,msg) mcasterr(ifp, dump, "setsockopt(IP_"msg")")
#else
#define MCASTERR(ifp, dump,msg) DBGERR(dump,"setsockopt(IP_" msg ")")
#endif
extern char *naddr_ntoa(naddr);
extern char *saddr_ntoa(struct sockaddr *);
extern void timevaladd(struct timeval *, struct timeval *);
extern void intvl_random(struct timeval *, u_long, u_long);
extern int getnet(char *, naddr *, naddr *);
extern int gethost(char *, naddr *);
extern void gwkludge(void);
extern char *parse_parms(char *);
extern void get_parms(struct interface *);
extern void lastlog(void);
extern void trace_on(char *, int);
extern void trace_off(char*, char*);
extern void trace_flush(void);
extern void set_tracelevel(void);
extern void trace_msg(char *, ...);
extern void trace_add_del(char *, struct rt_entry *);
extern void trace_change(struct rt_entry *, u_int, naddr, naddr, int,
u_short, struct interface *, time_t, char *);
extern void trace_if(char *, struct interface *);
extern void trace_upslot(struct rt_entry *, struct rt_spare *,
naddr, naddr,
struct interface *, int, u_short, time_t);
extern void trace_rip(char*, char*, struct sockaddr_in *,
struct interface *, struct rip *, int);
extern char *addrname(naddr, naddr, int);
extern void rdisc_age(naddr);
extern void set_rdisc_mg(struct interface *, int);
extern void set_supplier(void);
extern void ifbad_rdisc(struct interface *);
extern void ifok_rdisc(struct interface *);
extern void read_rip(int, struct interface *);
extern void read_rt(void);
extern void read_d(void);
extern void rdisc_adv(void);
extern void rdisc_sol(void);
extern void sigalrm(int);
extern void sigterm(int);
extern void sigtrace_on(int);
extern void sigtrace_off(int);
extern void fix_kern(void);
extern void flush_kern(void);
extern void age(naddr);
extern void ag_flush(naddr, naddr, void (*)(struct ag_info *));
extern void ag_check(naddr, naddr, naddr, char, char, u_int,
u_short, u_short, void (*)(struct ag_info *));
extern void del_static(naddr, naddr, int);
extern void del_redirects(naddr, time_t);
extern struct rt_entry *rtget(naddr, naddr);
extern struct rt_entry *rtfind(naddr);
extern void rtinit(void);
extern void rtadd(naddr, naddr, naddr, naddr,
int, u_short, u_int, struct interface *);
extern void rtchange(struct rt_entry *, u_int, naddr,naddr, int, u_short,
struct interface *ifp, time_t, char *);
extern void rtdelete(struct rt_entry *);
extern void rtbad_sub(struct rt_entry *);
extern void rtswitch(struct rt_entry *, struct rt_spare *);
extern void rtbad(struct rt_entry *);
extern struct rt_addrinfo rtinfo;
#define S_ADDR(x) (((struct sockaddr_in *)(x))->sin_addr.s_addr)
#define RTINFO_DST rtinfo.rti_info[RTAX_DST]
#define RTINFO_GATE rtinfo.rti_info[RTAX_GATEWAY]
#define RTINFO_NETMASK rtinfo.rti_info[RTAX_NETMASK]
#define RTINFO_IFA rtinfo.rti_info[RTAX_IFA]
#define RTINFO_AUTHOR rtinfo.rti_info[RTAX_AUTHOR]
#define RTINFO_BRD rtinfo.rti_info[RTAX_BRD]
#define RTINFO_IFP ((struct sockaddr_dl *)rtinfo.rti_info[RTAX_IFP])
void rt_xaddrs(struct sockaddr *, struct sockaddr *, int);
extern naddr std_mask(naddr);
extern naddr ripv1_mask_net(naddr, struct interface *, struct interface *);
extern naddr ripv1_mask_host(naddr,struct interface *, struct interface *);
#define on_net(tgt, net, mask) ((ntohl(tgt) & mask) == (net & mask))
extern int check_dst(naddr);
#ifdef sgi
extern int sysctl(int *, u_int, void *, size_t *, void *, size_t);
#endif
extern void addrouteforif(register struct interface *);
extern void ifinit(void);
extern int walk_bad(struct radix_node *, struct walkarg *);
extern int ifok(struct interface *, char *);
extern void ifbad(struct interface *, char *);
extern struct interface *ifwithaddr(naddr, int, int);
extern struct interface *ifwithname(char *, naddr);
extern struct interface *ifwithindex(u_short);
extern struct interface *iflookup(naddr);

File diff suppressed because it is too large Load diff

View file

@ -35,328 +35,672 @@
static char sccsid[] = "@(#)input.c 8.1 (Berkeley) 6/5/93";
#endif /* not lint */
/*
* Routing Table Management Daemon
*/
#ident "$Revision: 1.1.3.1 $"
#include "defs.h"
#include <sys/syslog.h>
/*
* Process a newly received packet.
static void input(struct sockaddr_in *, struct interface*, struct rip *, int);
static void input_route(struct interface *, naddr,
naddr, naddr, naddr, int, u_short);
/* process RIP input
*/
rip_input(from, rip, size)
struct sockaddr *from;
register struct rip *rip;
int size;
void
read_rip(int sock,
struct interface *ifp)
{
register struct rt_entry *rt;
register struct netinfo *n;
register struct interface *ifp;
struct interface *if_ifwithdstaddr();
int count, changes = 0;
register struct afswitch *afp;
static struct sockaddr badfrom, badfrom2;
struct sockaddr_in from;
int fromlen, cc;
union pkt_buf inbuf;
ifp = 0;
TRACE_INPUT(ifp, from, (char *)rip, size);
if (from->sa_family >= af_max ||
(afp = &afswitch[from->sa_family])->af_hash == (int (*)())0) {
syslog(LOG_INFO,
"\"from\" address in unsupported address family (%d), cmd %d\n",
from->sa_family, rip->rip_cmd);
return;
for (;;) {
fromlen = sizeof(from);
cc = recvfrom(sock, &inbuf, sizeof(inbuf), 0,
(struct sockaddr*)&from, &fromlen);
if (cc <= 0) {
if (cc < 0 && errno != EWOULDBLOCK)
LOGERR("recvfrom(rip)");
break;
}
if (fromlen != sizeof(struct sockaddr_in))
logbad(1,"impossible recvfrom(rip) fromlen=%d",
fromlen);
input(&from,
(ifp != 0) ? ifp : iflookup(from.sin_addr.s_addr),
&inbuf.rip, cc);
}
}
/* Process a RIP packet
*/
static void
input(struct sockaddr_in *from, /* received from this IP address */
struct interface *ifp,
struct rip *rip,
int size)
{
# define FROM_NADDR from->sin_addr.s_addr
static naddr use_auth, bad_len, bad_mask;
static naddr unk_router, bad_router, bad_nhop;
struct rt_entry *rt;
struct netinfo *n, *lim;
struct interface *ifp1;
naddr gate, mask, v1_mask, dst, ddst_h;
int i;
if (ifp != 0)
ifp->int_state |= IS_ACTIVE;
if (TRACEPACKETS)
trace_rip("Recv", "from", from, ifp, rip, size);
if (rip->rip_vers == 0) {
syslog(LOG_ERR,
"RIP version 0 packet received from %s! (cmd %d)",
(*afswitch[from->sa_family].af_format)(from), rip->rip_cmd);
if (from->sin_addr.s_addr != bad_router)
msglog("RIP version 0, cmd %d, packet received"
" from %s",
rip->rip_cmd, naddr_ntoa(FROM_NADDR));
bad_router = from->sin_addr.s_addr;
return;
}
if (size > MAXPACKETSIZE) {
if (from->sin_addr.s_addr != bad_router)
msglog("packet at least %d bytes too long received"
" from %s",
size-MAXPACKETSIZE, naddr_ntoa(FROM_NADDR));
bad_router = from->sin_addr.s_addr;
return;
}
n = rip->rip_nets;
lim = (struct netinfo *)((char*)rip + size);
/* Notice authentication.
* As required by section 4.2 in RFC 1723, discard authenticated
* RIPv2 messages, but only if configured for that silliness.
*
* RIPv2 authentication is lame, since snooping on the wire makes
* its simple passwords evident. Also, why authenticate queries?
* Why should a RIPv2 implementation with authentication disabled
* not be able to listen to RIPv2 packets with authenication, while
* RIPv1 systems will listen? Crazy!
*/
if (!auth_ok
&& rip->rip_vers >= RIPv2
&& n < lim && n->n_family == RIP_AF_AUTH) {
if (from->sin_addr.s_addr != use_auth)
msglog("RIPv2 message with authentication"
" from %s discarded",
naddr_ntoa(FROM_NADDR));
use_auth = from->sin_addr.s_addr;
if (TRACEPACKETS)
trace_msg("discard authenticated RIPv2 message\n");
return;
}
switch (rip->rip_cmd) {
case RIPCMD_REQUEST:
n = rip->rip_nets;
count = size - ((char *)n - (char *)rip);
if (count < sizeof (struct netinfo))
return;
for (; count > 0; n++) {
if (count < sizeof (struct netinfo))
break;
count -= sizeof (struct netinfo);
#if BSD < 198810
if (sizeof(n->rip_dst.sa_family) > 1) /* XXX */
n->rip_dst.sa_family = ntohs(n->rip_dst.sa_family);
#else
#define osa(x) ((struct osockaddr *)(&(x)))
n->rip_dst.sa_family =
ntohs(osa(n->rip_dst)->sa_family);
n->rip_dst.sa_len = sizeof(n->rip_dst);
#endif
n->rip_metric = ntohl(n->rip_metric);
/*
* A single entry with sa_family == AF_UNSPEC and
* metric ``infinity'' means ``all routes''.
* We respond to routers only if we are acting
* as a supplier, or to anyone other than a router
* (eg, query).
/* did the request come from a router?
*/
if (from->sin_port == htons(RIP_PORT)) {
/* yes, ignore it if RIP is off
*/
if (n->rip_dst.sa_family == AF_UNSPEC &&
n->rip_metric == HOPCNT_INFINITY && count == 0) {
if (supplier || (*afp->af_portmatch)(from) == 0)
supply(from, 0, 0, 0);
if (rip_sock < 0) {
trace_msg("ignore request while RIP off");
return;
}
/* Ignore the request if we talking to ourself
* (and not a remote gateway).
*/
ifp1 = ifwithaddr(FROM_NADDR, 0, 0);
if (ifp1 != 0
&& (!(ifp1->int_state & IS_REMOTE)
|| ifp->int_metric != 0)) {
if (TRACEPACKETS)
trace_msg("discard our own packet\n");
return;
}
if (n->rip_dst.sa_family < af_max &&
afswitch[n->rip_dst.sa_family].af_hash)
rt = rtlookup(&n->rip_dst);
else
rt = 0;
#define min(a, b) (a < b ? a : b)
n->rip_metric = rt == 0 ? HOPCNT_INFINITY :
min(rt->rt_metric + 1, HOPCNT_INFINITY);
#if BSD < 198810
if (sizeof(n->rip_dst.sa_family) > 1) /* XXX */
n->rip_dst.sa_family = htons(n->rip_dst.sa_family);
#else
osa(n->rip_dst)->sa_family =
htons(n->rip_dst.sa_family);
#endif
n->rip_metric = htonl(n->rip_metric);
}
/* According to RFC 1723, we should ignore unathenticated
* queries. That is too silly to bother with.
*/
if (n >= lim
|| size%sizeof(*n) != sizeof(struct rip)%sizeof(*n)) {
if (from->sin_addr.s_addr != bad_len)
msglog("request of bad length (%d) from %s",
size, naddr_ntoa(FROM_NADDR));
bad_len = from->sin_addr.s_addr;
}
for (; n < lim; n++) {
n->n_metric = ntohl(n->n_metric);
/* A single entry with family RIP_AF_UNSPEC and
* metric HOPCNT_INFINITY means "all routes".
* We respond to routers only if we are acting
* as a supplier, or to anyone other than a router
* (i.e. a query).
*
* Answer a query from a stray program with all
* we know. Filter the answer to a query from a
* router in the about same way broadcasts are
* filtered.
*
* Only answer a router if we are a supplier
* to keep an unwary host that is just starting
* from picking us an a router.
*/
if (n->n_family == RIP_AF_UNSPEC
&& n->n_metric == HOPCNT_INFINITY
&& n == rip->rip_nets
&& n+1 == lim) {
if (from->sin_port != htons(RIP_PORT)) {
/* query */
supply(from, ifp,
OUT_QUERY, 0, rip->rip_vers);
} else if (supplier) {
supply(from, ifp,
OUT_UNICAST, 0, rip->rip_vers);
}
return;
}
if (n->n_family != RIP_AF_INET) {
if (from->sin_addr.s_addr != bad_router)
msglog("request from %s"
" for unsupported (af %d) %s",
naddr_ntoa(FROM_NADDR),
ntohs(n->n_family),
naddr_ntoa(n->n_dst));
bad_router = from->sin_addr.s_addr;
return;
}
dst = n->n_dst;
if (!check_dst(dst)) {
if (from->sin_addr.s_addr != bad_router)
msglog("bad queried destination"
" %s from %s",
naddr_ntoa(dst),
naddr_ntoa(FROM_NADDR));
bad_router = from->sin_addr.s_addr;
return;
}
if (rip->rip_vers == RIPv1) {
mask = ripv1_mask_host(dst,ifp,0);
} else {
mask = ntohl(n->n_mask);
if (mask == 0)
mask = ripv1_mask_host(dst,ifp,0);
}
rt = rtget(dst, mask);
if (!rt)
rt = rtfind(n->n_dst);
n->n_tag = 0;
n->n_nhop = 0;
if (!rt) {
n->n_metric = HOPCNT_INFINITY;
} else {
n->n_metric = (rt->rt_metric
+ (ifp ? ifp->int_metric : 1));
if (n->n_metric > HOPCNT_INFINITY)
n->n_metric = HOPCNT_INFINITY;
if (rip->rip_vers == RIPv1) {
n->n_mask = 0;
} else {
n->n_tag = rt->rt_tag;
if (!ifp
|| !on_net(rt->rt_gate,
ifp->int_net,
ifp->int_mask)
|| rt->rt_gate != ifp->int_addr)
n->n_nhop = 0;
else
n->n_nhop = rt->rt_gate;
}
}
HTONL(n->n_metric);
}
/* Answer about specific routes.
* Only answer a router if we are a supplier
* to keep an unwary host that is just starting
* from picking us an a router.
*/
rip->rip_cmd = RIPCMD_RESPONSE;
bcopy((char *)rip, packet, size);
(*afp->af_output)(s, 0, from, size);
rip->rip_res1 = 0;
if (rip->rip_vers != RIPv1)
rip->rip_vers = RIPv2;
if (from->sin_port != htons(RIP_PORT)) {
/* query */
(void)output(OUT_QUERY, from, ifp, rip, size);
} else if (supplier) {
(void)output(OUT_UNICAST, from, ifp, rip, size);
}
return;
case RIPCMD_TRACEON:
case RIPCMD_TRACEOFF:
/* verify message came from a privileged port */
if ((*afp->af_portcheck)(from) == 0)
return;
if ((ifp = if_iflookup(from)) == 0 || (ifp->int_flags &
(IFF_BROADCAST | IFF_POINTOPOINT | IFF_REMOTE)) == 0 ||
ifp->int_flags & IFF_PASSIVE) {
syslog(LOG_ERR, "trace command from unknown router, %s",
(*afswitch[from->sa_family].af_format)(from));
if (ntohs(from->sin_port) > IPPORT_RESERVED) {
msglog("trace command from untrusted port on %s",
naddr_ntoa(FROM_NADDR));
return;
}
((char *)rip)[size] = '\0';
if (rip->rip_cmd == RIPCMD_TRACEON)
traceon(rip->rip_tracefile);
else
traceoff();
if (ifp == 0) {
msglog("trace command from unknown router %s",
naddr_ntoa(FROM_NADDR));
return;
}
if (rip->rip_cmd == RIPCMD_TRACEON) {
rip->rip_tracefile[size-4] = '\0';
trace_on(rip->rip_tracefile, 0);
} else {
trace_off("tracing turned off by ",
naddr_ntoa(FROM_NADDR));
}
return;
case RIPCMD_RESPONSE:
if (size%sizeof(*n) != sizeof(struct rip)%sizeof(*n)) {
if (from->sin_addr.s_addr != bad_len)
msglog("response of bad length (%d) from %s",
size, naddr_ntoa(FROM_NADDR));
bad_len = from->sin_addr.s_addr;
}
/* verify message came from a router */
if ((*afp->af_portmatch)(from) == 0)
if (from->sin_port != ntohs(RIP_PORT)) {
if (TRACEPACKETS)
trace_msg("discard response"
" from unknown port\n");
return;
(*afp->af_canon)(from);
/* are we talking to ourselves? */
ifp = if_ifwithaddr(from);
if (ifp) {
if (ifp->int_flags & IFF_PASSIVE) {
syslog(LOG_ERR,
"bogus input (from passive interface, %s)",
(*afswitch[from->sa_family].af_format)(from));
}
if (rip_sock < 0) {
if (TRACEPACKETS)
trace_msg("discard response while RIP off");
return;
}
/* Are we talking to ourself or a remote gateway?
*/
ifp1 = ifwithaddr(FROM_NADDR, 0, 1);
if (ifp1) {
if (ifp1->int_state & IS_PASSIVE) {
msglog("bogus input from %s on supposedly"
" passive interface %s",
naddr_ntoa(FROM_NADDR),
ifp1->int_name);
} else if (ifp1->int_state & IS_REMOTE) {
ifp1->int_act_time = now.tv_sec;
if (ifok(ifp1, "remote "))
addrouteforif(ifp1);
} else if (TRACEPACKETS) {
trace_msg("discard our own packet\n");
}
return;
}
/* Check the router from which message originated. We accept
* routing packets from routers directly connected via
* broadcast or point-to-point networks, and from
* those listed in /etc/gateways.
*/
if (!ifp || (ifp->int_state & IS_PASSIVE)) {
if (from->sin_addr.s_addr != unk_router)
msglog("packet from unknown router %s",
naddr_ntoa(FROM_NADDR));
unk_router = from->sin_addr.s_addr;
return;
}
/* Check required version
*/
if (((ifp->int_state & IS_NO_RIPV1_IN)
&& rip->rip_vers == RIPv1)
|| ((ifp->int_state & IS_NO_RIPV2_IN)
&& rip->rip_vers != RIPv1)) {
if (TRACEPACKETS)
trace_msg("discard RIPv%d response\n",
rip->rip_vers);
return;
}
/* Ignore routes via dead interface.
*/
if (ifp->int_state & IS_BROKE) {
if (TRACEPACKETS)
trace_msg("discard response via"
" broken interface %s\n",
ifp->int_name);
return;
}
/* Authenticate the packet.
*/
if (ifp->int_passwd[0] != '\0'
&& (n >= lim
|| n->n_family != RIP_AF_AUTH
|| ((struct netauth*)n)->a_type != RIP_AUTH_PW
|| 0 != bcmp(((struct netauth*)n)->au.au_pw,
ifp->int_passwd,
sizeof(ifp->int_passwd)))) {
if (from->sin_addr.s_addr != use_auth)
msglog("missing authentication from %s",
naddr_ntoa(FROM_NADDR));
use_auth = from->sin_addr.s_addr;
return;
}
for (; n < lim; n++) {
if (n->n_family == RIP_AF_AUTH)
continue;
NTOHL(n->n_metric);
dst = n->n_dst;
if (n->n_family != RIP_AF_INET
&& (n->n_family != RIP_AF_UNSPEC
|| dst != RIP_DEFAULT)) {
if (from->sin_addr.s_addr != bad_router)
msglog("route from %s to unsupported"
" address family %d,"
" destination %s",
naddr_ntoa(FROM_NADDR),
n->n_family,
naddr_ntoa(dst));
bad_router = from->sin_addr.s_addr;
continue;
}
if (!check_dst(dst)) {
if (from->sin_addr.s_addr != bad_router)
msglog("bad destination %s from %s",
naddr_ntoa(dst),
naddr_ntoa(FROM_NADDR));
bad_router = from->sin_addr.s_addr;
return;
}
rt = rtfind(from);
if (rt == 0 || ((rt->rt_state & RTS_INTERFACE) == 0) &&
rt->rt_metric >= ifp->int_metric)
addrouteforif(ifp);
else
rt->rt_timer = 0;
return;
}
/*
* Update timer for interface on which the packet arrived.
* If from other end of a point-to-point link that isn't
* in the routing tables, (re-)add the route.
*/
if ((rt = rtfind(from)) &&
(rt->rt_state & (RTS_INTERFACE | RTS_REMOTE)))
rt->rt_timer = 0;
else if ((ifp = if_ifwithdstaddr(from)) &&
(rt == 0 || rt->rt_metric >= ifp->int_metric))
addrouteforif(ifp);
/*
* "Authenticate" router from which message originated.
* We accept routing packets from routers directly connected
* via broadcast or point-to-point networks,
* and from those listed in /etc/gateways.
*/
if ((ifp = if_iflookup(from)) == 0 || (ifp->int_flags &
(IFF_BROADCAST | IFF_POINTOPOINT | IFF_REMOTE)) == 0 ||
ifp->int_flags & IFF_PASSIVE) {
if (bcmp((char *)from, (char *)&badfrom,
sizeof(badfrom)) != 0) {
syslog(LOG_ERR,
"packet from unknown router, %s",
(*afswitch[from->sa_family].af_format)(from));
badfrom = *from;
}
return;
}
size -= 4 * sizeof (char);
n = rip->rip_nets;
for (; size > 0; size -= sizeof (struct netinfo), n++) {
if (size < sizeof (struct netinfo))
break;
#if BSD < 198810
if (sizeof(n->rip_dst.sa_family) > 1) /* XXX */
n->rip_dst.sa_family =
ntohs(n->rip_dst.sa_family);
#else
n->rip_dst.sa_family =
ntohs(osa(n->rip_dst)->sa_family);
n->rip_dst.sa_len = sizeof(n->rip_dst);
#endif
n->rip_metric = ntohl(n->rip_metric);
if (n->rip_dst.sa_family >= af_max ||
(afp = &afswitch[n->rip_dst.sa_family])->af_hash ==
(int (*)())0) {
syslog(LOG_INFO,
"route in unsupported address family (%d), from %s (af %d)\n",
n->rip_dst.sa_family,
(*afswitch[from->sa_family].af_format)(from),
from->sa_family);
continue;
}
if (((*afp->af_checkhost)(&n->rip_dst)) == 0) {
syslog(LOG_DEBUG,
"bad host in route from %s (af %d)\n",
(*afswitch[from->sa_family].af_format)(from),
from->sa_family);
continue;
}
if (n->rip_metric == 0 ||
(unsigned) n->rip_metric > HOPCNT_INFINITY) {
if (bcmp((char *)from, (char *)&badfrom2,
sizeof(badfrom2)) != 0) {
syslog(LOG_ERR,
"bad metric (%d) from %s\n",
n->rip_metric,
(*afswitch[from->sa_family].af_format)(from));
badfrom2 = *from;
}
continue;
}
/*
* Adjust metric according to incoming interface.
*/
if ((unsigned) n->rip_metric < HOPCNT_INFINITY)
n->rip_metric += ifp->int_metric;
if ((unsigned) n->rip_metric > HOPCNT_INFINITY)
n->rip_metric = HOPCNT_INFINITY;
rt = rtlookup(&n->rip_dst);
if (rt == 0 ||
(rt->rt_state & (RTS_INTERNAL|RTS_INTERFACE)) ==
(RTS_INTERNAL|RTS_INTERFACE)) {
/*
* If we're hearing a logical network route
* back from a peer to which we sent it,
* ignore it.
*/
if (rt && rt->rt_state & RTS_SUBNET &&
(*afp->af_sendroute)(rt, from))
continue;
if ((unsigned)n->rip_metric < HOPCNT_INFINITY) {
/*
* Look for an equivalent route that
* includes this one before adding
* this route.
*/
rt = rtfind(&n->rip_dst);
if (rt && equal(from, &rt->rt_router))
continue;
rtadd(&n->rip_dst, from, n->rip_metric, 0);
changes++;
}
continue;
if (n->n_metric == 0
|| n->n_metric > HOPCNT_INFINITY) {
if (from->sin_addr.s_addr != bad_router)
msglog("bad metric %d from %s"
" for destination %s",
n->n_metric,
naddr_ntoa(FROM_NADDR),
naddr_ntoa(dst));
bad_router = from->sin_addr.s_addr;
return;
}
/*
* Update if from gateway and different,
* shorter, or equivalent but old route
* is getting stale.
/* Notice the next-hop.
*/
if (equal(from, &rt->rt_router)) {
if (n->rip_metric != rt->rt_metric) {
rtchange(rt, from, n->rip_metric);
changes++;
rt->rt_timer = 0;
if (rt->rt_metric >= HOPCNT_INFINITY)
rt->rt_timer =
GARBAGE_TIME - EXPIRE_TIME;
} else if (rt->rt_metric < HOPCNT_INFINITY)
rt->rt_timer = 0;
} else if ((unsigned) n->rip_metric < rt->rt_metric ||
(rt->rt_metric == n->rip_metric &&
rt->rt_timer > (EXPIRE_TIME/2) &&
(unsigned) n->rip_metric < HOPCNT_INFINITY)) {
rtchange(rt, from, n->rip_metric);
changes++;
rt->rt_timer = 0;
gate = from->sin_addr.s_addr;
if (n->n_nhop != 0
&& rip->rip_vers == RIPv2) {
/* Ignore the route if it points to us */
if (0 != ifwithaddr(n->n_nhop, 1, 0))
continue;
/* Use it only if it is valid. */
if (on_net(n->n_nhop,
ifp->int_net, ifp->int_mask)
&& check_dst(n->n_nhop)) {
gate = n->n_nhop;
} else {
if (bad_nhop != from->sin_addr.s_addr)
msglog("router %s to %s has"
" bad next hop %s",
naddr_ntoa(FROM_NADDR),
naddr_ntoa(dst),
naddr_ntoa(n->n_nhop));
bad_nhop = from->sin_addr.s_addr;
}
}
mask = ntohl(n->n_mask);
if (rip->rip_vers == RIPv1 || mask == 0) {
mask = ripv1_mask_host(dst,ifp,0);
} else if ((ntohl(dst) & ~mask) != 0) {
if (bad_mask != from->sin_addr.s_addr) {
msglog("router %s sent bad netmask"
" %#x with %s",
naddr_ntoa(FROM_NADDR),
mask,
naddr_ntoa(dst));
bad_mask = from->sin_addr.s_addr;
}
continue;
}
v1_mask = (have_ripv1
? ripv1_mask_host(dst,0,0)
: mask);
if (rip->rip_vers == RIPv1)
n->n_tag = 0;
/* Adjust metric according to incoming interface.
*/
n->n_metric += ifp->int_metric;
if (n->n_metric > HOPCNT_INFINITY)
n->n_metric = HOPCNT_INFINITY;
/* Recognize and ignore a default route we faked
* which is being sent back to us by a machine with
* broken split-horizon.
*/
if (ifp->int_d_metric != 0
&& dst == RIP_DEFAULT
&& n->n_family == RIP_AF_UNSPEC
&& n->n_metric > ifp->int_d_metric)
continue;
/* We can receive aggregated RIPv2 routes via one
* interface that must be broken down before
* they are transmitted by RIPv1 via an interface
* on a subnet. We might receive the same routes
* aggregated otherwise via other RIPv2 interfaces.
* This could cause duplicate routes to be sent on
* the RIPv1 interfaces. "Longest matching variable
* length netmasks" lets RIPv2 listeners understand,
* but breaking down the aggregated routes for RIPv1
* listeners can produce duplicate routes.
*
* Breaking down aggregated routes here bloats
* the daemon table, but does not hurt the kernel
* table, since routes are always aggregated for
* the kernel.
*
* Notice that this does not break down network
* routes corresponding to subnets. This is part
* of the defense against RS_NET_SUB.
*/
if (0 != (ntohl(dst) & (v1_mask & ~mask))) {
ddst_h = v1_mask & -v1_mask;
i = (v1_mask & ~mask)/ddst_h;
if (i >= 1024) {
/* Punt if we would have to generate
* an unreasonable number of routes.
*/
#ifdef DEBUG
msglog("accept %s from %s as-is"
" instead of as %d routes",
addrname(dst,mask,0),
naddr_ntoa(FROM_NADDR), i);
#endif
i = 0;
} else {
mask = v1_mask;
}
} else {
i = 0;
}
for (;;) {
input_route(ifp, FROM_NADDR,
dst, mask, gate,
n->n_metric, n->n_tag);
if (i-- == 0)
break;
dst = htonl(ntohl(dst) + ddst_h);
}
}
break;
}
/*
* If changes have occurred, and if we have not sent a broadcast
* recently, send a dynamic update. This update is sent only
* on interfaces other than the one on which we received notice
* of the change. If we are within MIN_WAITTIME of a full update,
* don't bother sending; if we just sent a dynamic update
* and set a timer (nextbcast), delay until that time.
* If we just sent a full update, delay the dynamic update.
* Set a timer for a randomized value to suppress additional
* dynamic updates until it expires; if we delayed sending
* the current changes, set needupdate.
*/
if (changes && supplier &&
now.tv_sec - lastfullupdate.tv_sec < SUPPLY_INTERVAL-MAX_WAITTIME) {
u_long delay;
extern long random();
if (now.tv_sec - lastbcast.tv_sec >= MIN_WAITTIME &&
timercmp(&nextbcast, &now, <)) {
if (traceactions)
fprintf(ftrace, "send dynamic update\n");
toall(supply, RTS_CHANGED, ifp);
lastbcast = now;
needupdate = 0;
nextbcast.tv_sec = 0;
} else {
needupdate++;
if (traceactions)
fprintf(ftrace, "delay dynamic update\n");
}
#define RANDOMDELAY() (MIN_WAITTIME * 1000000 + \
(u_long)random() % ((MAX_WAITTIME - MIN_WAITTIME) * 1000000))
if (nextbcast.tv_sec == 0) {
delay = RANDOMDELAY();
if (traceactions)
fprintf(ftrace,
"inhibit dynamic update for %d usec\n",
delay);
nextbcast.tv_sec = delay / 1000000;
nextbcast.tv_usec = delay % 1000000;
timevaladd(&nextbcast, &now);
/*
* If the next possibly dynamic update
* is within MIN_WAITTIME of the next full update,
* force the delay past the full update,
* or we might send a dynamic update just before
* the full update.
*/
if (nextbcast.tv_sec > lastfullupdate.tv_sec +
SUPPLY_INTERVAL - MIN_WAITTIME)
nextbcast.tv_sec = lastfullupdate.tv_sec +
SUPPLY_INTERVAL + 1;
}
}
}
/* Process a single input route.
*/
static void
input_route(struct interface *ifp,
naddr from,
naddr dst,
naddr mask,
naddr gate,
int metric,
u_short tag)
{
int i;
struct rt_entry *rt;
struct rt_spare *rts, *rts0;
struct interface *ifp1;
time_t new_time;
/* See if the other guy is telling us to send our packets to him.
* Sometimes network routes arrive over a point-to-point link for
* the network containing the address(es) of the link.
*
* If our interface is broken, switch to using the other guy.
*/
ifp1 = ifwithaddr(dst, 1, 1);
if (ifp1 != 0
&& !(ifp1->int_state & IS_BROKE))
return;
/* Look for the route in our table.
*/
rt = rtget(dst, mask);
/* Consider adding the route if we do not already have it.
*/
if (rt == 0) {
/* Usually ignore routes being poisoned.
*/
if (metric == HOPCNT_INFINITY)
return;
rtadd(dst, mask, gate, from, metric, tag, 0, ifp);
return;
}
/* We already know about the route. Consider
* this update.
*
* If (rt->rt_state & RS_NET_SUB), then this route
* is the same as a network route we have inferred
* for subnets we know, in order to tell RIPv1 routers
* about the subnets.
*
* It is impossible to tell if the route is coming
* from a distant RIPv2 router with the standard
* netmask because that router knows about the entire
* network, or if it is a round-about echo of a
* synthetic, RIPv1 network route of our own.
* The worst is that both kinds of routes might be
* received, and the bad one might have the smaller
* metric. Partly solve this problem by faking the
* RIPv1 route with a metric that reflects the most
* distant part of the subnet. Also never
* aggregate into such a route. Also keep it
* around as long as the interface exists.
*/
rts0 = rt->rt_spares;
for (rts = rts0, i = NUM_SPARES; i != 0; i--, rts++) {
if (rts->rts_router == from)
break;
/* Note the worst slot to reuse,
* other than the current slot.
*/
if (rts0 == rt->rt_spares
|| BETTER_LINK(rts0, rts))
rts0 = rts;
}
if (i != 0) {
/* Found the router
*/
int old_metric = rts->rts_metric;
if (old_metric < HOPCNT_INFINITY) {
new_time = now.tv_sec;
} else {
/* Keep poisoned routes around only long
* enough to pass the poison on.
*/
new_time = rts->rts_time;
if (new_time > now.tv_sec-POISON_SECS)
new_time = now.tv_sec-POISON_SECS;
}
/* If this is an update for the router we currently prefer,
* then note it.
*/
if (i == NUM_SPARES) {
rtchange(rt,rt->rt_state, gate,rt->rt_router,
metric, tag, ifp, new_time, 0);
/* If the route got worse, check for something better.
*/
if (metric > old_metric)
rtswitch(rt, 0);
return;
}
/* This is an update for a spare route.
* Finished if the route is unchanged.
*/
if (rts->rts_gate == gate
&& old_metric == metric
&& rts->rts_tag == tag) {
rts->rts_time = new_time;
return;
}
} else {
/* The update is for a route we know about,
* but not from a familiar router.
*/
rts = rts0;
/* Save the route as a spare only if it has
* a better metric than our worst spare.
* This also ignores poisoned routes (those
* with metric HOPCNT_INFINITY).
*/
if (metric >= rts->rts_metric)
return;
new_time = now.tv_sec;
}
if (TRACEACTIONS)
trace_upslot(rt, rts, gate, from, ifp, metric, tag, new_time);
rts->rts_gate = gate;
rts->rts_router = from;
rts->rts_metric = metric;
rts->rts_tag = tag;
rts->rts_time = new_time;
rts->rts_ifp = ifp;
/* try to switch to a better route */
rtswitch(rt, rts);
}

File diff suppressed because it is too large Load diff

View file

@ -35,138 +35,714 @@
static char sccsid[] = "@(#)output.c 8.1 (Berkeley) 6/5/93";
#endif /* not lint */
/*
* Routing Table Management Daemon
*/
#ident "$Revision: 1.1.3.1 $"
#include "defs.h"
/*
* Apply the function "f" to all non-passive
* interfaces. If the interface supports the
* use of broadcasting use it, otherwise address
* the output to the known router.
*/
toall(f, rtstate, skipif)
int (*f)();
int rtstate;
struct interface *skipif;
{
register struct interface *ifp;
register struct sockaddr *dst;
register int flags;
extern struct interface *ifnet;
for (ifp = ifnet; ifp; ifp = ifp->int_next) {
if (ifp->int_flags & IFF_PASSIVE || ifp == skipif)
continue;
dst = ifp->int_flags & IFF_BROADCAST ? &ifp->int_broadaddr :
ifp->int_flags & IFF_POINTOPOINT ? &ifp->int_dstaddr :
&ifp->int_addr;
flags = ifp->int_flags & IFF_INTERFACE ? MSG_DONTROUTE : 0;
(*f)(dst, flags, ifp, rtstate);
int update_seqno;
/* walk the tree of routes with this for output
*/
struct {
struct sockaddr_in to;
naddr to_mask;
naddr to_net;
naddr to_std_mask;
naddr to_std_net;
struct interface *ifp; /* usually output interface */
struct ws_buf { /* for each buffer */
struct rip *buf;
struct netinfo *n;
struct netinfo *base;
struct netinfo *lim;
enum output_type type;
} v2, mcast;
char metric; /* adjust metrics by interface */
int npackets;
int state;
#define WS_ST_FLASH 0x01 /* send only changed routes */
#define WS_ST_RIP2_SAFE 0x02 /* send RIPv2 safe for RIPv1 */
#define WS_ST_RIP2_ALL 0x04 /* full featured RIPv2 */
#define WS_ST_AG 0x08 /* ok to aggregate subnets */
#define WS_ST_SUPER_AG 0x10 /* ok to aggregate networks */
#define WS_ST_QUERY 0x20 /* responding to a query */
#define WS_ST_TO_ON_NET 0x40 /* sending onto one of our nets */
#define WS_ST_DEFAULT 0x80 /* faking a default */
} ws;
/* A buffer for what can be heard by both RIPv1 and RIPv2 listeners */
union pkt_buf ripv2_buf;
/* Another for only RIPv2 listeners */
union pkt_buf rip_mcast_buf;
/* Send the contents of the global buffer via the non-multicast socket
*/
int /* <0 on failure */
output(enum output_type type,
struct sockaddr_in *dst, /* send to here */
struct interface *ifp,
struct rip *buf,
int size) /* this many bytes */
{
struct sockaddr_in sin;
int flags;
char *msg;
int res, serrno;
naddr tgt_mcast;
int soc;
sin = *dst;
if (sin.sin_port == 0)
sin.sin_port = htons(RIP_PORT);
#ifdef _HAVE_SIN_LEN
if (sin.sin_len == 0)
sin.sin_len = sizeof(sin);
#endif
soc = rip_sock;
flags = 0;
switch (type) {
case OUT_QUERY:
msg = "Answer Query";
if (soc < 0)
soc = ifp->int_rip_sock;
break;
case OUT_UNICAST:
msg = "Send";
if (soc < 0)
soc = ifp->int_rip_sock;
flags = MSG_DONTROUTE;
break;
case OUT_BROADCAST:
if (ifp->int_if_flags & IFF_POINTOPOINT) {
msg = "Send pt-to-pt";
} else {
msg = "Send";
}
flags = MSG_DONTROUTE;
break;
case OUT_MULTICAST:
if (ifp->int_if_flags & IFF_POINTOPOINT) {
msg = "Send pt-to-pt";
} else {
msg = "Send mcast";
if (rip_sock_mcast != ifp) {
#ifdef MCAST_PPP_BUG
/* Do not specifiy the primary interface
* explicitly if we have the multicast
* point-to-point kernel bug, since the
* kernel will do the wrong thing if the
* local address of a point-to-point link
* is the same as the address of an ordinary
* interface.
*/
if (ifp->int_addr == myaddr) {
tgt_mcast = 0;
} else
#endif
tgt_mcast = ifp->int_addr;
if (setsockopt(rip_sock,
IPPROTO_IP, IP_MULTICAST_IF,
&tgt_mcast, sizeof(tgt_mcast)))
BADERR(1,"setsockopt(rip_sock,"
"IP_MULTICAST_IF)");
rip_sock_mcast = ifp;
}
sin.sin_addr.s_addr = htonl(INADDR_RIP_GROUP);
}
}
if (TRACEPACKETS)
trace_rip(msg, "to", &sin, ifp, buf, size);
res = sendto(soc, buf, size, flags,
(struct sockaddr *)&sin, sizeof(sin));
if (res < 0) {
serrno = errno;
msglog("sendto(%s%s%s.%d): %s",
ifp != 0 ? ifp->int_name : "",
ifp != 0 ? ", " : "",
inet_ntoa(sin.sin_addr),
ntohs(sin.sin_port),
strerror(errno));
errno = serrno;
}
return res;
}
/* install authentication if appropriate
*/
static void
set_auth(struct ws_buf *w)
{
if (ws.ifp != 0
&& ws.ifp->int_passwd[0] != '\0'
&& (ws.state & WS_ST_RIP2_SAFE)) {
w->n->n_family = RIP_AF_AUTH;
((struct netauth*)w->n)->a_type = RIP_AUTH_PW;
bcopy(ws.ifp->int_passwd, ((struct netauth*)w->n)->au.au_pw,
sizeof(((struct netauth*)w->n)->au.au_pw));
w->n++;
}
}
/*
* Output a preformed packet.
*/
/*ARGSUSED*/
sndmsg(dst, flags, ifp, rtstate)
struct sockaddr *dst;
int flags;
struct interface *ifp;
int rtstate;
{
(*afswitch[dst->sa_family].af_output)(s, flags,
dst, sizeof (struct rip));
TRACE_OUTPUT(ifp, dst, sizeof (struct rip));
/* Send the buffer
*/
static void
supply_write(struct ws_buf *w)
{
/* Output multicast only if legal.
* If we would multcast and it would be illegal, then discard the
* packet.
*/
if (w != &ws.mcast
|| ((ws.state & WS_ST_RIP2_SAFE)
&& (ws.ifp == 0
|| (ws.ifp->int_if_flags & IFF_MULTICAST)))) {
if (output(w->type, &ws.to, ws.ifp, w->buf,
((char *)w->n - (char*)w->buf)) < 0
&& ws.ifp != 0)
ifbad(ws.ifp, 0);
ws.npackets++;
}
bzero(w->n = w->base, sizeof(*w->n)*NETS_LEN);
if (w->buf->rip_vers == RIPv2)
set_auth(w);
}
/*
* Supply dst with the contents of the routing tables.
/* put an entry into the packet
*/
static void
supply_out(struct ag_info *ag)
{
int i;
naddr mask, v1_mask, s_mask, dst_h, ddst_h;
struct ws_buf *w;
/* Skip this route if doing a flash update and it and the routes
* it aggregates have not changed recently.
*/
if (ag->ag_seqno <= update_seqno
&& (ws.state & WS_ST_FLASH))
return;
dst_h = ag->ag_dst_h;
mask = ag->ag_mask;
v1_mask = ripv1_mask_host(htonl(dst_h),
(ws.state & WS_ST_TO_ON_NET) ? ws.ifp : 0,
0);
s_mask = std_mask(htonl(dst_h));
i = 0;
/* If we are sending RIPv2 packets that cannot (or must not) be
* heard by RIPv1 listeners, do not worry about sub- or supernets.
* Subnets (from other networks) can only be sent via multicast.
*/
if ((ws.state & WS_ST_RIP2_ALL)
|| ((ag->ag_state & AGS_RIPV2)
&& v1_mask != mask)) {
w = &ws.mcast; /* use the multicast-only buffer */
} else {
w = &ws.v2;
/* Convert supernet route into corresponding set of network
* routes for RIPv1, but leave non-contiguous netmasks
* to ag_check().
*/
if (v1_mask > mask
&& mask + (mask & -mask) == 0) {
ddst_h = v1_mask & -v1_mask;
i = (v1_mask & ~mask)/ddst_h;
if (i >= 1024) {
/* Punt if we would have to generate an
* unreasonable number of routes.
*/
#ifdef DEBUG
msglog("sending %s to %s as-is instead"
" of as %d routes",
addrname(htonl(dst_h),mask,0),
naddr_ntoa(ws.to.sin_addr.s_addr), i);
#endif
i = 0;
} else {
mask = v1_mask;
}
}
}
do {
w->n->n_family = RIP_AF_INET;
w->n->n_dst = htonl(dst_h);
w->n->n_metric = stopint ? HOPCNT_INFINITY : ag->ag_metric;
HTONL(w->n->n_metric);
if (w->buf->rip_vers == RIPv2) {
w->n->n_nhop = ag->ag_gate;
if ((ws.state & WS_ST_RIP2_ALL)
|| mask != s_mask)
w->n->n_mask = htonl(mask);
w->n->n_tag = ag->ag_tag;
}
dst_h += ddst_h;
if (++w->n >= w->lim)
supply_write(w);
} while (i-- != 0);
}
/* supply one route from the table
*/
/* ARGSUSED */
static int
walk_supply(struct radix_node *rn,
struct walkarg *w)
{
#define RT ((struct rt_entry *)rn)
u_short ags;
char metric, pref;
naddr dst, gate;
/* Do not advertise the loopback interface
* or external remote interfaces
*/
if (RT->rt_ifp != 0
&& ((RT->rt_ifp->int_if_flags & IFF_LOOPBACK)
|| (RT->rt_ifp->int_state & IS_EXTERNAL)))
return 0;
/* Do not send a route back to where it came from, except in
* response to a query. This is "split-horizon".
*
* That means not advertising back to the same network
* and so via the same interface.
*/
if (RT->rt_ifp == ws.ifp && ws.ifp != 0
&& !(ws.state & WS_ST_QUERY)
&& (ws.state & WS_ST_TO_ON_NET)
&& !(RT->rt_state & RS_IF))
return 0;
dst = RT->rt_dst;
/* If being quiet about our ability to forward, then
* do not say anything except our own host number,
* unless responding to a query.
*/
if (!supplier
&& (!mhome || myaddr != dst)
&& !(ws.state & WS_ST_QUERY))
return 0;
ags = 0;
/* do not override the fake default route */
if (dst == RIP_DEFAULT
&& (ws.state & WS_ST_DEFAULT))
return 0;
if (RT_ISHOST(RT)) {
/* We should always aggregate the host routes
* for the local end of our point-to-point links.
* If we are suppressing host routes, then do so.
*/
if ((RT->rt_state & RS_LOCAL)
|| ridhosts)
ags |= AGS_SUPPRESS;
} else if (ws.state & WS_ST_AG) {
/* Aggregate network routes, if we are allowed.
*/
ags |= AGS_SUPPRESS;
/* Generate supernets if allowed.
* If we can be heard by RIPv1 systems, we will
* later convert back to ordinary nets. This unifies
* dealing with received supernets.
*/
if ((RT->rt_state & RS_SUBNET)
|| (ws.state & WS_ST_SUPER_AG))
ags |= AGS_PROMOTE;
}
/* Never aggregate our own interfaces,
* or the host route for multi-homed servers.
*/
if (0 != (RT->rt_state & (RS_IF | RS_MHOME)))
ags &= ~(AGS_SUPPRESS | AGS_PROMOTE);
if (RT->rt_state & RS_SUBNET) {
/* Do not send authority routes into the subnet,
* or when RIP is off.
*/
if ((RT->rt_state & RS_NET_INT)
&& (on_net(dst, ws.to_net, ws.to_mask)
|| rip_sock < 0))
return 0;
/* Do not send RIPv1 advertisements of subnets to
* other networks.
*
* If possible, multicast them by RIPv2.
*/
if (!(ws.state & WS_ST_RIP2_ALL)
&& !on_net(dst, ws.to_std_net, ws.to_std_mask))
ags |= AGS_RIPV2;
} else if (RT->rt_state & RS_NET_SUB) {
/* do not send synthetic network routes if no RIPv1
* listeners might hear.
*/
if (ws.state & WS_ST_RIP2_ALL)
return 0;
/* Do not send synthetic network routes on the real subnet */
if (on_net(dst, ws.to_std_net, ws.to_std_mask))
return 0;
}
/* forget synthetic routes when RIP is off */
if (rip_sock < 0 && 0 != (RT->rt_state & RS_NET_S))
return 0;
/* Adjust outgoing metric by the cost of the link.
* Interface routes have already been adjusted.
*/
pref = metric = RT->rt_metric + ws.metric;
if (metric >= HOPCNT_INFINITY) {
metric = HOPCNT_INFINITY;
pref = ((RT->rt_hold_down > now.tv_sec)
? RT->rt_hold_metric
: metric);
}
/* Advertise the next hop if this is not a route for one
* of our interfaces and the next hop is on the same
* network as the target.
*/
if ((ws.state & WS_ST_RIP2_SAFE)
&& !(RT->rt_state & RS_IF)
&& ((ws.state & WS_ST_QUERY)
|| (on_net(RT->rt_gate, ws.ifp->int_net, ws.ifp->int_mask)
&& RT->rt_gate != ws.ifp->int_addr))) {
gate = RT->rt_gate;
} else {
gate = 0;
}
ag_check(dst, RT->rt_mask, gate, metric, pref,
RT->rt_seqno, RT->rt_tag, ags, supply_out);
return 0;
#undef RT
}
/* Supply dst with the contents of the routing tables.
* If this won't fit in one packet, chop it up into several.
*/
supply(dst, flags, ifp, rtstate)
struct sockaddr *dst;
int flags;
register struct interface *ifp;
int rtstate;
void
supply(struct sockaddr_in *dst,
struct interface *ifp, /* output interface */
enum output_type type,
int flash, /* 1=flash update */
int vers) /* RIP version */
{
register struct rt_entry *rt;
register struct netinfo *n = msg->rip_nets;
register struct rthash *rh;
struct rthash *base = hosthash;
int doinghost = 1, size;
int (*output)() = afswitch[dst->sa_family].af_output;
int (*sendroute)() = afswitch[dst->sa_family].af_sendroute;
int npackets = 0;
static int init = 1;
struct rt_entry *rt;
int metric;
msg->rip_cmd = RIPCMD_RESPONSE;
msg->rip_vers = RIPVERSION;
bzero(msg->rip_res1, sizeof(msg->rip_res1));
again:
for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++)
for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
/*
* Don't resend the information on the network
* from which it was received (unless sending
* in response to a query).
ws.state = 0;
ws.to = *dst;
ws.to_std_mask = std_mask(ws.to.sin_addr.s_addr);
ws.to_std_net = ntohl(ws.to.sin_addr.s_addr) & ws.to_std_mask;
if (ifp != 0) {
ws.to_mask = ifp->int_mask;
ws.to_net = ifp->int_net;
if (on_net(ws.to.sin_addr.s_addr, ws.to_net, ws.to_mask))
ws.state |= WS_ST_TO_ON_NET;
} else {
ws.to_mask = ripv1_mask_net(ws.to.sin_addr.s_addr, 0, 0);
ws.to_net = ntohl(ws.to.sin_addr.s_addr) & ws.to_mask;
rt = rtfind(dst->sin_addr.s_addr);
if (rt)
ifp = rt->rt_ifp;
}
ws.npackets = 0;
if (flash)
ws.state |= WS_ST_FLASH;
if (type == OUT_QUERY)
ws.state |= WS_ST_QUERY;
if ((ws.ifp = ifp) == 0) {
ws.metric = 0;
} else {
/* Adjust the advertised metric by the outgoing interface
* metric, but reduced by 1 to avoid counting this hop
* twice.
*/
if (ifp && rt->rt_ifp == ifp &&
(rt->rt_state & RTS_INTERFACE) == 0)
continue;
if (rt->rt_state & RTS_EXTERNAL)
continue;
/*
* For dynamic updates, limit update to routes
* with the specified state.
*/
if (rtstate && (rt->rt_state & rtstate) == 0)
continue;
/*
* Limit the spread of subnet information
* to those who are interested.
*/
if (doinghost == 0 && rt->rt_state & RTS_SUBNET) {
if (rt->rt_dst.sa_family != dst->sa_family)
continue;
if ((*sendroute)(rt, dst) == 0)
continue;
ws.metric = ifp->int_metric;
if (ws.metric > 0)
ws.metric--;
}
if (init) {
init = 0;
bzero(&ripv2_buf, sizeof(ripv2_buf));
ripv2_buf.rip.rip_cmd = RIPCMD_RESPONSE;
ws.v2.buf = &ripv2_buf.rip;
ws.v2.base = &ws.v2.buf->rip_nets[0];
ws.v2.lim = ws.v2.base + NETS_LEN;
bzero(&rip_mcast_buf, sizeof(rip_mcast_buf));
rip_mcast_buf.rip.rip_cmd = RIPCMD_RESPONSE;
rip_mcast_buf.rip.rip_vers = RIPv2;
ws.mcast.buf = &rip_mcast_buf.rip;
ws.mcast.base = &ws.mcast.buf->rip_nets[0];
ws.mcast.lim = ws.mcast.base + NETS_LEN;
}
ripv2_buf.rip.rip_vers = vers;
ws.v2.type = type;
ws.v2.n = ws.v2.base;
set_auth(&ws.v2);
ws.mcast.type = (type == OUT_BROADCAST) ? OUT_MULTICAST : type;
ws.mcast.n = ws.mcast.base;
set_auth(&ws.mcast);
if (vers == RIPv2) {
ws.state |= WS_ST_RIP2_SAFE;
/* full RIPv2 only if cannot be heard by RIPv1 listeners */
if (type != OUT_BROADCAST)
ws.state |= WS_ST_RIP2_ALL;
if (!(ws.state & WS_ST_TO_ON_NET)) {
ws.state |= (WS_ST_AG | WS_ST_SUPER_AG);
} else if (ws.ifp == 0 || !(ws.ifp->int_state & IS_NO_AG)) {
ws.state |= WS_ST_AG;
if (type != OUT_BROADCAST
&& (ws.ifp == 0
|| !(ws.ifp->int_state & IS_NO_SUPER_AG)))
ws.state |= WS_ST_SUPER_AG;
}
size = (char *)n - packet;
if (size > MAXPACKETSIZE - sizeof (struct netinfo)) {
TRACE_OUTPUT(ifp, dst, size);
(*output)(s, flags, dst, size);
/*
* If only sending to ourselves,
* one packet is enough to monitor interface.
*/
if (ifp && (ifp->int_flags &
(IFF_BROADCAST | IFF_POINTOPOINT | IFF_REMOTE)) == 0)
return;
n = msg->rip_nets;
npackets++;
}
n->rip_dst = rt->rt_dst;
#if BSD < 198810
if (sizeof(n->rip_dst.sa_family) > 1) /* XXX */
n->rip_dst.sa_family = htons(n->rip_dst.sa_family);
}
/* send the routes
*/
if ((metric = ifp->int_d_metric) != 0) {
/* Fake a default route if asked */
ws.state |= WS_ST_DEFAULT;
/* Use the metric of a real default, if there is one.
*/
rt = rtget(RIP_DEFAULT, 0);
if (rt != 0
&& rt->rt_metric+ws.metric < metric)
metric = rt->rt_metric+ws.metric;
if (metric < HOPCNT_INFINITY)
ag_check(0, 0, 0, metric,metric, 0, 0, 0, supply_out);
}
(void)rn_walktree(rhead, walk_supply, 0);
ag_flush(0,0,supply_out);
/* Flush the packet buffers */
if (ws.v2.n != ws.v2.base
&& (ws.v2.n > ws.v2.base+1
|| ws.v2.n->n_family != RIP_AF_AUTH))
supply_write(&ws.v2);
if (ws.mcast.n != ws.mcast.base
&& (ws.mcast.n > ws.mcast.base+1
|| ws.mcast.n->n_family != RIP_AF_AUTH))
supply_write(&ws.mcast);
/* If we sent nothing and this is an answer to a query, send
* an empty buffer.
*/
if (ws.npackets == 0
&& (ws.state & WS_ST_QUERY))
supply_write(&ws.v2);
}
/* send all of the routing table or just do a flash update
*/
void
rip_bcast(int flash)
{
#ifdef _HAVE_SIN_LEN
static struct sockaddr_in dst = {sizeof(dst), AF_INET};
#else
#define osa(x) ((struct osockaddr *)(&(x)))
osa(n->rip_dst)->sa_family = htons(n->rip_dst.sa_family);
static struct sockaddr_in dst = {AF_INET};
#endif
n->rip_metric = htonl(rt->rt_metric);
n++;
struct interface *ifp;
enum output_type type;
int vers;
struct timeval rtime;
need_flash = 0;
intvl_random(&rtime, MIN_WAITTIME, MAX_WAITTIME);
no_flash = rtime;
timevaladd(&no_flash, &now);
if (rip_sock < 0)
return;
trace_msg("send %s and inhibit dynamic updates for %.3f sec\n",
flash ? "dynamic update" : "all routes",
rtime.tv_sec + ((float)rtime.tv_usec)/1000000.0);
for (ifp = ifnet; ifp != 0; ifp = ifp->int_next) {
/* skip interfaces not doing RIP, those already queried,
* and aliases. Do try broken interfaces to see
* if they have healed.
*/
if (0 != (ifp->int_state & (IS_PASSIVE
| IS_ALIAS)))
continue;
/* skip turned off interfaces */
if (!iff_alive(ifp->int_if_flags))
continue;
/* Prefer RIPv1 announcements unless RIPv2 is on and
* RIPv2 is off.
*/
if (ifp->int_state & IS_NO_RIPV1_OUT) {
if (ifp->int_state & IS_NO_RIPV2_OUT)
continue;
vers = RIPv2;
} else {
vers = RIPv1;
}
if (ifp->int_if_flags & IFF_BROADCAST) {
/* ordinary, hardware interface */
dst.sin_addr.s_addr = ifp->int_brdaddr;
/* if RIPv1 is not turned off, then broadcast so
* that RIPv1 listeners can hear.
*/
if (!(ifp->int_state & IS_NO_RIPV1_OUT)) {
type = OUT_BROADCAST;
} else {
type = OUT_MULTICAST;
}
} else if (ifp->int_if_flags & IFF_POINTOPOINT) {
/* point-to-point hardware interface */
dst.sin_addr.s_addr = ifp->int_dstaddr;
type = OUT_UNICAST;
} else {
/* remote interface */
dst.sin_addr.s_addr = ifp->int_addr;
type = OUT_UNICAST;
}
supply(&dst, ifp, type, flash, vers);
}
if (doinghost) {
doinghost = 0;
base = nethash;
goto again;
}
if (n != msg->rip_nets || (npackets == 0 && rtstate == 0)) {
size = (char *)n - packet;
TRACE_OUTPUT(ifp, dst, size);
(*output)(s, flags, dst, size);
update_seqno++; /* all routes are up to date */
}
/* Ask for routes
* Do it only once to an interface, and not even after the interface
* was broken and recovered.
*/
void
rip_query(void)
{
#ifdef _HAVE_SIN_LEN
static struct sockaddr_in dst = {sizeof(dst), AF_INET};
#else
static struct sockaddr_in dst = {AF_INET};
#endif
struct interface *ifp;
struct rip buf;
enum output_type type;
if (rip_sock < 0)
return;
bzero(&buf, sizeof(buf));
for (ifp = ifnet; ifp; ifp = ifp->int_next) {
/* skip interfaces not doing RIP, those already queried,
* and aliases. Do try broken interfaces to see
* if they have healed.
*/
if (0 != (ifp->int_state & (IS_RIP_QUERIED
| IS_PASSIVE
| IS_ALIAS)))
continue;
/* skip turned off interfaces */
if (!iff_alive(ifp->int_if_flags))
continue;
/* prefer RIPv2 queries */
if (ifp->int_state & IS_NO_RIPV2_OUT) {
if (ifp->int_state & IS_NO_RIPV1_OUT)
continue;
buf.rip_vers = RIPv1;
} else {
buf.rip_vers = RIPv2;
}
buf.rip_cmd = RIPCMD_REQUEST;
buf.rip_nets[0].n_family = RIP_AF_UNSPEC;
buf.rip_nets[0].n_metric = htonl(HOPCNT_INFINITY);
if (ifp->int_if_flags & IFF_BROADCAST) {
/* ordinary, hardware interface */
dst.sin_addr.s_addr = ifp->int_brdaddr;
/* if RIPv1 is not turned off, then broadcast so
* that RIPv1 listeners can hear.
*/
if (!(ifp->int_state & IS_NO_RIPV1_OUT)) {
type = OUT_BROADCAST;
} else {
type = OUT_MULTICAST;
}
} else if (ifp->int_if_flags & IFF_POINTOPOINT) {
/* point-to-point hardware interface */
dst.sin_addr.s_addr = ifp->int_dstaddr;
type = OUT_UNICAST;
} else {
/* remote interface */
dst.sin_addr.s_addr = ifp->int_addr;
type = OUT_UNICAST;
}
ifp->int_state |= IS_RIP_QUERIED;
if (output(type, &dst, ifp, &buf, sizeof(buf)) < 0)
ifbad(ifp,0);
}
}

View file

@ -36,3 +36,8 @@
#include <paths.h>
#define _PATH_GATEWAYS "/etc/gateways"
/* all remotely requested trace files must either start with this prefix
* or be the same as the tracefile specified when the daemon was started.
*/
#define _PATH_TRACE "/tmp"

View file

@ -31,314 +31,483 @@
.\"
.\" @(#)routed.8 8.2 (Berkeley) 12/11/93
.\"
.Dd December 11, 1993
.Dd March 1, 1996
.Dt ROUTED 8
.Os BSD 4.2
.Os BSD 4.4
.Sh NAME
.Nm routed
.Nd network routing daemon
.Sh SYNOPSIS
.Nm routed
.Op Fl d
.Op Fl g
.Op Fl q
.Op Fl s
.Op Fl t
.Op Ar logfile
.Nm
.Op Fl sqdghmpAt
.Op Fl T Ar tracefile
.Oo
.Fl F
.Ar net Ns Op /mask Ns Op ,metric
.Oc
.OP Fl P Ar parms
.Sh DESCRIPTION
.Nm Routed
is invoked at boot time to manage the network routing tables.
The routing daemon uses a variant of the Xerox NS Routing
Information Protocol in maintaining up to date kernel routing
table entries.
It used a generalized protocol capable of use with multiple
address types, but is currently used only for Internet routing
within a cluster of networks.
is a dameon invoked at boot time to manage the network
routing tables.
It uses Routing Information Protocol, RIPv1 (RFC\ 1058),
RIPv2 (RFC\ 1723),
and Internet Router Discovery Protocol (RFC 1256)
to maintain the kernel routing table.
The version of the RIPv1 protocol implemented
is based on the RIPv1 protocol implemented in the reference 4.3BSD daemon.
.Pp
In normal operation
.Nm routed
listens on the
It listens on the
.Xr udp 4
socket for the
.Xr route 8
service (see
.Xr services 5 )
for routing information packets. If the host is an
internetwork router, it periodically supplies copies
of its routing tables to any directly connected hosts
and networks.
for Routing Information Protocol packets.
It also sends and receives multicast Router Discovery ICMP messages.
If the host is an router,
.Nm
periodically supplies copies
of its routing tables to any directly connected hosts and networks.
It also advertise or solicits default routes using Router Discovery
ICMP messages.
.Pp
When
.Nm routed
is started, it uses the
.Dv SIOCGIFCONF
.Xr ioctl 2
to find those
When started (or when a network interface is later turned on),
.Nm
uses an AF_ROUTE address family facility to find those
directly connected interfaces configured into the
system and marked ``up'' (the software loopback interface
is ignored). If multiple interfaces
are present, it is assumed that the host will forward packets
between networks.
.Nm Routed
then transmits a
.Em request
packet on each interface (using a broadcast packet if
the interface supports it) and enters a loop, listening
for
system and marked "up".
It adds necessary routes for the interfaces
to the kernel routing table.
Soon after being first started, and provided there is at least one
interface on which RIP has not been disabled,
.Nm
deletes all pre-existing
non-static routes in kernel table.
Static routes in the kernel table are preserved and
included in RIP responses if they have a valid RIP metric
(see
.Xr route 8 ).
.Pp
If more than one interface is present (not counting the loopback interface),
it is assumed that the host should forward packets among the
connected networks.
After transmitting a RIP
.Em request
and
.Em response
packets from other hosts.
Router Discovery Advertisements or Solicitations on a new interface,
the daemon enters a loop, listening for
RIP request and response and Router Discover packets from other hosts.
.Pp
When a
.Em request
packet is received,
.Nm routed
packet is received,
.Nm
formulates a reply based on the information maintained in its
internal tables. The
internal tables.
The
.Em response
packet generated contains a list of known routes, each marked
with a ``hop count'' metric (a count of 16, or greater, is
considered ``infinite''). The metric associated with each
route returned provides a metric
.Em relative to the sender .
with a "hop count" metric (a count of 16 or greater is
considered "infinite").
Advertised metrics reflect the metric associated with interface
(see
.Xr ifconfig 8 ),
so setting the metric on an interface
is an effective way to steer traffic.
.Pp
.Em Response
packets received by
.Nm routed
are used to update the routing tables if one of the following
conditions is satisfied:
.Bl -enum
.It
No routing table entry exists for the destination network
or host, and the metric indicates the destination is ``reachable''
(i.e. the hop count is not infinite).
.It
The source host of the packet is the same as the router in the
existing routing table entry. That is, updated information is
being received from the very internetwork router through which
packets for the destination are being routed.
.It
The existing entry in the routing table has not been updated for
some time (defined to be 90 seconds) and the route is at least
as cost effective as the current route.
.It
The new route describes a shorter route to the destination than
the one currently stored in the routing tables; the metric of
the new route is compared against the one stored in the table
to decide this.
.El
Responses do not contain routes with a first hop on the resquesting
network to implement
.Em split-horizon .
Requests from query programs
such as
.Xr rtquery 8
are answered with the complete table.
.Pp
The routing table maintained by the daemon
includes space for several gateways for each destination
to speed recovery from a failing router.
RIP
.Em response
packets received are used to update the routing tables provided they are
from one of the several currently recognized gateways or
advertise a better metric than at least one of the existing
gateways.
.Pp
When an update is applied,
.Nm routed
records the change in its internal tables and updates the kernel
routing table.
The change is reflected in the next
.Nm
records the change in its own tables and updates the kernel routing table
if the best route to the destination changes.
The change in the kernel routing tableis reflected in the next batch of
.Em response
packet sent.
packets sent.
If the next response is not scheduled for a while, a
.Em flash update
response containing only recently changed routes is sent.
.Pp
In addition to processing incoming packets,
.Nm routed
.Nm
also periodically checks the routing table entries.
If an entry has not been updated for 3 minutes, the entry's metric
is set to infinity and marked for deletion. Deletions are delayed
an additional 60 seconds to insure the invalidation is propagated
throughout the local internet.
is set to infinity and marked for deletion.
Deletions are delayed until the route has been advertised with
an infinite metric to insure the invalidation
is propagated throughout the local internet.
This is a form of
.Em poison reverse .
.Pp
Routes in the kernel table that are added or changed as a result
of ICMP Redirect messages are deleted after a while to minimze
.Em black-holes .
When a TCP connection suffers a timeout,
the kernel tells
.Nm routed ,
which deletes all redirected routes
through the gateway involved, advances the age of all RIP routes through
the gateway to allow an alternate to be chosen, and advances of the
age of any relevant Router Discovery Protocol default routes.
.Pp
Hosts acting as internetwork routers gratuitously supply their
routing tables every 30 seconds to all directly connected hosts
and networks.
The response is sent to the broadcast address on nets capable of that function,
The response is sent to the broadcast address on nets that support
broadcasting,
to the destination address on point-to-point links, and to the router's
own address on other networks.
The normal routing tables are bypassed when sending gratuitous responses.
The reception of responses on each network is used to determine that the
network and interface are functioning correctly.
If no response is received on an interface, another route may be chosen
to route around the interface, or the route may be dropped if no alternative
is available.
If RIPv2 is enabled, multicast packets are sent on interfaces that
support multicasting.
.Pp
If no response is received on a remote interface, if there are errors
while sending responses,
or if there are more errors than input or output (see
.Xr netstat 8 ),
then the cable or some other part of the interface is assumed to be
disconnected or broken, and routes are adjusted appropriately.
.Pp
The
.Em Internet Router Discovery Protocol
is handled similarly.
When the daemon is supplying RIP routes, it also listens for
Router Discovery Solicitations and sends Advertisements.
When it is quiet and only listening to other RIP routers, it
sends Solicitations and listens for Advertisements.
If it receives
a good Advertisement, it stops listening for broadcast or multicast
RIP responses.
It tracks several advertising routers to speed recovery when the
currently chosen router dies.
If all discovered routers disappear,
the daemon resumes listening to RIP responses.
.Pp
Options supported by
.Nm routed :
.Bl -tag -width Ds
.It Fl d
Enable additional debugging information to be logged,
such as bad packets received.
.It Fl g
This flag is used on internetwork routers to offer a route
to the ``default'' destination.
This is typically used on a gateway to the Internet,
or on a gateway that uses another routing protocol whose routes
are not reported to other local routers.
.It Fl s
Supplying this
option forces
.Nm routed
to supply routing information whether it is acting as an internetwork
router or not.
This is the default if multiple network interfaces are present,
or if a point-to-point link is in use.
this option forces
.Nm
to supply routing information.
This is the default if multiple network interfaces are present on which
RIP or Router Discovery have not been disabled, and if the kernel switch
ipforwarding=1.
.It Fl q
This
is the opposite of the
.Fl s
option.
.It Fl d
Do not run in the background.
This option is meant for interactive use.
.It Fl g
This flag is used on internetwork routers to offer a route
to the "default" destination.
This is typically used on a gateway to the Internet,
or on a gateway that uses another routing protocol whose routes
are not reported to other local routers.
.It Fl h
This causes host or point-to-point routes to not be advertised,
provided there is a network route going the same direction.
That is a limited kind of aggregation.
This option is useful on gateways to ethernets that have other gateway
machines connected with point-to-point links such as SLIP.
.It Fl m
This causes the machine to advertise a host or point-to-point route to
its primary interface.
It is useful on multi-homed machines such as NFS servers.
This option should not be used except when the cost of
the host routes it generates is justified by the popularity of
the server.
It is effective only when the machine is supplying
routing information, because there is more than one interface.
The
.Fl m
option overrides the
.Fl q
option to the limited extent of advertising the host route.
.It Fl p
causes routes received over point-to-point links to not be timed
out while the link is idle.
This is handy for "demand dialed" PPP links that filter routing packets.
.It Fl A
do not ignore RIPv2 authentication if we do not care about RIPv2
authentication.
This option is required for conformance wiht RFC 1723,
but it makes little sense and breaks using RIP as a discovery protocol
to ignore all RIPv2 packets that carry authentication when this machine
does not care about authentication.
.It Fl T Ar tracefile
increases the debugging level to at least 1 and
causes debugging information to be appended to the file.
.It Fl t
If the
.Fl t
option is specified, all packets sent or received are
printed on the standard output. In addition,
.Nm routed
will not divorce itself from the controlling terminal
so that interrupts from the keyboard will kill the process.
increases the debugging level, which causes more information to be logged
on the tracefile specified with
.Fl T
or standard out.
The debugging level can be increased or decreased
with the
.Em SIGUSR1
or
.Em SIGUSR2
signals.
.It Fl F Ar net[/mask][,metric]
minimize routes in transmissions to network
.Em net/mask ,
and synthesizes a default route to this machine with the
.Em metric .
The intent is to reduce RIP traffic on slow, point-to-point links
such as PPP links by replacing many large UDP packets of RIP information
with a single, small packet containing a "fake" default route.
If
.Em metric
is absent, a value of 14 is assumed to limit
the spread of the "fake" default route.
.It Fl P Ar parms
is equivalent to adding the parameter
line
.Em parms
to the
.Pa /etc/gateways
file.
.El
.Pp
Any other argument supplied is interpreted as the name
of file in which
.Nm routed Ns \'s
actions should be logged. This log contains information
about any changes to the routing tables and, if not tracing all packets,
a history of recent messages sent and received which are related to
the changed route.
of a file in which the actions of
.Nm
should be logged.
It is better to use
.Fl T
instead of
appending the name of the trace file to the command.
.Pp
In addition to the facilities described above,
.Nm routed
supports the notion of ``distant''
.Nm
also supports the notion of
"distant"
.Em passive
and
or
.Em active
gateways. When
.Nm routed
is started up, it reads the file
gateways.
When
.Nm
is started, it reads the file
.Pa /etc/gateways
to find gateways which may not be located using
only information from the
.Dv SIOCGIFCONF
.Xr ioctl 2 .
to find such distant gateways which may not be located using
only information from a routing socket, to discover if some
of the local gateways are
.Em passive ,
and to obtain other parameters.
Gateways specified in this manner should be marked passive
if they are not expected to exchange routing information,
while gateways marked active
should be willing to exchange routing information (i.e.
they should have a
.Nm routed
process running on the machine).
Routes through passive gateways are installed in the
kernel's routing tables once upon startup.
Such routes are not included in
any routing information transmitted.
Active gateways are treated equally to network
interfaces. Routing information is distributed
to the gateway and if no routing information is
received for a period of time, the associated
route is deleted.
should be willing to exchange RIP packets.
Routes through
.Em passive
gateways are installed in the
kernel's routing tables once upon startup and are not included in
transmitted RIP responses.
.Pp
Distant active gateways are treated like network interfaces.
RIP responses are sent
to the distant
.Em active
gateway and if no responses are received
in turn for a period of the time, the associated route deleted from
the kernel table and RIP responses advertised via other interfaces.
If the distant gateway resumes sending RIP responses, the associated
route is restored.
.Pp
Such gateways can be useful on media that do not support broadcasts
or multicasts but otherwise act like classic shared media like
Ethernets such as some ATM networks.
One can list all RIP routers reachable on the ATM network in
.Pa /etc/gateways
with a series of
"host" lines.
.Pp
Gateways marked
.Em external
are also passive, but are not placed in the kernel
routing table nor are they included in routing updates.
The function of external entries is to inform
.Nm routed
The function of external entries is to indicate
that another routing process
will install such a route, and that alternate routes to that destination
should not be installed.
will install such a route if ncessary,
and that alternate routes to that destination should not be installed
by
.Nm routed .
Such entries are only required when both routers may learn of routes
to the same destination.
.Pp
The
.Pa /etc/gateways
is comprised of a series of lines, each in
the following format:
The
.Em /etc/gateways
file is comprised of a series of lines, each in
one of the following formats or consist of parameters described below:
.Pp
.Bd -ragged
.Pf < Cm net No \&|
.Cm host Ns >
.Ar name1
.Cm net
.Ar Nname[/mask]
.Cm gateway
.Ar name2
.Ar Gname
.Cm metric
.Ar value
.Pf < Cm passive No \&|
.Cm external Ns >
.Cm active No \&|
.Cm extern Ns >
.Ed
.Bd -ragged
.Cm host
.Ar Hname
.Cm gateway
.Ar Gname
.Cm metric
.Ar value
.Pf < Cm passive No \&|
.Cm active No \&|
.Cm extern Ns >
.Ed
.Pp
The
.Cm net
.Ar Nname
or
.Cm host
keyword indicates if the route is to a network or specific host.
.Pp
.Ar Name1
is the name of the destination network or host. This may be a
symbolic name located in
.Ar Hname
is the name of the destination network or host.
It may be a symbolic network name or an Internet address
specified in "dot" notation (see
.Xr inet 3 ).
(If it is a name, then it must either be defined in
.Pa /etc/networks
or
.Pa /etc/hosts
(or, if started after
.Pa /etc/hosts ,
or
.Xr named 8 ,
known to the name server),
or an Internet address specified in ``dot'' notation; see
.Xr inet 3 .
must have been started before
.Xr routed Ns .)
.Pp
.Ar Name2
is the name or address of the gateway to which messages should
.Ar mask
is an optional number between 1 and 32 indicating the netmask associated
with
.Ar Nname .
.Pp
.Ar Gname
is the name or address of the gateway to which RIP responses should
be forwarded.
.Pp
.Ar Value
is a metric indicating the hop count to the destination host
or network.
is the hop count to the destination host or network.
.Ar " host hname "
is equivalent to
.Ar " net nname/32 ".
.Pp
One of the keywords
.Cm passive
.Cm passive ,
.Cm active
or
.Cm external
indicates if the gateway should be treated as
must be present to indicate whether the gateway should be treated as
.Em passive
or
.Em active
(as described above),
or whether the gateway is
.Em external
to the scope of the
.Nm routed
protocol.
to the scope of the RIP protocol.
.Pp
Lines that start with neither "net" nor "host" must consist of one
or more of the following parameter settings:
.Bl -tag -width Ds
.It Cm if Ns \&= Ns Ar ifname
indicates that the other parameters on the line apply to the interface
name
.Ar ifname .
.It Cm subnet Ns \&= Ns Ar nname[/mask]
causes other routes to be aggregated as if a compatible route to
Ar nname/mask
had been received.
This is useful for filling "holes" in CIDR allocations.
This parameter must appear by itself on a line.
.It Cm passwd Ns \&= Ns Ar XXX
specifies a RIPv2 password that will be included on all RIPv2
responses sent and checked on all RIPv2 responses received.
.It Cm no_ag
turns off aggregation of subnets in RIPv1 and RIPv2 responses.
.It Cm no_super_ag
turns off aggregation of networks into supernets in RIPv2 responses.
.It Cm no_rip
disables all RIP processing on the specified interface.
If no interfaces are allowed to process RIP packets,
.Nm
acts purely as a router discovery daemon.
.Ar " No_rip "
is equivalent to
.Ar " no_ripv1_in no_ripv2_in no_ripv1_out no_ripv2_out ."
.It Cm no_ripv1_in
causes RIPv1 received responses to be ignored.
.It Cm no_ripv2_in
causes RIPv2 received responses to be ignored.
.It Cm ripv2_out
disables the RIPv2 responses that are otherwise multicast containing
information that cannot be sent in RIPv2 packets.
.It Cm no_rdisc
disables the Internet Router Discovery Protocol.
.It Cm no_solicit
disables the tranmission of Router Discovery Solicitations.
.It Cm send_solicit
specifies that Router Discovery solicitations should be sent,
even on point-to-point links,
which by default only listen to Router Discovery messages.
.It Cm no_rdisc_adv
disables the transmission of Router Discovery Advertisements
.It Cm rdisc_adv
specifies that Router Discovery advertisements should be sent,
even on point-to-point links,
which by default only listen to Router Discovery messages
.It Cm bcast_rdisc
specifies that Router Discovery packets should be broadcast instead of
multicast.
.It Cm rdisc_pref Ns \&= Ns Ar N
sets the preference in Router Discovery Advertisements to the integer
.Ar N .
.It Cm rdisc_interval Ns \&= Ns Ar N
sets the nominal interval with which Router Discovery Advertisements
are transmitted to N seconds and their lifetime to 3*N.
.It Cm fake_default Ns \&= Ns Ar metric
has an identical effect to
.Fl F Ar net[/mask][,metric]
with the network and mask coming from the affected interface.
.El
.Pp
.Sh FILES
.Bl -tag -width /etc/gateways -compact
.It Pa /etc/gateways
for distant gateways
.El
.Sh SEE ALSO
.Xr gated 8 ,
.Xr udp 4 ,
.Xr icmp 4 ,
.Xr IPXrouted 8
.\" .Xr XNSrouted 8 ,
.\" .Xr htable 8
.Xr htable 8 ,
.Xr rtquery 8 .
.Rs
.%T Internet Transport Protocols
.%R XSIS 028112
.%Q Xerox System Integration Standard
.Re
.Sh BUGS
The kernel's routing tables may not correspond to those of
.Nm routed
when redirects change or add routes.
.Nm Routed
should note any redirects received by reading
the
.Tn ICMP
packets received via a raw socket.
.Pp
.Nm Routed
should incorporate other routing protocols,
such as Xerox
.Tn \&NS
.Pq Xr XNSrouted 8
and
.Tn EGP .
Using separate processes for each requires configuration options
to avoid redundant or competing routes.
.Pp
.Nm Routed
should listen to intelligent interfaces, such as an
.Tn IMP ,
to gather more information.
It does not always detect unidirectional failures in network interfaces
(e.g., when the output side fails).
.Sh HISTORY

View file

@ -35,392 +35,679 @@
static char sccsid[] = "@(#)trace.c 8.1 (Berkeley) 6/5/93";
#endif /* not lint */
/*
* Routing Table Management Daemon
*/
#ident "$Revision: 1.1.3.1 $"
#define RIPCMDS
#include "defs.h"
#include "pathnames.h"
#include <sys/stat.h>
#include <sys/signal.h>
#include <fcntl.h>
#include <stdlib.h>
#include "pathnames.h"
#ifdef sgi
/* use *stat64 for files on large filesystems */
#define stat stat64
#endif
#define NRECORDS 50 /* size of circular trace buffer */
#ifdef DEBUG
FILE *ftrace = stdout;
int traceactions = 0;
u_int tracelevel, new_tracelevel;
FILE *ftrace = stdout; /* output trace file */
char *tracelevel_msg = "";
char savetracename[MAXPATHLEN+1];
char *
naddr_ntoa(naddr a)
{
#define NUM_BUFS 4
static int i;
static struct {
char str[16]; /* xxx.xxx.xxx.xxx\0 */
} bufs[NUM_BUFS];
struct in_addr addr;
char *s;
addr.s_addr = a;
s = strcpy(bufs[i].str, inet_ntoa(addr));
i = (i+1) % NUM_BUFS;
return s;
}
char *
saddr_ntoa(struct sockaddr *sa)
{
return (sa == 0) ? "?" : naddr_ntoa(S_ADDR(sa));
}
static char *
ts(time_t secs) {
static char s[20];
secs += epoch.tv_sec;
#ifdef sgi
(void)cftime(s, "%T", &secs);
#else
bcopy(ctime(&secs)+11, s, 8);
s[8] = '\0';
#endif
static struct timeval lastlog;
static char *savetracename;
traceinit(ifp)
register struct interface *ifp;
{
static int iftraceinit();
if (iftraceinit(ifp, &ifp->int_input) &&
iftraceinit(ifp, &ifp->int_output))
return;
tracehistory = 0;
fprintf(stderr, "traceinit: can't init %s\n", ifp->int_name);
return s;
}
static
iftraceinit(ifp, ifd)
struct interface *ifp;
register struct ifdebug *ifd;
{
register struct iftrace *t;
ifd->ifd_records =
(struct iftrace *)malloc(NRECORDS * sizeof (struct iftrace));
if (ifd->ifd_records == 0)
return (0);
ifd->ifd_front = ifd->ifd_records;
ifd->ifd_count = 0;
for (t = ifd->ifd_records; t < ifd->ifd_records + NRECORDS; t++) {
t->ift_size = 0;
t->ift_packet = 0;
/* On each event, display a time stamp.
* This assumes that 'now' is update once for each event, and
* that at least now.tv_usec changes.
*/
void
lastlog(void)
{
static struct timeval last;
if (last.tv_sec != now.tv_sec
|| last.tv_usec != now.tv_usec) {
(void)fprintf(ftrace, "--- %s ---\n", ts(now.tv_sec));
last = now;
}
ifd->ifd_if = ifp;
return (1);
}
traceon(file)
char *file;
{
struct stat stbuf;
if (ftrace != NULL)
return;
if (stat(file, &stbuf) >= 0 && (stbuf.st_mode & S_IFMT) != S_IFREG)
return;
savetracename = file;
(void) gettimeofday(&now, (struct timezone *)NULL);
ftrace = fopen(file, "a");
if (ftrace == NULL)
return;
dup2(fileno(ftrace), 1);
dup2(fileno(ftrace), 2);
traceactions = 1;
fprintf(ftrace, "Tracing enabled %s\n", ctime((time_t *)&now.tv_sec));
static void
tmsg(char *msg1, char* msg2)
{
if (ftrace != 0) {
lastlog();
(void)fprintf(ftrace, "%s%s\n", msg1,msg2);
}
}
traceoff()
{
if (!traceactions)
return;
if (ftrace != NULL) {
int fd = open(_PATH_DEVNULL, O_RDWR);
fprintf(ftrace, "Tracing disabled %s\n",
ctime((time_t *)&now.tv_sec));
static void
trace_close(char *msg1, char *msg2)
{
int fd;
fflush(stdout);
fflush(stderr);
if (ftrace != 0) {
tmsg(msg1,msg2);
fflush(ftrace);
(void) dup2(fd, 1);
(void) dup2(fd, 2);
(void) close(fd);
fclose(ftrace);
ftrace = NULL;
if (savetracename[0] != '\0') {
fd = open(_PATH_DEVNULL, O_RDWR);
(void)dup2(fd, STDOUT_FILENO);
(void)dup2(fd, STDERR_FILENO);
(void)close(fd);
fclose(ftrace);
ftrace = 0;
}
}
traceactions = 0;
tracehistory = 0;
tracepackets = 0;
tracecontents = 0;
}
void
sigtrace(s)
int s;
trace_flush(void)
{
if (s == SIGUSR2)
traceoff();
else if (ftrace == NULL && savetracename)
traceon(savetracename);
else
bumploglevel();
}
/*
* Move to next higher level of tracing when -t option processed or
* SIGUSR1 is received. Successive levels are:
* traceactions
* traceactions + tracepackets
* traceactions + tracehistory (packets and contents after change)
* traceactions + tracepackets + tracecontents
*/
bumploglevel()
{
(void) gettimeofday(&now, (struct timezone *)NULL);
if (traceactions == 0) {
traceactions++;
if (ftrace)
fprintf(ftrace, "Tracing actions started %s\n",
ctime((time_t *)&now.tv_sec));
} else if (tracepackets == 0) {
tracepackets++;
tracehistory = 0;
tracecontents = 0;
if (ftrace)
fprintf(ftrace, "Tracing packets started %s\n",
ctime((time_t *)&now.tv_sec));
} else if (tracehistory == 0) {
tracehistory++;
if (ftrace)
fprintf(ftrace, "Tracing history started %s\n",
ctime((time_t *)&now.tv_sec));
} else {
tracepackets++;
tracecontents++;
tracehistory = 0;
if (ftrace)
fprintf(ftrace, "Tracing packet contents started %s\n",
ctime((time_t *)&now.tv_sec));
}
if (ftrace)
if (ftrace != 0) {
fflush(ftrace);
if (ferror(ftrace))
trace_off("tracing off: ", strerror(ferror(ftrace)));
}
}
trace(ifd, who, p, len, m)
register struct ifdebug *ifd;
struct sockaddr *who;
char *p;
int len, m;
{
register struct iftrace *t;
if (ifd->ifd_records == 0)
void
trace_off(char *msg1, char *msg2)
{
trace_close(msg1, msg2);
new_tracelevel = tracelevel = 0;
}
void
trace_on(char *filename,
int trusted)
{
struct stat stbuf;
FILE *n_ftrace;
if (stat(filename, &stbuf) >= 0 &&
(stbuf.st_mode & S_IFMT) != S_IFREG) {
msglog("wrong type (%#x) of trace file \"%s\"",
stbuf.st_mode, filename);
return;
t = ifd->ifd_front++;
if (ifd->ifd_front >= ifd->ifd_records + NRECORDS)
ifd->ifd_front = ifd->ifd_records;
if (ifd->ifd_count < NRECORDS)
ifd->ifd_count++;
if (t->ift_size > 0 && t->ift_size < len && t->ift_packet) {
free(t->ift_packet);
t->ift_packet = 0;
}
t->ift_stamp = now;
t->ift_who = *who;
if (len > 0 && t->ift_packet == 0) {
t->ift_packet = malloc(len);
if (t->ift_packet == 0)
len = 0;
if (!trusted
&& strcmp(filename, savetracename)
&& strncmp(filename, _PATH_TRACE, sizeof(_PATH_TRACE)-1)) {
msglog("wrong directory for trace file %s", filename);
return;
}
n_ftrace = fopen(filename, "a");
if (n_ftrace == 0) {
msglog("failed to open trace file \"%s\": %s",
filename, strerror(errno));
return;
}
trace_close("switch to trace file ", filename);
if (filename != savetracename)
strncpy(savetracename, filename, sizeof(savetracename)-1);
ftrace = n_ftrace;
fflush(stdout);
fflush(stderr);
dup2(fileno(ftrace), STDOUT_FILENO);
dup2(fileno(ftrace), STDERR_FILENO);
if (new_tracelevel == 0) {
tracelevel_msg = "trace command: ";
new_tracelevel = 1;
} else {
tmsg("trace command","");
}
if (len > 0)
bcopy(p, t->ift_packet, len);
t->ift_size = len;
t->ift_metric = m;
}
traceaction(fd, action, rt)
FILE *fd;
char *action;
struct rt_entry *rt;
/* ARGSUSED */
void
sigtrace_on(int s)
{
struct sockaddr_in *dst, *gate;
static struct bits {
int t_bits;
char *t_name;
} flagbits[] = {
{ RTF_UP, "UP" },
{ RTF_GATEWAY, "GATEWAY" },
{ RTF_HOST, "HOST" },
{ 0 }
}, statebits[] = {
{ RTS_PASSIVE, "PASSIVE" },
{ RTS_REMOTE, "REMOTE" },
{ RTS_INTERFACE,"INTERFACE" },
{ RTS_CHANGED, "CHANGED" },
{ RTS_INTERNAL, "INTERNAL" },
{ RTS_EXTERNAL, "EXTERNAL" },
{ RTS_SUBNET, "SUBNET" },
{ 0 }
new_tracelevel++;
tracelevel_msg = "SIGUSR1: ";
}
/* ARGSUSED */
void
sigtrace_off(int s)
{
new_tracelevel--;
tracelevel_msg = "SIGUSR2: ";
}
/* Move to next higher level of tracing when -t option processed or
* SIGUSR1 is received. Successive levels are:
* actions
* actions + packets
* actions + packets + contents
*/
void
set_tracelevel(void)
{
static char *off_msgs[MAX_TRACELEVEL] = {
"Tracing actions stopped",
"Tracing packets stopped",
"Tracing packet contents stopped",
};
static char *on_msgs[MAX_TRACELEVEL] = {
"Tracing actions started",
"Tracing packets started",
"Tracing packet contents started",
};
register struct bits *p;
register int first;
char *cp;
struct interface *ifp;
if (fd == NULL)
return;
if (lastlog.tv_sec != now.tv_sec || lastlog.tv_usec != now.tv_usec) {
fprintf(fd, "\n%.19s:\n", ctime((time_t *)&now.tv_sec));
lastlog = now;
}
fprintf(fd, "%s ", action);
dst = (struct sockaddr_in *)&rt->rt_dst;
gate = (struct sockaddr_in *)&rt->rt_router;
fprintf(fd, "dst %s, ", inet_ntoa(dst->sin_addr));
fprintf(fd, "router %s, metric %d, flags",
inet_ntoa(gate->sin_addr), rt->rt_metric);
cp = " %s";
for (first = 1, p = flagbits; p->t_bits > 0; p++) {
if ((rt->rt_flags & p->t_bits) == 0)
continue;
fprintf(fd, cp, p->t_name);
if (first) {
cp = "|%s";
first = 0;
if (new_tracelevel > MAX_TRACELEVEL)
new_tracelevel = MAX_TRACELEVEL;
while (new_tracelevel != tracelevel) {
if (new_tracelevel < tracelevel) {
if (--tracelevel == 0)
trace_off(tracelevel_msg, off_msgs[0]);
else
tmsg(tracelevel_msg, off_msgs[tracelevel]);
} else {
if (ftrace == 0) {
if (savetracename[0] != '\0')
trace_on(savetracename, 1);
else
ftrace = stdout;
}
tmsg(tracelevel_msg, on_msgs[tracelevel++]);
}
}
fprintf(fd, " state");
cp = " %s";
for (first = 1, p = statebits; p->t_bits > 0; p++) {
if ((rt->rt_state & p->t_bits) == 0)
continue;
fprintf(fd, cp, p->t_name);
if (first) {
cp = "|%s";
first = 0;
}
/* display an address
*/
char *
addrname(naddr addr, /* in network byte order */
naddr mask,
int force) /* 0=show mask if nonstandard, */
{ /* 1=always show mask, 2=never */
static char s[15+20];
char *sp;
naddr dmask;
int i;
(void)strcpy(s, naddr_ntoa(addr));
if (force == 1 || (force == 0 && mask != std_mask(addr))) {
sp = &s[strlen(s)];
dmask = mask & -mask;
if (mask + dmask == 0) {
for (i = 0; i != 32 && ((1<<i) & mask) == 0; i++)
continue;
(void)sprintf(sp, "/%d", 32-i);
} else {
(void)sprintf(sp, " (mask %#x)", mask);
}
}
fprintf(fd, " timer %d\n", rt->rt_timer);
if (tracehistory && !tracepackets &&
(rt->rt_state & RTS_PASSIVE) == 0 && rt->rt_ifp)
dumpif(fd, rt->rt_ifp);
fflush(fd);
if (ferror(fd))
traceoff();
return s;
}
tracenewmetric(fd, rt, newmetric)
FILE *fd;
struct rt_entry *rt;
int newmetric;
{
struct sockaddr_in *dst, *gate;
if (fd == NULL)
return;
if (lastlog.tv_sec != now.tv_sec || lastlog.tv_usec != now.tv_usec) {
fprintf(fd, "\n%.19s:\n", ctime((time_t *)&now.tv_sec));
lastlog = now;
/* display a bit-field
*/
struct bits {
int bits_mask;
char *bits_name;
};
static struct bits if_bits[] = {
{ IFF_UP, "" },
{ IFF_BROADCAST, "" },
{ IFF_LOOPBACK, "LOOPBACK" },
{ IFF_POINTOPOINT, "PT-TO-PT" },
{ IFF_RUNNING, "" },
{ IFF_MULTICAST, "" },
{ -1, ""},
{ 0 }
};
static struct bits is_bits[] = {
{ IS_SUBNET, "" },
{ IS_REMOTE, "REMOTE" },
{ IS_PASSIVE, "PASSIVE" },
{ IS_EXTERNAL, "EXTERNAL" },
{ IS_CHECKED, "" },
{ IS_ALL_HOSTS, "" },
{ IS_ALL_ROUTERS, "" },
{ IS_RIP_QUERIED, "" },
{ IS_BROKE, "BROKE" },
{ IS_ACTIVE, "ACTIVE" },
{ IS_QUIET, "QUIET" },
{ IS_NEED_NET_SUB, "" },
{ IS_NO_AG, "NO_AG" },
{ IS_NO_SUPER_AG, "NO_SUPER_AG" },
{ (IS_NO_RIPV1_IN
| IS_NO_RIPV2_IN
| IS_NO_RIPV1_OUT
| IS_NO_RIPV2_OUT), "NO_RIP" },
{ IS_NO_RIPV1_IN, "NO_RIPV1_IN" },
{ IS_NO_RIPV2_IN, "NO_RIPV2_IN" },
{ IS_NO_RIPV1_OUT, "NO_RIPV1_OUT" },
{ IS_NO_RIPV2_OUT, "NO_RIPV2_OUT" },
{ (IS_NO_ADV_IN
| IS_NO_SOL_OUT
| IS_NO_ADV_OUT), "NO_RDISC" },
{ IS_NO_SOL_OUT, "NO_SOLICIT" },
{ IS_SOL_OUT, "SEND_SOLICIT" },
{ IS_NO_ADV_OUT, "NO_RDISC_ADV" },
{ IS_ADV_OUT, "RDISC_ADV" },
{ IS_BCAST_RDISC, "BCAST_RDISC" },
{ 0 }
};
static struct bits rs_bits[] = {
{ RS_IF, "IF" },
{ RS_NET_SUB, "NET_SUB" },
{ RS_NET_HOST, "NET_HOST" },
{ RS_NET_INT, "NET_INT" },
{ RS_SUBNET, "" },
{ RS_LOCAL, "LOCAL" },
{ RS_MHOME, "MHOME" },
{ RS_GW, "GW" },
{ RS_STATIC, "STATIC" },
{ RS_RDISC, "RDISC" },
{ 0 }
};
static void
trace_bits(struct bits *tbl,
u_int field)
{
int first = 1;
int b;
while (field != 0) {
b = tbl->bits_mask;
if (b == 0)
break;
if ((b & field) == b
&& tbl->bits_name[0] != '\0') {
(void)fprintf(ftrace, first ? "<%s" : "|%s",
tbl->bits_name);
first = 0;
}
field &= ~b;
tbl++;
}
dst = (struct sockaddr_in *)&rt->rt_dst;
gate = (struct sockaddr_in *)&rt->rt_router;
fprintf(fd, "CHANGE metric dst %s, ", inet_ntoa(dst->sin_addr));
fprintf(fd, "router %s, from %d to %d\n",
inet_ntoa(gate->sin_addr), rt->rt_metric, newmetric);
fflush(fd);
if (ferror(fd))
traceoff();
if (field != 0) {
(void)fputc(first ? '<' : '|', ftrace);
(void)fprintf(ftrace, "%#x", field);
first = 0;
}
if (!first)
(void)fputs("> ", ftrace);
}
dumpif(fd, ifp)
FILE *fd;
register struct interface *ifp;
void
trace_if(char *act,
struct interface *ifp)
{
if (ifp->int_input.ifd_count || ifp->int_output.ifd_count) {
fprintf(fd, "*** Packet history for interface %s ***\n",
ifp->int_name);
#ifdef notneeded
dumptrace(fd, "to", &ifp->int_output);
#endif
dumptrace(fd, "from", &ifp->int_input);
fprintf(fd, "*** end packet history ***\n");
if (ftrace == 0)
return;
lastlog();
(void)fprintf(ftrace, "%s interface %-4s ", act, ifp->int_name);
(void)fprintf(ftrace, "%-15s --> %s ",
naddr_ntoa(ifp->int_addr),
((ifp->int_if_flags & IFF_POINTOPOINT)
? naddr_ntoa(ifp->int_dstaddr)
: addrname(htonl(ifp->int_net), ifp->int_mask, 0)));
(void)fprintf(ftrace, "metric=%d ", ifp->int_metric);
trace_bits(if_bits, ifp->int_if_flags);
trace_bits(is_bits, ifp->int_state);
(void)fputc('\n',ftrace);
}
void
trace_upslot(struct rt_entry *rt,
struct rt_spare *rts,
naddr gate,
naddr router,
struct interface *ifp,
int metric,
u_short tag,
time_t new_time)
{
if (ftrace == 0)
return;
if (rts->rts_gate == gate
&& rts->rts_router == router
&& rts->rts_metric == metric
&& rts->rts_tag == tag)
return;
lastlog();
if (rts->rts_gate != RIP_DEFAULT) {
(void)fprintf(ftrace, "Chg #%d %-16s--> ",
rts - rt->rt_spares,
addrname(rt->rt_dst, rt->rt_mask, 0));
(void)fprintf(ftrace, "%-15s ",
naddr_ntoa(rts->rts_gate));
if (rts->rts_gate != rts->rts_gate)
(void)fprintf(ftrace, "router=%s ",
naddr_ntoa(rts->rts_gate));
if (rts->rts_tag != 0)
(void)fprintf(ftrace, "tag=%#x ", rts->rts_tag);
(void)fprintf(ftrace, "metric=%-2d ", rts->rts_metric);
if (rts->rts_ifp != 0)
(void)fprintf(ftrace, "%s ",
rts->rts_ifp->int_name);
(void)fprintf(ftrace, "%s\n", ts(rts->rts_time));
(void)fprintf(ftrace, " %-16s--> ",
addrname(rt->rt_dst, rt->rt_mask, 0));
(void)fprintf(ftrace, "%-15s ",
gate != rts->rts_gate ? naddr_ntoa(gate) : "");
if (gate != router)
(void)fprintf(ftrace,"router=%s ",naddr_ntoa(router));
if (tag != rts->rts_tag)
(void)fprintf(ftrace, "tag=%#x ", tag);
if (metric != rts->rts_metric)
(void)fprintf(ftrace, "metric=%-2d ", metric);
if (ifp != rts->rts_ifp && ifp != 0 )
(void)fprintf(ftrace, "%s ", ifp->int_name);
(void)fprintf(ftrace, "%s\n",
new_time != rts->rts_time ? ts(new_time) : "");
} else {
(void)fprintf(ftrace, "Add #%d %-16s--> ",
rts - rt->rt_spares,
addrname(rt->rt_dst, rt->rt_mask, 0));
(void)fprintf(ftrace, "%-15s ", naddr_ntoa(gate));
if (gate != router)
(void)fprintf(ftrace, "router=%s ", naddr_ntoa(gate));
if (tag != 0)
(void)fprintf(ftrace, "tag=%#x ", tag);
(void)fprintf(ftrace, "metric=%-2d ", metric);
if (ifp != 0)
(void)fprintf(ftrace, "%s ", ifp->int_name);
(void)fprintf(ftrace, "%s\n", ts(new_time));
}
}
dumptrace(fd, dir, ifd)
FILE *fd;
char *dir;
register struct ifdebug *ifd;
{
register struct iftrace *t;
char *cp = !strcmp(dir, "to") ? "Output" : "Input";
if (ifd->ifd_front == ifd->ifd_records &&
ifd->ifd_front->ift_size == 0) {
fprintf(fd, "%s: no packets.\n", cp);
fflush(fd);
void
trace_msg(char *p, ...)
{
va_list args;
if (!TRACEACTIONS || ftrace == 0)
return;
}
fprintf(fd, "%s trace:\n", cp);
t = ifd->ifd_front - ifd->ifd_count;
if (t < ifd->ifd_records)
t += NRECORDS;
for ( ; ifd->ifd_count; ifd->ifd_count--, t++) {
if (t >= ifd->ifd_records + NRECORDS)
t = ifd->ifd_records;
if (t->ift_size == 0)
continue;
dumppacket(fd, dir, &t->ift_who, t->ift_packet, t->ift_size,
&t->ift_stamp);
}
lastlog();
va_start(args, p);
vfprintf(ftrace, p, args);
}
dumppacket(fd, dir, who, cp, size, stamp)
FILE *fd;
struct sockaddr_in *who; /* should be sockaddr */
char *dir, *cp;
register int size;
struct timeval *stamp;
{
register struct rip *msg = (struct rip *)cp;
register struct netinfo *n;
if (fd == NULL)
void
trace_change(struct rt_entry *rt,
u_int state,
naddr gate, /* forward packets here */
naddr router, /* on the authority of this router */
int metric,
u_short tag,
struct interface *ifp,
time_t new_time,
char *label)
{
if (ftrace == 0)
return;
if (msg->rip_cmd && msg->rip_cmd < RIPCMD_MAX)
fprintf(fd, "%s %s %s.%d %.19s:\n", ripcmds[msg->rip_cmd],
dir, inet_ntoa(who->sin_addr), ntohs(who->sin_port),
ctime((time_t *)&stamp->tv_sec));
else {
fprintf(fd, "Bad cmd 0x%x %s %x.%d %.19s\n", msg->rip_cmd,
dir, inet_ntoa(who->sin_addr), ntohs(who->sin_port));
fprintf(fd, "size=%d cp=%x packet=%x\n", size, cp, packet,
ctime((time_t *)&stamp->tv_sec));
fflush(fd);
if (rt->rt_metric == metric
&& rt->rt_gate == gate
&& rt->rt_router == router
&& rt->rt_state == state
&& rt->rt_tag == tag)
return;
lastlog();
(void)fprintf(ftrace, "%s %-16s--> %-15s metric=%-2d ",
label,
addrname(rt->rt_dst, rt->rt_mask, 0),
naddr_ntoa(rt->rt_gate), rt->rt_metric);
if (rt->rt_router != rt->rt_gate)
(void)fprintf(ftrace, "router=%s ",
naddr_ntoa(rt->rt_router));
if (rt->rt_tag != 0)
(void)fprintf(ftrace, "tag=%#x ", rt->rt_tag);
trace_bits(rs_bits, rt->rt_state);
(void)fprintf(ftrace, "%s ",
rt->rt_ifp == 0 ? "-" : rt->rt_ifp->int_name);
(void)fprintf(ftrace, "%s\n",
AGE_RT(rt, rt->rt_ifp) ? ts(rt->rt_time) : "");
(void)fprintf(ftrace, "%*s %-16s--> %-15s ",
strlen(label), "",
addrname(rt->rt_dst, rt->rt_mask, 0),
(rt->rt_gate != gate) ? naddr_ntoa(gate) : "");
if (rt->rt_metric != metric)
(void)fprintf(ftrace, "metric=%-2d ", metric);
if (router != gate)
(void)fprintf(ftrace, "router=%s ", naddr_ntoa(router));
if (rt->rt_tag != tag)
(void)fprintf(ftrace, "tag=%#x ", tag);
if (rt->rt_state != state)
trace_bits(rs_bits, state);
if (rt->rt_ifp != ifp)
(void)fprintf(ftrace, "%s ",
ifp != 0 ? ifp->int_name : "-");
if (rt->rt_hold_down > now.tv_sec)
(void)fprintf(ftrace, "hold-down=%d ",
rt->rt_hold_down - now.tv_sec);
(void)fprintf(ftrace, "%s\n",
((rt->rt_time == new_time || !AGE_RT(rt, ifp))
? "" : ts(new_time)));
}
void
trace_add_del(char * action, struct rt_entry *rt)
{
u_int state = rt->rt_state;
if (ftrace == 0)
return;
lastlog();
(void)fprintf(ftrace, "%s %-16s--> %-15s metric=%-2d ",
action,
addrname(rt->rt_dst, rt->rt_mask, 0),
naddr_ntoa(rt->rt_gate), rt->rt_metric);
if (rt->rt_router != rt->rt_gate)
(void)fprintf(ftrace, "router=%s ",
naddr_ntoa(rt->rt_router));
if (rt->rt_tag != 0)
(void)fprintf(ftrace, "tag=%#x ", rt->rt_tag);
trace_bits(rs_bits, state);
if (rt->rt_ifp != 0)
(void)fprintf(ftrace, "%s ", rt->rt_ifp->int_name);
(void)fprintf(ftrace, "%s\n", ts(rt->rt_time));
}
void
trace_rip(char *dir1, char *dir2,
struct sockaddr_in *who,
struct interface *ifp,
struct rip *msg,
int size) /* total size of message */
{
struct netinfo *n, *lim;
struct netauth *a;
int i;
if (ftrace == 0)
return;
lastlog();
if (msg->rip_cmd >= RIPCMD_MAX
|| msg->rip_vers == 0) {
(void)fprintf(ftrace, "%s bad RIPv%d cmd=%d %s %s.%d%s%s"
" size=%d msg=%#x\n",
dir1, msg->rip_vers, msg->rip_cmd, dir2,
naddr_ntoa(who->sin_addr.s_addr),
ntohs(who->sin_port),
size, msg);
return;
}
if (tracepackets && tracecontents == 0) {
fflush(fd);
(void)fprintf(ftrace, "%s RIPv%d %s %s %s.%d%s%s\n",
dir1, msg->rip_vers, ripcmds[msg->rip_cmd], dir2,
naddr_ntoa(who->sin_addr.s_addr), ntohs(who->sin_port),
ifp ? " via " : "", ifp ? ifp->int_name : "");
if (!TRACECONTENTS)
return;
}
switch (msg->rip_cmd) {
case RIPCMD_REQUEST:
case RIPCMD_RESPONSE:
size -= 4 * sizeof (char);
n = msg->rip_nets;
for (; size > 0; n++, size -= sizeof (struct netinfo)) {
if (size < sizeof (struct netinfo)) {
fprintf(fd, "(truncated record, len %d)\n",
size);
break;
lim = (struct netinfo *)((char*)msg + size);
for (; n < lim; n++) {
if (n->n_family == RIP_AF_UNSPEC
&& ntohl(n->n_metric) == HOPCNT_INFINITY
&& n+1 == lim
&& n == msg->rip_nets
&& msg->rip_cmd == RIPCMD_REQUEST) {
(void)fputs("\tQUERY ", ftrace);
if (n->n_dst != 0)
(void)fprintf(ftrace, "%s ",
naddr_ntoa(n->n_dst));
if (n->n_mask != 0)
(void)fprintf(ftrace, "mask=%#x ",
ntohl(n->n_mask));
if (n->n_nhop != 0)
(void)fprintf(ftrace, " nhop=%s ",
naddr_ntoa(n->n_nhop));
if (n->n_tag != 0)
(void)fprintf(ftrace, "tag=%#x",
n->n_tag);
(void)fputc('\n',ftrace);
continue;
}
if (sizeof(n->rip_dst.sa_family) > 1)
n->rip_dst.sa_family = ntohs(n->rip_dst.sa_family);
switch ((int)n->rip_dst.sa_family) {
case AF_INET:
fprintf(fd, "\tdst %s metric %d\n",
#define satosin(sa) ((struct sockaddr_in *)&sa)
inet_ntoa(satosin(n->rip_dst)->sin_addr),
ntohl(n->rip_metric));
break;
default:
fprintf(fd, "\taf %d? metric %d\n",
n->rip_dst.sa_family,
ntohl(n->rip_metric));
break;
if (n->n_family == RIP_AF_AUTH) {
a = (struct netauth*)n;
(void)fprintf(ftrace,
"\tAuthentication type %d: ",
ntohs(a->a_type));
for (i = 0;
i < sizeof(a->au.au_pw);
i++)
(void)fprintf(ftrace, "%02x ",
a->au.au_pw[i]);
(void)fputc('\n',ftrace);
continue;
}
if (n->n_family != RIP_AF_INET) {
(void)fprintf(ftrace,
"\t(af %d) %-18s mask=%#x",
ntohs(n->n_family),
naddr_ntoa(n->n_dst),
ntohl(n->n_mask));
} else if (msg->rip_vers == RIPv1) {
(void)fprintf(ftrace, "\t%-18s ",
addrname(n->n_dst,
ntohl(n->n_mask),
n->n_mask==0 ? 2 : 1));
} else {
(void)fprintf(ftrace, "\t%-18s ",
addrname(n->n_dst,
ntohl(n->n_mask),
n->n_mask==0 ? 2 : 0));
}
(void)fprintf(ftrace, "metric=%-2d ",
ntohl(n->n_metric));
if (n->n_nhop != 0)
(void)fprintf(ftrace, " nhop=%s ",
naddr_ntoa(n->n_nhop));
if (n->n_tag != 0)
(void)fprintf(ftrace, "tag=%#x",
n->n_tag);
(void)fputc('\n',ftrace);
}
if (size != (char *)n - (char *)msg)
(void)fprintf(ftrace, "truncated record, len %d\n",
size);
break;
case RIPCMD_TRACEON:
fprintf(fd, "\tfile=%*s\n", size, msg->rip_tracefile);
fprintf(ftrace, "\tfile=%*s\n", size-4, msg->rip_tracefile);
break;
case RIPCMD_TRACEOFF:
break;
}
fflush(fd);
if (ferror(fd))
traceoff();
}