summaryrefslogtreecommitdiffstats
path: root/usr.sbin/routed
diff options
context:
space:
mode:
authorwollman <wollman@FreeBSD.org>1996-05-30 16:31:46 +0000
committerwollman <wollman@FreeBSD.org>1996-05-30 16:31:46 +0000
commit516f199fd34286e05546181e1dd2a4fc5be0cec6 (patch)
tree48549499fb1d157da3b508a4087652c73d4b3f75 /usr.sbin/routed
parent29f9ba3bbb34d92e4f37152e892f5f608754e24f (diff)
downloadFreeBSD-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/Makefile8
-rw-r--r--usr.sbin/routed/defs.h555
-rw-r--r--usr.sbin/routed/if.c1077
-rw-r--r--usr.sbin/routed/input.c874
-rw-r--r--usr.sbin/routed/main.c968
-rw-r--r--usr.sbin/routed/output.c790
-rw-r--r--usr.sbin/routed/pathnames.h5
-rw-r--r--usr.sbin/routed/routed.8575
-rw-r--r--usr.sbin/routed/trace.c889
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();
}
OpenPOWER on IntegriCloud