diff options
author | wollman <wollman@FreeBSD.org> | 1996-05-30 16:31:46 +0000 |
---|---|---|
committer | wollman <wollman@FreeBSD.org> | 1996-05-30 16:31:46 +0000 |
commit | 516f199fd34286e05546181e1dd2a4fc5be0cec6 (patch) | |
tree | 48549499fb1d157da3b508a4087652c73d4b3f75 /usr.sbin/routed | |
parent | 29f9ba3bbb34d92e4f37152e892f5f608754e24f (diff) | |
download | FreeBSD-src-516f199fd34286e05546181e1dd2a4fc5be0cec6.zip FreeBSD-src-516f199fd34286e05546181e1dd2a4fc5be0cec6.tar.gz |
Copy SGI routed onto head.
Diffstat (limited to 'usr.sbin/routed')
-rw-r--r-- | usr.sbin/routed/Makefile | 8 | ||||
-rw-r--r-- | usr.sbin/routed/defs.h | 555 | ||||
-rw-r--r-- | usr.sbin/routed/if.c | 1077 | ||||
-rw-r--r-- | usr.sbin/routed/input.c | 874 | ||||
-rw-r--r-- | usr.sbin/routed/main.c | 968 | ||||
-rw-r--r-- | usr.sbin/routed/output.c | 790 | ||||
-rw-r--r-- | usr.sbin/routed/pathnames.h | 5 | ||||
-rw-r--r-- | usr.sbin/routed/routed.8 | 575 | ||||
-rw-r--r-- | usr.sbin/routed/trace.c | 889 |
9 files changed, 4475 insertions, 1266 deletions
diff --git a/usr.sbin/routed/Makefile b/usr.sbin/routed/Makefile index 40e2701..7436a28 100644 --- a/usr.sbin/routed/Makefile +++ b/usr.sbin/routed/Makefile @@ -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> diff --git a/usr.sbin/routed/defs.h b/usr.sbin/routed/defs.h index 5672210..3db0515 100644 --- a/usr.sbin/routed/defs.h +++ b/usr.sbin/routed/defs.h @@ -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. +#ident "$Revision: 1.1.3.1 $" + +/* 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 <sys/time.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 <stdio.h> -#include <netdb.h> +/* 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. + */ +#ifdef sgi +#define naddr __uint32_t +#else +#define naddr u_long +#define _HAVE_SA_LEN +#define _HAVE_SIN_LEN +#endif -#include "trace.h" -#include "interface.h" -#include "table.h" -#include "af.h" +#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 -/* - * When we find any interfaces marked down we rescan the - * kernel every CHECK_INTERVAL seconds to see if they've - * come up. +#define NEVER (24*60*60) /* a long time */ +#define EPOCH NEVER /* bias time by this to avoid <0 */ + +/* 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 */ + + +/* set times to this to continue poisoning a route */ +#define POISON_SECS (GARBAGE_TIME - POISON_TIME) + +#define NET_S_METRIC 1 /* metric used on synthetic routes */ + +#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). */ -#define CHECK_INTERVAL (1*60) - -#define equal(a1, a2) \ - (bcmp((caddr_t)(a1), (caddr_t)(a2), sizeof (struct sockaddr)) == 0) - -struct sockaddr_in addr; /* address of daemon's socket */ - -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 */ - -char packet[MAXPACKETSIZE+1]; -struct rip *msg; - -char **argv0; -struct servent *sp; - -struct in_addr inet_makeaddr(); -int inet_addr(); -int inet_maskof(); -int sndmsg(); -int supply(); -int cleanup(); - -int rtioctl(); -#define ADD 1 -#define DELETE 2 -#define CHANGE 3 +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); diff --git a/usr.sbin/routed/if.c b/usr.sbin/routed/if.c index 77a4674..5499f19 100644 --- a/usr.sbin/routed/if.c +++ b/usr.sbin/routed/if.c @@ -35,114 +35,1041 @@ static char sccsid[] = "@(#)if.c 8.1 (Berkeley) 6/5/93"; #endif /* not lint */ -/* - * Routing Table Management Daemon - */ +#ident "$Revision: 1.1.3.1 $" + #include "defs.h" +#include "pathnames.h" -extern struct interface *ifnet; +struct interface *ifnet; /* all interfaces */ +int tot_interfaces; /* # of remote and local interfaces */ +int rip_interfaces; /* # of interfaces doing RIP */ +int foundloopback; /* valid flag for loopaddr */ +naddr loopaddr; /* our address on loopback */ -/* - * Find the interface with address addr. +struct timeval ifinit_timer; + +int have_ripv1; /* have a RIPv1 interface */ + + +/* Find the interface with an address */ struct interface * -if_ifwithaddr(addr) - struct sockaddr *addr; +ifwithaddr(naddr addr, + int bcast, /* notice IFF_BROADCAST address */ + int remote) /* include IS_REMOTE interfaces */ { - register struct interface *ifp; + struct interface *ifp, *possible = 0; -#define same(a1, a2) \ - (bcmp((caddr_t)((a1)->sa_data), (caddr_t)((a2)->sa_data), 14) == 0) for (ifp = ifnet; ifp; ifp = ifp->int_next) { - if (ifp->int_flags & IFF_REMOTE) + if ((ifp->int_state & IS_REMOTE) && !remote) continue; - if (ifp->int_addr.sa_family != addr->sa_family) - continue; - if (same(&ifp->int_addr, addr)) - break; - if ((ifp->int_flags & IFF_BROADCAST) && - same(&ifp->int_broadaddr, addr)) - break; + + if (ifp->int_addr == addr + || ((ifp->int_if_flags & IFF_BROADCAST) + && ifp->int_brdaddr == addr + && bcast)) { + if (!(ifp->int_state & IS_BROKE)) + return ifp; + possible = ifp; + } } - return (ifp); + + return possible; } -/* - * Find the point-to-point interface with destination address addr. + +/* find the interface with a name */ struct interface * -if_ifwithdstaddr(addr) - struct sockaddr *addr; +ifwithname(char *name, /* enp0 or whatever */ + naddr addr) /* 0 or network address */ { - register struct interface *ifp; + struct interface *ifp; - for (ifp = ifnet; ifp; ifp = ifp->int_next) { - if ((ifp->int_flags & IFF_POINTOPOINT) == 0) - continue; - if (same(&ifp->int_dstaddr, addr)) - break; + + for (ifp = ifnet; 0 != ifp; ifp = ifp->int_next) { + if (!strcmp(ifp->int_name, name) + && ((addr == 0 && !(ifp->int_state & IS_ALIAS) + || ifp->int_addr == addr))) + return ifp; } - return (ifp); + return 0; } -/* - * Find the interface on the network - * of the specified address. - */ + struct interface * -if_ifwithnet(addr) - register struct sockaddr *addr; +ifwithindex(u_short index) { - register struct interface *ifp; - register int af = addr->sa_family; - register int (*netmatch)(); + struct interface *ifp; - if (af >= af_max) - return (0); - netmatch = afswitch[af].af_netmatch; - for (ifp = ifnet; ifp; ifp = ifp->int_next) { - if (ifp->int_flags & IFF_REMOTE) - continue; - if (af != ifp->int_addr.sa_family) - continue; - if ((*netmatch)(addr, &ifp->int_addr)) - break; + + for (ifp = ifnet; 0 != ifp; ifp = ifp->int_next) { + if (ifp->int_index == index) + return ifp; } - return (ifp); + return 0; } -/* - * Find an interface from which the specified address + +/* Find an interface from which the specified address * should have come from. Used for figuring out which * interface a packet came in on -- for tracing. */ struct interface * -if_iflookup(addr) - struct sockaddr *addr; +iflookup(naddr addr) { - register struct interface *ifp, *maybe; - register int af = addr->sa_family; - register int (*netmatch)(); + struct interface *ifp, *maybe; + int twice; - if (af >= af_max) - return (0); + twice = 0; maybe = 0; - netmatch = afswitch[af].af_netmatch; - for (ifp = ifnet; ifp; ifp = ifp->int_next) { - if (ifp->int_addr.sa_family != af) + do { + for (ifp = ifnet; ifp; ifp = ifp->int_next) { + /* finished with an exact match */ + if (ifp->int_addr == addr) + return ifp; + + if ((ifp->int_if_flags & IFF_BROADCAST) + && ifp->int_brdaddr == addr) + return ifp; + + if ((ifp->int_if_flags & IFF_POINTOPOINT) + && ifp->int_dstaddr == addr) + return ifp; + + /* Look for the longest approximate match. + */ + if (on_net(addr, + ifp->int_net, ifp->int_mask) + && (maybe == 0 + || ifp->int_mask > maybe->int_mask)) + maybe = ifp; + } + + if (maybe != 0) + return maybe; + + /* See if an interface has come up since we checked. + */ + ifinit(); + } while (twice++ == 0); + + return 0; +} + + +/* Return the classical netmask for an IP address. + */ +naddr +std_mask(naddr addr) +{ + NTOHL(addr); /* was a host, not a network */ + + if (addr == 0) /* default route has mask 0 */ + return 0; + if (IN_CLASSA(addr)) + return IN_CLASSA_NET; + if (IN_CLASSB(addr)) + return IN_CLASSB_NET; + return IN_CLASSC_NET; +} + + +/* find the netmask that would be inferred by RIPv1 listeners + * on the given interface + */ +naddr +ripv1_mask_net(naddr addr, /* in network byte order */ + struct interface *ifp1, /* as seen on this interface */ + struct interface *ifp2) /* but not this interface */ +{ + naddr mask = 0; + + if (addr == 0) /* default always has 0 mask */ + return mask; + + if (ifp1 != 0) { + /* If the target is that of the associated interface on which + * it arrived, then use the netmask of the interface. + */ + if (on_net(addr, ifp1->int_net, ifp1->int_std_mask)) + mask = ifp1->int_mask; + + } else { + /* Examine all interfaces, and if it the target seems + * to have the same network number of an interface, use the + * netmask of that interface. If there is more than one + * such interface, prefer the interface with the longest + * match. + */ + for (ifp1 = ifnet; ifp1 != 0; ifp1 = ifp1->int_next) { + if (ifp1 != ifp2 + && !(ifp1->int_if_flags & IFF_POINTOPOINT) + && on_net(addr, + ifp1->int_std_net, ifp1->int_std_mask) + && ifp1->int_mask > mask) + mask = ifp1->int_mask; + } + } + + /* Otherwise, make the classic A/B/C guess. + */ + if (mask == 0) + mask = std_mask(addr); + + return mask; +} + + +naddr +ripv1_mask_host(naddr addr, /* in network byte order */ + struct interface *ifp1, /* as seen on this interface */ + struct interface *ifp2) /* but not this interface */ +{ + naddr mask = ripv1_mask_net(addr, ifp1, ifp2); + + + /* If the computed netmask does not mask the address, + * then assume it is a host address + */ + if ((ntohl(addr) & ~mask) != 0) + mask = HOST_MASK; + return mask; +} + + +/* See if a IP address looks reasonable as a destination + */ +int /* 0=bad */ +check_dst(naddr addr) +{ + NTOHL(addr); + + if (IN_CLASSA(addr)) { + if (addr == 0) + return 1; /* default */ + + addr >>= IN_CLASSA_NSHIFT; + return (addr != 0 && addr != IN_LOOPBACKNET); + } + + return (IN_CLASSB(addr) || IN_CLASSC(addr)); +} + + +/* Delete an interface. + */ +static void +ifdel(struct interface *ifp) +{ + struct ip_mreq m; + struct interface *ifp1; + + + if (TRACEACTIONS) + trace_if("Del", ifp); + + if (!(ifp->int_state & IS_ALIAS)) { + if ((ifp->int_if_flags & IFF_MULTICAST) +#ifdef MCAST_PPP_BUG + && !(ifp->int_if_flags & IFF_POINTOPOINT) +#endif + && rip_sock >= 0) { + m.imr_multiaddr.s_addr = htonl(INADDR_RIP_GROUP); + m.imr_interface.s_addr = ((ifp->int_if_flags + & IFF_POINTOPOINT) + ? ifp->int_dstaddr + : ifp->int_addr); + if (setsockopt(rip_sock,IPPROTO_IP,IP_DROP_MEMBERSHIP, + &m, sizeof(m)) < 0) + DBGERR(1,"setsockopt(IP_DROP_MEMBERSHIP" + " RIP)"); + } + if (ifp->int_rip_sock >= 0) { + (void)close(ifp->int_rip_sock); + ifp->int_rip_sock = -1; + fix_select(); + } + set_rdisc_mg(ifp, 0); + } + + /* Zap all routes associated with this interface. + * Assume routes just using gateways beyond this interface will + * timeout naturally, and have probably already died. + */ + ifp->int_state |= IS_BROKE; + (void)rn_walktree(rhead, walk_bad, 0); + ifbad_rdisc(ifp); + + if (!(ifp->int_state & IS_ALIAS)) { + tot_interfaces--; + if (0 == (ifp->int_state & (IS_NO_RIP_IN|IS_PASSIVE))) + rip_interfaces--; + } + + /* unlink and forget the interface */ + if (rip_sock_mcast == ifp) + rip_sock_mcast = 0; + if (ifp->int_next != 0) + ifp->int_next->int_prev = ifp->int_prev; + if (ifp->int_prev != 0) + ifp->int_prev->int_next = ifp->int_next; + else + ifnet = ifp->int_next; + + if (!(ifp->int_state & IS_ALIAS)) { + /* delete aliases of primary interface */ + for (ifp1 = ifnet; 0 != ifp1; ifp1 = ifp1->int_next) { + if (!strcmp(ifp->int_name, ifp1->int_name)) + ifdel(ifp1); + } + } + + free(ifp); +} + + +/* Mark an interface dead. + */ +void +ifbad(struct interface *ifp, + char *pat) +{ + if (ifp->int_state & IS_BROKE) + return; + + if (pat) + msglog(pat, ifp->int_name, naddr_ntoa(ifp->int_addr)); + + LIM_SEC(ifinit_timer, now.tv_sec+CHECK_BAD_INTERVAL); + + ifp->int_state |= IS_BROKE; + ifp->int_state &= ~(IS_RIP_QUERIED | IS_ACTIVE | IS_QUIET); + ifp->int_quiet_time = now.tv_sec - MaxMaxAdvertiseInterval; + ifp->int_data_ts = 0; + + trace_if("Chg", ifp); + + (void)rn_walktree(rhead, walk_bad, 0); + + ifbad_rdisc(ifp); +} + + +/* Mark an interface alive + */ +int /* 1=it was dead */ +ifok(struct interface *ifp, + char *type) +{ + if (!(ifp->int_state & IS_BROKE)) + return 0; + + msglog("%sinterface %s to %s restored", + type, ifp->int_name, naddr_ntoa(ifp->int_addr)); + ifp->int_state &= ~IS_BROKE; + ifp->int_data_ts = 0; + + ifok_rdisc(ifp); + return 1; +} + + +struct rt_addrinfo rtinfo; + +/* disassemble routing message + */ +void +rt_xaddrs(struct sockaddr *sa, + struct sockaddr *lim, + int addrs) +{ + int i; +#ifdef _HAVE_SA_LEN + static struct sockaddr sa_zero; +#endif +#ifdef sgi +#define ROUNDUP(a) ((a) > 0 ? (1 + (((a) - 1) | (sizeof(__uint64_t) - 1))) \ + : sizeof(__uint64_t)) +#else +#define ROUNDUP(a) ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) \ + : sizeof(long)) +#endif + + + bzero(rtinfo.rti_info, sizeof(rtinfo.rti_info)); + rtinfo.rti_addrs = addrs; + + for (i = 0; i < RTAX_MAX && sa < lim; i++) { + if ((addrs & (1 << i)) == 0) continue; - if (same(&ifp->int_addr, addr)) - break; - if ((ifp->int_flags & IFF_BROADCAST) && - same(&ifp->int_broadaddr, addr)) - break; - if ((ifp->int_flags & IFF_POINTOPOINT) && - same(&ifp->int_dstaddr, addr)) +#ifdef _HAVE_SA_LEN + rtinfo.rti_info[i] = (sa->sa_len != 0) ? sa : &sa_zero; + sa = (struct sockaddr *)((char*)(sa) + + ROUNDUP(sa->sa_len)); +#else + rtinfo.rti_info[i] = sa; + sa = (struct sockaddr *)((char*)(sa) + + ROUNDUP(_FAKE_SA_LEN_DST(sa))); +#endif + } +} + + +/* Find the network interfaces which have configured themselves. + * This must be done regularly, if only for extra addresses + * that come and go on interfaces. + */ +void +ifinit(void) +{ + static char *sysctl_buf; + static size_t sysctl_buf_size = 0; + uint complaints = 0; + static u_int prev_complaints = 0; +# define COMP_NOT_INET 0x01 +# define COMP_WIERD 0x02 +# define COMP_NOADDR 0x04 +# define COMP_NODST 0x08 +# define COMP_NOBADR 0x10 +# define COMP_NOMASK 0x20 +# define COMP_DUP 0x40 + + struct interface ifs, ifs0, *ifp, *ifp1; + struct rt_entry *rt; + size_t needed; + int mib[6]; + struct if_msghdr *ifm; + struct ifa_msghdr *ifam, *ifam_lim, *ifam2; + struct sockaddr_dl *sdl; + int in, ierr, out, oerr; + struct intnet *intnetp; +#ifdef SIOCGIFMETRIC + struct ifreq ifr; +#endif + + + ifinit_timer.tv_sec = now.tv_sec + (supplier + ? CHECK_ACT_INTERVAL + : CHECK_QUIET_INTERVAL); + + /* mark all interfaces so we can get rid of thost that disappear */ + for (ifp = ifnet; 0 != ifp; ifp = ifp->int_next) + ifp->int_state &= ~IS_CHECKED; + + /* Fetch the interface list, without too many system calls + * since we do it repeatedly. + */ + mib[0] = CTL_NET; + mib[1] = PF_ROUTE; + mib[2] = 0; + mib[3] = AF_INET; + mib[4] = NET_RT_IFLIST; + mib[5] = 0; + for (;;) { + if ((needed = sysctl_buf_size) != 0) { + if (sysctl(mib, 6, sysctl_buf,&needed, 0, 0) >= 0) + break; + + if (errno != ENOMEM && errno != EFAULT) + BADERR(1, "ifinit: get interface table"); + free(sysctl_buf); + needed = 0; + } + if (sysctl(mib, 6, 0, &needed, 0, 0) < 0) + BADERR(1,"ifinit: route-sysctl-estimate"); + if ((sysctl_buf = malloc(sysctl_buf_size = needed)) == 0) + BADERR(1,"ifinit: malloc"); + } + + ifam_lim = (struct ifa_msghdr *)(sysctl_buf + needed); + for (ifam = (struct ifa_msghdr *)sysctl_buf; + ifam < ifam_lim; + ifam = ifam2) { + + ifam2 = (struct ifa_msghdr*)((char*)ifam + ifam->ifam_msglen); + + if (ifam->ifam_type == RTM_IFINFO) { + ifm = (struct if_msghdr *)ifam; + bzero(&ifs0, sizeof(ifs0)); + ifs0.int_rip_sock = -1; + ifs0.int_index = ifm->ifm_index; + ifs0.int_if_flags = ifm->ifm_flags; + ifs0.int_state = IS_CHECKED; + ifs0.int_act_time = now.tv_sec; + ifs0.int_quiet_time = (now.tv_sec + - MaxMaxAdvertiseInterval); + ifs0.int_data_ts = now.tv_sec; + ifs0.int_data_ipackets = ifm->ifm_data.ifi_ipackets; + ifs0.int_data_ierrors = ifm->ifm_data.ifi_ierrors; + ifs0.int_data_opackets = ifm->ifm_data.ifi_opackets; + ifs0.int_data_oerrors = ifm->ifm_data.ifi_oerrors; +#ifdef sgi + ifs0.int_data_odrops = ifm->ifm_data.ifi_odrops; +#endif + sdl = (struct sockaddr_dl *)(ifm + 1); + sdl->sdl_data[sdl->sdl_nlen] = 0; + continue; + } + if (ifam->ifam_type != RTM_NEWADDR) { + DBGERR(1,"ifinit: out of sync"); + continue; + } + + rt_xaddrs((struct sockaddr *)(ifam+1), + (struct sockaddr *)ifam2, + ifam->ifam_addrs); + + if (RTINFO_IFA == 0) { + if (iff_alive(ifs.int_if_flags)) { + if (!(prev_complaints & COMP_NOADDR)) + msglog("%s has a bad address", + sdl->sdl_data); + complaints |= COMP_NOADDR; + } + continue; + } + if (RTINFO_IFA->sa_family != AF_INET) { + if (iff_alive(ifs.int_if_flags)) { + if (!(prev_complaints & COMP_NOT_INET)) + trace_msg("%s: not AF_INET", + sdl->sdl_data); + complaints |= COMP_NOT_INET; + } + continue; + } + + bcopy(&ifs0, &ifs, sizeof(ifs0)); + ifs0.int_state |= IS_ALIAS; /* next will be an alias */ + + ifs.int_addr = S_ADDR(RTINFO_IFA); + + if (ifs.int_if_flags & IFF_BROADCAST) { + if (RTINFO_NETMASK == 0) { + if (iff_alive(ifs.int_if_flags)) { + if (!(prev_complaints & COMP_NOMASK)) + msglog("%s has no netmask", + sdl->sdl_data); + complaints |= COMP_NOMASK; + } + continue; + } + ifs.int_mask = ntohl(S_ADDR(RTINFO_NETMASK)); + ifs.int_net = ntohl(ifs.int_addr) & ifs.int_mask; + ifs.int_std_mask = std_mask(ifs.int_addr); + if (ifs.int_mask != ifs.int_std_mask) + ifs.int_state |= IS_SUBNET; + + if (RTINFO_BRD == 0) { + if (iff_alive(ifs.int_if_flags)) { + if (!(prev_complaints & COMP_NOBADR)) + msglog("%s has no" + " broadcast address", + sdl->sdl_data); + complaints |= COMP_NOBADR; + } + continue; + } + ifs.int_brdaddr = S_ADDR(RTINFO_BRD); + + } else if (ifs.int_if_flags & IFF_POINTOPOINT) { + if (RTINFO_BRD == 0 + || RTINFO_BRD->sa_family != AF_INET) { + if (iff_alive(ifs.int_if_flags)) { + if (!(prev_complaints & COMP_NODST)) + msglog("%s has a bad" + " destination address", + sdl->sdl_data); + complaints |= COMP_NODST; + } + continue; + } + ifs.int_dstaddr = S_ADDR(RTINFO_BRD); + ifs.int_net = ntohl(ifs.int_dstaddr); + ifs.int_mask = HOST_MASK; + ifs.int_std_mask = std_mask(ifs.int_dstaddr); + + } else if (ifs.int_if_flags & IFF_LOOPBACK) { + ifs.int_state |= IS_PASSIVE; + ifs.int_dstaddr = ifs.int_addr; + ifs.int_net = ntohl(ifs.int_dstaddr); + ifs.int_mask = HOST_MASK; + ifs.int_std_mask = std_mask(ifs.int_dstaddr); + if (!foundloopback) { + foundloopback = 1; + loopaddr = ifs.int_addr; + } + + } else { + if (TRACEACTIONS + && !(prev_complaints & COMP_WIERD)) + msglog("%s is neither broadcast" + " nor point-to-point nor loopback", + sdl->sdl_data); + complaints |= COMP_WIERD; + continue; + } + ifs.int_std_net = ifs.int_net & ifs.int_std_mask; + ifs.int_std_addr = htonl(ifs.int_std_net); + + /* Use a minimum metric of one. Treat the interface metric + * (default 0) as an increment to the hop count of one. + * + * The metric obtained from the routing socket dump of + * interface addresses is wrong. It is not set by the + * SIOCSIFMETRIC ioctl. + */ +#ifdef SIOCGIFMETRIC + strncpy(ifr.ifr_name, sdl->sdl_data, sizeof(ifr.ifr_name)); + if (ioctl(rt_sock, SIOCGIFMETRIC, &ifr) < 0) { + DBGERR(1, "ioctl(SIOCGIFMETRIC)"); + ifs.int_metric = HOPCNT_INFINITY; + } else { + ifs.int_metric = ifr.ifr_metric+1; + } +#else + ifs.int_metric = ifam->ifam_metric+1; +#endif + if (ifs.int_metric >= HOPCNT_INFINITY) + ifs.int_metric = HOPCNT_INFINITY; + + /* See if this is a familiar interface. + * If so, stop worrying about it if it is the same. + * Start it over if it now is to somewhere else, as happens + * frequently with PPP and SLIP. + */ + ifp = ifwithname(sdl->sdl_data, ((ifs.int_state & IS_ALIAS) + ? ifs.int_addr + : 0)); + if (ifp != 0) { + ifp->int_state |= IS_CHECKED; + + if (0 != ((ifp->int_if_flags ^ ifs.int_if_flags) + & (IFF_BROADCAST + | IFF_LOOPBACK + | IFF_POINTOPOINT + | IFF_MULTICAST)) + || 0 != ((ifp->int_state ^ ifs.int_state) + & IS_ALIAS) + || ifp->int_addr != ifs.int_addr + || ifp->int_brdaddr != ifs.int_brdaddr + || ifp->int_dstaddr != ifs.int_dstaddr + || ifp->int_mask != ifs.int_mask + || ifp->int_metric != ifs.int_metric) { + /* Forget old information about + * a changed interface. + */ + trace_msg("interface %s has changed", + ifp->int_name); + ifdel(ifp); + ifp = 0; + } + } + + if (ifp != 0) { + /* note interfaces that have been turned off + */ + if (!iff_alive(ifs.int_if_flags)) { + if (iff_alive(ifp->int_if_flags)) + ifbad(ifp, "interface %s to %s" + " turned off"); + ifp->int_if_flags &= ~IFF_UP_RUNNING; + continue; + } + /* or that were off and are now ok */ + if (!iff_alive(ifp->int_if_flags)) { + ifp->int_if_flags |= IFF_UP_RUNNING; + (void)ifok(ifp, ""); + } + + /* If it has been long enough, + * see if the interface is broken. + */ + if (now.tv_sec < ifp->int_data_ts+CHECK_BAD_INTERVAL) + continue; + + in = ifs.int_data_ipackets - ifp->int_data_ipackets; + ierr = ifs.int_data_ierrors - ifp->int_data_ierrors; + out = ifs.int_data_opackets - ifp->int_data_opackets; +#ifdef sgi + oerr = (ifs.int_data_oerrors - ifp->int_data_oerrors + + ifs.int_data_odrops - ifp->int_data_odrops); +#else + oerr = ifs.int_data_oerrors - ifp->int_data_oerrors; +#endif + + ifp->int_data_ipackets = ifs.int_data_ipackets; + ifp->int_data_ierrors = ifs.int_data_ierrors; + ifp->int_data_opackets = ifs.int_data_opackets; + ifp->int_data_oerrors = ifs.int_data_oerrors; +#ifdef sgi + ifp->int_data_odrops = ifs.int_data_odrops; +#endif + + /* If the interface just awoke, restart the counters. + */ + if (ifp->int_data_ts == 0) { + ifp->int_data_ts = now.tv_sec; + continue; + } + ifp->int_data_ts = now.tv_sec; + + /* Withhold judgement when the short error + * counters wrap or the interface is reset. + */ + if (ierr < 0 || in < 0 || oerr < 0 || out < 0) + continue; + + /* Withhold judgement when there is no traffic + */ + if (in == 0 && out == 0 && ierr == 0 && oerr == 0) { + if (!(ifp->int_state & IS_QUIET)) { + ifp->int_state |= IS_QUIET; + ifp->int_quiet_time = now.tv_sec; + } + continue; + } + + /* It is bad if input or output is not working + */ + if ((in <= ierr && ierr > 0) + || (out <= oerr && oerr > 0)) { + ifbad(ifp,"interface %s to %s not working"); + continue; + } + + /* otherwise, it is active and healthy + */ + ifp->int_act_time = now.tv_sec; + ifp->int_state &= ~IS_QUIET; + if (ifok(ifp, "")) + addrouteforif(ifp); + continue; + } + + /* See if this new interface duplicates an existing + * interface. + */ + for (ifp = ifnet; 0 != ifp; ifp = ifp->int_next) { + if (ifp->int_addr == ifs.int_addr + && ifp->int_mask == ifs.int_mask) + break; + } + if (ifp != 0) { + if (iff_alive(ifs.int_if_flags)) { + if (!(prev_complaints & COMP_DUP)) + msglog("%s is duplicated by %s at %s", + sdl->sdl_data, ifp->int_name, + naddr_ntoa(ifp->int_addr)); + complaints |= COMP_DUP; + } + continue; + } + + strncpy(ifs.int_name, sdl->sdl_data, + MIN(sizeof(ifs.int_name)-1, sdl->sdl_nlen)); + + get_parms(&ifs); + + ifok_rdisc(&ifs); + + /* create the interface */ + ifp = (struct interface *)malloc(sizeof(*ifp)); + if (ifp == 0) + BADERR(1,"ifinit: out of memory"); + bcopy(&ifs, ifp, sizeof(*ifp)); + if (ifnet != 0) { + ifp->int_next = ifnet; + ifnet->int_prev = ifp; + } + ifnet = ifp; + + /* Count the # of directly connected networks. + */ + if (!(ifp->int_state & IS_ALIAS)) { + if (!(ifp->int_if_flags & IFF_LOOPBACK)) + tot_interfaces++; + if (0 == (ifp->int_state & (IS_NO_RIP_IN|IS_PASSIVE))) + rip_interfaces++; + } + + /* note dead interfaces */ + if (iff_alive(ifs.int_if_flags)) { + set_rdisc_mg(ifp, 1); + } else { + LIM_SEC(ifinit_timer, now.tv_sec+CHECK_BAD_INTERVAL); + ifp->int_state |= IS_BROKE; + } + + if (TRACEACTIONS) + trace_if("Add", ifp); + + rip_on(ifp); + } + + /* If we are multi-homed and have at least one interface + * listening to RIP, then output by default. + */ + if (!supplier_set && rip_interfaces > 1) + set_supplier(); + + /* If we are multi-homed, optionally advertise a route to + * our main address. + */ + if (advertise_mhome + || (tot_interfaces > 1 + && mhome + && (ifp = ifwithaddr(myaddr, 0, 0)) != 0 + && foundloopback)) { + advertise_mhome = 1; + del_static(myaddr, HOST_MASK, 0); + rt = rtget(myaddr, HOST_MASK); + if (rt != 0) { + if (rt->rt_ifp != ifp + || rt->rt_router != loopaddr) { + rtdelete(rt); + rt = 0; + } else { + rtchange(rt, rt->rt_state | RS_MHOME, + loopaddr, loopaddr, + ifp->int_metric, 0, + ifp, rt->rt_time, 0); + } + } + if (rt == 0) + rtadd(myaddr, HOST_MASK, loopaddr, loopaddr, + ifp->int_metric, 0, RS_MHOME, ifp); + } + + for (ifp = ifnet; ifp != 0; ifp = ifp1) { + ifp1 = ifp->int_next; /* because we may delete it */ + + /* Forget any interfaces that have disappeared. + */ + if (!(ifp->int_state & (IS_CHECKED | IS_REMOTE))) { + trace_msg("interface %s has disappeared", + ifp->int_name); + ifdel(ifp); + continue; + } + + if (ifp->int_state & IS_BROKE) + LIM_SEC(ifinit_timer, now.tv_sec+CHECK_BAD_INTERVAL); + + /* If we ever have a RIPv1 interface, assume we always will. + * It might come back if it ever goes away. + */ + if ((ifp->int_state & IS_NO_RIPV2_OUT) + && !(ifp->int_if_flags & IFF_LOOPBACK)) + have_ripv1 = 1; + } + + /* add the authority interfaces */ + for (intnetp = intnets; intnetp!=0; intnetp = intnetp->intnet_next) { + rt = rtget(intnetp->intnet_addr, intnetp->intnet_mask); + if (rt != 0 + && !(rt->rt_state & RS_IF) + && !(rt->rt_state & RS_NET_INT)) { + rtdelete(rt); + rt = 0; + } + if (rt == 0) + rtadd(intnetp->intnet_addr, intnetp->intnet_mask, + loopaddr, loopaddr, + 1, 0, RS_NET_INT, 0); + } + + for (ifp = ifnet; ifp != 0; ifp = ifp->int_next) { + /* Ensure there is always a network route for interfaces, + * after any dead interfaces have been deleted, which + * might affect routes for point-to-point links. + */ + addrouteforif(ifp); + + /* Add routes to the local end of point-to-point interfaces + * using loopback. + */ + if ((ifp->int_if_flags & IFF_POINTOPOINT) + && !(ifp->int_state & IS_REMOTE) + && foundloopback) { + /* Delete any routes to the network address through + * foreign routers. Remove even static routes. + */ + del_static(ifp->int_addr, HOST_MASK, 0); + rt = rtget(ifp->int_addr, HOST_MASK); + if (rt != 0 && rt->rt_router != loopaddr) { + rtdelete(rt); + rt = 0; + } + if (rt != 0) { + if (!(rt->rt_state & RS_LOCAL) + || rt->rt_metric > ifp->int_metric) { + ifp1 = ifp; + } else { + ifp1 = rt->rt_ifp; + } + rtchange(rt,((rt->rt_state | (RS_IF|RS_LOCAL)) + & ~RS_NET_S), + loopaddr, loopaddr, + ifp1->int_metric, 0, + ifp1, rt->rt_time, 0); + } else { + rtadd(ifp->int_addr, HOST_MASK, + loopaddr, loopaddr, + ifp->int_metric, 0, + (RS_IF | RS_LOCAL), ifp); + } + } + } + + prev_complaints = complaints; +} + + +static void +add_net_sub(struct interface *ifp, + naddr dst, + naddr mask, + u_int state) +{ + struct rt_entry *rt; + + rt = rtget(dst, mask); + if (rt != 0) { + if (0 != (rt->rt_state & (RS_STATIC | RS_LOCAL + | RS_MHOME | RS_GW))) + return; + + if ((rt->rt_state & state) != state + || rt->rt_metric != NET_S_METRIC) { + rtchange(rt, rt->rt_state | state, + rt->rt_gate, rt->rt_router, + NET_S_METRIC, rt->rt_tag, + rt->rt_ifp, rt->rt_time, 0); + } + return; + } + + rtadd(dst, mask, ifp->int_addr, ifp->int_addr, + NET_S_METRIC, 0, state, ifp); +} + + +static void +check_net_sub(struct interface *ifp) +{ + struct interface *ifp2; + struct rt_entry *rt; + + /* See if there are any RIPv1 listeners, to determine if + * we need to synthesize a network route for an interface + * on a subnet. + */ + for (ifp2 = ifnet; ifp2; ifp2 = ifp2->int_next) { + if (ifp2 != ifp + && !(ifp->int_state & IS_PASSIVE) + && !(ifp->int_state & IS_NO_RIPV1_OUT) + && !on_net(ifp->int_addr, + ifp2->int_std_net, + ifp2->int_std_mask)) break; - if (maybe == 0 && (*netmatch)(addr, &ifp->int_addr)) - maybe = ifp; } - if (ifp == 0) - ifp = maybe; - return (ifp); + + /* only if running RIPv1 somewhere */ + if (ifp2 != 0) { + ifp->int_state |= IS_NEED_NET_SUB; + add_net_sub(ifp, ifp->int_std_addr, ifp->int_std_mask, + RS_IF | RS_NET_SUB); + + } else { + ifp->int_state &= ~IS_NEED_NET_SUB; + + rt = rtget(ifp->int_std_addr, + ifp->int_std_mask); + if (rt != 0 + && 0 != (rt->rt_state & RS_NET_S) + && rt->rt_ifp == ifp) + rtbad_sub(rt); + } +} + + +/* Add route for interface if not currently installed. + * Create route to other end if a point-to-point link, + * otherwise a route to this (sub)network. + */ +void +addrouteforif(struct interface *ifp) +{ + struct rt_entry *rt; + naddr dst, mask; + + + /* skip sick interfaces + */ + if (ifp->int_state & IS_BROKE) + return; + + /* If the interface on a subnet, then install a RIPv1 route to + * the network as well (unless it is sick). + */ + if (ifp->int_metric != HOPCNT_INFINITY + && !(ifp->int_state & IS_PASSIVE)) { + if (ifp->int_state & IS_SUBNET) { + check_net_sub(ifp); + + } else if ((ifp->int_if_flags & IFF_POINTOPOINT) + && ridhosts) { + + /* The (dis)appearance of other interfaces can change + * the parent (sub)net. + */ + mask = ripv1_mask_net(ifp->int_dstaddr,0,ifp); + if (mask != ifp->int_host_mask) { + rt = rtget(ifp->int_host_addr, + ifp->int_host_mask); + ifp->int_host_addr = htonl(ntohl(ifp->int_dstaddr) + & mask); + ifp->int_host_mask = mask; + if (rt != 0 + && (rt->rt_state & RS_NET_S) + && rt->rt_ifp == ifp) + rtbad_sub(rt); + } + + add_net_sub(ifp, ifp->int_host_addr, + ifp->int_host_mask, + RS_IF | RS_NET_HOST); + } + } + + dst = (0 != (ifp->int_if_flags & (IFF_POINTOPOINT | IFF_LOOPBACK)) + ? ifp->int_dstaddr + : htonl(ifp->int_net)); + + /* We are finished if the right, main interface route exists. + * The right route must be for the right interface, not synthesized + * from a subnet, be a "gateway" or not as appropriate, and so forth. + */ + del_static(dst, ifp->int_mask, 0); + rt = rtget(dst, ifp->int_mask); + if (rt != 0) { + if (rt->rt_ifp != ifp + || rt->rt_router != ifp->int_addr) { + rtdelete(rt); + rt = 0; + } else { + rtchange(rt, ((rt->rt_state | RS_IF) + & ~(RS_NET_S | RS_LOCAL)), + ifp->int_addr, ifp->int_addr, + ifp->int_metric, 0, ifp, now.tv_sec, 0); + } + } + if (rt == 0) { + if (ifp->int_transitions++ > 0) + trace_msg("re-install interface %s", ifp->int_name); + + rtadd(dst, ifp->int_mask, ifp->int_addr, ifp->int_addr, + ifp->int_metric, 0, RS_IF, ifp); + } } diff --git a/usr.sbin/routed/input.c b/usr.sbin/routed/input.c index 5a70e1a..46ae9dc 100644 --- a/usr.sbin/routed/input.c +++ b/usr.sbin/routed/input.c @@ -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; - - 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; + struct sockaddr_in from; + int fromlen, cc; + union pkt_buf inbuf; + + + 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) { + 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''. + /* did the request come from a router? + */ + if (from->sin_port == htons(RIP_PORT)) { + /* yes, ignore it if RIP is off + */ + 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; + } + } + + /* 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 - * (eg, query). + * (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->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 (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->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); + + 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) + if (ntohs(from->sin_port) > IPPORT_RESERVED) { + msglog("trace command from untrusted port on %s", + naddr_ntoa(FROM_NADDR)); 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 (ifp == 0) { + msglog("trace command from unknown router %s", + naddr_ntoa(FROM_NADDR)); return; } - ((char *)rip)[size] = '\0'; - if (rip->rip_cmd == RIPCMD_TRACEON) - traceon(rip->rip_tracefile); - else - traceoff(); + 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)); - return; + } + + 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"); } - 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. + + /* 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 ((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->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 = 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; - } + if (ifp->int_state & IS_BROKE) { + if (TRACEPACKETS) + trace_msg("discard response via" + " broken interface %s\n", + ifp->int_name); 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); + + /* 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; - } - 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); + + 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 (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; + 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; } - /* - * Adjust metric according to incoming interface. + 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; + } + + /* Notice the next-hop. */ - 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)) + 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; - 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++; + + /* 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; - /* - * Update if from gateway and different, - * shorter, or equivalent but old route - * is getting stale. + /* Recognize and ignore a default route we faked + * which is being sent back to us by a machine with + * broken split-horizon. */ - 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; + 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. +/* 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. */ - 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; + 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 { - needupdate++; - if (traceactions) - fprintf(ftrace, "delay dynamic update\n"); + /* 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; } -#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 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 (nextbcast.tv_sec > lastfullupdate.tv_sec + - SUPPLY_INTERVAL - MIN_WAITTIME) - nextbcast.tv_sec = lastfullupdate.tv_sec + - SUPPLY_INTERVAL + 1; + 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); } diff --git a/usr.sbin/routed/main.c b/usr.sbin/routed/main.c index d073a0a..b33e299 100644 --- a/usr.sbin/routed/main.c +++ b/usr.sbin/routed/main.c @@ -35,235 +35,723 @@ static char copyright[] = "@(#) Copyright (c) 1983, 1988, 1993\n\ The Regents of the University of California. All rights reserved.\n"; -#endif /* not lint */ -#ifndef lint static char sccsid[] = "@(#)main.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/ioctl.h> +#include "pathnames.h" +#ifdef sgi +#include "math.h" +#endif +#include <signal.h> +#include <fcntl.h> #include <sys/file.h> -#include <net/if.h> +pid_t mypid; -#include <sys/errno.h> -#include <sys/signal.h> -#include <sys/syslog.h> -#include "pathnames.h" +naddr myaddr; /* system address */ +char myname[MAXHOSTNAMELEN+1]; -int supplier = -1; /* process should supply updates */ -int gateway = 0; /* 1 if we are a gateway to parts beyond */ -int debug = 0; -int bufspace = 127*1024; /* max. input buffer size to request */ +int supplier; /* supply or broadcast updates */ +int supplier_set; +int ipforwarding = 1; /* kernel forwarding on */ -struct rip *msg = (struct rip *)packet; -void hup(), rtdeleteall(), sigtrace(), timer(); +int default_gateway; /* 1=advertise default */ +int background = 1; +int ridhosts; /* 1=reduce host routes */ +int ppp_noage; /* do not age routes on quiet links */ +int mhome; /* 1=want multi-homed host route */ +int advertise_mhome; /* 1=must continue adverising it */ +int auth_ok = 1; /* 1=ignore auth if we do not care */ -main(argc, argv) - int argc; - char *argv[]; +struct timeval epoch; /* when started */ +struct timeval clk, prev_clk; +struct timeval now; /* current idea of time */ +time_t now_stale; +time_t now_garbage; + +struct timeval next_bcast; /* next general broadcast */ +struct timeval no_flash = {EPOCH+SUPPLY_INTERVAL}; /* inhibit flash update */ + +fd_set fdbits; +int sock_max; +int rip_sock = -1; /* RIP socket */ +struct interface *rip_sock_mcast; /* current multicast interface */ +int rt_sock; /* routing socket */ +int rt_sock_seqno; + + +static int get_rip_sock(naddr, int); +static void timevalsub(struct timeval *, struct timeval *, struct timeval *); + +int +main(int argc, + char *argv[]) { - int n, cc, nfd, omask, tflags = 0; - struct sockaddr from; - struct timeval *tvp, waittime; - struct itimerval itval; - register struct rip *query = msg; + int n, mib[4], off; + size_t len; + char *p, *q; + struct timeval wtime, wtime2; + time_t dt; fd_set ibits; - u_char retry; + naddr p_addr_h, p_mask; + struct parm *parmp; + struct interface *ifp; + char *tracename = 0; + - argv0 = argv; -#if BSD >= 43 openlog("routed", LOG_PID | LOG_ODELAY, LOG_DAEMON); - setlogmask(LOG_UPTO(LOG_WARNING)); + ftrace = stdout; + + gettimeofday(&clk, 0); + prev_clk = clk; + epoch = clk; + epoch.tv_sec -= EPOCH; + now.tv_sec = EPOCH; + now_stale = EPOCH - STALE_TIME; + now_garbage = EPOCH - GARBAGE_TIME; + wtime.tv_sec = 0; + + (void)gethostname(myname, sizeof(myname)-1); + (void)gethost(myname, &myaddr); + + while ((n = getopt(argc, argv, "sqdghmpAtT:F:P:")) != EOF) { + switch (n) { + case 's': + supplier = 1; + supplier_set = 1; + break; + + case 'q': + supplier = 0; + supplier_set = 1; + break; + + case 'd': + background = 0; + break; + + case 'g': + default_gateway = 1; + break; + + case 'h': /* suppress extra host routes */ + ridhosts = 1; + break; + + case 'm': /* advertise host route */ + mhome = 1; /* on multi-homed hosts */ + break; + + case 'p': /* do not age routes on quiet */ + ppp_noage = 1; /* point-to-point links */ + break; + + case 'A': + /* Ignore authentication if we do not care. + * Crazy as it is, that is what RFC 1723 requires. + */ + auth_ok = 0; + break; + + case 't': + new_tracelevel++; + break; + + case 'T': + tracename = optarg; + break; + + case 'F': /* minimal routes for SLIP */ + n = HOPCNT_INFINITY-2; + p = strchr(optarg,','); + if (p && *p != '\0') { + n = (int)strtoul(p+1, &q, 0); + if (*q == '\0' + && n <= HOPCNT_INFINITY-2 + && n >= 1) + *p = '\0'; + } + if (!getnet(optarg, &p_addr_h, &p_mask)) { + msglog("routed: bad network;" + " \"-F %s\" ignored", + optarg); + break; + } + parmp = (struct parm*)malloc(sizeof(*parmp)); + bzero(parmp, sizeof(*parmp)); + parmp->parm_next = parms; + parms = parmp; + parmp->parm_a_h = p_addr_h; + parmp->parm_m = p_mask; + parmp->parm_d_metric = n; + break; + + case 'P': + /* handle arbirary, (usually) per-interface + * parameters. + */ + p = parse_parms(optarg); + if (p != 0) { + msglog("routed: bad \"%s\" in \"%s\"", + p, optarg); + } + break; + + default: + goto usage; + } + } + argc -= optind; + argv += optind; + + if (tracename == 0 && argc >= 1) { + tracename = *argv++; + argc--; + } + if (argc != 0) { +usage: + logbad(0, "usage: routed [-sqdghmpAt] [-T /tracefile]" + " [-F net[,metric]] [-P parms]"); + } + + mib[0] = CTL_NET; + mib[1] = PF_INET; + mib[2] = IPPROTO_IP; + mib[3] = IPCTL_FORWARDING; + len = sizeof(ipforwarding); + if (sysctl(mib, 4, &ipforwarding, &len, 0, 0) < 0) + LOGERR("sysctl(IPCTL_FORWARDING)"); + + if (!ipforwarding) { + if (supplier) + msglog("-s incompatible with ipforwarding=0"); + if (default_gateway) { + msglog("-g incompatible with ipforwarding=0"); + default_gateway = 0; + } + supplier = 0; + supplier_set = 1; + } + if (default_gateway) { + if (supplier_set && !supplier) { + msglog("-g and -q incompatible"); + } else { + supplier = 1; + supplier_set = 1; + } + } + + + /* get into the background */ + if (background) { +#ifdef sgi + if (_daemonize(_DF_NOCHDIR,STDOUT_FILENO,STDERR_FILENO,-1)<0) + BADERR(0, "_daemonize()"); #else - openlog("routed", LOG_PID); -#define LOG_UPTO(x) (x) -#define setlogmask(x) (x) + if (daemon(1, 1) < 0) + BADERR(0,"daemon()"); #endif - sp = getservbyname("router", "udp"); - if (sp == NULL) { - fprintf(stderr, "routed: router/udp: unknown service\n"); - exit(1); } - addr.sin_family = AF_INET; - addr.sin_port = sp->s_port; - r = socket(AF_ROUTE, SOCK_RAW, 0); - /* later, get smart about lookingforinterfaces */ - if (r) - shutdown(r, 0); /* for now, don't want reponses */ - else { - fprintf(stderr, "routed: no routing socket\n"); - exit(1); + + mypid = getpid(); + srandom((int)(clk.tv_sec ^ clk.tv_usec ^ mypid)); + + /* prepare socket connected to the kernel. + */ + rt_sock = socket(AF_ROUTE, SOCK_RAW, 0); + if (rt_sock < 0) + BADERR(1,"rt_sock = socket()"); + if (fcntl(rt_sock, F_SETFL, O_NONBLOCK) == -1) + logbad(1, "fcntl(rt_sock) O_NONBLOCK: %s", strerror(errno)); + off = 0; + if (setsockopt(rt_sock, SOL_SOCKET,SO_USELOOPBACK, + &off,sizeof(off)) < 0) + LOGERR("setsockopt(SO_USELOOPBACK,0)"); + + /* prepare Router Discovery socket. + */ + rdisc_sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); + if (rdisc_sock < 0) + BADERR(1,"rdisc_sock = socket()"); + fix_sock(rdisc_sock,"rdisc_sock"); + + fix_select(); + + + if (background && new_tracelevel == 0) + ftrace = 0; + if (tracename != 0) { + trace_on(tracename, 1); + if (new_tracelevel == 0) + new_tracelevel = 1; } - s = getsocket(AF_INET, SOCK_DGRAM, &addr); - if (s < 0) - exit(1); - argv++, argc--; - while (argc > 0 && **argv == '-') { - if (strcmp(*argv, "-s") == 0) { - supplier = 1; - argv++, argc--; + set_tracelevel(); + + /* initialize radix tree */ + rtinit(); + + /* Pick a random part of the second for our output to minimize + * collisions. + * + * Start broadcasting after hearing from other routers, and + * at a random time so a bunch of systems do not get synchronized + * after a power failure. + */ + intvl_random(&next_bcast, EPOCH+MIN_WAITTIME, EPOCH+SUPPLY_INTERVAL); + age_timer.tv_usec = next_bcast.tv_usec; + age_timer.tv_sec = EPOCH+MIN_WAITTIME; + rdisc_timer = next_bcast; + ifinit_timer.tv_usec = next_bcast.tv_usec; + + signal(SIGALRM, sigalrm); + signal(SIGHUP, sigterm); + signal(SIGTERM, sigterm); + signal(SIGINT, sigterm); + signal(SIGUSR1, sigtrace_on); + signal(SIGUSR2, sigtrace_off); + + /* If we have an interface to the wide, wide world, add an entry for + * an Internet default route to the internal tables and advertise it. + * This route is not added to the kernel routes, but this entry + * prevents us from listening to default routes from other + * systems and installing them in the kernel. + */ + if (default_gateway > 0) + rtadd(RIP_DEFAULT, 0, myaddr, myaddr, 1, 0, RS_GW, 0); + + /* Collect an initial view of the world by checking the interface + * configuration and the kludge file. + */ + gwkludge(); + ifinit(); + flush_kern(); + + /* Ask for routes */ + rip_query(); + if (!supplier) + rdisc_sol(); + + /* Loop forever, listening and broadcasting. + */ + for (;;) { + prev_clk = clk; + gettimeofday(&clk, 0); + timevalsub(&wtime2, &clk, &prev_clk); + if (wtime2.tv_sec < 0 + || wtime2.tv_sec > wtime.tv_sec + 5) { + /* Deal with time changes before other housekeeping to + * keep everything straight. + */ + dt = wtime2.tv_sec; + if (dt > 0) + dt -= wtime.tv_sec; + trace_msg("time changed by %d sec\n", dt); + epoch.tv_sec += dt; + } + timevalsub(&now, &clk, &epoch); + now_stale = now.tv_sec - STALE_TIME; + now_garbage = now.tv_sec - GARBAGE_TIME; + + /* deal with interrupts that should affect tracing */ + set_tracelevel(); + + if (stopint != 0) { + if (supplier) { + rip_bcast(0); + rdisc_adv(); + } + trace_off("exiting",""); + exit(stopint | 128); + } + + /* look for new or dead interfaces */ + timevalsub(&wtime, &ifinit_timer, &now); + if (wtime.tv_sec <= 0) { + ifinit(); + rip_query(); continue; } - if (strcmp(*argv, "-q") == 0) { - supplier = 0; - argv++, argc--; + + /* If it is time, then broadcast our routes. + */ + if (supplier || advertise_mhome) { + timevalsub(&wtime2, &next_bcast, &now); + if (wtime2.tv_sec <= 0) { + /* Synchronize the aging and broadcast + * timers to minimize awakenings + */ + age(0); + + rip_bcast(0); + + /* It is desirable to send routing updates + * regularly. So schedule the next update + * 30 seconds after the previous one was + * secheduled, instead of 30 seconds after + * the previous update was finished. + * Even if we just started after discovering + * a 2nd interface or were otherwise delayed, + * pick a 30-second aniversary of the + * original broadcast time. + */ + n = 1 + (0-wtime2.tv_sec)/SUPPLY_INTERVAL; + next_bcast.tv_sec += n*SUPPLY_INTERVAL; + + continue; + } + + if (timercmp(&wtime2, &wtime, <)) + wtime = wtime2; + } + + /* If we need a flash update, either do it now or + * set the delay to end when it is time. + * + * If we are within MIN_WAITTIME seconds of a full update, + * do not bother. + */ + if (need_flash + && supplier + && no_flash.tv_sec+MIN_WAITTIME < next_bcast.tv_sec) { + /* accurate to the millisecond */ + if (!timercmp(&no_flash, &now, >)) + rip_bcast(1); + timevalsub(&wtime2, &no_flash, &now); + if (timercmp(&wtime2, &wtime, <)) + wtime = wtime2; + } + + /* trigger the main aging timer. + */ + timevalsub(&wtime2, &age_timer, &now); + if (wtime2.tv_sec <= 0) { + age(0); continue; } - if (strcmp(*argv, "-t") == 0) { - tflags++; - setlogmask(LOG_UPTO(LOG_DEBUG)); - argv++, argc--; + if (timercmp(&wtime2, &wtime, <)) + wtime = wtime2; + + /* update the kernel routing table + */ + timevalsub(&wtime2, &need_kern, &now); + if (wtime2.tv_sec <= 0) { + age(0); continue; } - if (strcmp(*argv, "-d") == 0) { - debug++; - setlogmask(LOG_UPTO(LOG_DEBUG)); - argv++, argc--; + if (timercmp(&wtime2, &wtime, <)) + wtime = wtime2; + + /* take care of router discovery, + * but do it to the millisecond + */ + if (!timercmp(&rdisc_timer, &now, >)) { + rdisc_age(0); continue; } - if (strcmp(*argv, "-g") == 0) { - gateway = 1; - argv++, argc--; + timevalsub(&wtime2, &rdisc_timer, &now); + if (timercmp(&wtime2, &wtime, <)) + wtime = wtime2; + + + /* wait for input or a timer to expire. + */ + ibits = fdbits; + trace_flush(); + n = select(sock_max, &ibits, 0, 0, &wtime); + if (n <= 0) { + if (n < 0 && errno != EINTR && errno != EAGAIN) + BADERR(1,"select"); continue; } - fprintf(stderr, - "usage: routed [ -s ] [ -q ] [ -t ] [ -g ]\n"); - exit(1); + + if (FD_ISSET(rt_sock, &ibits)) { + read_rt(); + n--; + } + if (rdisc_sock >= 0 && FD_ISSET(rdisc_sock, &ibits)) { + read_d(); + n--; + } + if (rip_sock >= 0 && FD_ISSET(rip_sock, &ibits)) { + read_rip(rip_sock, 0); + n--; + } + + for (ifp = ifnet; n > 0 && 0 != ifp; ifp = ifp->int_next) { + if (ifp->int_rip_sock >= 0 + && FD_ISSET(ifp->int_rip_sock, &ibits)) { + read_rip(ifp->int_rip_sock, ifp); + n--; + } + } } +} - if (debug == 0 && tflags == 0) - daemon(0, 0); - /* - * Any extra argument is considered - * a tracing log file. - */ - if (argc > 0) - traceon(*argv); - while (tflags-- > 0) - bumploglevel(); - - (void) gettimeofday(&now, (struct timezone *)NULL); - /* - * Collect an initial view of the world by - * checking the interface configuration and the gateway kludge - * file. Then, send a request packet on all - * directly connected networks to find out what - * everyone else thinks. + +/* ARGSUSED */ +void +sigalrm(int sig) +{ + /* Historically, SIGALRM would cause the daemon to check for + * new and broken interfaces. */ - rtinit(); - ifinit(); - gwkludge(); - if (gateway > 0) - rtdefault(); - if (supplier < 0) - supplier = 0; - query->rip_cmd = RIPCMD_REQUEST; - query->rip_vers = RIPVERSION; - if (sizeof(query->rip_nets[0].rip_dst.sa_family) > 1) /* XXX */ - query->rip_nets[0].rip_dst.sa_family = htons((u_short)AF_UNSPEC); - else - query->rip_nets[0].rip_dst.sa_family = AF_UNSPEC; - query->rip_nets[0].rip_metric = htonl((u_long)HOPCNT_INFINITY); - toall(sndmsg); - signal(SIGALRM, timer); - signal(SIGHUP, hup); - signal(SIGTERM, hup); - signal(SIGINT, rtdeleteall); - signal(SIGUSR1, sigtrace); - signal(SIGUSR2, sigtrace); - itval.it_interval.tv_sec = TIMER_RATE; - itval.it_value.tv_sec = TIMER_RATE; - itval.it_interval.tv_usec = 0; - itval.it_value.tv_usec = 0; - srandom(getpid()); - if (setitimer(ITIMER_REAL, &itval, (struct itimerval *)NULL) < 0) - syslog(LOG_ERR, "setitimer: %m\n"); - - FD_ZERO(&ibits); - nfd = s + 1; /* 1 + max(fd's) */ - for (;;) { - FD_SET(s, &ibits); - /* - * If we need a dynamic update that was held off, - * needupdate will be set, and nextbcast is the time - * by which we want select to return. Compute time - * until dynamic update should be sent, and select only - * until then. If we have already passed nextbcast, - * just poll. - */ - if (needupdate) { - waittime = nextbcast; - timevalsub(&waittime, &now); - if (waittime.tv_sec < 0) { - waittime.tv_sec = 0; - waittime.tv_usec = 0; + ifinit_timer.tv_sec = now.tv_sec; +} + + +/* watch for fatal signals */ +void +sigterm(int sig) +{ + stopint = sig; + (void)signal(sig, SIG_DFL); /* catch it only once */ +} + + +void +fix_select(void) +{ + struct interface *ifp; + + + FD_ZERO(&fdbits); + sock_max = 0; + + FD_SET(rt_sock, &fdbits); + if (sock_max <= rt_sock) + sock_max = rt_sock+1; + if (rip_sock >= 0) { + FD_SET(rip_sock, &fdbits); + if (sock_max <= rip_sock) + sock_max = rip_sock+1; + } + for (ifp = ifnet; 0 != ifp; ifp = ifp->int_next) { + if (ifp->int_rip_sock >= 0) { + FD_SET(ifp->int_rip_sock, &fdbits); + if (sock_max <= ifp->int_rip_sock) + sock_max = ifp->int_rip_sock+1; + } + } + if (rdisc_sock >= 0) { + FD_SET(rdisc_sock, &fdbits); + if (sock_max <= rdisc_sock) + sock_max = rdisc_sock+1; + } +} + + +void +fix_sock(int sock, + char *name) +{ + int on; +#define MIN_SOCKBUF (4*1024) + static int rbuf; + + if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1) + logbad(1, "fcntl(%s) O_NONBLOCK: %s", + name, strerror(errno)); + on = 1; + if (setsockopt(sock, SOL_SOCKET,SO_BROADCAST, + &on,sizeof(on)) < 0) + msglog("setsockopt(%s,SO_BROADCAST): %s", + name, strerror(errno)); + if (rbuf >= MIN_SOCKBUF) { + if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, + &rbuf, sizeof(rbuf)) < 0) + msglog("setsockopt(%s,SO_RCVBUF=%d): %s", + name, rbuf, strerror(errno)); + } else { + for (rbuf = 60*1024; ; rbuf -= 4096) { + if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, + &rbuf, sizeof(rbuf)) == 0) { + trace_msg("RCVBUF=%d\n", rbuf); + break; } - if (traceactions) - fprintf(ftrace, - "select until dynamic update %d/%d sec/usec\n", - waittime.tv_sec, waittime.tv_usec); - tvp = &waittime; - } else - tvp = (struct timeval *)NULL; - n = select(nfd, &ibits, 0, 0, tvp); - if (n <= 0) { - /* - * Need delayed dynamic update if select returned - * nothing and we timed out. Otherwise, ignore - * errors (e.g. EINTR). - */ - if (n < 0) { - if (errno == EINTR) - continue; - syslog(LOG_ERR, "select: %m"); + if (rbuf < MIN_SOCKBUF) { + msglog("setsockopt(%s,SO_RCVBUF = %d): %s", + name, rbuf, strerror(errno)); + break; } - omask = sigblock(sigmask(SIGALRM)); - if (n == 0 && needupdate) { - if (traceactions) - fprintf(ftrace, - "send delayed dynamic update\n"); - (void) gettimeofday(&now, - (struct timezone *)NULL); - toall(supply, RTS_CHANGED, - (struct interface *)NULL); - lastbcast = now; - needupdate = 0; - nextbcast.tv_sec = 0; + } + } +} + + +/* get a rip socket + */ +static int /* <0 or file descriptor */ +get_rip_sock(naddr addr, + int serious) /* 1=failure to bind is serious */ +{ + struct sockaddr_in sin; + unsigned char ttl; + int s; + + + if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) + BADERR(1,"rip_sock = socket()"); + + bzero(&sin,sizeof(sin)); +#ifdef _HAVE_SIN_LEN + sin.sin_len = sizeof(sin); +#endif + sin.sin_family = AF_INET; + sin.sin_port = htons(RIP_PORT); + sin.sin_addr.s_addr = addr; + if (bind(s, (struct sockaddr *)&sin,sizeof(sin)) < 0) { + if (serious) + BADERR(errno != EADDRINUSE, "bind(rip_sock)"); + return -1; + } + fix_sock(s,"rip_sock"); + + ttl = 1; + if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_TTL, + &ttl, sizeof(ttl)) < 0) + DBGERR(1,"rip_sock setsockopt(IP_MULTICAST_TTL)"); + + return s; +} + + +/* turn off main RIP socket */ +void +rip_off(void) +{ + struct interface *ifp; + register naddr addr; + + + if (rip_sock >= 0) { + trace_msg("turn off RIP\n"); + + (void)close(rip_sock); + rip_sock = -1; + + /* get non-broadcast sockets to listen to queries. + */ + for (ifp = ifnet; ifp != 0; ifp = ifp->int_next) { + if (ifp->int_rip_sock < 0 + && !(ifp->int_state & IS_ALIAS)) { + addr = ((ifp->int_if_flags & IFF_POINTOPOINT) + ? ifp->int_dstaddr + : ifp->int_addr); + ifp->int_rip_sock = get_rip_sock(addr, 0); } - sigsetmask(omask); - continue; } - (void) gettimeofday(&now, (struct timezone *)NULL); - omask = sigblock(sigmask(SIGALRM)); -#ifdef doesntwork -/* -printf("s %d, ibits %x index %d, mod %d, sh %x, or %x &ibits %x\n", - s, - ibits.fds_bits[0], - (s)/(sizeof(fd_mask) * 8), - ((s) % (sizeof(fd_mask) * 8)), - (1 << ((s) % (sizeof(fd_mask) * 8))), - ibits.fds_bits[(s)/(sizeof(fd_mask) * 8)] & (1 << ((s) % (sizeof(fd_mask) * 8))), - &ibits - ); -*/ - if (FD_ISSET(s, &ibits)) -#else - if (ibits.fds_bits[s/32] & (1 << s)) + + fix_select(); + + age(0); + } +} + + +/* Prepare socket used for RIP. + */ +void +rip_on(struct interface *ifp) +{ + struct ip_mreq m; + + + if (rip_sock >= 0) { + if (ifp != 0 + && 0 == (ifp->int_state & (IS_NO_RIP_IN|IS_PASSIVE)) + && (ifp->int_if_flags & IFF_MULTICAST) +#ifdef MCAST_PPP_BUG + && !(ifp->int_if_flags & IFF_POINTOPOINT) #endif - process(s); - /* handle ICMP redirects */ - sigsetmask(omask); + && !(ifp->int_state & IS_ALIAS)) { + m.imr_multiaddr.s_addr = htonl(INADDR_RIP_GROUP); + m.imr_interface.s_addr = ((ifp->int_if_flags + & IFF_POINTOPOINT) + ? ifp->int_dstaddr + : ifp->int_addr); + if (setsockopt(rip_sock,IPPROTO_IP, IP_ADD_MEMBERSHIP, + &m, sizeof(m)) < 0) + DBGERR(1,"setsockopt(IP_ADD_MEMBERSHIP RIP)"); + } + return; + } + + if (rip_interfaces > 0 && !rdisc_ok) { + trace_msg("turn on RIP\n"); + + /* Close all of the query sockets so that we can open + * the main socket. SO_REUSEPORT is not a solution, + * since that would let two daemons bind to the broadcast + * socket. + */ + for (ifp = ifnet; ifp != 0; ifp = ifp->int_next) { + if (ifp->int_rip_sock >= 0) { + (void)close(ifp->int_rip_sock); + ifp->int_rip_sock = -1; + } + } + + rip_sock = get_rip_sock(INADDR_ANY, 1); + rip_sock_mcast = 0; + + /* Do not advertise anything until we have heard something + */ + if (next_bcast.tv_sec < now.tv_sec+MIN_WAITTIME) + next_bcast.tv_sec = now.tv_sec+MIN_WAITTIME; + + for (ifp = ifnet; ifp != 0; ifp = ifp->int_next) { + if ((ifp->int_state & IS_NO_RIP_IN) != IS_NO_RIP_IN) + ifp->int_state &= ~IS_RIP_QUERIED; + + if ((ifp->int_if_flags & IFF_MULTICAST) + && !(ifp->int_state & IS_ALIAS)) { + m.imr_multiaddr.s_addr = htonl(INADDR_RIP_GROUP); + m.imr_interface.s_addr = ifp->int_addr; + if (setsockopt(rip_sock, IPPROTO_IP, + IP_ADD_MEMBERSHIP, + &m, sizeof(m)) < 0) + DBGERR(1,"setsockopt(IP_ADD_MEMBERSHIP RIP)"); + } + } + + ifinit_timer.tv_sec = now.tv_sec; + + fix_select(); + + } else if (ifp != 0 + && ifp->int_rip_sock < 0 + && !(ifp->int_state & IS_ALIAS)) { + /* RIP is off, so ensure there are sockets on which + * to listen for queries. + */ + ifp->int_rip_sock = get_rip_sock(ifp->int_addr, 0); + + fix_select(); } } -timevaladd(t1, t2) - struct timeval *t1, *t2; + +/* get a random instant in an interval + */ +void +intvl_random(struct timeval *tp, /* put value here */ + u_long lo, /* value is after this second */ + u_long hi) /* and before this */ +{ + tp->tv_sec = (time_t)(hi == lo + ? lo + : (lo + random() % ((hi - lo)))); + tp->tv_usec = random() % 1000000; +} + + +void +timevaladd(struct timeval *t1, + struct timeval *t2) { t1->tv_sec += t2->tv_sec; @@ -273,79 +761,57 @@ timevaladd(t1, t2) } } -timevalsub(t1, t2) - struct timeval *t1, *t2; -{ - t1->tv_sec -= t2->tv_sec; - if ((t1->tv_usec -= t2->tv_usec) < 0) { +/* t1 = t2 - t3 + */ +static void +timevalsub(struct timeval *t1, + struct timeval *t2, + struct timeval *t3) +{ + t1->tv_sec = t2->tv_sec - t3->tv_sec; + if ((t1->tv_usec = t2->tv_usec - t3->tv_usec) < 0) { t1->tv_sec--; t1->tv_usec += 1000000; } } -process(fd) - int fd; + +void +msglog(char *p, ...) { - struct sockaddr from; - int fromlen, cc; - union { - char buf[MAXPACKETSIZE+1]; - struct rip rip; - } inbuf; + va_list args; - for (;;) { - fromlen = sizeof (from); - cc = recvfrom(fd, &inbuf, sizeof (inbuf), 0, &from, &fromlen); - if (cc <= 0) { - if (cc < 0 && errno != EWOULDBLOCK) - perror("recvfrom"); - break; - } - if (fromlen != sizeof (struct sockaddr_in)) - break; - rip_input(&from, &inbuf.rip, cc); + trace_flush(); + + va_start(args, p); + vsyslog(LOG_ERR, p, args); + + if (ftrace != 0) { + if (ftrace == stdout) + (void)fputs("routed: ", ftrace); + (void)vfprintf(ftrace, p, args); + (void)fputc('\n', ftrace); } } -getsocket(domain, type, sin) - int domain, type; - struct sockaddr_in *sin; + +void +logbad(int dump, char *p, ...) { - int sock, on = 1; + va_list args; - if ((sock = socket(domain, type, 0)) < 0) { - perror("socket"); - syslog(LOG_ERR, "socket: %m"); - return (-1); - } -#ifdef SO_BROADCAST - if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &on, sizeof (on)) < 0) { - syslog(LOG_ERR, "setsockopt SO_BROADCAST: %m"); - close(sock); - return (-1); - } -#endif -#ifdef SO_RCVBUF - for (on = bufspace; ; on -= 1024) { - if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, - &on, sizeof (on)) == 0) - break; - if (on <= 8*1024) { - syslog(LOG_ERR, "setsockopt SO_RCVBUF: %m"); - break; - } - } - if (traceactions) - fprintf(ftrace, "recv buf %d\n", on); -#endif - if (bind(sock, (struct sockaddr *)sin, sizeof (*sin)) < 0) { - perror("bind"); - syslog(LOG_ERR, "bind: %m"); - close(sock); - return (-1); - } - if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1) - syslog(LOG_ERR, "fcntl O_NONBLOCK: %m\n"); - return (sock); + trace_flush(); + + va_start(args, p); + vsyslog(LOG_ERR, p, args); + + (void)fputs("routed: ", stderr); + (void)vfprintf(stderr, p, args); + (void)fputs("; giving up\n",stderr); + (void)fflush(stderr); + + if (dump) + abort(); + exit(1); } diff --git a/usr.sbin/routed/output.c b/usr.sbin/routed/output.c index 528f7fc..43788a2 100644 --- a/usr.sbin/routed/output.c +++ b/usr.sbin/routed/output.c @@ -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. + +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 */ -toall(f, rtstate, skipif) - int (*f)(); - int rtstate; - struct interface *skipif; +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 */ { - register struct interface *ifp; - register struct sockaddr *dst; - register int flags; - extern struct interface *ifnet; + struct sockaddr_in sin; + int flags; + char *msg; + int res, serrno; + naddr tgt_mcast; + int soc; - 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); + 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; } -/* - * Output a preformed packet. + +/* install authentication if appropriate */ -/*ARGSUSED*/ -sndmsg(dst, flags, ifp, rtstate) - struct sockaddr *dst; - int flags; - struct interface *ifp; - int rtstate; +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++; + } +} - (*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; - - 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). + static int init = 1; + struct rt_entry *rt; + int metric; + + + 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. + 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; + } + } + + /* 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. */ - if (rtstate && (rt->rt_state & rtstate) == 0) + 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 + static struct sockaddr_in dst = {AF_INET}; +#endif + 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; - /* - * Limit the spread of subnet information - * to those who are interested. + + /* Prefer RIPv1 announcements unless RIPv2 is on and + * RIPv2 is off. */ - if (doinghost == 0 && rt->rt_state & RTS_SUBNET) { - if (rt->rt_dst.sa_family != dst->sa_family) - continue; - if ((*sendroute)(rt, dst) == 0) + if (ifp->int_state & IS_NO_RIPV1_OUT) { + if (ifp->int_state & IS_NO_RIPV2_OUT) continue; + vers = RIPv2; + } else { + vers = RIPv1; } - 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->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 && (ifp->int_flags & - (IFF_BROADCAST | IFF_POINTOPOINT | IFF_REMOTE)) == 0) - return; - n = msg->rip_nets; - npackets++; + 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; } - 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); + + supply(&dst, ifp, type, flash, vers); + } + + 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 -#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++; - } - 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); + 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); } } diff --git a/usr.sbin/routed/pathnames.h b/usr.sbin/routed/pathnames.h index 461d82b..9b2c28c 100644 --- a/usr.sbin/routed/pathnames.h +++ b/usr.sbin/routed/pathnames.h @@ -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" diff --git a/usr.sbin/routed/routed.8 b/usr.sbin/routed/routed.8 index fe4538d..eac9d5e 100644 --- a/usr.sbin/routed/routed.8 +++ b/usr.sbin/routed/routed.8 @@ -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 s +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 +is the opposite of the +.Fl s +option. .It Fl d -Enable additional debugging information to be logged, -such as bad packets received. +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. +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. -.It Fl q -This -is the opposite of the -.Fl s -option. +.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 -.Pp -The -.Cm net -or +.Bd -ragged .Cm host -keyword indicates if the route is to a network or specific host. +.Ar Hname +.Cm gateway +.Ar Gname +.Cm metric +.Ar value +.Pf < Cm passive No \&| +.Cm active No \&| +.Cm extern Ns > +.Ed .Pp -.Ar Name1 -is the name of the destination network or host. This may be a -symbolic name located in +.Ar Nname +or +.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 mask +is an optional number between 1 and 32 indicating the netmask associated +with +.Ar Nname . .Pp -.Ar Name2 -is the name or address of the gateway to which messages should +.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 diff --git a/usr.sbin/routed/trace.c b/usr.sbin/routed/trace.c index f630001..608ce04 100644 --- a/usr.sbin/routed/trace.c +++ b/usr.sbin/routed/trace.c @@ -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; + return s; +} -traceinit(ifp) - register struct interface *ifp; + +/* 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 int iftraceinit(); + static struct timeval last; - 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); + if (last.tv_sec != now.tv_sec + || last.tv_usec != now.tv_usec) { + (void)fprintf(ftrace, "--- %s ---\n", ts(now.tv_sec)); + last = now; + } } -static -iftraceinit(ifp, ifd) - struct interface *ifp; - register struct ifdebug *ifd; + +static void +tmsg(char *msg1, char* msg2) { - 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; + if (ftrace != 0) { + lastlog(); + (void)fprintf(ftrace, "%s%s\n", msg1,msg2); } - ifd->ifd_if = ifp; - return (1); } -traceon(file) - char *file; + +static void +trace_close(char *msg1, char *msg2) { - struct stat stbuf; + int fd; - 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)); + fflush(stdout); + fflush(stderr); + + if (ftrace != 0) { + tmsg(msg1,msg2); + fflush(ftrace); + + 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; + } + } } -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)); +void +trace_flush(void) +{ + if (ftrace != 0) { fflush(ftrace); - (void) dup2(fd, 1); - (void) dup2(fd, 2); - (void) close(fd); - fclose(ftrace); - ftrace = NULL; + if (ferror(ftrace)) + trace_off("tracing off: ", strerror(ferror(ftrace))); } - traceactions = 0; - tracehistory = 0; - tracepackets = 0; - tracecontents = 0; } + void -sigtrace(s) - int s; +trace_off(char *msg1, char *msg2) { + trace_close(msg1, msg2); - if (s == SIGUSR2) - traceoff(); - else if (ftrace == NULL && savetracename) - traceon(savetracename); - else - bumploglevel(); + new_tracelevel = tracelevel = 0; } -/* - * 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 +trace_on(char *filename, + int trusted) { + struct stat stbuf; + FILE *n_ftrace; - (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)); + + 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; + } + 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 { - tracepackets++; - tracecontents++; - tracehistory = 0; - if (ftrace) - fprintf(ftrace, "Tracing packet contents started %s\n", - ctime((time_t *)&now.tv_sec)); + tmsg("trace command",""); } - if (ftrace) - fflush(ftrace); } -trace(ifd, who, p, len, m) - register struct ifdebug *ifd; - struct sockaddr *who; - char *p; - int len, m; + +/* ARGSUSED */ +void +sigtrace_on(int s) { - register struct iftrace *t; + new_tracelevel++; + tracelevel_msg = "SIGUSR1: "; +} - if (ifd->ifd_records == 0) - 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 (len > 0) - bcopy(p, t->ift_packet, len); - t->ift_size = len; - t->ift_metric = m; + +/* ARGSUSED */ +void +sigtrace_off(int s) +{ + new_tracelevel--; + tracelevel_msg = "SIGUSR2: "; } -traceaction(fd, action, rt) - FILE *fd; - char *action; - struct rt_entry *rt; + +/* 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) { - 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 } + 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; + + 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, "%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; +} + + +/* 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, " 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"; + + return s; +} + + +/* 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++; } - 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(); + if (field != 0) { + (void)fputc(first ? '<' : '|', ftrace); + (void)fprintf(ftrace, "%#x", field); + first = 0; + } + + if (!first) + (void)fputs("> ", ftrace); } -tracenewmetric(fd, rt, newmetric) - FILE *fd; - struct rt_entry *rt; - int newmetric; + +void +trace_if(char *act, + struct interface *ifp) { - struct sockaddr_in *dst, *gate; + 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); +} + - if (fd == NULL) +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 (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; + 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)); } - 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(); } -dumpif(fd, ifp) - FILE *fd; - register struct interface *ifp; + +void +trace_msg(char *p, ...) { - 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"); - } + va_list args; + + if (!TRACEACTIONS || ftrace == 0) + return; + + lastlog(); + va_start(args, p); + vfprintf(ftrace, p, args); } -dumptrace(fd, dir, ifd) - FILE *fd; - char *dir; - register struct ifdebug *ifd; + +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) { - register struct iftrace *t; - char *cp = !strcmp(dir, "to") ? "Output" : "Input"; + if (ftrace == 0) + return; - if (ifd->ifd_front == ifd->ifd_records && - ifd->ifd_front->ift_size == 0) { - fprintf(fd, "%s: no packets.\n", cp); - fflush(fd); + if (rt->rt_metric == metric + && rt->rt_gate == gate + && rt->rt_router == router + && rt->rt_state == state + && rt->rt_tag == tag) 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(); + (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))); } -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; + +void +trace_add_del(char * action, struct rt_entry *rt) { - register struct rip *msg = (struct rip *)cp; - register struct netinfo *n; + u_int state = rt->rt_state; - if (fd == NULL) + 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); + + 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; - } - if (tracepackets && tracecontents == 0) { - fflush(fd); + + 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; } - switch (msg->rip_cmd) { + (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 (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 (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_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(); } |