From 5ad8429e52c73cfc64a037d4edd40d167e9724be Mon Sep 17 00:00:00 2001 From: wollman Date: Mon, 16 Sep 1996 16:51:32 +0000 Subject: Virgin import of new BSD/SGI routed. This update contains a number of important bug fixes. Obtained from: Vernon J. Schryver --- sbin/routed/defs.h | 566 ++++++++++++ sbin/routed/if.c | 1153 ++++++++++++++++++++++++ sbin/routed/input.c | 741 ++++++++++++++++ sbin/routed/main.c | 830 +++++++++++++++++ sbin/routed/output.c | 874 ++++++++++++++++++ sbin/routed/parms.c | 630 +++++++++++++ sbin/routed/pathnames.h | 50 ++ sbin/routed/radix.c | 895 +++++++++++++++++++ sbin/routed/radix.h | 161 ++++ sbin/routed/rdisc.c | 1032 +++++++++++++++++++++ sbin/routed/routed.8 | 605 +++++++++++++ sbin/routed/rtquery/rtquery.8 | 103 +++ sbin/routed/rtquery/rtquery.c | 654 ++++++++++++++ sbin/routed/table.c | 1970 +++++++++++++++++++++++++++++++++++++++++ sbin/routed/trace.c | 887 +++++++++++++++++++ 15 files changed, 11151 insertions(+) create mode 100644 sbin/routed/defs.h create mode 100644 sbin/routed/if.c create mode 100644 sbin/routed/input.c create mode 100644 sbin/routed/main.c create mode 100644 sbin/routed/output.c create mode 100644 sbin/routed/parms.c create mode 100644 sbin/routed/pathnames.h create mode 100644 sbin/routed/radix.c create mode 100644 sbin/routed/radix.h create mode 100644 sbin/routed/rdisc.c create mode 100644 sbin/routed/routed.8 create mode 100644 sbin/routed/rtquery/rtquery.8 create mode 100644 sbin/routed/rtquery/rtquery.c create mode 100644 sbin/routed/table.c create mode 100644 sbin/routed/trace.c (limited to 'sbin/routed') diff --git a/sbin/routed/defs.h b/sbin/routed/defs.h new file mode 100644 index 0000000..452b71a --- /dev/null +++ b/sbin/routed/defs.h @@ -0,0 +1,566 @@ +/* + * Copyright (c) 1983, 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)defs.h 8.1 (Berkeley) 6/5/93 + * + * $NetBSD$ + */ + +#ifndef __NetBSD__ +#ident "$Revision: 1.16 $" +#endif + +/* 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 +#include +#include +#include +#include +#include +#ifdef sgi +#include +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef sgi +#include +#else +#include "radix.h" +#endif +#include +#include +#include +#include +#include +#define RIPVERSION RIPv2 +#include + + +/* 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 +#ifdef __NetBSD__ +#define naddr u_int32_t +#else +#define naddr u_long +#endif +#define _HAVE_SA_LEN +#define _HAVE_SIN_LEN +#endif + +/* 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 */ + +#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 */ + +#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; +}; + + +/* no more routes than this, to protect ourself in case something goes + * whacko and starts broadcast zillions of bogus routes. + */ +#define MAX_ROUTES (128*1024) +extern int total_routes; + +/* 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_INT 0x002 /* authority route */ +# define RS_NET_SYN 0x004 /* fake net route for subnet */ +# define RS_NO_NET_SYN (RS_LOCAL | RS_LOCAL | RS_IF) +# define RS_SUBNET 0x008 /* subnet route from any source */ +# define RS_LOCAL 0x010 /* loopback for pt-to-pt */ +# define RS_MHOME 0x020 /* from -m */ +# define RS_STATIC 0x040 /* from the kernel */ +# define RS_RDISC 0x080 /* 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_poison_metric; /* to notice maximum recently */ + time_t rt_poison_time; /* advertised metric */ +}; +#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_state,ifp) (0 == ((rt_state) & (RS_MHOME | RS_STATIC \ + | RS_NET_SYN | RS_RDISC)) \ + && (!((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 + * - or it is a host route advertised by a system for itself + */ +#define BETTER_LINK(rt,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) \ + || (RT_ISHOST(rt) \ + && (rt)->rt_dst == (A)->rts_router \ + && (A)->rts_metric == (B)->rts_metric))) + + +/* An "interface" is similar to a kernel ifnet structure, except it also + * handles "logical" or "IS_REMOTE" interfaces (remote gateways). + */ +struct interface { + struct interface *int_next, *int_prev; + char int_name[IFNAMSIZ+15+1]; /* big enough for IS_REMOTE */ + u_short int_index; + naddr int_addr; /* address on this host (net order) */ + naddr int_brdaddr; /* broadcast address (n) */ + naddr int_dstaddr; /* other end of pt-to-pt link (n) */ + naddr int_net; /* working network # (host order)*/ + naddr int_mask; /* working net mask (host order) */ + naddr int_ripv1_mask; /* for inferring a mask (n) */ + 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) */ + int int_rip_sock; /* for queries */ + int int_if_flags; /* some bits copied from kernel */ + u_int int_state; + time_t int_act_time; /* last thought healthy */ + u_short int_transitions; /* times gone up-down */ + char int_metric; + char int_d_metric; /* for faked default route */ + struct int_data { + u_int ipackets; /* previous network stats */ + u_int ierrors; + u_int opackets; + u_int oerrors; +#ifdef sgi + u_int odrops; +#endif + time_t ts; /* timestamp on network stats */ + } int_data; + 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; +}; + +/* bits in int_state */ +#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_SICK 0x0000400 /* seems to be broken */ +#define IS_DUP 0x0000800 /* has a duplicate address */ +#define IS_ACTIVE 0x0001000 /* heard from it at least once */ +#define IS_NEED_NET_SYN 0x0002000 /* need RS_NET_SYN route */ +#define IS_NO_AG 0x0004000 /* do not aggregate subnets */ +#define IS_NO_SUPER_AG 0x0008000 /* do not aggregate networks */ +#define IS_NO_RIPV1_IN 0x0010000 /* no RIPv1 input at all */ +#define IS_NO_RIPV2_IN 0x0020000 /* no RIPv2 input at all */ +#define IS_NO_RIP_IN (IS_NO_RIPV1_IN | IS_NO_RIPV2_IN) +#define IS_RIP_IN_OFF(s) (((s) & IS_NO_RIP_IN) == IS_NO_RIP_IN) +#define IS_NO_RIPV1_OUT 0x0040000 /* no RIPv1 output at all */ +#define IS_NO_RIPV2_OUT 0x0080000 /* no RIPv2 output at all */ +#define IS_NO_RIP_OUT (IS_NO_RIPV1_OUT | IS_NO_RIPV2_OUT) +#define IS_NO_RIP (IS_NO_RIP_OUT | IS_NO_RIP_IN) +#define IS_RIP_OUT_OFF(s) (((s) & IS_NO_RIP_OUT) == IS_NO_RIP_OUT) +#define IS_RIP_OFF(s) (((s) & IS_NO_RIP) == IS_NO_RIP) +#define IS_NO_ADV_IN 0x0100000 +#define IS_NO_SOL_OUT 0x0200000 /* no solicitations */ +#define IS_SOL_OUT 0x0400000 /* send solicitations */ +#define GROUP_IS_SOL (IS_NO_ADV_IN|IS_NO_SOL_OUT) +#define IS_NO_ADV_OUT 0x0800000 /* do not advertise rdisc */ +#define IS_ADV_OUT 0x1000000 /* advertise rdisc */ +#define GROUP_IS_ADV (IS_NO_ADV_OUT|IS_ADV_OUT) +#define IS_BCAST_RDISC 0x2000000 /* broadcast instead of multicast */ +#define IS_NO_RDISC (IS_NO_ADV_IN | IS_NO_SOL_OUT | IS_NO_ADV_OUT) +#define IS_PM_RDISC 0x4000000 /* poor-man's router discovery */ + +#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; + naddr ag_nhop; + 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 0x001 /* 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_IF 0x020 /* for an interface */ +#define AGS_RIPV2 0x040 /* send only as RIPv2 */ +#define AGS_FINE_GATE 0x080 /* ignore differing ag_gate when this + * has the finer netmask */ +#define AGS_CORS_GATE 0x100 /* ignore differing gate when this + * has the coarser netmaks */ +#define AGS_SPLIT_HZ 0x200 /* suppress for split horizon */ + + /* some bits are set if they are set on either route */ +#define AGS_PROMOTE_EITHER (AGS_RIPV2 | AGS_GATEWAY | \ + AGS_SUPPRESS | AGS_CORS_GATE) +}; + + +/* parameters for interfaces */ +extern struct parm { + struct parm *parm_next; + char parm_name[IFNAMSIZ+1]; + naddr parm_addr_h; + naddr parm_mask; + + 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; + char intnet_metric; +} *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 lookforinterfaces; /* 1=probe for new up interfaces */ +extern int supplier_set; /* -s or -q requested */ +extern int ridhosts; /* 1=reduce host 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_expire; +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_out; /* have a RIPv1 interface */ +extern int have_ripv1_in; +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 4 +#define TRACEKERNEL (tracelevel >= 4) /* log kernel changes */ +#define TRACECONTENTS (tracelevel >= 3) /* display packet contents */ +#define TRACEPACKETS (tracelevel >= 2) /* note packets */ +#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, + NO_OUT_MULTICAST, NO_OUT_RIPV2}; +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 +extern char *naddr_ntoa(naddr); +extern char *saddr_ntoa(struct sockaddr *); + +extern void *rtmalloc(size_t, char *); +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 char *check_parms(struct parm *); +extern void get_parms(struct interface *); + +extern void lastlog(void); +extern void trace_on(char *, int); +extern void trace_off(char*, ...); +extern void trace_flush(void); +extern void set_tracelevel(void); +extern void trace_kernel(char *, ...); +extern void trace_act(char *, ...); +extern void trace_pkt(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 if_bad_rdisc(struct interface *); +extern void if_ok_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 flush_kern(void); +extern void age(naddr); + +extern void ag_flush(naddr, naddr, void (*)(struct ag_info *)); +extern void ag_check(naddr, 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 *); + + +#define S_ADDR(x) (((struct sockaddr_in *)(x))->sin_addr.s_addr) +#define INFO_DST(I) ((I)->rti_info[RTAX_DST]) +#define INFO_GATE(I) ((I)->rti_info[RTAX_GATEWAY]) +#define INFO_MASK(I) ((I)->rti_info[RTAX_NETMASK]) +#define INFO_IFA(I) ((I)->rti_info[RTAX_IFA]) +#define INFO_IFP(I) ((I)->rti_info[RTAX_IFP]) +#define INFO_AUTHOR(I) ((I)->rti_info[RTAX_AUTHOR]) +#define INFO_BRD(I) ((I)->rti_info[RTAX_BRD]) +void rt_xaddrs(struct rt_addrinfo *, struct sockaddr *, struct sockaddr *, + int); + +extern naddr std_mask(naddr); +extern naddr ripv1_mask_net(naddr, struct interface *); +extern naddr ripv1_mask_host(naddr,struct interface *); +#define on_net(a,net,mask) (((ntohl(a) ^ (net)) & (mask)) == 0) +extern int check_dst(naddr); +extern void addrouteforif(register struct interface *); +extern void ifinit(void); +extern int walk_bad(struct radix_node *, struct walkarg *); +extern int if_ok(struct interface *, char *); +extern void if_sick(struct interface *); +extern void if_bad(struct interface *); +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/sbin/routed/if.c b/sbin/routed/if.c new file mode 100644 index 0000000..539b073 --- /dev/null +++ b/sbin/routed/if.c @@ -0,0 +1,1153 @@ +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if !defined(lint) && !defined(sgi) && !defined(__NetBSD__) +static char sccsid[] = "@(#)if.c 8.1 (Berkeley) 6/5/93"; +#elif defined(__NetBSD__) +static char rcsid[] = "$NetBSD$"; +#endif +#ident "$Revision: 1.17 $" + +#include "defs.h" +#include "pathnames.h" + +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 */ + +struct timeval ifinit_timer; + +int have_ripv1_out; /* have a RIPv1 interface */ +int have_ripv1_in; + + +/* Find the interface with an address + */ +struct interface * +ifwithaddr(naddr addr, + int bcast, /* notice IFF_BROADCAST address */ + int remote) /* include IS_REMOTE interfaces */ +{ + struct interface *ifp, *possible = 0; + + for (ifp = ifnet; ifp; ifp = ifp->int_next) { + if (ifp->int_addr == addr + || ((ifp->int_if_flags & IFF_BROADCAST) + && ifp->int_brdaddr == addr + && bcast)) { + if ((ifp->int_state & IS_REMOTE) && !remote) + continue; + + if (!(ifp->int_state & IS_BROKE) + && !(ifp->int_state & IS_PASSIVE)) + return ifp; + + possible = ifp; + } + } + + return possible; +} + + +/* find the interface with a name + */ +struct interface * +ifwithname(char *name, /* "ec0" or whatever */ + naddr addr) /* 0 or network address */ +{ + struct interface *ifp; + + + for (ifp = ifnet; 0 != ifp; ifp = ifp->int_next) { + if (!strcmp(ifp->int_name, name) + && (ifp->int_addr == addr + || (addr == 0 && !(ifp->int_state & IS_ALIAS)))) + return ifp; + } + return 0; +} + + +struct interface * +ifwithindex(u_short index) +{ + struct interface *ifp; + + + for (ifp = ifnet; 0 != ifp; ifp = ifp->int_next) { + if (ifp->int_index == index) + return ifp; + } + return 0; +} + + +/* 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 * +iflookup(naddr addr) +{ + struct interface *ifp, *maybe; + + maybe = 0; + for (ifp = ifnet; ifp; ifp = ifp->int_next) { + if (ifp->int_if_flags & IFF_POINTOPOINT) { + if (ifp->int_dstaddr == addr) + /* finished with a match */ + return ifp; + + } else { + /* 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; + + /* 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; + } + } + + return maybe; +} + + +/* Return the classical netmask for an IP address. + */ +naddr +std_mask(naddr addr) /* in network order */ +{ + 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 for a given network. + * If no interface is specified, look for the best fitting interface. + */ +naddr +ripv1_mask_net(naddr addr, /* in network byte order */ + struct interface *ifp) /* as seen on this interface */ +{ + naddr mask = 0; + + if (addr == 0) /* default always has 0 mask */ + return mask; + + if (ifp != 0) { + /* If the target network is that of the associated interface + * on which it arrived, then use the netmask of the interface. + */ + if (on_net(addr, ifp->int_net, ifp->int_std_mask)) + mask = ifp->int_ripv1_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 (ifp = ifnet; ifp != 0; ifp = ifp->int_next) { + if (on_net(addr, ifp->int_std_net, ifp->int_std_mask) + && ifp->int_ripv1_mask > mask) + mask = ifp->int_ripv1_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 *ifp) /* as seen on this interface */ +{ + naddr mask = ripv1_mask_net(addr, ifp); + + + /* 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; + + + trace_if("Del", ifp); + + ifp->int_state |= IS_BROKE; + + /* unlink 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 + */ + for (ifp1 = ifnet; 0 != ifp1; ifp1 = ifp1->int_next) { + if (ifp1 != ifp + && !strcmp(ifp->int_name, ifp1->int_name)) + ifdel(ifp1); + } + + 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 + && errno != EADDRNOTAVAIL + && !TRACEACTIONS) + LOGERR("setsockopt(IP_DROP_MEMBERSHIP RIP)"); + } + if (ifp->int_rip_sock >= 0) { + (void)close(ifp->int_rip_sock); + ifp->int_rip_sock = -1; + fix_select(); + } + + tot_interfaces--; + if (!IS_RIP_OFF(ifp->int_state)) + rip_interfaces--; + + /* Zap all routes associated with this interface. + * Assume routes just using gateways beyond this interface will + * timeout naturally, and have probably already died. + */ + (void)rn_walktree(rhead, walk_bad, 0); + + set_rdisc_mg(ifp, 0); + if_bad_rdisc(ifp); + } + + free(ifp); +} + + +/* Mark an interface ill. + */ +void +if_sick(struct interface *ifp) +{ + if (0 == (ifp->int_state & (IS_SICK | IS_BROKE))) { + ifp->int_state |= IS_SICK; + trace_if("Chg", ifp); + + LIM_SEC(ifinit_timer, now.tv_sec+CHECK_BAD_INTERVAL); + } +} + + +/* Mark an interface dead. + */ +void +if_bad(struct interface *ifp) +{ + struct interface *ifp1; + + + if (ifp->int_state & IS_BROKE) + return; + + LIM_SEC(ifinit_timer, now.tv_sec+CHECK_BAD_INTERVAL); + + ifp->int_state |= (IS_BROKE | IS_SICK); + ifp->int_state &= ~(IS_RIP_QUERIED | IS_ACTIVE); + ifp->int_data.ts = 0; + + trace_if("Chg", ifp); + + if (!(ifp->int_state & IS_ALIAS)) { + for (ifp1 = ifnet; 0 != ifp1; ifp1 = ifp1->int_next) { + if (ifp1 != ifp + && !strcmp(ifp->int_name, ifp1->int_name)) + if_bad(ifp1); + } + (void)rn_walktree(rhead, walk_bad, 0); + if_bad_rdisc(ifp); + } +} + + +/* Mark an interface alive + */ +int /* 1=it was dead */ +if_ok(struct interface *ifp, + char *type) +{ + struct interface *ifp1; + + + if (!(ifp->int_state & IS_BROKE)) { + if (ifp->int_state & IS_SICK) { + trace_act("%sinterface %s to %s working better\n", + type, + ifp->int_name, naddr_ntoa(ifp->int_addr)); + ifp->int_state &= ~IS_SICK; + } + return 0; + } + + msglog("%sinterface %s to %s restored", + type, ifp->int_name, naddr_ntoa(ifp->int_addr)); + ifp->int_state &= ~(IS_BROKE | IS_SICK); + ifp->int_data.ts = 0; + + if (!(ifp->int_state & IS_ALIAS)) { + for (ifp1 = ifnet; 0 != ifp1; ifp1 = ifp1->int_next) { + if (ifp1 != ifp + && !strcmp(ifp->int_name, ifp1->int_name)) + if_ok(ifp1, type); + } + if_ok_rdisc(ifp); + } + return 1; +} + + +/* disassemble routing message + */ +void +rt_xaddrs(struct rt_addrinfo *info, + 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(info, sizeof(*info)); + info->rti_addrs = addrs; + for (i = 0; i < RTAX_MAX && sa < lim; i++) { + if ((addrs & (1 << i)) == 0) + continue; +#ifdef _HAVE_SA_LEN + info->rti_info[i] = (sa->sa_len != 0) ? sa : &sa_zero; + sa = (struct sockaddr *)((char*)(sa) + + ROUNDUP(sa->sa_len)); +#else + info->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 0x001 +# define COMP_WIERD 0x002 +# define COMP_NOADDR 0x004 +# define COMP_BADADDR 0x008 +# define COMP_NODST 0x010 +# define COMP_NOBADR 0x020 +# define COMP_NOMASK 0x040 +# define COMP_DUP 0x080 +# define COMP_BAD_METRIC 0x100 +# define COMP_NETMASK 0x200 + + 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; + struct rt_addrinfo info; +#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 | IS_DUP); + + /* 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"); + sysctl_buf = rtmalloc(sysctl_buf_size = needed, "ifinit"); + } + + 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; + /* make prototype structure for the IP aliases + */ + 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_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) { + logbad(1,"ifinit: out of sync"); + continue; + } + + rt_xaddrs(&info, (struct sockaddr *)(ifam+1), + (struct sockaddr *)ifam2, + ifam->ifam_addrs); + + if (INFO_IFA(&info) == 0) { + if (iff_alive(ifs.int_if_flags)) { + if (!(prev_complaints & COMP_NOADDR)) + msglog("%s has no address", + sdl->sdl_data); + complaints |= COMP_NOADDR; + } + continue; + } + if (INFO_IFA(&info)->sa_family != AF_INET) { + if (iff_alive(ifs.int_if_flags)) { + if (!(prev_complaints & COMP_NOT_INET)) + trace_act("%s: not AF_INET\n", + 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(INFO_IFA(&info)); + + if (ntohl(ifs.int_addr)>>24 == 0 + || ntohl(ifs.int_addr)>>24 == 0xff) { + if (iff_alive(ifs.int_if_flags)) { + if (!(prev_complaints & COMP_BADADDR)) + msglog("%s has a bad address", + sdl->sdl_data); + complaints |= COMP_BADADDR; + } + continue; + } + + if (ifs.int_if_flags & IFF_BROADCAST) { + if (INFO_MASK(&info) == 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_dstaddr = ifs.int_addr; + ifs.int_mask = ntohl(S_ADDR(INFO_MASK(&info))); + ifs.int_ripv1_mask = ifs.int_mask; + 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 (INFO_BRD(&info) == 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(INFO_BRD(&info)); + + } else if (ifs.int_if_flags & IFF_POINTOPOINT) { + if (INFO_BRD(&info) == 0 + || INFO_BRD(&info)->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(INFO_BRD(&info)); + if (ntohl(ifs.int_dstaddr)>>24 == 0 + || ntohl(ifs.int_dstaddr)>>24 == 0xff) { + 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_mask = HOST_MASK; + ifs.int_ripv1_mask = ntohl(S_ADDR(INFO_MASK(&info))); + ifs.int_net = ntohl(ifs.int_dstaddr); + ifs.int_std_mask = std_mask(ifs.int_dstaddr); + + } else if (ifs.int_if_flags & IFF_LOOPBACK) { + ifs.int_state |= IS_PASSIVE | IS_NO_RIP; + ifs.int_dstaddr = ifs.int_addr; + ifs.int_mask = HOST_MASK; + ifs.int_ripv1_mask = HOST_MASK; + ifs.int_net = ntohl(ifs.int_dstaddr); + ifs.int_std_mask = std_mask(ifs.int_dstaddr); + if (!foundloopback) { + foundloopback = 1; + loopaddr = ifs.int_addr; + } + + } else { + if (!(prev_complaints & COMP_WIERD)) + trace_act("%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 = 0; + } else { + ifs.int_metric = ifr.ifr_metric; + } +#else + ifs.int_metric = ifam->ifam_metric; +#endif + if (ifs.int_metric > HOPCNT_INFINITY) { + ifs.int_metric = 0; + if (!(prev_complaints & COMP_BAD_METRIC) + && iff_alive(ifs.int_if_flags)) { + complaints |= COMP_BAD_METRIC; + msglog("%s has a metric of %d", + sdl->sdl_data, ifs.int_metric); + } + } + + /* 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_act("interface %s has changed\n", + ifp->int_name); + ifdel(ifp); + ifp = 0; + } + } + + if (ifp != 0) { + /* The primary representative of an alias worries + * about how things are working. + */ + if (ifp->int_state & IS_ALIAS) + continue; + + /* note interfaces that have been turned off + */ + if (!iff_alive(ifs.int_if_flags)) { + if (iff_alive(ifp->int_if_flags)) { + msglog("interface %s to %s turned off", + ifp->int_name, + naddr_ntoa(ifp->int_addr)); + if_bad(ifp); + 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)if_ok(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; + oerr = ifs.int_data.oerrors - ifp->int_data.oerrors; +#ifdef sgi + /* Through at least IRIX 6.2, PPP and SLIP + * count packets dropped by the filters. + * But FDDI rings stuck non-operational count + * dropped packets as they wait for improvement. + */ + if (!(ifp->int_if_flags & IFF_POINTOPOINT)) + oerr += (ifs.int_data.odrops + - ifp->int_data.odrops); +#endif + /* If the interface just awoke, restart the counters. + */ + if (ifp->int_data.ts == 0) { + ifp->int_data = ifs.int_data; + continue; + } + ifp->int_data = ifs.int_data; + + /* Withhold judgement when the short error + * counters wrap or the interface is reset. + */ + if (ierr < 0 || in < 0 || oerr < 0 || out < 0) { + LIM_SEC(ifinit_timer, + now.tv_sec+CHECK_BAD_INTERVAL); + continue; + } + + /* Withhold judgement when there is no traffic + */ + if (in == 0 && out == 0 && ierr == 0 && oerr == 0) + continue; + + /* It is bad if input or output is not working. + * Require presistent problems before marking it dead. + */ + if ((in <= ierr && ierr > 0) + || (out <= oerr && oerr > 0)) { + if (!(ifp->int_state & IS_SICK)) { + trace_act("interface %s to %s" + " sick: in=%d ierr=%d" + " out=%d oerr=%d\n", + ifp->int_name, + naddr_ntoa(ifp->int_addr), + in, ierr, out, oerr); + if_sick(ifp); + continue; + } + if (!(ifp->int_state & IS_BROKE)) { + msglog("interface %s to %s bad:" + " in=%d ierr=%d out=%d oerr=%d", + ifp->int_name, + naddr_ntoa(ifp->int_addr), + in, ierr, out, oerr); + if_bad(ifp); + } + continue; + } + + /* otherwise, it is active and healthy + */ + ifp->int_act_time = now.tv_sec; + (void)if_ok(ifp, ""); + continue; + } + + /* This is a new interface. + * If it is dead, forget it. + */ + if (!iff_alive(ifs.int_if_flags)) + continue; + + /* See if it duplicates an existing interface. + */ + for (ifp = ifnet; 0 != ifp; ifp = ifp->int_next) { + if (ifp->int_mask != ifs.int_mask) + continue; + if (((ifp->int_addr != ifs.int_addr + && ifs.int_mask != HOST_MASK) + || (ifp->int_dstaddr != ifs.int_dstaddr + && ifs.int_mask == HOST_MASK))) + continue; + if (!iff_alive(ifp->int_if_flags)) + continue; + /* Let one of our real interfaces be marked + * passive. + */ + if ((ifp->int_state & IS_PASSIVE) + && !(ifp->int_state & IS_EXTERNAL)) + continue; + + /* It does duplicate an existing interface, + * so complain about it, mark the other one + * duplicated, and for get this one. + */ + if (!(prev_complaints & COMP_DUP)) { + complaints |= COMP_DUP; + msglog("%s is duplicated by %s at %s", + sdl->sdl_data, ifp->int_name, + naddr_ntoa(ifp->int_addr)); + } + ifp->int_state |= IS_DUP; + break; + } + if (ifp != 0) + continue; + + /* It is new and ok. So make it real + */ + strncpy(ifs.int_name, sdl->sdl_data, + MIN(sizeof(ifs.int_name)-1, sdl->sdl_nlen)); + get_parms(&ifs); + + /* Add it to the list of interfaces + */ + ifp = (struct interface *)rtmalloc(sizeof(*ifp), "ifinit"); + bcopy(&ifs, ifp, sizeof(*ifp)); + if (ifnet != 0) { + ifp->int_next = ifnet; + ifnet->int_prev = ifp; + } + ifnet = ifp; + trace_if("Add", ifp); + + /* Notice likely bad netmask. + */ + if (!(prev_complaints & COMP_NETMASK) + && !(ifp->int_if_flags & IFF_POINTOPOINT)) { + for (ifp1 = ifnet; 0 != ifp1; ifp1 = ifp1->int_next) { + if (ifp1->int_mask == ifp->int_mask) + continue; + if (ifp1->int_if_flags & IFF_POINTOPOINT) + continue; + if (on_net(ifp->int_addr, + ifp1->int_net, ifp1->int_mask) + || on_net(ifp1->int_addr, + ifp->int_net, ifp->int_mask)) { + msglog("possible netmask problem" + " betwen %s:%s and %s:%s", + ifp->int_name, + addrname(htonl(ifp->int_net), + ifp->int_mask, 1), + ifp1->int_name, + addrname(htonl(ifp1->int_net), + ifp1->int_mask, 1)); + complaints |= COMP_NETMASK; + } + } + } + + /* Count the # of directly connected networks. + */ + if (!(ifp->int_state & IS_ALIAS)) { + if (!(ifp->int_if_flags & IFF_LOOPBACK)) + tot_interfaces++; + if (!IS_RIP_OFF(ifp->int_state)) + rip_interfaces++; + } + + if_ok_rdisc(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; + 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, + 0, 0, ifp, rt->rt_time, 0); + } + } + if (rt == 0) + rtadd(myaddr, HOST_MASK, loopaddr, loopaddr, + 0, 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_act("interface %s has disappeared\n", + ifp->int_name); + ifdel(ifp); + continue; + } + + if ((ifp->int_state & IS_BROKE) + && !(ifp->int_state & IS_PASSIVE)) + 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_RIPV1_OUT) && supplier) + have_ripv1_out = 1; + if (!(ifp->int_state & IS_NO_RIPV1_IN)) + have_ripv1_in = 1; + } + + 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_NET_SYN) + | (RS_IF|RS_LOCAL)), + loopaddr, loopaddr, + 0, 0, ifp1, rt->rt_time, 0); + } else { + rtadd(ifp->int_addr, HOST_MASK, + loopaddr, loopaddr, + 0, 0, (RS_IF | RS_LOCAL), ifp); + } + } + } + + /* add the authority routes */ + for (intnetp = intnets; intnetp!=0; intnetp = intnetp->intnet_next) { + rt = rtget(intnetp->intnet_addr, intnetp->intnet_mask); + if (rt != 0 + && !(rt->rt_state & RS_NO_NET_SYN) + && !(rt->rt_state & RS_NET_INT)) { + rtdelete(rt); + rt = 0; + } + if (rt == 0) + rtadd(intnetp->intnet_addr, intnetp->intnet_mask, + loopaddr, loopaddr, intnetp->intnet_metric-1, + 0, RS_NET_SYN | RS_NET_INT, 0); + } + + prev_complaints = complaints; +} + + +static void +check_net_syn(struct interface *ifp) +{ + struct rt_entry *rt; + + + /* Turn on the need to automatically synthesize a network route + * for this interface only if we are running RIPv1 on some other + * interface that is on a different class-A,B,or C network. + */ + if (have_ripv1_out || have_ripv1_in) { + ifp->int_state |= IS_NEED_NET_SYN; + rt = rtget(ifp->int_std_addr, ifp->int_std_mask); + if (rt != 0 + && 0 == (rt->rt_state & RS_NO_NET_SYN) + && (!(rt->rt_state & RS_NET_SYN) + || rt->rt_metric > ifp->int_metric)) { + rtdelete(rt); + rt = 0; + } + if (rt == 0) + rtadd(ifp->int_std_addr, ifp->int_std_mask, + ifp->int_addr, ifp->int_addr, + ifp->int_metric, 0, RS_NET_SYN, ifp); + + } else { + ifp->int_state &= ~IS_NEED_NET_SYN; + + rt = rtget(ifp->int_std_addr, + ifp->int_std_mask); + if (rt != 0 + && (rt->rt_state & RS_NET_SYN) + && 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, gate; + + + /* 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_state & IS_SUBNET) + check_net_syn(ifp); + + if (ifp->int_state & IS_REMOTE) { + dst = ifp->int_addr; + gate = ifp->int_dstaddr; + /* If we are going to send packets to the gateway, + * it must be reachable using our physical interfaces + */ + if (!(ifp->int_state && IS_EXTERNAL) + && !rtfind(ifp->int_dstaddr) + && ifp->int_transitions == 0) { + msglog("unreachable gateway %s in " + _PATH_GATEWAYS" entry %s", + naddr_ntoa(gate), ifp->int_name); + return; + } + + } else { + dst = (0 != (ifp->int_if_flags & (IFF_POINTOPOINT + | IFF_LOOPBACK)) + ? ifp->int_dstaddr + : htonl(ifp->int_net)); + gate = ifp->int_addr; + } + + /* We are finished if the correct 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) + && (!(ifp->int_state & IS_DUP) + || rt->rt_ifp == 0 + || (rt->rt_ifp->int_state & IS_BROKE))) { + rtdelete(rt); + rt = 0; + } else { + rtchange(rt, ((rt->rt_state | RS_IF) + & ~(RS_NET_SYN | 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_act("re-install interface %s\n", + ifp->int_name); + + rtadd(dst, ifp->int_mask, gate, gate, + ifp->int_metric, 0, RS_IF, ifp); + } +} diff --git a/sbin/routed/input.c b/sbin/routed/input.c new file mode 100644 index 0000000..a854c41 --- /dev/null +++ b/sbin/routed/input.c @@ -0,0 +1,741 @@ +/* + * Copyright (c) 1983, 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if !defined(lint) && !defined(sgi) && !defined(__NetBSD__) +static char sccsid[] = "@(#)input.c 8.1 (Berkeley) 6/5/93"; +#elif defined(__NetBSD__) +static char rcsid[] = "$NetBSD$"; +#endif +#ident "$Revision: 1.16 $" + +#include "defs.h" + +static void input(struct sockaddr_in *, struct interface*, struct rip *, int); +static void input_route(struct interface *, naddr, + naddr, naddr, naddr, struct netinfo *); + + +/* process RIP input + */ +void +read_rip(int sock, + struct interface *ifp) +{ + 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, &inbuf.rip, cc); + } +} + + +/* Process a RIP packet + */ +static void +input(struct sockaddr_in *from, /* received from this IP address */ + struct interface *sifp, /* interface by which it arrived */ + 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 interface *aifp; /* interface if via 1 hop */ + struct rt_entry *rt; + struct netinfo *n, *lim; + struct interface *ifp1; + naddr gate, mask, v1_mask, dst, ddst_h; + int i; + + aifp = iflookup(from->sin_addr.s_addr); + if (sifp == 0) + sifp = aifp; + + if (sifp != 0) + sifp->int_state |= IS_ACTIVE; + + trace_rip("Recv", "from", from, sifp, rip, size); + + if (rip->rip_vers == 0) { + 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; + } else if (rip->rip_vers > RIPv2) { + rip->rip_vers = RIPv2; + } + 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; + trace_pkt("discard authenticated RIPv2 message\n"); + return; + } + + switch (rip->rip_cmd) { + case RIPCMD_REQUEST: + /* did the request come from a router? + */ + if (from->sin_port == htons(RIP_PORT)) { + /* yes, ignore it if RIP is off so that it does not + * depend on us. + */ + if (rip_sock < 0) { + trace_pkt("ignore request while RIP off\n"); + return; + } + + /* Ignore the request if we talking to ourself + * (and not a remote gateway). + */ + if (ifwithaddr(FROM_NADDR, 0, 0) != 0) { + trace_pkt("discard our own RIP request\n"); + return; + } + } + + /* According to RFC 1723, we should ignore unathenticated + * queries. That is too silly to bother with. Sheesh! + * Are forwarding tables supposed to be secret? When + * a bad guy can infer them with test traffic? + * Maybe on firewalls you'd care, but not enough to + * give up the diagnostic facilities of remote probing. + */ + + if (n >= lim + || size%sizeof(*n) != sizeof(struct rip)%sizeof(*n)) { + if (from->sin_addr.s_addr != bad_len) + msglog("request of bad length (%d) from %s", + size, naddr_ntoa(FROM_NADDR)); + bad_len = from->sin_addr.s_addr; + } + for (; n < lim; n++) { + n->n_metric = ntohl(n->n_metric); + + /* A single entry with family RIP_AF_UNSPEC and + * metric HOPCNT_INFINITY means "all routes". + * We respond to routers only if we are acting + * as a supplier, or to anyone other than a router + * (i.e. a query). + */ + 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)) { + /* Answer a query from a utility + * program with all we know. + */ + supply(from, sifp, OUT_QUERY, 0, + rip->rip_vers); + return; + } + /* A router trying to prime its tables. + * Filter the answer 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 as a router. Respond with + * RIPv1 instead of RIPv2 if that is what we + * are broadcasting on the interface to keep + * the remote router from getting the wrong + * initial idea of the routes we send. + */ + if (!supplier + || aifp == 0 + || (aifp->int_state & IS_PASSIVE) + || (aifp->int_state & IS_ALIAS) + || ((aifp->int_state & IS_NO_RIPV1_OUT) + && (aifp->int_state&IS_NO_RIPV2_OUT))) + return; + + supply(from, aifp, OUT_UNICAST, 0, + (aifp->int_state&IS_NO_RIPV1_OUT) + ? RIPv2 : RIPv1); + return; + } + + if (n->n_family != RIP_AF_INET) { + if (from->sin_addr.s_addr != bad_router) + msglog("request from %s" + " for unsupported (af %d) %s", + naddr_ntoa(FROM_NADDR), + ntohs(n->n_family), + naddr_ntoa(n->n_dst)); + bad_router = from->sin_addr.s_addr; + return; + } + + dst = n->n_dst; + if (!check_dst(dst)) { + if (from->sin_addr.s_addr != bad_router) + msglog("bad queried destination" + " %s from %s", + naddr_ntoa(dst), + naddr_ntoa(FROM_NADDR)); + bad_router = from->sin_addr.s_addr; + return; + } + + if (rip->rip_vers == RIPv1 + || 0 == (mask = ntohl(n->n_mask)) + || 0 != (ntohl(dst) & ~mask)) + mask = ripv1_mask_host(dst,sifp); + + rt = rtget(dst, mask); + if (!rt && dst != RIP_DEFAULT) + rt = rtfind(n->n_dst); + + n->n_tag = 0; + n->n_nhop = 0; + if (rip->rip_vers == RIPv1) { + n->n_mask = 0; + } else { + n->n_mask = mask; + } + if (rt == 0) { + n->n_metric = HOPCNT_INFINITY; + } else { + n->n_metric = rt->rt_metric+1; + n->n_metric += (sifp!=0)?sifp->int_metric : 1; + if (n->n_metric > HOPCNT_INFINITY) + n->n_metric = HOPCNT_INFINITY; + if (rip->rip_vers != RIPv1) { + n->n_tag = rt->rt_tag; + if (sifp != 0 + && on_net(rt->rt_gate, + sifp->int_net, + sifp->int_mask) + && rt->rt_gate != sifp->int_addr) + 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; + 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, sifp, rip, size); + } else if (supplier) { + (void)output(OUT_UNICAST, from, sifp, rip, size); + } + return; + + case RIPCMD_TRACEON: + case RIPCMD_TRACEOFF: + /* verify message came from a privileged port */ + if (ntohs(from->sin_port) > IPPORT_RESERVED) { + msglog("trace command from untrusted port on %s", + naddr_ntoa(FROM_NADDR)); + return; + } + if (aifp == 0) { + msglog("trace command from unknown router %s", + naddr_ntoa(FROM_NADDR)); + return; + } + if (rip->rip_cmd == RIPCMD_TRACEON) { + rip->rip_tracefile[size-4] = '\0'; + trace_on((char*)rip->rip_tracefile, 0); + } else { + trace_off("tracing turned off by %s\n", + 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 (from->sin_port != ntohs(RIP_PORT)) { + trace_pkt("discard RIP response from unknown port\n"); + return; + } + + if (rip_sock < 0) { + trace_pkt("discard response while RIP off\n"); + return; + } + + /* Are we talking to ourself or a remote gateway? + */ + ifp1 = ifwithaddr(FROM_NADDR, 0, 1); + if (ifp1) { + if (ifp1->int_state & IS_REMOTE) { + if (ifp1->int_state & IS_PASSIVE) { + msglog("bogus input from %s on" + " supposedly passive %s", + naddr_ntoa(FROM_NADDR), + ifp1->int_name); + } else { + ifp1->int_act_time = now.tv_sec; + if (if_ok(ifp1, "remote ")) + addrouteforif(ifp1); + } + } else { + trace_pkt("discard our own RIP response\n"); + } + return; + } + + /* Check the router from which message originated. We accept + * routing packets from routers directly connected via + * broadcast or point-to-point networks, and from + * those listed in /etc/gateways. + */ + if (!aifp) { + if (from->sin_addr.s_addr != unk_router) + msglog("discard packet from unknown router %s" + " or via unidentified interface", + naddr_ntoa(FROM_NADDR)); + unk_router = from->sin_addr.s_addr; + return; + } + if (aifp->int_state & IS_PASSIVE) { + trace_act("discard packet from %s" + " via passive interface %s\n", + naddr_ntoa(FROM_NADDR), + aifp->int_name); + return; + } + + /* Check required version + */ + if (((aifp->int_state & IS_NO_RIPV1_IN) + && rip->rip_vers == RIPv1) + || ((aifp->int_state & IS_NO_RIPV2_IN) + && rip->rip_vers != RIPv1)) { + trace_pkt("discard RIPv%d response\n", + rip->rip_vers); + return; + } + + /* Ignore routes via dead interface. + */ + if (aifp->int_state & IS_BROKE) { + trace_pkt("discard response via broken interface %s\n", + aifp->int_name); + return; + } + + /* Authenticate the packet if we have a secret. + */ + if (aifp->int_passwd[0] != '\0') { + if (n >= lim + || n->n_family != RIP_AF_AUTH + || ((struct netauth*)n)->a_type != RIP_AUTH_PW) { + if (from->sin_addr.s_addr != use_auth) + msglog("missing password from %s", + naddr_ntoa(FROM_NADDR)); + use_auth = from->sin_addr.s_addr; + return; + + } else if (0 != bcmp(((struct netauth*)n)->au.au_pw, + aifp->int_passwd, + sizeof(aifp->int_passwd))) { + if (from->sin_addr.s_addr != use_auth) + msglog("bad password from %s", + naddr_ntoa(FROM_NADDR)); + use_auth = from->sin_addr.s_addr; + return; + } + } + + for (; n < lim; n++) { + if (n->n_family == RIP_AF_AUTH) + continue; + + NTOHL(n->n_metric); + dst = n->n_dst; + if (n->n_family != RIP_AF_INET + && (n->n_family != RIP_AF_UNSPEC + || dst != RIP_DEFAULT)) { + if (from->sin_addr.s_addr != bad_router) + msglog("route from %s to unsupported" + " address family %d," + " destination %s", + naddr_ntoa(FROM_NADDR), + n->n_family, + naddr_ntoa(dst)); + bad_router = from->sin_addr.s_addr; + continue; + } + if (!check_dst(dst)) { + if (from->sin_addr.s_addr != bad_router) + msglog("bad destination %s from %s", + naddr_ntoa(dst), + naddr_ntoa(FROM_NADDR)); + bad_router = from->sin_addr.s_addr; + return; + } + 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. + */ + gate = from->sin_addr.s_addr; + if (n->n_nhop != 0) { + if (rip->rip_vers == RIPv2) { + n->n_nhop = 0; + } else { + /* Use it only if it is valid. */ + if (on_net(n->n_nhop, + aifp->int_net, aifp->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; + n->n_nhop = 0; + } + } + } + + if (rip->rip_vers == RIPv1 + || 0 == (mask = ntohl(n->n_mask))) { + mask = ripv1_mask_host(dst,aifp); + } 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; + } + if (rip->rip_vers == RIPv1) + n->n_tag = 0; + + /* Adjust metric according to incoming interface.. + */ + n->n_metric += aifp->int_metric; + if (n->n_metric > HOPCNT_INFINITY) + n->n_metric = HOPCNT_INFINITY; + + /* Recognize and ignore a default route we faked + * which is being sent back to us by a machine with + * broken split-horizon. + * Be a little more paranoid than that, and reject + * default routes with the same metric we advertised. + */ + if (aifp->int_d_metric != 0 + && dst == RIP_DEFAULT + && n->n_metric >= aifp->int_d_metric) + continue; + + /* We can receive aggregated RIPv2 routes that must + * be broken down before they are transmitted by + * RIPv1 via an interface on a subnet. + * We might also receive the same routes aggregated + * 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_SYN. + */ + if (have_ripv1_out + && (v1_mask = ripv1_mask_net(dst,0)) > mask + && (((rt = rtget(dst,mask)) == 0 + || !(rt->rt_state & RS_NET_SYN)))) { + ddst_h = v1_mask & -v1_mask; + i = (v1_mask & ~mask)/ddst_h; + if (i >= 511) { + /* Punt if we would have to generate + * an unreasonable number of routes. + */ +#ifdef DEBUG + msglog("accept %s from %s as 1" + " instead of %d routes", + addrname(dst,mask,0), + naddr_ntoa(FROM_NADDR), + i+1); +#endif + i = 0; + } else { + mask = v1_mask; + } + } else { + i = 0; + } + + for (;;) { + input_route(aifp, FROM_NADDR, + dst, mask, gate, n); + if (i-- == 0) + break; + dst = htonl(ntohl(dst) + ddst_h); + } + } + break; + } +} + + +/* Process a single input route. + */ +static void +input_route(struct interface *ifp, + naddr from, + naddr dst, + naddr mask, + naddr gate, + struct netinfo *n) +{ + int i; + struct rt_entry *rt; + struct rt_spare *rts, *rts0; + struct interface *ifp1; + time_t new_time; + + + /* See if the other guy is telling us to send our packets to him. + * Sometimes network routes arrive over a point-to-point link for + * the network containing the address(es) of the link. + * + * If our interface is broken, switch to using the other guy. + */ + ifp1 = ifwithaddr(dst, 1, 1); + if (ifp1 != 0 + && !(ifp1->int_state & IS_BROKE)) + return; + + /* Look for the route in our table. + */ + rt = rtget(dst, mask); + + /* Consider adding the route if we do not already have it. + */ + if (rt == 0) { + /* Ignore unknown routes being poisoned. + */ + if (n->n_metric == HOPCNT_INFINITY) + return; + + /* Ignore the route if it points to us */ + if (n->n_nhop != 0 + && 0 != ifwithaddr(n->n_nhop, 1, 0)) + return; + + /* If something has not gone crazy and tried to fill + * our memory, accept the new route. + */ + if (total_routes < MAX_ROUTES) + rtadd(dst, mask, gate, from, n->n_metric, + n->n_tag, 0, ifp); + return; + } + + /* We already know about the route. Consider this update. + * + * If (rt->rt_state & RS_NET_SYN), 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 never + * aggregating 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(rt, rts0, rts)) + rts0 = rts; + } + if (i != 0) { + /* Found the router + */ + int old_metric = rts->rts_metric; + + /* Keep poisoned routes around only long enough to pass + * the poison on. Get a new timestamp for good routes. + */ + new_time =((old_metric == HOPCNT_INFINITY) + ? rts->rts_time + : now.tv_sec); + + /* 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, + n->n_metric, n->n_tag, ifp, new_time, 0); + /* If the route got worse, check for something better. + */ + if (n->n_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 == n->n_metric + && rts->rts_tag == n->n_tag) { + rts->rts_time = new_time; + return; + } + + } else { + /* The update is for a route we know about, + * but not from a familiar router. + * + * Ignore the route if it points to us. + */ + if (n->n_nhop != 0 + && 0 != ifwithaddr(n->n_nhop, 1, 0)) + return; + + 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 + * received with metric HOPCNT_INFINITY). + */ + if (n->n_metric >= rts->rts_metric) + return; + + new_time = now.tv_sec; + } + + trace_upslot(rt, rts, gate, from, ifp, n->n_metric,n->n_tag, new_time); + + rts->rts_gate = gate; + rts->rts_router = from; + rts->rts_metric = n->n_metric; + rts->rts_tag = n->n_tag; + rts->rts_time = new_time; + rts->rts_ifp = ifp; + + /* try to switch to a better route */ + rtswitch(rt, rts); +} diff --git a/sbin/routed/main.c b/sbin/routed/main.c new file mode 100644 index 0000000..84a7fac --- /dev/null +++ b/sbin/routed/main.c @@ -0,0 +1,830 @@ +/* + * Copyright (c) 1983, 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +char copyright[] = +"@(#) Copyright (c) 1983, 1988, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#if !defined(lint) && !defined(sgi) && !defined(__NetBSD__) +static char sccsid[] = "@(#)main.c 8.1 (Berkeley) 6/5/93"; +#elif defined(__NetBSD__) +static char rcsid[] = "$NetBSD$"; +#endif +#ident "$Revision: 1.17 $" + +#include "defs.h" +#include "pathnames.h" +#ifdef sgi +#include "math.h" +#endif +#include +#include +#include + +pid_t mypid; + +naddr myaddr; /* system address */ +char myname[MAXHOSTNAMELEN+1]; + +int supplier; /* supply or broadcast updates */ +int supplier_set; +int ipforwarding = 1; /* kernel forwarding on */ + +int default_gateway; /* 1=advertise default */ +int background = 1; +int ridhosts; /* 1=reduce host routes */ +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 */ + +struct timeval epoch; /* when started */ +struct timeval clk, prev_clk; +struct timeval now; /* current idea of time */ +time_t now_stale; +time_t now_expire; +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, mib[4], off; + size_t len; + char *p, *q; + struct timeval wtime, t2; + time_t dt; + fd_set ibits; + naddr p_addr, p_mask; + struct interface *ifp; + struct parm parm; + char *tracename = 0; + + + openlog("routed", LOG_PID | LOG_ODELAY, LOG_DAEMON); + ftrace = stdout; + + gettimeofday(&clk, 0); + prev_clk = clk; + epoch = clk; + epoch.tv_sec -= EPOCH; + now.tv_sec = EPOCH; + now_stale = EPOCH - STALE_TIME; + now_expire = EPOCH - EXPIRE_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': + bzero(&parm, sizeof(parm)); + parm.parm_d_metric = 1; + p = check_parms(&parm); + if (p != 0) + msglog("bad -g: %s", p); + else + 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 '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-1 + && n >= 1) + *p = '\0'; + } + if (!getnet(optarg, &p_addr, &p_mask)) { + msglog("bad network; \"-F %s\"", + optarg); + break; + } + bzero(&parm, sizeof(parm)); + parm.parm_addr_h = ntohl(p_addr); + parm.parm_mask = p_mask; + parm.parm_d_metric = n; + p = check_parms(&parm); + if (p != 0) + msglog("bad -F: %s", p); + break; + + case 'P': + /* handle arbirary, (usually) per-interface + * parameters. + */ + p = parse_parms(optarg); + if (p != 0) + msglog("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]"); + } + if (geteuid() != 0) + logbad(0, "requires UID 0"); + + 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; + } + } + + + signal(SIGALRM, sigalrm); + if (!background) + signal(SIGHUP, sigterm); /* SIGHUP fatal during debugging */ + else + signal(SIGHUP, SIG_IGN); + signal(SIGTERM, sigterm); + signal(SIGINT, sigterm); + signal(SIGUSR1, sigtrace_on); + signal(SIGUSR2, sigtrace_off); + + /* get into the background */ + if (background) { +#ifdef sgi + if (0 > _daemonize(_DF_NOCHDIR, + new_tracelevel == 0 ? -1 : STDOUT_FILENO, + new_tracelevel == 0 ? -1 : STDERR_FILENO, + -1)) + BADERR(0, "_daemonize()"); +#else + if (daemon(1, 1) < 0) + BADERR(0,"daemon()"); +#endif + } + + 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)"); + + fix_select(); + + + if (background && new_tracelevel == 0) + ftrace = 0; + if (tracename != 0) { + trace_on(tracename, 1); + if (new_tracelevel == 0) /* use stdout if file is bad */ + new_tracelevel = 1; + } + 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; + + /* 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(&t2, &clk, &prev_clk); + if (t2.tv_sec < 0 + || t2.tv_sec > wtime.tv_sec + 5) { + /* Deal with time changes before other housekeeping to + * keep everything straight. + */ + dt = t2.tv_sec; + if (dt > 0) + dt -= wtime.tv_sec; + trace_act("time changed by %d sec\n", dt); + epoch.tv_sec += dt; + } + timevalsub(&now, &clk, &epoch); + now_stale = now.tv_sec - STALE_TIME; + now_expire = now.tv_sec - EXPIRE_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 with signal %d\n", stopint); + exit(stopint | 128); + } + + /* look for new or dead interfaces */ + timevalsub(&wtime, &ifinit_timer, &now); + if (wtime.tv_sec <= 0) { + wtime.tv_sec = 0; + ifinit(); + rip_query(); + continue; + } + + /* If it is time, then broadcast our routes. + */ + if (supplier || advertise_mhome) { + timevalsub(&t2, &next_bcast, &now); + if (t2.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-t2.tv_sec)/SUPPLY_INTERVAL; + next_bcast.tv_sec += n*SUPPLY_INTERVAL; + + continue; + } + + if (timercmp(&t2, &wtime, <)) + wtime = t2; + } + + /* 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(&t2, &no_flash, &now); + if (timercmp(&t2, &wtime, <)) + wtime = t2; + } + + /* trigger the main aging timer. + */ + timevalsub(&t2, &age_timer, &now); + if (t2.tv_sec <= 0) { + age(0); + continue; + } + if (timercmp(&t2, &wtime, <)) + wtime = t2; + + /* update the kernel routing table + */ + timevalsub(&t2, &need_kern, &now); + if (t2.tv_sec <= 0) { + age(0); + continue; + } + if (timercmp(&t2, &wtime, <)) + wtime = t2; + + /* take care of router discovery, + * but do it to the millisecond + */ + if (!timercmp(&rdisc_timer, &now, >)) { + rdisc_age(0); + continue; + } + timevalsub(&t2, &rdisc_timer, &now); + if (timercmp(&t2, &wtime, <)) + wtime = t2; + + + /* wait for input or a timer to expire. + */ + trace_flush(); + ibits = fdbits; + n = select(sock_max, &ibits, 0, 0, &wtime); + if (n <= 0) { + if (n < 0 && errno != EINTR && errno != EAGAIN) + BADERR(1,"select"); + continue; + } + + 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--; + } + } + } +} + + +/* ARGSUSED */ +void +sigalrm(int sig) +{ + /* Historically, SIGALRM would cause the daemon to check for + * new and broken interfaces. + */ + ifinit_timer.tv_sec = now.tv_sec; + trace_act("SIGALRM\n"); +} + + +/* 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_act("RCVBUF=%d\n", rbuf); + break; + } + if (rbuf < MIN_SOCKBUF) { + msglog("setsockopt(%s,SO_RCVBUF = %d): %s", + name, rbuf, strerror(errno)); + break; + } + } + } +} + + +/* 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 && !mhome) { + trace_act("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); + } + } + + fix_select(); + + age(0); + } +} + + +/* turn on RIP multicast input via an interface + */ +static void +rip_mcast_on(struct interface *ifp) +{ + struct ip_mreq m; + + if (!IS_RIP_IN_OFF(ifp->int_state) + && (ifp->int_if_flags & IFF_MULTICAST) +#ifdef MCAST_PPP_BUG + && !(ifp->int_if_flags & IFF_POINTOPOINT) +#endif + && !(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) + LOGERR("setsockopt(IP_ADD_MEMBERSHIP RIP)"); + } +} + + +/* Prepare socket used for RIP. + */ +void +rip_on(struct interface *ifp) +{ + /* If the main RIP socket is already alive, only start receiving + * multicasts for this interface. + */ + if (rip_sock >= 0) { + if (ifp != 0) + rip_mcast_on(ifp); + return; + } + + /* If the main RIP socket is off, and it makes sense to turn it on, + * turn it on for all of the interfaces. + */ + if (rip_interfaces > 0 && !rdisc_ok) { + trace_act("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 (!IS_RIP_IN_OFF(ifp->int_state)) + ifp->int_state &= ~IS_RIP_QUERIED; + rip_mcast_on(ifp); + } + + 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(); + } +} + + +/* die if malloc(3) fails + */ +void * +rtmalloc(size_t size, + char *msg) +{ + void *p = malloc(size); + if (p == 0) + logbad(1,"malloc() failed in %s", msg); + return p; +} + + +/* 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; + if ((t1->tv_usec += t2->tv_usec) > 1000000) { + t1->tv_sec++; + t1->tv_usec -= 1000000; + } +} + + +/* 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; + } +} + + +void +msglog(char *p, ...) +{ + va_list args; + + 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); + } +} + + +void +logbad(int dump, char *p, ...) +{ + va_list args; + + 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/sbin/routed/output.c b/sbin/routed/output.c new file mode 100644 index 0000000..eafcf31 --- /dev/null +++ b/sbin/routed/output.c @@ -0,0 +1,874 @@ +/* + * Copyright (c) 1983, 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if !defined(lint) && !defined(sgi) && !defined(__NetBSD__) +static char sccsid[] = "@(#)output.c 8.1 (Berkeley) 6/5/93"; +#elif defined(__NetBSD__) +static char rcsid[] = "$NetBSD$"; +#endif +#ident "$Revision: 1.17 $" + +#include "defs.h" + + +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 { /* info for each buffer */ + struct rip *buf; + struct netinfo *n; + struct netinfo *base; + struct netinfo *lim; + enum output_type type; + } v12, v2; + char metric; /* adjust metrics by interface */ + int npackets; + int gen_limit; + u_int state; +#define WS_ST_FLASH 0x001 /* send only changed routes */ +#define WS_ST_RIP2_SAFE 0x002 /* send RIPv2 safe for RIPv1 */ +#define WS_ST_RIP2_ALL 0x004 /* send full featured RIPv2 */ +#define WS_ST_AG 0x008 /* ok to aggregate subnets */ +#define WS_ST_SUPER_AG 0x010 /* ok to aggregate networks */ +#define WS_ST_SUB_AG 0x020 /* aggregate subnets in odd case */ +#define WS_ST_QUERY 0x040 /* responding to a query */ +#define WS_ST_TO_ON_NET 0x080 /* sending onto one of our nets */ +#define WS_ST_DEFAULT 0x100 /* faking a default */ +#define WS_ST_PM_RDISC 0x200 /* poor-man's router discovery */ +} ws; + +/* A buffer for what can be heard by both RIPv1 and RIPv2 listeners */ +union pkt_buf ripv12_buf; + +/* Another for only RIPv2 listeners */ +union pkt_buf rip_v2_buf; + + + +/* Send the contents of the global buffer via the non-multicast socket + */ +int /* <0 on failure */ +output(enum output_type type, + struct sockaddr_in *dst, /* send to here */ + struct interface *ifp, + struct rip *buf, + int size) /* this many bytes */ +{ + struct sockaddr_in sin; + int flags; + char *msg; + int res; + naddr tgt_mcast; + int soc; + int serrno; + + 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"; + } else { + msg = "Send bcast"; + } + flags = MSG_DONTROUTE; + break; + case OUT_MULTICAST: + if (ifp->int_if_flags & IFF_POINTOPOINT) { + msg = "Send pt-to-pt"; + } else if (ifp->int_state & IS_DUP) { + trace_act("abort multicast output via %s" + " with duplicate address\n", + ifp->int_name); + return 0; + } 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 (0 > setsockopt(rip_sock, + IPPROTO_IP, IP_MULTICAST_IF, + &tgt_mcast, + sizeof(tgt_mcast))) { + serrno = errno; + LOGERR("setsockopt(rip_sock," + "IP_MULTICAST_IF)"); + errno = serrno; + ifp = 0; + return -1; + } + rip_sock_mcast = ifp; + } + sin.sin_addr.s_addr = htonl(INADDR_RIP_GROUP); + } + + case NO_OUT_MULTICAST: + case NO_OUT_RIPV2: + break; + } + + trace_rip(msg, "to", &sin, ifp, buf, size); + + res = sendto(soc, buf, size, flags, + (struct sockaddr *)&sin, sizeof(sin)); + if (res < 0 + && (ifp == 0 || !(ifp->int_state & IS_BROKE))) { + serrno = errno; + msglog("%s sendto(%s%s%s.%d): %s", msg, + ifp != 0 ? ifp->int_name : "", + ifp != 0 ? ", " : "", + inet_ntoa(sin.sin_addr), + ntohs(sin.sin_port), + strerror(errno)); + errno = serrno; + } + + return res; +} + + +/* install authentication if appropriate + */ +static void +set_auth(struct ws_buf *w) +{ + if (ws.ifp != 0 + && ws.ifp->int_passwd[0] != '\0' + && (ws.state & WS_ST_RIP2_SAFE)) { + w->n->n_family = RIP_AF_AUTH; + ((struct netauth*)w->n)->a_type = RIP_AUTH_PW; + bcopy(ws.ifp->int_passwd, ((struct netauth*)w->n)->au.au_pw, + sizeof(((struct netauth*)w->n)->au.au_pw)); + w->n++; + } +} + + +/* Send the buffer + */ +static void +supply_write(struct ws_buf *wb) +{ + /* Output multicast only if legal. + * If we would multcast and it would be illegal, then discard the + * packet. + */ + switch (wb->type) { + case NO_OUT_MULTICAST: + trace_pkt("skip multicast to %s because impossible\n", + naddr_ntoa(ws.to.sin_addr.s_addr)); + break; + case NO_OUT_RIPV2: + break; + default: + if (output(wb->type, &ws.to, ws.ifp, wb->buf, + ((char *)wb->n - (char*)wb->buf)) < 0 + && ws.ifp != 0) + if_sick(ws.ifp); + ws.npackets++; + break; + } + + bzero(wb->n = wb->base, sizeof(*wb->n)*NETS_LEN); + if (wb->buf->rip_vers == RIPv2) + set_auth(wb); +} + + +/* 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 *wb; + + + /* 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; + + /* Skip this route if required by split-horizon. + */ + if (ag->ag_state & AGS_SPLIT_HZ) + 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); + 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. + * A pair of subnet routes might have been promoted so that they + * are legal to send by RIPv1. + * If RIPv1 is off, use the multicast buffer, unless this is the + * fake default route and it is acting as a poor-man's router- + * discovery mechanism. + */ + if (((ws.state & WS_ST_RIP2_ALL) + && (dst_h != RIP_DEFAULT || !(ws.state & WS_ST_PM_RDISC))) + || ((ag->ag_state & AGS_RIPV2) && v1_mask != mask)) { + /* use the RIPv2-only buffer */ + wb = &ws.v2; + + } else { + /* use the RIPv1-or-RIPv2 buffer */ + wb = &ws.v12; + + /* 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 > ws.gen_limit) { + /* Punt if we would have to generate an + * unreasonable number of routes. + */ +#ifdef DEBUG + msglog("sending %s to %s as 1 instead" + " of %d routes", + addrname(htonl(dst_h),mask,1), + naddr_ntoa(ws.to.sin_addr.s_addr), + i+1); +#endif + i = 0; + + } else { + mask = v1_mask; + ws.gen_limit -= i; + } + } + } + + do { + wb->n->n_family = RIP_AF_INET; + wb->n->n_dst = htonl(dst_h); + /* If the route is from router-discovery or we are + * shutting down, admit only a bad metric. + */ + wb->n->n_metric = ((stopint || ag->ag_metric < 1) + ? HOPCNT_INFINITY + : ag->ag_metric); + HTONL(wb->n->n_metric); + if (wb->buf->rip_vers == RIPv2) { + if (ag->ag_nhop != 0 + && (ws.state & WS_ST_RIP2_SAFE) + && ((ws.state & WS_ST_QUERY) + || (ag->ag_nhop != ws.ifp->int_addr + && on_net(ag->ag_nhop, + ws.ifp->int_net, + ws.ifp->int_mask)))) + wb->n->n_nhop = ag->ag_nhop; + if ((ws.state & WS_ST_RIP2_ALL) + || mask != s_mask) + wb->n->n_mask = htonl(mask); + wb->n->n_tag = ag->ag_tag; + } + dst_h += ddst_h; + + if (++wb->n >= wb->lim) + supply_write(wb); + } 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, nhop; + + + /* Do not advertise the loopback interface + * or external remote interfaces + */ + if ((RT->rt_state & RS_IF) + && RT->rt_ifp != 0 + && ((RT->rt_ifp->int_if_flags & IFF_LOOPBACK) + || (RT->rt_ifp->int_state & IS_EXTERNAL)) + && !(RT->rt_state & RS_MHOME)) + return 0; + + /* If being quiet about our ability to forward, then + * do not say anything unless responding to a query. + */ + if (!supplier && !(ws.state & WS_ST_QUERY)) + return 0; + + dst = RT->rt_dst; + + /* do not collide with the fake default route */ + if (dst == RIP_DEFAULT + && (ws.state & WS_ST_DEFAULT)) + return 0; + + if (RT->rt_state & RS_NET_SYN) { + if (RT->rt_state & RS_NET_INT) { + /* Do not send manual synthetic network routes + * into the subnet. + */ + if (on_net(ws.to.sin_addr.s_addr, + ntohl(dst), RT->rt_mask)) + return 0; + + } else { + /* Do not send automatic synthetic network routes + * if they are not needed becaus no RIPv1 listeners + * can hear them. + */ + if (ws.state & WS_ST_RIP2_ALL) + return 0; + + /* Do not send automatic synthetic network routes to + * the real subnet. + */ + if (on_net(ws.to.sin_addr.s_addr, + ntohl(dst), RT->rt_mask)) + return 0; + } + nhop = 0; + + } else { + /* 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 (!(RT->rt_state & RS_IF) + && RT->rt_gate != myaddr + && RT->rt_gate != loopaddr) + nhop = RT->rt_gate; + else + nhop = 0; + } + + metric = RT->rt_metric; + ags = 0; + + if (RT->rt_state & RS_MHOME) { + /* retain host route of multi-homed servers */ + ; + + } else 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 in general, then do so. + * Avoid advertising host routes onto their own network, + * where they should be handled by proxy-ARP. + */ + if ((RT->rt_state & RS_LOCAL) + || ridhosts + || (ws.state & WS_ST_SUPER_AG) + || on_net(dst, ws.to_net, ws.to_mask)) + ags |= AGS_SUPPRESS; + + if (ws.state & WS_ST_SUPER_AG) + ags |= AGS_PROMOTE; + + } 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; + + } + + /* Do not send RIPv1 advertisements of subnets to other + * networks. If possible, multicast them by RIPv2. + */ + if ((RT->rt_state & RS_SUBNET) + && !(ws.state & WS_ST_RIP2_ALL) + && !on_net(dst, ws.to_std_net, ws.to_std_mask)) { + ags |= AGS_RIPV2 | AGS_PROMOTE; + if (ws.state & WS_ST_SUB_AG) + ags |= AGS_SUPPRESS; + } + + /* 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. + * + * We want to suppress routes that might have been fragmented + * from this route by a RIPv1 router and sent back to us, and so we + * cannot forget this route here. Let the split-horizon route + * aggregate (suppress) the fragmented routes and then itself be + * forgotten. + * + * Include the routes for both ends of point-to-point interfaces + * since the other side presumably knows them as well as we do. + */ + 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) + || ws.ifp->int_if_flags & IFF_POINTOPOINT)) { + /* Poison-reverse the route instead of only not advertising it + * it is recently changed from some other route. + * In almost all cases, if there is no spare for the route + * then it is either old or a brand new route, and if it + * is brand new, there is no need for poison-reverse. + */ + metric = HOPCNT_INFINITY; + if (RT->rt_poison_time < now_expire + || RT->rt_spares[1].rts_gate ==0) { + ags |= AGS_SPLIT_HZ; + ags &= ~(AGS_PROMOTE | AGS_SUPPRESS); + } + } + + /* Adjust the outgoing metric by the cost of the link. + */ + pref = metric + ws.metric; + if (pref < HOPCNT_INFINITY) { + /* Keep track of the best metric with which the + * route has been advertised recently. + */ + if (RT->rt_poison_metric >= metric + || RT->rt_poison_time < now_expire) { + RT->rt_poison_time = now.tv_sec; + RT->rt_poison_metric = metric; + } + metric = pref; + + } else { + /* Do not advertise stable routes that will be ignored, + * unless they are being held down and poisoned. If the + * route recently was advertised with a metric that would + * have been less than infinity through this interface, we + * need to continue to advertise it in order to poison it. + */ + pref = RT->rt_poison_metric + ws.metric; + if (pref >= HOPCNT_INFINITY + || RT->rt_poison_time < now_garbage ) + return 0; + + metric = HOPCNT_INFINITY; + } + + ag_check(dst, RT->rt_mask, 0, nhop, 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. + */ +void +supply(struct sockaddr_in *dst, + struct interface *ifp, /* output interface */ + enum output_type type, + int flash, /* 1=flash update */ + int vers) /* RIP version */ +{ + static int init = 1; + struct rt_entry *rt; + + + ws.state = 0; + ws.gen_limit = 1024; + + 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); + 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 = 1; + } else { + /* Adjust the advertised metric by the outgoing interface + * metric. + */ + ws.metric = ifp->int_metric+1; + } + + if (init) { + init = 0; + + bzero(&ripv12_buf, sizeof(ripv12_buf)); + ripv12_buf.rip.rip_cmd = RIPCMD_RESPONSE; + ws.v12.buf = &ripv12_buf.rip; + ws.v12.base = &ws.v12.buf->rip_nets[0]; + ws.v12.lim = ws.v12.base + NETS_LEN; + + bzero(&rip_v2_buf, sizeof(rip_v2_buf)); + rip_v2_buf.rip.rip_cmd = RIPCMD_RESPONSE; + rip_v2_buf.rip.rip_vers = RIPv2; + ws.v2.buf = &rip_v2_buf.rip; + ws.v2.base = &ws.v2.buf->rip_nets[0]; + ws.v2.lim = ws.v2.base + NETS_LEN; + } + ripv12_buf.rip.rip_vers = vers; + + ws.v12.n = ws.v12.base; + set_auth(&ws.v12); + ws.v2.n = ws.v2.base; + set_auth(&ws.v2); + + switch (type) { + case OUT_BROADCAST: + ws.v2.type = ((ws.ifp != 0 + && (ws.ifp->int_if_flags & IFF_MULTICAST)) + ? OUT_MULTICAST + : NO_OUT_MULTICAST); + ws.v12.type = OUT_BROADCAST; + break; + case OUT_MULTICAST: + ws.v2.type = ((ws.ifp != 0 + && (ws.ifp->int_if_flags & IFF_MULTICAST)) + ? OUT_MULTICAST + : NO_OUT_MULTICAST); + ws.v12.type = OUT_BROADCAST; + break; + case OUT_UNICAST: + case OUT_QUERY: + ws.v2.type = (vers == RIPv2) ? type : NO_OUT_RIPV2; + ws.v12.type = type; + break; + default: + ws.v2.type = type; + ws.v12.type = type; + break; + } + + if (vers == RIPv2) { + /* if asked to send RIPv2, send at least that which can + * be safely heard by RIPv1 listeners. + */ + 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; + } + + } else if (ws.ifp == 0 || !(ws.ifp->int_state & IS_NO_AG)) { + ws.state |= WS_ST_SUB_AG; + } + + if (supplier) { + /* Fake a default route if asked, and if there is not + * a better, real default route. + */ + if (ifp->int_d_metric != 0 + && (0 == (rt = rtget(RIP_DEFAULT, 0)) + || rt->rt_metric+ws.metric >= ifp->int_d_metric)) { + ws.state |= WS_ST_DEFAULT; + ag_check(0, 0, 0, 0, + ifp->int_d_metric,ifp->int_d_metric, + 0, 0, 0, supply_out); + } + if ((ws.state & WS_ST_RIP2_ALL) + && (ifp->int_state & IS_PM_RDISC)) { + ws.state |= WS_ST_PM_RDISC; + ripv12_buf.rip.rip_vers = RIPv1; + } + } + + (void)rn_walktree(rhead, walk_supply, 0); + ag_flush(0,0,supply_out); + + /* Flush the packet buffers, provided they are not empty and + * do not contain only the password. + */ + if (ws.v12.n != ws.v12.base + && (ws.v12.n > ws.v12.base+1 + || ws.v12.n->n_family != RIP_AF_AUTH)) + supply_write(&ws.v12); + 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 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.v12); +} + + +/* 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_act("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; + + /* default to RIPv1 output */ + if (ifp->int_state & IS_NO_RIPV1_OUT) { + /* Say nothing if this interface is turned off */ + if (ifp->int_state & IS_NO_RIPV2_OUT) + continue; + vers = RIPv2; + } else { + vers = RIPv1; + } + + if (ifp->int_if_flags & IFF_BROADCAST) { + /* ordinary, hardware interface */ + dst.sin_addr.s_addr = ifp->int_brdaddr; + /* if RIPv1 is not turned off, then broadcast so + * that RIPv1 listeners can hear. + */ + if (vers == RIPv2 + && (ifp->int_state & IS_NO_RIPV1_OUT)) { + type = OUT_MULTICAST; + } else { + type = OUT_BROADCAST; + } + + } else if (ifp->int_if_flags & IFF_POINTOPOINT) { + /* point-to-point hardware interface */ + dst.sin_addr.s_addr = ifp->int_dstaddr; + type = OUT_UNICAST; + + } else { + /* remote interface */ + dst.sin_addr.s_addr = ifp->int_addr; + type = OUT_UNICAST; + } + + supply(&dst, ifp, type, flash, vers); + } + + update_seqno++; /* all routes are up to date */ +} + + +/* Ask for routes + * Do it only once to an interface, and not even after the interface + * was broken and recovered. + */ +void +rip_query(void) +{ +#ifdef _HAVE_SIN_LEN + static struct sockaddr_in dst = {sizeof(dst), AF_INET}; +#else + static struct sockaddr_in dst = {AF_INET}; +#endif + struct interface *ifp; + struct rip buf; + enum output_type type; + + + if (rip_sock < 0) + return; + + bzero(&buf, sizeof(buf)); + + for (ifp = ifnet; ifp; ifp = ifp->int_next) { + /* skip interfaces not doing RIP, those already queried, + * and aliases. Do try broken interfaces to see + * if they have healed. + */ + if (0 != (ifp->int_state & (IS_RIP_QUERIED + | IS_PASSIVE | IS_ALIAS))) + continue; + + /* skip turned off interfaces */ + if (!iff_alive(ifp->int_if_flags)) + continue; + + /* default to RIPv1 output */ + if (ifp->int_state & IS_NO_RIPV2_OUT) { + /* Say nothing if this interface is turned off */ + 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 (buf.rip_vers == RIPv2 + && (ifp->int_state & IS_NO_RIPV1_OUT)) { + type = OUT_MULTICAST; + } else { + type = OUT_BROADCAST; + } + + } 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) + if_sick(ifp); + } +} diff --git a/sbin/routed/parms.c b/sbin/routed/parms.c new file mode 100644 index 0000000..0d178c3 --- /dev/null +++ b/sbin/routed/parms.c @@ -0,0 +1,630 @@ +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if !defined(lint) && !defined(sgi) && !defined(__NetBSD__) +static char sccsid[] = "@(#)if.c 8.1 (Berkeley) 6/5/93"; +#elif defined(__NetBSD__) +static char rcsid[] = "$NetBSD$"; +#endif +#ident "$Revision: 1.9 $" + +#include "defs.h" +#include "pathnames.h" + + +struct parm *parms; +struct intnet *intnets; + + +/* use configured parameters + */ +void +get_parms(struct interface *ifp) +{ + struct parm *parmp; + + /* get all relevant parameters + */ + for (parmp = parms; parmp != 0; parmp = parmp->parm_next) { + if ((parmp->parm_name[0] == '\0' + && on_net(ifp->int_addr, + parmp->parm_addr_h, parmp->parm_mask)) + || (parmp->parm_name[0] != '\0' + && !strcmp(ifp->int_name, parmp->parm_name))) { + /* this group of parameters is relevant, + * so get its settings + */ + ifp->int_state |= parmp->parm_int_state; + if (parmp->parm_passwd[0] != '\0') + bcopy(parmp->parm_passwd, ifp->int_passwd, + sizeof(ifp->int_passwd)); + if (parmp->parm_rdisc_pref != 0) + ifp->int_rdisc_pref = parmp->parm_rdisc_pref; + if (parmp->parm_rdisc_int != 0) + ifp->int_rdisc_int = parmp->parm_rdisc_int; + if (parmp->parm_d_metric != 0) + ifp->int_d_metric = parmp->parm_d_metric; + } + } + /* default poor-man's router discovery to a metric that will + * be heard by old versions of routed. + */ + if ((ifp->int_state & IS_PM_RDISC) + && ifp->int_d_metric == 0) + ifp->int_d_metric = HOPCNT_INFINITY-2; + + if (IS_RIP_IN_OFF(ifp->int_state)) + ifp->int_state |= IS_NO_RIP_OUT; + + if (ifp->int_rdisc_int == 0) + ifp->int_rdisc_int = DefMaxAdvertiseInterval; + + if (!(ifp->int_if_flags & IFF_MULTICAST) + && !(ifp->int_if_flags & IFF_POINTOPOINT)) + ifp->int_state |= IS_NO_RIPV2_OUT; + + if (!(ifp->int_if_flags & IFF_MULTICAST)) + ifp->int_state |= IS_BCAST_RDISC; + + if (ifp->int_if_flags & IFF_POINTOPOINT) { + ifp->int_state |= IS_BCAST_RDISC; + /* By default, point-to-point links should be passive + * about router-discovery for the sake of demand-dialing. + */ + if (0 == (ifp->int_state & GROUP_IS_SOL)) + ifp->int_state |= IS_NO_SOL_OUT; + if (0 == (ifp->int_state & GROUP_IS_ADV)) + ifp->int_state |= IS_NO_ADV_OUT; + } + + if (0 != (ifp->int_state & (IS_PASSIVE | IS_REMOTE))) + ifp->int_state |= IS_NO_RDISC; + if (ifp->int_state & IS_PASSIVE) + ifp->int_state |= (IS_NO_RIP | IS_NO_RDISC); + if ((ifp->int_state & (IS_NO_RIP | IS_NO_RDISC)) + == (IS_NO_RIP|IS_NO_RDISC)) + ifp->int_state |= IS_PASSIVE; +} + + +/* Read a list of gateways from /etc/gateways and add them to our tables. + * + * This file contains a list of "remote" gateways. That is usually + * a gateway which we cannot immediately determine if it is present or + * not as we can do for those provided by directly connected hardware. + * + * If a gateway is marked "passive" in the file, then we assume it + * does not understand RIP and assume it is always present. Those + * not marked passive are treated as if they were directly connected + * and assumed to be broken if they do not send us advertisements. + * All remote interfaces are added to our list, and those not marked + * passive are sent routing updates. + * + * A passive interface can also be local, hardware interface exempt + * from RIP. + */ +void +gwkludge(void) +{ + FILE *fp; + char *p, *lptr; + char lbuf[200], net_host[5], dname[64+1+64+1], gname[64+1], qual[9]; + struct interface *ifp; + naddr dst, netmask, gate; + int metric, n; + u_int state; + char *type; + struct parm *parmp; + + + fp = fopen(_PATH_GATEWAYS, "r"); + if (fp == 0) + return; + + for (;;) { + if (0 == fgets(lbuf, sizeof(lbuf)-1, fp)) + break; + lptr = lbuf; + while (*lptr == ' ') + lptr++; + if (*lptr == '\n' /* ignore null and comment lines */ + || *lptr == '#') + continue; + p = lptr+strlen(lptr)-1; + while (*p == '\n' + || *p == ' ') + *p-- = '\0'; + + /* notice newfangled parameter lines + */ + if (strncasecmp("net", lptr, 3) + && strncasecmp("host", lptr, 4)) { + p = parse_parms(lptr); + if (p != 0) { + if (strcmp(p,lptr)) + msglog("bad \"%s\" in "_PATH_GATEWAYS + " entry \"%s\"", lptr, p); + else + msglog("bad \"%s\" in "_PATH_GATEWAYS, + lptr); + } + continue; + } + +/* {net | host} XX[/M] XX gateway XX metric DD [passive | external]\n */ + n = sscanf(lptr, "%4s %129[^ \t] gateway" + " %64[^ / \t] metric %d %8s\n", + net_host, dname, gname, &metric, qual); + if (n != 5) { + msglog("bad "_PATH_GATEWAYS" entry \"%s\"", lptr); + continue; + } + if (metric < 0 || metric >= HOPCNT_INFINITY) { + msglog("bad metric in "_PATH_GATEWAYS" entry \"%s\"", + lptr); + continue; + } + if (!strcmp(net_host, "host")) { + if (!gethost(dname, &dst)) { + msglog("bad host \"%s\" in "_PATH_GATEWAYS + " entry \"%s\"", dname, lptr); + continue; + } + netmask = HOST_MASK; + } else if (!strcmp(net_host, "net")) { + if (!getnet(dname, &dst, &netmask)) { + msglog("bad net \"%s\" in "_PATH_GATEWAYS + " entry \"%s\"", dname, lptr); + continue; + } + } else { + msglog("bad \"%s\" in "_PATH_GATEWAYS + " entry \"%s\"", lptr); + continue; + } + + if (!gethost(gname, &gate)) { + msglog("bad gateway \"%s\" in "_PATH_GATEWAYS + " entry \"%s\"", gname, lptr); + continue; + } + + if (strcmp(qual, type = "passive") == 0) { + /* Passive entries are not placed in our tables, + * only the kernel's, so we don't copy all of the + * external routing information within a net. + * Internal machines should use the default + * route to a suitable gateway (like us). + */ + state = IS_REMOTE | IS_PASSIVE; + if (metric == 0) + metric = 1; + + } else if (strcmp(qual, type = "external") == 0) { + /* External entries are handled by other means + * such as EGP, and are placed only in the daemon + * tables to prevent overriding them with something + * else. + */ + state = IS_REMOTE | IS_PASSIVE | IS_EXTERNAL; + if (metric == 0) + metric = 1; + + } else if (qual[0] == '\0') { + if (metric != 0) { + /* Entries that are neither "passive" nor + * "external" are "remote" and must behave + * like physical interfaces. If they are not + * heard from regularly, they are deleted. + */ + state = IS_REMOTE; + type = "remote"; + } else { + /* "remote" entries with a metric of 0 + * are aliases for our own interfaces + */ + state = IS_REMOTE | IS_PASSIVE; + type = "alias"; + } + + } else { + msglog("bad "_PATH_GATEWAYS" entry \"%s\"", lptr); + continue; + } + + /* Remember to advertise the corresponding logical network. + */ + if (!(state & IS_EXTERNAL) + && netmask != std_mask(dst)) + state |= IS_SUBNET; + + if (0 != (state & (IS_PASSIVE | IS_REMOTE))) + state |= IS_NO_RDISC; + if (state & IS_PASSIVE) + state |= (IS_NO_RIP | IS_NO_RDISC); + if ((state & (IS_NO_RIP | IS_NO_RDISC)) + == (IS_NO_RIP|IS_NO_RDISC)) + state |= IS_PASSIVE; + + parmp = (struct parm*)malloc(sizeof(*parmp)); + bzero(parmp, sizeof(*parmp)); + parmp->parm_next = parms; + parms = parmp; + parmp->parm_addr_h = ntohl(dst); + parmp->parm_mask = -1; + parmp->parm_d_metric = 0; + parmp->parm_int_state = state; + + /* See if this new interface duplicates an existing + * interface. + */ + for (ifp = ifnet; 0 != ifp; ifp = ifp->int_next) { + if (ifp->int_mask == netmask + && ((ifp->int_addr == dst + && netmask != HOST_MASK) + || (ifp->int_dstaddr == dst + && netmask == HOST_MASK))) + break; + } + if (ifp != 0) { + /* Let one of our real interfaces be marked passive. + */ + if ((state & IS_PASSIVE) && !(state & IS_EXTERNAL)) { + ifp->int_state |= state; + } else { + msglog("%s is duplicated in "_PATH_GATEWAYS + " by %s", + ifp->int_name, lptr); + } + continue; + } + + tot_interfaces++; + + ifp = (struct interface *)malloc(sizeof(*ifp)); + bzero(ifp, sizeof(*ifp)); + if (ifnet != 0) { + ifp->int_next = ifnet; + ifnet->int_prev = ifp; + } + ifnet = ifp; + + ifp->int_state = state; + ifp->int_net = ntohl(dst) & netmask; + ifp->int_mask = netmask; + if (netmask == HOST_MASK) + ifp->int_if_flags |= IFF_POINTOPOINT; + ifp->int_dstaddr = dst; + ifp->int_addr = gate; + ifp->int_metric = metric; + (void)sprintf(ifp->int_name, "%s-%s", type, naddr_ntoa(dst)); + ifp->int_index = -1; + + get_parms(ifp); + + trace_if("Add", ifp); + } +} + + +/* parse a set of parameters for an interface + */ +char * /* 0 or error message */ +parse_parms(char *line) +{ +#define PARS(str) (0 == (tgt = str, strcasecmp(tok, tgt))) +#define PARSE(str) (0 == (tgt = str, strncasecmp(tok, str "=", sizeof(str)))) +#define CKF(g,b) {if (0 != (parm.parm_int_state & ((g) & ~(b)))) break; \ + parm.parm_int_state |= (b);} +#define DELIMS " ,\t\n" + struct parm parm; + struct intnet *intnetp; + char *tok, *tgt, *p; + + + /* "subnet=x.y.z.u/mask" must be alone on the line */ + if (!strncasecmp("subnet=",line,7)) { + intnetp = (struct intnet*)malloc(sizeof(*intnetp)); + intnetp->intnet_metric = 1; + if ((p = strrchr(line,','))) { + *p++ = '\0'; + intnetp->intnet_metric = (int)strtol(p,&p,0); + if (*p != '\0' + || intnetp->intnet_metric <= 0 + || intnetp->intnet_metric >= HOPCNT_INFINITY) + return line; + } + if (!getnet(&line[7], &intnetp->intnet_addr, + &intnetp->intnet_mask) + || intnetp->intnet_mask == HOST_MASK + || intnetp->intnet_addr == RIP_DEFAULT) { + free(intnetp); + return line; + } + intnetp->intnet_next = intnets; + intnets = intnetp; + return 0; + } + + bzero(&parm, sizeof(parm)); + + tgt = "null"; + for (tok = strtok(line, DELIMS); + tok != 0 && tok[0] != '\0'; + tgt = 0, tok = strtok(0,DELIMS)) { + if (PARSE("if")) { + if (parm.parm_name[0] != '\0' + || tok[3] == '\0' + || strlen(tok) > IFNAMSIZ+3) + break; + strcpy(parm.parm_name, tok+3); + + } else if (PARSE("passwd")) { + if (tok[7] == '\0' + || strlen(tok) > RIP_AUTH_PW_LEN+7) + break; + strcpy(parm.parm_passwd, tok+7); + + } else if (PARS("no_ag")) { + parm.parm_int_state |= (IS_NO_AG | IS_NO_SUPER_AG); + + } else if (PARS("no_super_ag")) { + parm.parm_int_state |= IS_NO_SUPER_AG; + + } else if (PARS("no_ripv1_in")) { + parm.parm_int_state |= IS_NO_RIPV1_IN; + + } else if (PARS("no_ripv2_in")) { + parm.parm_int_state |= IS_NO_RIPV2_IN; + + } else if (PARS("ripv2_out")) { + if (parm.parm_int_state & IS_NO_RIPV2_OUT) + break; + parm.parm_int_state |= IS_NO_RIPV1_OUT; + + } else if (PARS("no_rip")) { + parm.parm_int_state |= IS_NO_RIP; + + } else if (PARS("no_rdisc")) { + CKF((GROUP_IS_SOL|GROUP_IS_ADV), IS_NO_RDISC); + + } else if (PARS("no_solicit")) { + CKF(GROUP_IS_SOL, IS_NO_SOL_OUT); + + } else if (PARS("send_solicit")) { + CKF(GROUP_IS_SOL, IS_SOL_OUT); + + } else if (PARS("no_rdisc_adv")) { + CKF(GROUP_IS_ADV, IS_NO_ADV_OUT); + + } else if (PARS("rdisc_adv")) { + CKF(GROUP_IS_ADV, IS_ADV_OUT); + + } else if (PARS("bcast_rdisc")) { + parm.parm_int_state |= IS_BCAST_RDISC; + + } else if (PARS("passive")) { + CKF((GROUP_IS_SOL|GROUP_IS_ADV), IS_NO_RDISC); + parm.parm_int_state |= IS_NO_RIP; + + } else if (PARSE("rdisc_pref")) { + if (parm.parm_rdisc_pref != 0 + || tok[11] == '\0' + || (parm.parm_rdisc_pref = (int)strtol(&tok[11], + &p,0), + *p != '\0')) + break; + + } else if (PARS("pm_rdisc")) { + parm.parm_int_state |= IS_PM_RDISC; + + } else if (PARSE("rdisc_interval")) { + if (parm.parm_rdisc_int != 0 + || tok[15] == '\0' + || (parm.parm_rdisc_int = (int)strtol(&tok[15], + &p,0), + *p != '\0') + || parm.parm_rdisc_int < MinMaxAdvertiseInterval + || parm.parm_rdisc_int > MaxMaxAdvertiseInterval) + break; + + } else if (PARSE("fake_default")) { + if (parm.parm_d_metric != 0 + || tok[13] == '\0' + || (parm.parm_d_metric=(int)strtol(&tok[13],&p,0), + *p != '\0') + || parm.parm_d_metric > HOPCNT_INFINITY-1) + break; + + } else { + tgt = tok; + break; + } + } + if (tgt != 0) + return tgt; + + return check_parms(&parm); +#undef DELIMS +#undef PARS +#undef PARSE +} + + +/* check for duplicate parameter specifications */ +char * /* 0 or error message */ +check_parms(struct parm *new) +{ + struct parm *parmp; + + + /* set implicit values + */ + if (!supplier && supplier_set) + new->parm_int_state |= (IS_NO_RIPV1_OUT + | IS_NO_RIPV2_OUT + | IS_NO_ADV_OUT); + if (new->parm_int_state & IS_NO_ADV_IN) + new->parm_int_state |= IS_NO_SOL_OUT; + + if ((new->parm_int_state & (IS_NO_RIP | IS_NO_RDISC)) + == (IS_NO_RIP | IS_NO_RDISC)) + new->parm_int_state |= IS_PASSIVE; + + /* compare with existing sets of parameters + */ + for (parmp = parms; parmp != 0; parmp = parmp->parm_next) { + if (strcmp(new->parm_name, parmp->parm_name)) + continue; + if (!on_net(htonl(parmp->parm_addr_h), + new->parm_addr_h, new->parm_mask) + && !on_net(htonl(new->parm_addr_h), + parmp->parm_addr_h, parmp->parm_mask)) + continue; + + if (strcmp(parmp->parm_passwd, new->parm_passwd) + || (0 != (new->parm_int_state & GROUP_IS_SOL) + && 0 != (parmp->parm_int_state & GROUP_IS_SOL) + && 0 != ((new->parm_int_state ^ parmp->parm_int_state) + && GROUP_IS_SOL)) + || (0 != (new->parm_int_state & GROUP_IS_ADV) + && 0 != (parmp->parm_int_state & GROUP_IS_ADV) + && 0 != ((new->parm_int_state ^ parmp->parm_int_state) + && GROUP_IS_ADV)) + || (new->parm_rdisc_pref != 0 + && parmp->parm_rdisc_pref != 0 + && new->parm_rdisc_pref != parmp->parm_rdisc_pref) + || (new->parm_rdisc_int != 0 + && parmp->parm_rdisc_int != 0 + && new->parm_rdisc_int != parmp->parm_rdisc_int) + || (new->parm_d_metric != 0 + && parmp->parm_d_metric != 0 + && new->parm_d_metric != parmp->parm_d_metric)) + return "duplicate"; + } + + parmp = (struct parm*)malloc(sizeof(*parmp)); + bcopy(new, parmp, sizeof(*parmp)); + parmp->parm_next = parms; + parms = parmp; + + return 0; +} + + +/* get a network number as a name or a number, with an optional "/xx" + * netmask. + */ +int /* 0=bad */ +getnet(char *name, + naddr *addrp, /* host byte order */ + naddr *maskp) +{ + int i; + struct netent *np; + naddr mask; + struct in_addr in; + char hname[MAXHOSTNAMELEN+1]; + char *mname, *p; + + + /* Detect and separate "1.2.3.4/24" + */ + if (0 != (mname = rindex(name,'/'))) { + i = (int)(mname - name); + if (i > sizeof(hname)-1) /* name too long */ + return 0; + bcopy(name, hname, i); + hname[i] = '\0'; + mname++; + name = hname; + } + + np = getnetbyname(name); + if (np != 0) { + in.s_addr = (naddr)np->n_net; + } else if (inet_aton(name, &in) == 1) { + HTONL(in.s_addr); + } else { + return 0; + } + + if (mname == 0) { + /* we cannot use the interfaces here because we have not + * looked at them yet. + */ + mask = std_mask(in.s_addr); + if ((~mask & ntohl(in.s_addr)) != 0) + mask = HOST_MASK; + } else { + mask = (naddr)strtoul(mname, &p, 0); + if (*p != '\0' || mask > 32) + return 0; + mask = HOST_MASK << (32-mask); + } + if (mask != 0 && in.s_addr == RIP_DEFAULT) + return 0; + if ((~mask & ntohl(in.s_addr)) != 0) + return 0; + + *addrp = in.s_addr; + *maskp = mask; + return 1; +} + + +int /* 0=bad */ +gethost(char *name, + naddr *addrp) +{ + struct hostent *hp; + struct in_addr in; + + + /* Try for a number first, even in IRIX where gethostbyname() + * is smart. This avoids hitting the name server which + * might be sick because routing is. + */ + if (inet_aton(name, &in) == 1) { + *addrp = in.s_addr; + return 1; + } + + hp = gethostbyname(name); + if (hp) { + bcopy(hp->h_addr, addrp, sizeof(*addrp)); + return 1; + } + + return 0; +} diff --git a/sbin/routed/pathnames.h b/sbin/routed/pathnames.h new file mode 100644 index 0000000..14b721c --- /dev/null +++ b/sbin/routed/pathnames.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)pathnames.h 8.1 (Berkeley) 6/5/93 + * + * $NetBSD$ + */ + +#include + +#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. + * If this is a directory, routed will create log files in it. That + * might be a security problem. + * + * Leave this undefined, and only the trace file originally specified + * when routed was started, if any, will be appended to. + */ +#define _PATH_TRACE "/etc/routed.trace" diff --git a/sbin/routed/radix.c b/sbin/routed/radix.c new file mode 100644 index 0000000..7f7e1e4 --- /dev/null +++ b/sbin/routed/radix.c @@ -0,0 +1,895 @@ +/* + * Copyright (c) 1988, 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)radix.c 8.4 (Berkeley) 11/2/94 + */ + +/* + * Routines to build and maintain radix trees for routing lookups. + */ +#if !defined(lint) && !defined(sgi) && !defined(__NetBSD__) +static char sccsid[] = "@(#)rdisc.c 8.1 (Berkeley) x/y/95"; +#elif defined(__NetBSD__) +static char rcsid[] = "$NetBSD$"; +#endif +#ident "$Revision: 1.10 $" + +#include "defs.h" + +#define log(x, msg) syslog(x, msg) +#define panic(s) {log(LOG_ERR,s); exit(1);} +#define min(a,b) (((a)<(b))?(a):(b)) + +int max_keylen; +struct radix_mask *rn_mkfreelist; +struct radix_node_head *mask_rnhead; +static char *addmask_key; +static char normal_chars[] = {0, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, -1}; +static char *rn_zeros, *rn_ones; + +#define rn_masktop (mask_rnhead->rnh_treetop) +#undef Bcmp +#define Bcmp(a, b, l) (l == 0 ? 0 : bcmp((caddr_t)(a), (caddr_t)(b), (u_long)l)) + +static int rn_satsifies_leaf(char *, struct radix_node *, int); + +/* + * The data structure for the keys is a radix tree with one way + * branching removed. The index rn_b at an internal node n represents a bit + * position to be tested. The tree is arranged so that all descendants + * of a node n have keys whose bits all agree up to position rn_b - 1. + * (We say the index of n is rn_b.) + * + * There is at least one descendant which has a one bit at position rn_b, + * and at least one with a zero there. + * + * A route is determined by a pair of key and mask. We require that the + * bit-wise logical and of the key and mask to be the key. + * We define the index of a route to associated with the mask to be + * the first bit number in the mask where 0 occurs (with bit number 0 + * representing the highest order bit). + * + * We say a mask is normal if every bit is 0, past the index of the mask. + * If a node n has a descendant (k, m) with index(m) == index(n) == rn_b, + * and m is a normal mask, then the route applies to every descendant of n. + * If the index(m) < rn_b, this implies the trailing last few bits of k + * before bit b are all 0, (and hence consequently true of every descendant + * of n), so the route applies to all descendants of the node as well. + * + * Similar logic shows that a non-normal mask m such that + * index(m) <= index(n) could potentially apply to many children of n. + * Thus, for each non-host route, we attach its mask to a list at an internal + * node as high in the tree as we can go. + * + * The present version of the code makes use of normal routes in short- + * circuiting an explict mask and compare operation when testing whether + * a key satisfies a normal route, and also in remembering the unique leaf + * that governs a subtree. + */ + +struct radix_node * +rn_search(void *v_arg, + struct radix_node *head) +{ + register struct radix_node *x; + register caddr_t v; + + for (x = head, v = v_arg; x->rn_b >= 0;) { + if (x->rn_bmask & v[x->rn_off]) + x = x->rn_r; + else + x = x->rn_l; + } + return (x); +} + +struct radix_node * +rn_search_m(void *v_arg, + struct radix_node *head, + void *m_arg) +{ + register struct radix_node *x; + register caddr_t v = v_arg, m = m_arg; + + for (x = head; x->rn_b >= 0;) { + if ((x->rn_bmask & m[x->rn_off]) && + (x->rn_bmask & v[x->rn_off])) + x = x->rn_r; + else + x = x->rn_l; + } + return x; +} + +int +rn_refines(void* m_arg, void *n_arg) +{ + register caddr_t m = m_arg, n = n_arg; + register caddr_t lim, lim2 = lim = n + *(u_char *)n; + int longer = (*(u_char *)n++) - (int)(*(u_char *)m++); + int masks_are_equal = 1; + + if (longer > 0) + lim -= longer; + while (n < lim) { + if (*n & ~(*m)) + return 0; + if (*n++ != *m++) + masks_are_equal = 0; + } + while (n < lim2) + if (*n++) + return 0; + if (masks_are_equal && (longer < 0)) + for (lim2 = m - longer; m < lim2; ) + if (*m++) + return 1; + return (!masks_are_equal); +} + +struct radix_node * +rn_lookup(v_arg, m_arg, head) + void *v_arg, *m_arg; + struct radix_node_head *head; +{ + register struct radix_node *x; + caddr_t netmask = 0; + + if (m_arg) { + if ((x = rn_addmask(m_arg, 1, head->rnh_treetop->rn_off)) == 0) + return (0); + netmask = x->rn_key; + } + x = rn_match(v_arg, head); + if (x && netmask) { + while (x && x->rn_mask != netmask) + x = x->rn_dupedkey; + } + return x; +} + +static int +rn_satsifies_leaf(char *trial, + register struct radix_node *leaf, + int skip) +{ + register char *cp = trial, *cp2 = leaf->rn_key, *cp3 = leaf->rn_mask; + char *cplim; + int length = min(*(u_char *)cp, *(u_char *)cp2); + + if (cp3 == 0) + cp3 = rn_ones; + else + length = min(length, *(u_char *)cp3); + cplim = cp + length; cp3 += skip; cp2 += skip; + for (cp += skip; cp < cplim; cp++, cp2++, cp3++) + if ((*cp ^ *cp2) & *cp3) + return 0; + return 1; +} + +struct radix_node * +rn_match(void *v_arg, + struct radix_node_head *head) +{ + caddr_t v = v_arg; + register struct radix_node *t = head->rnh_treetop, *x; + register caddr_t cp = v, cp2; + caddr_t cplim; + struct radix_node *saved_t, *top = t; + int off = t->rn_off, vlen = *(u_char *)cp, matched_off; + register int test, b, rn_b; + + /* + * Open code rn_search(v, top) to avoid overhead of extra + * subroutine call. + */ + for (; t->rn_b >= 0; ) { + if (t->rn_bmask & cp[t->rn_off]) + t = t->rn_r; + else + t = t->rn_l; + } + /* + * See if we match exactly as a host destination + * or at least learn how many bits match, for normal mask finesse. + * + * It doesn't hurt us to limit how many bytes to check + * to the length of the mask, since if it matches we had a genuine + * match and the leaf we have is the most specific one anyway; + * if it didn't match with a shorter length it would fail + * with a long one. This wins big for class B&C netmasks which + * are probably the most common case... + */ + if (t->rn_mask) + vlen = *(u_char *)t->rn_mask; + cp += off; cp2 = t->rn_key + off; cplim = v + vlen; + for (; cp < cplim; cp++, cp2++) + if (*cp != *cp2) + goto on1; + /* + * This extra grot is in case we are explicitly asked + * to look up the default. Ugh! + * Or 255.255.255.255 + * + * In this case, we have a complete match of the key. Unless + * the node is one of the roots, we are finished. + * If it is the zeros root, then take what we have, prefering + * any real data. + * If it is the ones root, then pretend the target key was followed + * by a byte of zeros. + */ + if (!(t->rn_flags & RNF_ROOT)) + return t; /* not a root */ + if (t->rn_dupedkey) { + t = t->rn_dupedkey; + return t; /* have some real data */ + } + if (*(cp-1) == 0) + return t; /* not the ones root */ + b = 0; /* fake a zero after 255.255.255.255 */ + goto on2; +on1: + test = (*cp ^ *cp2) & 0xff; /* find first bit that differs */ + for (b = 7; (test >>= 1) > 0;) + b--; +on2: + matched_off = cp - v; + b += matched_off << 3; + rn_b = -1 - b; + /* + * If there is a host route in a duped-key chain, it will be first. + */ + if ((saved_t = t)->rn_mask == 0) + t = t->rn_dupedkey; + for (; t; t = t->rn_dupedkey) + /* + * Even if we don't match exactly as a host, + * we may match if the leaf we wound up at is + * a route to a net. + */ + if (t->rn_flags & RNF_NORMAL) { + if (rn_b <= t->rn_b) + return t; + } else if (rn_satsifies_leaf(v, t, matched_off)) + return t; + t = saved_t; + /* start searching up the tree */ + do { + register struct radix_mask *m; + t = t->rn_p; + if ((m = t->rn_mklist)) { + /* + * If non-contiguous masks ever become important + * we can restore the masking and open coding of + * the search and satisfaction test and put the + * calculation of "off" back before the "do". + */ + do { + if (m->rm_flags & RNF_NORMAL) { + if (rn_b <= m->rm_b) + return (m->rm_leaf); + } else { + off = min(t->rn_off, matched_off); + x = rn_search_m(v, t, m->rm_mask); + while (x && x->rn_mask != m->rm_mask) + x = x->rn_dupedkey; + if (x && rn_satsifies_leaf(v, x, off)) + return x; + } + } while ((m = m->rm_mklist)); + } + } while (t != top); + return 0; +} + +#ifdef RN_DEBUG +int rn_nodenum; +struct radix_node *rn_clist; +int rn_saveinfo; +int rn_debug = 1; +#endif + +struct radix_node * +rn_newpair(void *v, int b, struct radix_node nodes[2]) +{ + register struct radix_node *tt = nodes, *t = tt + 1; + t->rn_b = b; t->rn_bmask = 0x80 >> (b & 7); + t->rn_l = tt; t->rn_off = b >> 3; + tt->rn_b = -1; tt->rn_key = (caddr_t)v; tt->rn_p = t; + tt->rn_flags = t->rn_flags = RNF_ACTIVE; +#ifdef RN_DEBUG + tt->rn_info = rn_nodenum++; t->rn_info = rn_nodenum++; + tt->rn_twin = t; tt->rn_ybro = rn_clist; rn_clist = tt; +#endif + return t; +} + +struct radix_node * +rn_insert(void* v_arg, + struct radix_node_head *head, + int *dupentry, + struct radix_node nodes[2]) +{ + caddr_t v = v_arg; + struct radix_node *top = head->rnh_treetop; + int head_off = top->rn_off, vlen = (int)*((u_char *)v); + register struct radix_node *t = rn_search(v_arg, top); + register caddr_t cp = v + head_off; + register int b; + struct radix_node *tt; + + /* + * Find first bit at which v and t->rn_key differ + */ + { + register caddr_t cp2 = t->rn_key + head_off; + register int cmp_res; + caddr_t cplim = v + vlen; + + while (cp < cplim) + if (*cp2++ != *cp++) + goto on1; + /* handle adding 255.255.255.255 */ + if (!(t->rn_flags & RNF_ROOT) || *(cp2-1) == 0) { + *dupentry = 1; + return t; + } +on1: + *dupentry = 0; + cmp_res = (cp[-1] ^ cp2[-1]) & 0xff; + for (b = (cp - v) << 3; cmp_res; b--) + cmp_res >>= 1; + } + { + register struct radix_node *p, *x = top; + cp = v; + do { + p = x; + if (cp[x->rn_off] & x->rn_bmask) + x = x->rn_r; + else x = x->rn_l; + } while (b > (unsigned) x->rn_b); /* x->rn_b < b && x->rn_b >= 0 */ +#ifdef RN_DEBUG + if (rn_debug) + log(LOG_DEBUG, "rn_insert: Going In:\n"), traverse(p); +#endif + t = rn_newpair(v_arg, b, nodes); tt = t->rn_l; + if ((cp[p->rn_off] & p->rn_bmask) == 0) + p->rn_l = t; + else + p->rn_r = t; + x->rn_p = t; t->rn_p = p; /* frees x, p as temp vars below */ + if ((cp[t->rn_off] & t->rn_bmask) == 0) { + t->rn_r = x; + } else { + t->rn_r = tt; t->rn_l = x; + } +#ifdef RN_DEBUG + if (rn_debug) + log(LOG_DEBUG, "rn_insert: Coming Out:\n"), traverse(p); +#endif + } + return (tt); +} + +struct radix_node * +rn_addmask(void *n_arg, int search, int skip) +{ + caddr_t netmask = (caddr_t)n_arg; + register struct radix_node *x; + register caddr_t cp, cplim; + register int b = 0, mlen, j; + int maskduplicated, m0, isnormal; + struct radix_node *saved_x; + static int last_zeroed = 0; + + if ((mlen = *(u_char *)netmask) > max_keylen) + mlen = max_keylen; + if (skip == 0) + skip = 1; + if (mlen <= skip) + return (mask_rnhead->rnh_nodes); + if (skip > 1) + Bcopy(rn_ones + 1, addmask_key + 1, skip - 1); + if ((m0 = mlen) > skip) + Bcopy(netmask + skip, addmask_key + skip, mlen - skip); + /* + * Trim trailing zeroes. + */ + for (cp = addmask_key + mlen; (cp > addmask_key) && cp[-1] == 0;) + cp--; + mlen = cp - addmask_key; + if (mlen <= skip) { + if (m0 >= last_zeroed) + last_zeroed = mlen; + return (mask_rnhead->rnh_nodes); + } + if (m0 < last_zeroed) + Bzero(addmask_key + m0, last_zeroed - m0); + *addmask_key = last_zeroed = mlen; + x = rn_search(addmask_key, rn_masktop); + if (Bcmp(addmask_key, x->rn_key, mlen) != 0) + x = 0; + if (x || search) + return (x); + R_Malloc(x, struct radix_node *, max_keylen + 2 * sizeof (*x)); + if ((saved_x = x) == 0) + return (0); + Bzero(x, max_keylen + 2 * sizeof (*x)); + netmask = cp = (caddr_t)(x + 2); + Bcopy(addmask_key, cp, mlen); + x = rn_insert(cp, mask_rnhead, &maskduplicated, x); + if (maskduplicated) { + log(LOG_ERR, "rn_addmask: mask impossibly already in tree"); + Free(saved_x); + return (x); + } + /* + * Calculate index of mask, and check for normalcy. + */ + cplim = netmask + mlen; isnormal = 1; + for (cp = netmask + skip; (cp < cplim) && *(u_char *)cp == 0xff;) + cp++; + if (cp != cplim) { + for (j = 0x80; (j & *cp) != 0; j >>= 1) + b++; + if (*cp != normal_chars[b] || cp != (cplim - 1)) + isnormal = 0; + } + b += (cp - netmask) << 3; + x->rn_b = -1 - b; + if (isnormal) + x->rn_flags |= RNF_NORMAL; + return (x); +} + +static int /* XXX: arbitrary ordering for non-contiguous masks */ +rn_lexobetter(void *m_arg, void *n_arg) +{ + register u_char *mp = m_arg, *np = n_arg, *lim; + + if (*mp > *np) + return 1; /* not really, but need to check longer one first */ + if (*mp == *np) + for (lim = mp + *mp; mp < lim;) + if (*mp++ > *np++) + return 1; + return 0; +} + +static struct radix_mask * +rn_new_radix_mask(register struct radix_node *tt, + register struct radix_mask *next) +{ + register struct radix_mask *m; + + MKGet(m); + if (m == 0) { + log(LOG_ERR, "Mask for route not entered\n"); + return (0); + } + Bzero(m, sizeof *m); + m->rm_b = tt->rn_b; + m->rm_flags = tt->rn_flags; + if (tt->rn_flags & RNF_NORMAL) + m->rm_leaf = tt; + else + m->rm_mask = tt->rn_mask; + m->rm_mklist = next; + tt->rn_mklist = m; + return m; +} + +struct radix_node * +rn_addroute(void *v_arg, + void *n_arg, + struct radix_node_head *head, + struct radix_node treenodes[2]) +{ + caddr_t v = (caddr_t)v_arg, netmask = (caddr_t)n_arg; + register struct radix_node *t, *x = 0, *tt; + struct radix_node *saved_tt, *top = head->rnh_treetop; + short b = 0, b_leaf = 0; + int keyduplicated; + caddr_t mmask; + struct radix_mask *m, **mp; + + /* + * In dealing with non-contiguous masks, there may be + * many different routes which have the same mask. + * We will find it useful to have a unique pointer to + * the mask to speed avoiding duplicate references at + * nodes and possibly save time in calculating indices. + */ + if (netmask) { + if ((x = rn_addmask(netmask, 0, top->rn_off)) == 0) + return (0); + b_leaf = x->rn_b; + b = -1 - x->rn_b; + netmask = x->rn_key; + } + /* + * Deal with duplicated keys: attach node to previous instance + */ + saved_tt = tt = rn_insert(v, head, &keyduplicated, treenodes); + if (keyduplicated) { + for (t = tt; tt; t = tt, tt = tt->rn_dupedkey) { + if (tt->rn_mask == netmask) + return (0); + if (netmask == 0 || + (tt->rn_mask && + ((b_leaf < tt->rn_b) || /* index(netmask) > node */ + rn_refines(netmask, tt->rn_mask) || + rn_lexobetter(netmask, tt->rn_mask)))) + break; + } + /* + * If the mask is not duplicated, we wouldn't + * find it among possible duplicate key entries + * anyway, so the above test doesn't hurt. + * + * We sort the masks for a duplicated key the same way as + * in a masklist -- most specific to least specific. + * This may require the unfortunate nuisance of relocating + * the head of the list. + */ + if (tt == saved_tt) { + struct radix_node *xx = x; + /* link in at head of list */ + (tt = treenodes)->rn_dupedkey = t; + tt->rn_flags = t->rn_flags; + tt->rn_p = x = t->rn_p; + if (x->rn_l == t) x->rn_l = tt; else x->rn_r = tt; + saved_tt = tt; x = xx; + } else { + (tt = treenodes)->rn_dupedkey = t->rn_dupedkey; + t->rn_dupedkey = tt; + } +#ifdef RN_DEBUG + t=tt+1; tt->rn_info = rn_nodenum++; t->rn_info = rn_nodenum++; + tt->rn_twin = t; tt->rn_ybro = rn_clist; rn_clist = tt; +#endif + tt->rn_key = (caddr_t) v; + tt->rn_b = -1; + tt->rn_flags = RNF_ACTIVE; + } + /* + * Put mask in tree. + */ + if (netmask) { + tt->rn_mask = netmask; + tt->rn_b = x->rn_b; + tt->rn_flags |= x->rn_flags & RNF_NORMAL; + } + t = saved_tt->rn_p; + if (keyduplicated) + goto on2; + b_leaf = -1 - t->rn_b; + if (t->rn_r == saved_tt) x = t->rn_l; else x = t->rn_r; + /* Promote general routes from below */ + if (x->rn_b < 0) { + for (mp = &t->rn_mklist; x; x = x->rn_dupedkey) + if (x->rn_mask && (x->rn_b >= b_leaf) && x->rn_mklist == 0) { + if ((*mp = m = rn_new_radix_mask(x, 0))) + mp = &m->rm_mklist; + } + } else if (x->rn_mklist) { + /* + * Skip over masks whose index is > that of new node + */ + for (mp = &x->rn_mklist; (m = *mp); mp = &m->rm_mklist) + if (m->rm_b >= b_leaf) + break; + t->rn_mklist = m; *mp = 0; + } +on2: + /* Add new route to highest possible ancestor's list */ + if ((netmask == 0) || (b > t->rn_b )) + return tt; /* can't lift at all */ + b_leaf = tt->rn_b; + do { + x = t; + t = t->rn_p; + } while (b <= t->rn_b && x != top); + /* + * Search through routes associated with node to + * insert new route according to index. + * Need same criteria as when sorting dupedkeys to avoid + * double loop on deletion. + */ + for (mp = &x->rn_mklist; (m = *mp); mp = &m->rm_mklist) { + if (m->rm_b < b_leaf) + continue; + if (m->rm_b > b_leaf) + break; + if (m->rm_flags & RNF_NORMAL) { + mmask = m->rm_leaf->rn_mask; + if (tt->rn_flags & RNF_NORMAL) { + log(LOG_ERR, + "Non-unique normal route, mask not entered"); + return tt; + } + } else + mmask = m->rm_mask; + if (mmask == netmask) { + m->rm_refs++; + tt->rn_mklist = m; + return tt; + } + if (rn_refines(netmask, mmask) || rn_lexobetter(netmask, mmask)) + break; + } + *mp = rn_new_radix_mask(tt, *mp); + return tt; +} + +struct radix_node * +rn_delete(void *v_arg, + void *netmask_arg, + struct radix_node_head *head) +{ + register struct radix_node *t, *p, *x, *tt; + struct radix_mask *m, *saved_m, **mp; + struct radix_node *dupedkey, *saved_tt, *top; + caddr_t v, netmask; + int b, head_off, vlen; + + v = v_arg; + netmask = netmask_arg; + x = head->rnh_treetop; + tt = rn_search(v, x); + head_off = x->rn_off; + vlen = *(u_char *)v; + saved_tt = tt; + top = x; + if (tt == 0 || + Bcmp(v + head_off, tt->rn_key + head_off, vlen - head_off)) + return (0); + /* + * Delete our route from mask lists. + */ + if (netmask) { + if ((x = rn_addmask(netmask, 1, head_off)) == 0) + return (0); + netmask = x->rn_key; + while (tt->rn_mask != netmask) + if ((tt = tt->rn_dupedkey) == 0) + return (0); + } + if (tt->rn_mask == 0 || (saved_m = m = tt->rn_mklist) == 0) + goto on1; + if (tt->rn_flags & RNF_NORMAL) { + if (m->rm_leaf != tt || m->rm_refs > 0) { + log(LOG_ERR, "rn_delete: inconsistent annotation\n"); + return 0; /* dangling ref could cause disaster */ + } + } else { + if (m->rm_mask != tt->rn_mask) { + log(LOG_ERR, "rn_delete: inconsistent annotation\n"); + goto on1; + } + if (--m->rm_refs >= 0) + goto on1; + } + b = -1 - tt->rn_b; + t = saved_tt->rn_p; + if (b > t->rn_b) + goto on1; /* Wasn't lifted at all */ + do { + x = t; + t = t->rn_p; + } while (b <= t->rn_b && x != top); + for (mp = &x->rn_mklist; (m = *mp); mp = &m->rm_mklist) + if (m == saved_m) { + *mp = m->rm_mklist; + MKFree(m); + break; + } + if (m == 0) { + log(LOG_ERR, "rn_delete: couldn't find our annotation\n"); + if (tt->rn_flags & RNF_NORMAL) + return (0); /* Dangling ref to us */ + } +on1: + /* + * Eliminate us from tree + */ + if (tt->rn_flags & RNF_ROOT) + return (0); +#ifdef RN_DEBUG + /* Get us out of the creation list */ + for (t = rn_clist; t && t->rn_ybro != tt; t = t->rn_ybro) {} + if (t) t->rn_ybro = tt->rn_ybro; +#endif + t = tt->rn_p; + if ((dupedkey = saved_tt->rn_dupedkey)) { + if (tt == saved_tt) { + x = dupedkey; x->rn_p = t; + if (t->rn_l == tt) t->rn_l = x; else t->rn_r = x; + } else { + for (x = p = saved_tt; p && p->rn_dupedkey != tt;) + p = p->rn_dupedkey; + if (p) p->rn_dupedkey = tt->rn_dupedkey; + else log(LOG_ERR, "rn_delete: couldn't find us\n"); + } + t = tt + 1; + if (t->rn_flags & RNF_ACTIVE) { +#ifndef RN_DEBUG + *++x = *t; p = t->rn_p; +#else + b = t->rn_info; *++x = *t; t->rn_info = b; p = t->rn_p; +#endif + if (p->rn_l == t) p->rn_l = x; else p->rn_r = x; + x->rn_l->rn_p = x; x->rn_r->rn_p = x; + } + goto out; + } + if (t->rn_l == tt) x = t->rn_r; else x = t->rn_l; + p = t->rn_p; + if (p->rn_r == t) p->rn_r = x; else p->rn_l = x; + x->rn_p = p; + /* + * Demote routes attached to us. + */ + if (t->rn_mklist) { + if (x->rn_b >= 0) { + for (mp = &x->rn_mklist; (m = *mp);) + mp = &m->rm_mklist; + *mp = t->rn_mklist; + } else { + /* If there are any key,mask pairs in a sibling + duped-key chain, some subset will appear sorted + in the same order attached to our mklist */ + for (m = t->rn_mklist; m && x; x = x->rn_dupedkey) + if (m == x->rn_mklist) { + struct radix_mask *mm = m->rm_mklist; + x->rn_mklist = 0; + if (--(m->rm_refs) < 0) + MKFree(m); + m = mm; + } + if (m) + syslog(LOG_ERR, "%s %lx at %lx\n", + "rn_delete: Orphaned Mask", + (unsigned long)m, + (unsigned long)x); + } + } + /* + * We may be holding an active internal node in the tree. + */ + x = tt + 1; + if (t != x) { +#ifndef RN_DEBUG + *t = *x; +#else + b = t->rn_info; *t = *x; t->rn_info = b; +#endif + t->rn_l->rn_p = t; t->rn_r->rn_p = t; + p = x->rn_p; + if (p->rn_l == x) p->rn_l = t; else p->rn_r = t; + } +out: + tt->rn_flags &= ~RNF_ACTIVE; + tt[1].rn_flags &= ~RNF_ACTIVE; + return (tt); +} + +int +rn_walktree(struct radix_node_head *h, + register int (*f)(struct radix_node *, struct walkarg*), + struct walkarg *w) +{ + int error; + struct radix_node *base, *next; + register struct radix_node *rn = h->rnh_treetop; + /* + * This gets complicated because we may delete the node + * while applying the function f to it, so we need to calculate + * the successor node in advance. + */ + /* First time through node, go left */ + while (rn->rn_b >= 0) + rn = rn->rn_l; + for (;;) { + base = rn; + /* If at right child go back up, otherwise, go right */ + while (rn->rn_p->rn_r == rn && (rn->rn_flags & RNF_ROOT) == 0) + rn = rn->rn_p; + /* Find the next *leaf* since next node might vanish, too */ + for (rn = rn->rn_p->rn_r; rn->rn_b >= 0;) + rn = rn->rn_l; + next = rn; + /* Process leaves */ + while ((rn = base)) { + base = rn->rn_dupedkey; + if (!(rn->rn_flags & RNF_ROOT) && (error = (*f)(rn, w))) + return (error); + } + rn = next; + if (rn->rn_flags & RNF_ROOT) + return (0); + } + /* NOTREACHED */ +} + +int +rn_inithead(void **head, int off) +{ + register struct radix_node_head *rnh; + register struct radix_node *t, *tt, *ttt; + if (*head) + return (1); + R_Malloc(rnh, struct radix_node_head *, sizeof (*rnh)); + if (rnh == 0) + return (0); + Bzero(rnh, sizeof (*rnh)); + *head = rnh; + t = rn_newpair(rn_zeros, off, rnh->rnh_nodes); + ttt = rnh->rnh_nodes + 2; + t->rn_r = ttt; + t->rn_p = t; + tt = t->rn_l; + tt->rn_flags = t->rn_flags = RNF_ROOT | RNF_ACTIVE; + tt->rn_b = -1 - off; + *ttt = *tt; + ttt->rn_key = rn_ones; + rnh->rnh_addaddr = rn_addroute; + rnh->rnh_deladdr = rn_delete; + rnh->rnh_matchaddr = rn_match; + rnh->rnh_lookup = rn_lookup; + rnh->rnh_walktree = rn_walktree; + rnh->rnh_treetop = t; + return (1); +} + +void +rn_init(void) +{ + char *cp, *cplim; + if (max_keylen == 0) { + printf("rn_init: radix functions require max_keylen be set\n"); + return; + } + R_Malloc(rn_zeros, char *, 3 * max_keylen); + if (rn_zeros == NULL) + panic("rn_init"); + Bzero(rn_zeros, 3 * max_keylen); + rn_ones = cp = rn_zeros + max_keylen; + addmask_key = cplim = rn_ones + max_keylen; + while (cp < cplim) + *cp++ = -1; + if (rn_inithead((void **)&mask_rnhead, 0) == 0) + panic("rn_init 2"); +} + diff --git a/sbin/routed/radix.h b/sbin/routed/radix.h new file mode 100644 index 0000000..fddf02e --- /dev/null +++ b/sbin/routed/radix.h @@ -0,0 +1,161 @@ +/* + * Copyright (c) 1988, 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)radix.h 8.2 (Berkeley) 10/31/94 + */ + +#ifndef __RADIX_H_ +#define __RADIX_H_ + +#include +struct walkarg; + +/* + * Radix search tree node layout. + */ + +struct radix_node { + struct radix_mask *rn_mklist; /* list of masks contained in subtree */ + struct radix_node *rn_p; /* parent */ + short rn_b; /* bit offset; -1-index(netmask) */ + char rn_bmask; /* node: mask for bit test*/ + u_char rn_flags; /* enumerated next */ +#define RNF_NORMAL 1 /* leaf contains normal route */ +#define RNF_ROOT 2 /* leaf is root leaf for tree */ +#define RNF_ACTIVE 4 /* This node is alive (for rtfree) */ + union { + struct { /* leaf only data: */ + caddr_t rn_Key; /* object of search */ + caddr_t rn_Mask; /* netmask, if present */ + struct radix_node *rn_Dupedkey; + } rn_leaf; + struct { /* node only data: */ + int rn_Off; /* where to start compare */ + struct radix_node *rn_L;/* progeny */ + struct radix_node *rn_R;/* progeny */ + }rn_node; + } rn_u; +#ifdef RN_DEBUG + int rn_info; + struct radix_node *rn_twin; + struct radix_node *rn_ybro; +#endif +}; + +#define rn_dupedkey rn_u.rn_leaf.rn_Dupedkey +#define rn_key rn_u.rn_leaf.rn_Key +#define rn_mask rn_u.rn_leaf.rn_Mask +#define rn_off rn_u.rn_node.rn_Off +#define rn_l rn_u.rn_node.rn_L +#define rn_r rn_u.rn_node.rn_R + +/* + * Annotations to tree concerning potential routes applying to subtrees. + */ + +extern struct radix_mask { + short rm_b; /* bit offset; -1-index(netmask) */ + char rm_unused; /* cf. rn_bmask */ + u_char rm_flags; /* cf. rn_flags */ + struct radix_mask *rm_mklist; /* more masks to try */ + union { + caddr_t rmu_mask; /* the mask */ + struct radix_node *rmu_leaf; /* for normal routes */ + } rm_rmu; + int rm_refs; /* # of references to this struct */ +} *rn_mkfreelist; + +#define rm_mask rm_rmu.rmu_mask +#define rm_leaf rm_rmu.rmu_leaf /* extra field would make 32 bytes */ + +#define MKGet(m) {\ + if (rn_mkfreelist) {\ + m = rn_mkfreelist; \ + rn_mkfreelist = (m)->rm_mklist; \ + } else \ + R_Malloc(m, struct radix_mask *, sizeof (*(m))); }\ + +#define MKFree(m) { (m)->rm_mklist = rn_mkfreelist; rn_mkfreelist = (m);} + +struct radix_node_head { + struct radix_node *rnh_treetop; + int rnh_addrsize; /* permit, but not require fixed keys */ + int rnh_pktsize; /* permit, but not require fixed keys */ + struct radix_node *(*rnh_addaddr) /* add based on sockaddr */ + __P((void *v, void *mask, + struct radix_node_head *head, struct radix_node nodes[])); + struct radix_node *(*rnh_addpkt) /* add based on packet hdr */ + __P((void *v, void *mask, + struct radix_node_head *head, struct radix_node nodes[])); + struct radix_node *(*rnh_deladdr) /* remove based on sockaddr */ + __P((void *v, void *mask, struct radix_node_head *head)); + struct radix_node *(*rnh_delpkt) /* remove based on packet hdr */ + __P((void *v, void *mask, struct radix_node_head *head)); + struct radix_node *(*rnh_matchaddr) /* locate based on sockaddr */ + __P((void *v, struct radix_node_head *head)); + struct radix_node *(*rnh_lookup) /* locate based on sockaddr */ + __P((void *v, void *mask, struct radix_node_head *head)); + struct radix_node *(*rnh_matchpkt) /* locate based on packet hdr */ + __P((void *v, struct radix_node_head *head)); + int (*rnh_walktree) /* traverse tree */ + (struct radix_node_head *head, + int (*f)(struct radix_node *, struct walkarg *), + struct walkarg *w); + struct radix_node rnh_nodes[3]; /* empty tree for common case */ +}; + + +#define Bcmp(a, b, n) bcmp(((char *)(a)), ((char *)(b)), (n)) +#define Bcopy(a, b, n) bcopy(((char *)(a)), ((char *)(b)), (unsigned)(n)) +#define Bzero(p, n) bzero((char *)(p), (int)(n)); +#define R_Malloc(p, t, n) (p = (t) malloc((unsigned int)(n))) +#define Free(p) free((char *)p); + +void rn_init __P((void)); +int rn_inithead __P((void **, int)); +int rn_refines __P((void *, void *)); +int rn_walktree __P((struct radix_node_head *, + int (*)__P((struct radix_node *, struct walkarg*)), + struct walkarg*)); +struct radix_node + *rn_addmask __P((void *, int, int)), + *rn_addroute __P((void *, void *, struct radix_node_head *, + struct radix_node [2])), + *rn_delete __P((void *, void *, struct radix_node_head *)), + *rn_insert __P((void *, struct radix_node_head *, int *, + struct radix_node [2])), + *rn_match __P((void *, struct radix_node_head *)), + *rn_newpair __P((void *, int, struct radix_node[2])), + *rn_search __P((void *, struct radix_node *)), + *rn_search_m __P((void *, struct radix_node *, void *)); + +#endif /* __RADIX_H_ */ diff --git a/sbin/routed/rdisc.c b/sbin/routed/rdisc.c new file mode 100644 index 0000000..da1784c --- /dev/null +++ b/sbin/routed/rdisc.c @@ -0,0 +1,1032 @@ +/* + * Copyright (c) 1995 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if !defined(lint) && !defined(sgi) && !defined(__NetBSD__) +static char sccsid[] = "@(#)rdisc.c 8.1 (Berkeley) x/y/95"; +#elif defined(__NetBSD__) +static char rcsid[] = "$NetBSD$"; +#endif +#ident "$Revision: 1.16 $" + +#include "defs.h" +#include +#include +#include + +/* router advertisement ICMP packet */ +struct icmp_ad { + u_int8_t icmp_type; /* type of message */ + u_int8_t icmp_code; /* type sub code */ + u_int16_t icmp_cksum; /* ones complement cksum of struct */ + u_int8_t icmp_ad_num; /* # of following router addresses */ + u_int8_t icmp_ad_asize; /* 2--words in each advertisement */ + u_int16_t icmp_ad_life; /* seconds of validity */ + struct icmp_ad_info { + n_long icmp_ad_addr; + n_long icmp_ad_pref; + } icmp_ad_info[1]; +}; + +/* router solicitation ICMP packet */ +struct icmp_so { + u_int8_t icmp_type; /* type of message */ + u_int8_t icmp_code; /* type sub code */ + u_int16_t icmp_cksum; /* ones complement cksum of struct */ + n_long icmp_so_rsvd; +}; + +union ad_u { + struct icmp icmp; + struct icmp_ad ad; + struct icmp_so so; +}; + + +int rdisc_sock = -1; /* router-discovery raw socket */ +struct interface *rdisc_sock_mcast; /* current multicast interface */ + +struct timeval rdisc_timer; +int rdisc_ok; /* using solicited route */ + + +#define MAX_ADS 5 +struct dr { /* accumulated advertisements */ + struct interface *dr_ifp; + naddr dr_gate; /* gateway */ + time_t dr_ts; /* when received */ + time_t dr_life; /* lifetime */ + n_long dr_recv_pref; /* received but biased preference */ + n_long dr_pref; /* preference adjusted by metric */ +} *cur_drp, drs[MAX_ADS]; + +/* adjust preference by interface metric without driving it to infinity */ +#define PREF(p, ifp) ((p) <= (ifp)->int_metric ? ((p) != 0 ? 1 : 0) \ + : (p) - ((ifp)->int_metric)) + +static void rdisc_sort(void); + + +/* dump an ICMP Router Discovery Advertisement Message + */ +static void +trace_rdisc(char *act, + naddr from, + naddr to, + struct interface *ifp, + union ad_u *p, + u_int len) +{ + int i; + n_long *wp, *lim; + + + if (!TRACEPACKETS || ftrace == 0) + return; + + lastlog(); + + if (p->icmp.icmp_type == ICMP_ROUTERADVERT) { + (void)fprintf(ftrace, "%s Router Ad" + " from %s to %s via %s life=%d\n", + act, naddr_ntoa(from), naddr_ntoa(to), + ifp ? ifp->int_name : "?", + ntohs(p->ad.icmp_ad_life)); + if (!TRACECONTENTS) + return; + + wp = &p->ad.icmp_ad_info[0].icmp_ad_addr; + lim = &wp[(len - sizeof(p->ad)) / sizeof(*wp)]; + for (i = 0; i < p->ad.icmp_ad_num && wp <= lim; i++) { + (void)fprintf(ftrace, "\t%s preference=%#x", + naddr_ntoa(wp[0]), (int)ntohl(wp[1])); + wp += p->ad.icmp_ad_asize; + } + (void)fputc('\n',ftrace); + + } else { + trace_act("%s Router Solic. from %s to %s via %s" + " value=%#x\n", + act, naddr_ntoa(from), naddr_ntoa(to), + ifp ? ifp->int_name : "?", + ntohl(p->so.icmp_so_rsvd)); + } +} + +/* prepare Router Discovery socket. + */ +static void +get_rdisc_sock(void) +{ + if (rdisc_sock < 0) { + 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(); + } +} + + +/* Pick multicast group for router-discovery socket + */ +void +set_rdisc_mg(struct interface *ifp, + int on) { /* 0=turn it off */ + struct ip_mreq m; + + if (rdisc_sock < 0) { + /* Create the raw socket so that we can hear at least + * broadcast router discovery packets. + */ + if ((ifp->int_state & IS_NO_RDISC) == IS_NO_RDISC + || !on) + return; + get_rdisc_sock(); + } + + if (!(ifp->int_if_flags & IFF_MULTICAST) + || (ifp->int_state & IS_ALIAS)) { + ifp->int_state &= ~(IS_ALL_HOSTS | IS_ALL_ROUTERS); + return; + } + +#ifdef MCAST_PPP_BUG + if (ifp->int_if_flags & IFF_POINTOPOINT) + return; +#endif + bzero(&m, sizeof(m)); + m.imr_interface.s_addr = ((ifp->int_if_flags & IFF_POINTOPOINT) + ? ifp->int_dstaddr + : ifp->int_addr); + if (supplier + || (ifp->int_state & IS_NO_ADV_IN) + || !on) { + /* stop listening to advertisements + */ + if (ifp->int_state & IS_ALL_HOSTS) { + m.imr_multiaddr.s_addr = htonl(INADDR_ALLHOSTS_GROUP); + if (setsockopt(rdisc_sock, IPPROTO_IP, + IP_DROP_MEMBERSHIP, + &m, sizeof(m)) < 0) + LOGERR("IP_DROP_MEMBERSHIP ALLHOSTS"); + ifp->int_state &= ~IS_ALL_HOSTS; + } + + } else if (!(ifp->int_state & IS_ALL_HOSTS)) { + /* start listening to advertisements + */ + m.imr_multiaddr.s_addr = htonl(INADDR_ALLHOSTS_GROUP); + if (setsockopt(rdisc_sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, + &m, sizeof(m)) < 0) { + LOGERR("IP_ADD_MEMBERSHIP ALLHOSTS"); + } else { + ifp->int_state |= IS_ALL_HOSTS; + } + } + + if (!supplier + || (ifp->int_state & IS_NO_ADV_OUT) + || !on) { + /* stop listening to solicitations + */ + if (ifp->int_state & IS_ALL_ROUTERS) { + m.imr_multiaddr.s_addr=htonl(INADDR_ALLROUTERS_GROUP); + if (setsockopt(rdisc_sock, IPPROTO_IP, + IP_DROP_MEMBERSHIP, + &m, sizeof(m)) < 0) + LOGERR("IP_DROP_MEMBERSHIP ALLROUTERS"); + ifp->int_state &= ~IS_ALL_ROUTERS; + } + + } else if (!(ifp->int_state & IS_ALL_ROUTERS)) { + /* start hearing solicitations + */ + m.imr_multiaddr.s_addr=htonl(INADDR_ALLROUTERS_GROUP); + if (setsockopt(rdisc_sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, + &m, sizeof(m)) < 0) { + LOGERR("IP_ADD_MEMBERSHIP ALLROUTERS"); + } else { + ifp->int_state |= IS_ALL_ROUTERS; + } + } +} + + +/* start supplying routes + */ +void +set_supplier(void) +{ + struct interface *ifp; + struct dr *drp; + + if (supplier_set) + return; + + trace_act("start suppying routes\n"); + + /* Forget discovered routes. + */ + for (drp = drs; drp < &drs[MAX_ADS]; drp++) { + drp->dr_recv_pref = 0; + drp->dr_life = 0; + } + rdisc_age(0); + + supplier_set = 1; + supplier = 1; + + /* Do not start advertising until we have heard some RIP routes */ + LIM_SEC(rdisc_timer, now.tv_sec+MIN_WAITTIME); + + /* Switch router discovery multicast groups from soliciting + * to advertising. + */ + for (ifp = ifnet; ifp; ifp = ifp->int_next) { + if (ifp->int_state & IS_BROKE) + continue; + ifp->int_rdisc_cnt = 0; + ifp->int_rdisc_timer.tv_usec = rdisc_timer.tv_usec; + ifp->int_rdisc_timer.tv_sec = now.tv_sec+MIN_WAITTIME; + set_rdisc_mg(ifp, 1); + } + + /* get rid of any redirects */ + del_redirects(0,0); +} + + +/* age discovered routes and find the best one + */ +void +rdisc_age(naddr bad_gate) +{ + time_t sec; + struct dr *drp; + + + /* If only adverising, then do only that. */ + if (supplier) { + /* if switching from client to server, get rid of old + * default routes. + */ + if (cur_drp != 0) + rdisc_sort(); + rdisc_adv(); + return; + } + + /* If we are being told about a bad router, + * then age the discovered default route, and if there is + * no alternative, solicite a replacement. + */ + if (bad_gate != 0) { + /* Look for the bad discovered default route. + * Age it and note its interface. + */ + for (drp = drs; drp < &drs[MAX_ADS]; drp++) { + if (drp->dr_ts == 0) + continue; + + /* When we find the bad router, then age the route + * to at most SUPPLY_INTERVAL. + * This is contrary to RFC 1256, but defends against + * black holes. + */ + if (drp->dr_gate == bad_gate) { + sec = (now.tv_sec - drp->dr_life + + SUPPLY_INTERVAL); + if (drp->dr_ts > sec) { + trace_act("age 0.0.0.0 --> %s" + " via %s\n", + naddr_ntoa(drp->dr_gate), + drp->dr_ifp->int_name); + drp->dr_ts = sec; + } + break; + } + } + } + + /* delete old redirected routes to keep the kernel table small + */ + sec = (cur_drp == 0) ? MaxMaxAdvertiseInterval : cur_drp->dr_life; + del_redirects(bad_gate, now.tv_sec-sec); + + rdisc_sol(); + + rdisc_sort(); +} + + +/* Zap all routes discovered via an interface that has gone bad + * This should only be called when !(ifp->int_state & IS_ALIAS) + */ +void +if_bad_rdisc(struct interface *ifp) +{ + struct dr *drp; + + for (drp = drs; drp < &drs[MAX_ADS]; drp++) { + if (drp->dr_ifp != ifp) + continue; + drp->dr_recv_pref = 0; + drp->dr_life = 0; + } + + rdisc_sort(); +} + + +/* mark an interface ok for router discovering. + */ +void +if_ok_rdisc(struct interface *ifp) +{ + set_rdisc_mg(ifp, 1); + + ifp->int_rdisc_cnt = 0; + ifp->int_rdisc_timer.tv_sec = now.tv_sec + (supplier + ? MIN_WAITTIME + : MAX_SOLICITATION_DELAY); + if (timercmp(&rdisc_timer, &ifp->int_rdisc_timer, >)) + rdisc_timer = ifp->int_rdisc_timer; +} + + +/* get rid of a dead discovered router + */ +static void +del_rdisc(struct dr *drp) +{ + struct interface *ifp; + int i; + + + del_redirects(drp->dr_gate, 0); + drp->dr_ts = 0; + drp->dr_life = 0; + + + /* Count the other discovered routes on the interface. + */ + i = 0; + ifp = drp->dr_ifp; + for (drp = drs; drp < &drs[MAX_ADS]; drp++) { + if (drp->dr_ts != 0 + && drp->dr_ifp == ifp) + i++; + } + + /* If that was the last good discovered router on the interface, + * then solicit a new one. + * This is contrary to RFC 1256, but defends against black holes. + */ + if (i == 0 + && ifp->int_rdisc_cnt >= MAX_SOLICITATIONS) { + trace_act("discovered route is bad" + "--re-solicit routers via %s\n", ifp->int_name); + ifp->int_rdisc_cnt = 0; + ifp->int_rdisc_timer.tv_sec = 0; + rdisc_sol(); + } +} + + +/* Find the best discovered route, + * and discard stale routers. + */ +static void +rdisc_sort(void) +{ + struct dr *drp, *new_drp; + struct rt_entry *rt; + struct interface *ifp; + u_int new_st; + n_long new_pref; + + + /* Find the best discovered route. + */ + new_drp = 0; + for (drp = drs; drp < &drs[MAX_ADS]; drp++) { + if (drp->dr_ts == 0) + continue; + ifp = drp->dr_ifp; + + /* Get rid of expired discovered routers. + */ + if (drp->dr_ts + drp->dr_life <= now.tv_sec) { + del_rdisc(drp); + continue; + } + + LIM_SEC(rdisc_timer, drp->dr_ts+drp->dr_life+1); + + /* Update preference with possibly changed interface + * metric. + */ + drp->dr_pref = PREF(drp->dr_recv_pref, ifp); + + /* Prefer the current route to prevent thrashing. + * Prefer shorter lifetimes to speed the detection of + * bad routers. + * Avoid sick interfaces. + */ + if (new_drp == 0 + || (!((new_st ^ drp->dr_ifp->int_state) & IS_SICK) + && (new_pref < drp->dr_pref + || (new_pref == drp->dr_pref + && (drp == cur_drp + || (new_drp != cur_drp + && new_drp->dr_life > drp->dr_life))))) + || ((new_st & IS_SICK) + && !(drp->dr_ifp->int_state & IS_SICK))) { + new_drp = drp; + new_st = drp->dr_ifp->int_state; + new_pref = drp->dr_pref; + } + } + + /* switch to a better default route + */ + if (new_drp != cur_drp) { + rt = rtget(RIP_DEFAULT, 0); + + /* Stop using discovered routes if they are all bad + */ + if (new_drp == 0) { + trace_act("turn off Router Discovery client\n"); + rdisc_ok = 0; + + if (rt != 0 + && (rt->rt_state & RS_RDISC)) { + rtchange(rt, rt->rt_state & ~RS_RDISC, + rt->rt_gate, rt->rt_router, + HOPCNT_INFINITY, 0, rt->rt_ifp, + now.tv_sec - GARBAGE_TIME, 0); + rtswitch(rt, 0); + } + + /* turn on RIP if permitted */ + rip_on(0); + + } else { + if (cur_drp == 0) { + trace_act("turn on Router Discovery client" + " using %s via %s\n", + naddr_ntoa(new_drp->dr_gate), + new_drp->dr_ifp->int_name); + + rdisc_ok = 1; + + } else { + trace_act("switch Router Discovery from" + " %s via %s to %s via %s\n", + naddr_ntoa(cur_drp->dr_gate), + cur_drp->dr_ifp->int_name, + naddr_ntoa(new_drp->dr_gate), + new_drp->dr_ifp->int_name); + } + + if (rt != 0) { + rtchange(rt, rt->rt_state | RS_RDISC, + new_drp->dr_gate, new_drp->dr_gate, + 0,0, new_drp->dr_ifp, + now.tv_sec, 0); + } else { + rtadd(RIP_DEFAULT, 0, + new_drp->dr_gate, new_drp->dr_gate, + 0, 0, RS_RDISC, new_drp->dr_ifp); + } + + /* Now turn off RIP and delete RIP routes, + * which might otherwise include the default + * we just modified. + */ + rip_off(); + } + + cur_drp = new_drp; + } +} + + +/* handle a single address in an advertisement + */ +static void +parse_ad(naddr from, + naddr gate, + n_long pref, + u_short life, + struct interface *ifp) +{ + static naddr bad_gate; + struct dr *drp, *new_drp; + + + if (gate == RIP_DEFAULT + || !check_dst(gate)) { + if (bad_gate != from) { + msglog("router %s advertising bad gateway %s", + naddr_ntoa(from), + naddr_ntoa(gate)); + bad_gate = from; + } + return; + } + + /* ignore pointers to ourself and routes via unreachable networks + */ + if (ifwithaddr(gate, 1, 0) != 0) { + trace_pkt("\tdiscard Router Discovery Ad pointing at us\n"); + return; + } + if (!on_net(gate, ifp->int_net, ifp->int_mask)) { + trace_pkt("\tdiscard Router Discovery Ad" + " toward unreachable net\n"); + return; + } + + /* Convert preference to an unsigned value + * and later bias it by the metric of the interface. + */ + pref = ntohl(pref) ^ MIN_PreferenceLevel; + + if (pref == 0 || life == 0) { + pref = 0; + life = 0; + } + + for (new_drp = 0, drp = drs; drp < &drs[MAX_ADS]; drp++) { + /* accept new info for a familiar entry + */ + if (drp->dr_gate == gate) { + new_drp = drp; + break; + } + + if (life == 0) + continue; /* do not worry about dead ads */ + + if (drp->dr_ts == 0) { + new_drp = drp; /* use unused entry */ + + } else if (new_drp == 0) { + /* look for an entry worse than the new one to + * reuse. + */ + if ((!(ifp->int_state & IS_SICK) + && (drp->dr_ifp->int_state & IS_SICK)) + || (pref > drp->dr_pref + && !((ifp->int_state ^ drp->dr_ifp->int_state) + & IS_SICK))) + new_drp = drp; + + } else if (new_drp->dr_ts != 0) { + /* look for the least valueable entry to reuse + */ + if ((!(new_drp->dr_ifp->int_state & IS_SICK) + && (drp->dr_ifp->int_state & IS_SICK)) + || (new_drp->dr_pref > drp->dr_pref + && !((new_drp->dr_ifp->int_state + ^ drp->dr_ifp->int_state) + & IS_SICK))) + new_drp = drp; + } + } + + /* forget it if all of the current entries are better */ + if (new_drp == 0) + return; + + new_drp->dr_ifp = ifp; + new_drp->dr_gate = gate; + new_drp->dr_ts = now.tv_sec; + new_drp->dr_life = ntohs(life); + new_drp->dr_recv_pref = pref; + /* bias functional preference by metric of the interface */ + new_drp->dr_pref = PREF(pref,ifp); + + /* after hearing a good advertisement, stop asking + */ + if (!(ifp->int_state & IS_SICK)) + ifp->int_rdisc_cnt = MAX_SOLICITATIONS; +} + + +/* Compute the IP checksum + * This assumes the packet is less than 32K long. + */ +static u_short +in_cksum(u_short *p, + u_int len) +{ + u_int sum = 0; + int nwords = len >> 1; + + while (nwords-- != 0) + sum += *p++; + + if (len & 1) + sum += *(u_char *)p; + + /* end-around-carry */ + sum = (sum >> 16) + (sum & 0xffff); + sum += (sum >> 16); + return (~sum); +} + + +/* Send a router discovery advertisement or solicitation ICMP packet. + */ +static void +send_rdisc(union ad_u *p, + int p_size, + struct interface *ifp, + naddr dst, /* 0 or unicast destination */ + int type) /* 0=unicast, 1=bcast, 2=mcast */ +{ + struct sockaddr_in sin; + int flags; + char *msg; + naddr tgt_mcast; + + + bzero(&sin, sizeof(sin)); + sin.sin_addr.s_addr = dst; + sin.sin_family = AF_INET; +#ifdef _HAVE_SIN_LEN + sin.sin_len = sizeof(sin); +#endif + flags = MSG_DONTROUTE; + + switch (type) { + case 0: /* unicast */ + msg = "Send"; + break; + + case 1: /* broadcast */ + if (ifp->int_if_flags & IFF_POINTOPOINT) { + msg = "Send pt-to-pt"; + sin.sin_addr.s_addr = ifp->int_dstaddr; + } else { + msg = "Send broadcast"; + sin.sin_addr.s_addr = ifp->int_brdaddr; + } + break; + + case 2: /* multicast */ + msg = "Send multicast"; + if (ifp->int_state & IS_DUP) { + trace_act("abort multicast output via %s" + " with duplicate address\n", + ifp->int_name); + return; + } + if (rdisc_sock_mcast != ifp) { + /* select the right interface. */ +#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 (0 > setsockopt(rdisc_sock, + IPPROTO_IP, IP_MULTICAST_IF, + &tgt_mcast, sizeof(tgt_mcast))) { + LOGERR("setsockopt(rdisc_sock," + "IP_MULTICAST_IF)"); + rdisc_sock_mcast = 0; + return; + } + rdisc_sock_mcast = ifp; + } + flags = 0; + break; + } + + if (rdisc_sock < 0) + get_rdisc_sock(); + + trace_rdisc(msg, ifp->int_addr, sin.sin_addr.s_addr, ifp, + p, p_size); + + if (0 > sendto(rdisc_sock, p, p_size, flags, + (struct sockaddr *)&sin, sizeof(sin))) { + if (ifp == 0 || !(ifp->int_state & IS_BROKE)) + msglog("sendto(%s%s%s): %s", + ifp != 0 ? ifp->int_name : "", + ifp != 0 ? ", " : "", + inet_ntoa(sin.sin_addr), + strerror(errno)); + if (ifp != 0) + if_sick(ifp); + } +} + + +/* Send an advertisement + */ +static void +send_adv(struct interface *ifp, + naddr dst, /* 0 or unicast destination */ + int type) /* 0=unicast, 1=bcast, 2=mcast */ +{ + union ad_u u; + n_long pref; + + + bzero(&u,sizeof(u.ad)); + + u.ad.icmp_type = ICMP_ROUTERADVERT; + u.ad.icmp_ad_num = 1; + u.ad.icmp_ad_asize = sizeof(u.ad.icmp_ad_info[0])/4; + + u.ad.icmp_ad_life = stopint ? 0 : htons(ifp->int_rdisc_int*3); + pref = ifp->int_rdisc_pref ^ MIN_PreferenceLevel; + pref = PREF(pref, ifp) ^ MIN_PreferenceLevel; + u.ad.icmp_ad_info[0].icmp_ad_pref = htonl(pref); + + u.ad.icmp_ad_info[0].icmp_ad_addr = ifp->int_addr; + + u.ad.icmp_cksum = in_cksum((u_short*)&u.ad, sizeof(u.ad)); + + send_rdisc(&u, sizeof(u.ad), ifp, dst, type); +} + + +/* Advertise for Router Discovery + */ +void +rdisc_adv(void) +{ + struct interface *ifp; + + + rdisc_timer.tv_sec = now.tv_sec + NEVER; + + for (ifp = ifnet; ifp; ifp = ifp->int_next) { + if (0 != (ifp->int_state & (IS_NO_ADV_OUT + | IS_PASSIVE + | IS_ALIAS + | IS_BROKE))) + continue; + + if (!timercmp(&ifp->int_rdisc_timer, &now, >) + || stopint) { + send_adv(ifp, htonl(INADDR_ALLHOSTS_GROUP), + (ifp->int_state&IS_BCAST_RDISC) ? 1 : 2); + ifp->int_rdisc_cnt++; + + intvl_random(&ifp->int_rdisc_timer, + (ifp->int_rdisc_int*3)/4, + ifp->int_rdisc_int); + if (ifp->int_rdisc_cnt < MAX_INITIAL_ADVERTS + && (ifp->int_rdisc_timer.tv_sec + > MAX_INITIAL_ADVERT_INTERVAL)) { + ifp->int_rdisc_timer.tv_sec + = MAX_INITIAL_ADVERT_INTERVAL; + } + timevaladd(&ifp->int_rdisc_timer, &now); + } + + if (timercmp(&rdisc_timer, &ifp->int_rdisc_timer, >)) + rdisc_timer = ifp->int_rdisc_timer; + } +} + + +/* Solicit for Router Discovery + */ +void +rdisc_sol(void) +{ + struct interface *ifp; + union ad_u u; + + + rdisc_timer.tv_sec = now.tv_sec + NEVER; + + for (ifp = ifnet; ifp; ifp = ifp->int_next) { + if (0 != (ifp->int_state & (IS_NO_SOL_OUT + | IS_PASSIVE + | IS_ALIAS + | IS_BROKE)) + || ifp->int_rdisc_cnt >= MAX_SOLICITATIONS) + continue; + + if (!timercmp(&ifp->int_rdisc_timer, &now, >)) { + bzero(&u,sizeof(u.so)); + u.so.icmp_type = ICMP_ROUTERSOLICIT; + u.so.icmp_cksum = in_cksum((u_short*)&u.so, + sizeof(u.so)); + send_rdisc(&u, sizeof(u.so), ifp, + htonl(INADDR_ALLROUTERS_GROUP), + ((ifp->int_state&IS_BCAST_RDISC) ? 1 : 2)); + + if (++ifp->int_rdisc_cnt >= MAX_SOLICITATIONS) + continue; + + ifp->int_rdisc_timer.tv_sec = SOLICITATION_INTERVAL; + ifp->int_rdisc_timer.tv_usec = 0; + timevaladd(&ifp->int_rdisc_timer, &now); + } + + if (timercmp(&rdisc_timer, &ifp->int_rdisc_timer, >)) + rdisc_timer = ifp->int_rdisc_timer; + } +} + + +/* check the IP header of a possible Router Discovery ICMP packet */ +static struct interface * /* 0 if bad */ +ck_icmp(char *act, + naddr from, + naddr to, + union ad_u *p, + u_int len) +{ + struct interface *ifp; + char *type; + + + /* If we could tell the interface on which a packet from address 0 + * arrived, we could deal with such solicitations. + */ + + ifp = ((from == 0) ? 0 : iflookup(from)); + + if (p->icmp.icmp_type == ICMP_ROUTERADVERT) { + type = "advertisement"; + } else if (p->icmp.icmp_type == ICMP_ROUTERSOLICIT) { + type = "solicitation"; + } else { + return 0; + } + + if (p->icmp.icmp_code != 0) { + trace_pkt("unrecognized ICMP Router" + " %s code=%d from %s to %s\n", + type, p->icmp.icmp_code, + naddr_ntoa(from), naddr_ntoa(to)); + return 0; + } + + trace_rdisc(act, from, to, ifp, p, len); + + if (ifp == 0) + trace_pkt("unknown interface for router-discovery %s" + " from %s to %s", + type, naddr_ntoa(from), naddr_ntoa(to)); + + return ifp; +} + + +/* read packets from the router discovery socket + */ +void +read_d(void) +{ + static naddr bad_asize, bad_len; + struct sockaddr_in from; + int n, fromlen, cc, hlen; + union { + struct ip ip; + u_short s[512/2]; + u_char b[512]; + } pkt; + union ad_u *p; + n_long *wp; + struct interface *ifp; + + + for (;;) { + fromlen = sizeof(from); + cc = recvfrom(rdisc_sock, &pkt, sizeof(pkt), 0, + (struct sockaddr*)&from, + &fromlen); + if (cc <= 0) { + if (cc < 0 && errno != EWOULDBLOCK) + LOGERR("recvfrom(rdisc_sock)"); + break; + } + if (fromlen != sizeof(struct sockaddr_in)) + logbad(1,"impossible recvfrom(rdisc_sock) fromlen=%d", + fromlen); + + hlen = pkt.ip.ip_hl << 2; + if (cc < hlen + ICMP_MINLEN) + continue; + p = (union ad_u *)&pkt.b[hlen]; + cc -= hlen; + + ifp = ck_icmp("Recv", + from.sin_addr.s_addr, pkt.ip.ip_dst.s_addr, + p, cc); + if (ifp == 0) + continue; + if (ifwithaddr(from.sin_addr.s_addr, 0, 0)) { + trace_pkt("\tdiscard our own Router Discovery msg\n"); + continue; + } + + switch (p->icmp.icmp_type) { + case ICMP_ROUTERADVERT: + if (p->ad.icmp_ad_asize*4 + < sizeof(p->ad.icmp_ad_info[0])) { + if (bad_asize != from.sin_addr.s_addr) { + msglog("intolerable rdisc address" + " size=%d", + p->ad.icmp_ad_asize); + bad_asize = from.sin_addr.s_addr; + } + continue; + } + if (p->ad.icmp_ad_num == 0) { + trace_pkt("\tempty?\n"); + continue; + } + if (cc != (sizeof(p->ad) - sizeof(p->ad.icmp_ad_info) + + (p->ad.icmp_ad_num + * sizeof(p->ad.icmp_ad_info[0])))) { + if (bad_len != from.sin_addr.s_addr) { + msglog("rdisc length %d does not" + " match ad_num %d", + cc, p->ad.icmp_ad_num); + bad_len = from.sin_addr.s_addr; + } + continue; + } + if (supplier) + continue; + if (ifp->int_state & IS_NO_ADV_IN) + continue; + + wp = &p->ad.icmp_ad_info[0].icmp_ad_addr; + for (n = 0; n < p->ad.icmp_ad_num; n++) { + parse_ad(from.sin_addr.s_addr, + wp[0], wp[1], + ntohs(p->ad.icmp_ad_life), + ifp); + wp += p->ad.icmp_ad_asize; + } + break; + + + case ICMP_ROUTERSOLICIT: + if (!supplier) + continue; + if (ifp->int_state & IS_NO_ADV_OUT) + continue; + + /* XXX + * We should handle messages from address 0. + */ + + /* Respond with a point-to-point advertisement */ + send_adv(ifp, from.sin_addr.s_addr, 0); + break; + } + } + + rdisc_sort(); +} diff --git a/sbin/routed/routed.8 b/sbin/routed/routed.8 new file mode 100644 index 0000000..de3abf3 --- /dev/null +++ b/sbin/routed/routed.8 @@ -0,0 +1,605 @@ +.\" Copyright (c) 1983, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)routed.8 8.2 (Berkeley) 12/11/93 +.\" +.Dd June 1, 1996 +.Dt ROUTED 8 +.Os BSD 4.4 +.Sh NAME +.Nm routed +.Nd network RIP and router discovery routing daemon +.Sh SYNOPSIS +.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 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 RIPv1 protocol is based on the reference 4.3BSD daemon. +.Pp +It listens on the +.Xr udp 4 +socket for the +.Xr route 8 +service (see +.Xr services 5 ) +for Routing Information Protocol packets. +It also sends and receives multicast Router Discovery ICMP messages. +If the host is a 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 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". +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 +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 +formulates a reply based on the information maintained in its +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"). +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 +Responses do not contain routes with a first hop on the requesting +network to implement in part +.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 +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 +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 +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 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 minimize +.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. +These RIP responses are 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. +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 +While using Router Discovery (which happens by default when +the system has a single network interface and a Router Discover Advertisement +is received), there is a single default route and a variable number of +redirected host routes in the kernel table. +.Pp +The Router Discover standard requires that advertisements +have a default "lifetime" of 30 minutes. That means should +something happen, a client can be without a good route for +30 minutes. It is a good idea to reduce the default to 45 +seconds using +.Fl P Cm rdisc_interval=45 +on the command line or +.Cm rdisc_interval=45 +in the +.Pa /etc/gateways +file. +.Pp +While using Router Discovery (which happens by default when +the system has a single network interface and a Router Discover Advertisement +is received), there is a single default route and a variable number of +redirected host routes in the kernel table. +.Pp +See the +.Cm pm_rdisc +facility described below to support "legacy" systems +that can handle neither RIPv2 nor Router Discovery. +.Pp +By default, neither Router Discovery advertisements nor solicications +are sent over point to point links (e.g. PPP). + +.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 +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. +It is equivalent to +.Fl F +.Cm 0/0,1 +and is present mostly for historical reasons. +A better choice is +.Fl P Cm pm_rdisc +on the command line or +.CM pm_rdisc in the +.Pa /etc/gateways +file. +since a larger metric +will be used, reducing the spread of the potentially dangerous +default route. +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. +Notice that because a metric of 1 is used, this feature is +dangerous. It is more commonly accidently used to create chaos with routing +loop than to solve problems. +.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 A +do not ignore RIPv2 authentication if we do not care about RIPv2 +authentication. +This option is required for conformance with RFC 1723. +However, it makes no 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 trace file. +Note that because of security concerns, it is wisest to not run +.Nm routed +routinely with tracing directed to a file. +.It Fl t +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 or with the +.Cm rtquery +command. +.It Fl F Ar net[/mask][,metric] +minimize routes in transmissions via interfaces with addresses that match +.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. + +This is a dangerous feature that when used carelessly can cause routing +loops. +Notice also that more than one interface can match the specified network +number and mask. +See also +.Fl g . +.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 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 +.Nm +also supports the notion of +"distant" +.Em passive +or +.Em active +gateways. +When +.Nm +is started, it reads the file +.Pa /etc/gateways +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 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. +If no responses are received, the associated route is 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 indicate +that another routing process +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 +.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 +.Cm net +.Ar Nname[/mask] +.Cm gateway +.Ar Gname +.Cm metric +.Ar value +.Pf < Cm passive No \&| +.Cm active No \&| +.Cm extern Ns > +.Ed +.Bd -ragged +.Cm host +.Ar Hname +.Cm gateway +.Ar Gname +.Cm metric +.Ar value +.Pf < Cm passive No \&| +.Cm active No \&| +.Cm extern Ns > +.Ed +.Pp +.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 +.Xr named 8 , +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 Gname +is the name or address of the gateway to which RIP responses should +be forwarded. +.Pp +.Ar Value +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 active +or +.Cm external +must be present to indicate whether the gateway should be treated as +.Cm passive +or +.Cm active +(as described above), +or whether the gateway is +.Cm external +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, separated by commas or +blanks: +.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][,metric] +advertises a route to network +.AR nname +with mask +.AR mask +and the supplied metric (default 1). +This is useful for filling "holes" in CIDR allocations. +This parameter must appear by itself on a line. +.Pp +Do not use this feature unless necessary. It is dangerous. +.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. +The password must not contain any blanks, tab characters, commas +or '#' characters. +.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 passive +is equivalent +.Cm no_rip Cm no_rdisc . +.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. +.Cm No_rip +is equivalent to +.Cm no_ripv1_in no_ripv2_in no_ripv1_out no_ripv2_out . + +Note that turning off RIP without explicitly turning on router +discovery advertisements with +.Cm rdisc_adv +or +.Fl s +causes +.Nm routed +to act as a client router discovery daemon, not adveritising. +.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 +turns off RIPv1 output and causes RIPv2 advertisements to be +multicast when possible. +.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 sepcified interface. +.It Cm pm_rdisc +is similar to +.Cm fake_default . +When RIPv2 routes are multicast, so that RIPv1 listeners cannot +receive them, this feature causes a RIPv1 default route to be +broadcast to RIPv1 listeners. +Unless modified with +.Cm fake_default , +the default route is broadcast with a metric of 14. +That serves as a "poor man's router discovery" protocol. +.El +.Pp +Note that the netmask associated with point-to-point links (such as SLIP +or PPP, with the IFF_POINTOPOINT flag) is used by +.Nm routed +to infer the netmask used by the remote system when RIPv1 is used. +.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 htable 8 , +.Xr rtquery 8 . +.Rs +.%T Internet Transport Protocols +.%R XSIS 028112 +.%Q Xerox System Integration Standard +.Re +.Sh BUGS +It does not always detect unidirectional failures in network interfaces +(e.g., when the output side fails). +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.2 . diff --git a/sbin/routed/rtquery/rtquery.8 b/sbin/routed/rtquery/rtquery.8 new file mode 100644 index 0000000..d77ca30 --- /dev/null +++ b/sbin/routed/rtquery/rtquery.8 @@ -0,0 +1,103 @@ +.Dd June 1, 1996 +.Dt RTQUERY 8 +.Os BSD 4.4 +.Sh NAME +.Nm rtquery +.Nd query routing daemons for their routing tables +.Sh SYNOPSIS +.Nm +.Op Fl np1 +.Op Fl w Ar timeout +.Op Fl r Ar addr +.Ar host ... +.Sh DESCRIPTION +.Nm Rtquery +is used to query a network routing daemon, +.Xr routed 8 +or +.Xr gated 8 , +for its routing table by sending a +.Em request +or +.Em poll +command. The routing information in any routing +.Em response +packets returned is displayed numerically and symbolically. +.Pp +.Em Rtquery +by default uses the +.Em request +command. +When the +.Ar -p +option is specified, +.Nm rtquery +uses the +.Em poll +command, an +undocumented extension to the RIP protocol supported by +.Xr gated 8 . +When querying gated, the +.Em poll +command is preferred over the +.I Request +command because the response is not subject to Split Horizon and/or +Poisoned Reverse, and because some versions of gated do not answer +the Request command. Routed does not answer the Poll command, but +recognizes Requests coming from rtquery and so answers completely. +.Pp +.Em Rtquery +is also used to turn tracing on or off in +.Em routed . +.Pp +Options supported by +.Nm rtquery : +.Bl -tag -width Ds +.It Fl n +Normally network and host numbers are displayed both symbolically +and numerically. +The +.Fl n +option displays only the numeric network and host numbers. +.It Fl p +Uses the +.Em Poll +command to request full routing information from +.Xr gated 8 , +This is an undocumented extension RIP protocol supported only by +.Xr gated 8 . +.It Fl 1 +query using RIP version 1 instead of RIP version 2. +.It Fl w Ar timeout +changes the delay for an answer from each host. +By default, each host is given 15 seconds to respond. +.It Fl r Ar addr +ask about the route to destination +.Em addr . +.It Fl t Ar op +change tracing, where +.Em op +is one of the following. +Requests from processes not running with UID 0 or on distant networks +are generally ignored by the daemon except for a message in the system log. +.El +.Bl -tag -width Ds -offset indent-two +.It Em on=tracefile +turn tracing on into the specified file. That file must usually +have been specified when the daemon was started or be the same +as a fixed name, often +.Pa /etc/routed.trace . +.It Em more +increases the debugging level. +.It Em off +turns off tracing. +.It Em dump +dumps the daemon's routing table to the current tracefile. +.El +.Sh SEE ALSO +.Xr routed 8 , +.Xr gated 8 . +.br +RFC\ 1058 - Routing Information Protocol, RIPv1 +.br +RFC\ 1723 - Routing Information Protocol, RIPv2 diff --git a/sbin/routed/rtquery/rtquery.c b/sbin/routed/rtquery/rtquery.c new file mode 100644 index 0000000..7d6913a --- /dev/null +++ b/sbin/routed/rtquery/rtquery.c @@ -0,0 +1,654 @@ +/*- + * Copyright (c) 1982, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +char copyright[] = +"@(#) Copyright (c) 1982, 1986, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; + +#if !defined(lint) && !defined(sgi) && !defined(__NetBSD__) +static char sccsid[] = "@(#)query.c 8.1 (Berkeley) 6/5/93"; +#elif defined(__NetBSD__) +static char rcsid[] = "$NetBSD$"; +#endif +#ident "$Revision: 1.9 $" + +#include +#include +#include +#include +#include +#define RIPVERSION RIPv2 +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef sgi +#include +#include +#endif + +#ifndef sgi +#define _HAVE_SIN_LEN +#endif + +#define WTIME 15 /* Time to wait for all responses */ +#define STIME (250*1000) /* usec to wait for another response */ + +int s; + +char *pgmname; + +union { + struct rip rip; + char packet[MAXPACKETSIZE+MAXPATHLEN]; +} omsg_buf; +#define OMSG omsg_buf.rip +int omsg_len = sizeof(struct rip); + +union { + struct rip rip; + char packet[MAXPACKETSIZE+1024]; + } imsg_buf; +#define IMSG imsg_buf.rip + +int nflag; /* numbers, no names */ +int pflag; /* play the `gated` game */ +int ripv2 = 1; /* use RIP version 2 */ +int wtime = WTIME; +int rflag; /* 1=ask about a particular route */ +int trace; +int not_trace; + +struct timeval sent; /* when query sent */ + +static void rip_input(struct sockaddr_in*, int); +static int out(char *); +static void trace_loop(char *argv[]); +static void query_loop(char *argv[], int); +static int getnet(char *, struct netinfo *); +static u_int std_mask(u_int); + + +int +main(int argc, + char *argv[]) +{ + int ch, bsize; + char *p, *options, *value; + + OMSG.rip_nets[0].n_dst = RIP_DEFAULT; + OMSG.rip_nets[0].n_family = RIP_AF_UNSPEC; + OMSG.rip_nets[0].n_metric = htonl(HOPCNT_INFINITY); + + pgmname = argv[0]; + while ((ch = getopt(argc, argv, "np1w:r:t:")) != EOF) + switch (ch) { + case 'n': + not_trace = 1; + nflag = 1; + break; + + case 'p': + not_trace = 1; + pflag = 1; + break; + + case '1': + ripv2 = 0; + break; + + case 'w': + not_trace = 1; + wtime = (int)strtoul(optarg, &p, 0); + if (*p != '\0' + || wtime <= 0) + goto usage; + break; + + case 'r': + not_trace = 1; + if (rflag) + goto usage; + rflag = getnet(optarg, &OMSG.rip_nets[0]); + if (!rflag) { + struct hostent *hp = gethostbyname(optarg); + if (hp == 0) { + fprintf(stderr, "%s: %s:", + pgmname, optarg); + herror(0); + exit(1); + } + bcopy(hp->h_addr, &OMSG.rip_nets[0].n_dst, + sizeof(OMSG.rip_nets[0].n_dst)); + OMSG.rip_nets[0].n_family = RIP_AF_INET; + OMSG.rip_nets[0].n_mask = -1; + rflag = 1; + } + break; + + case 't': + trace = 1; + options = optarg; + while (*options != '\0') { + char *traceopts[] = { +# define TRACE_ON 0 + "on", +# define TRACE_MORE 1 + "more", +# define TRACE_OFF 2 + "off", +# define TRACE_DUMP 3 + "dump", + 0 + }; + switch (getsubopt(&options,traceopts,&value)) { + case TRACE_ON: + OMSG.rip_cmd = RIPCMD_TRACEON; + if (!value + || strlen(value) > MAXPATHLEN) + goto usage; + break; + case TRACE_MORE: + if (value) + goto usage; + OMSG.rip_cmd = RIPCMD_TRACEON; + value = ""; + break; + case TRACE_OFF: + if (value) + goto usage; + OMSG.rip_cmd = RIPCMD_TRACEOFF; + value = ""; + break; + case TRACE_DUMP: + if (value) + goto usage; + OMSG.rip_cmd = RIPCMD_TRACEON; + value = "dump/../table"; + break; + default: + goto usage; + } + strcpy((char*)OMSG.rip_tracefile, value); + omsg_len += strlen(value) - sizeof(OMSG.ripun); + } + break; + + default: + goto usage; + } + argv += optind; + argc -= optind; + if ((not_trace && trace) || argc == 0) { +usage: fprintf(stderr, "%s: [-np1v] [-r tgt_rt] [-w wtime]" + " host1 [host2 ...]\n" + "or\t-t {on=filename|more|off} host1 host2 ...\n", + pgmname); + exit(1); + } + + s = socket(AF_INET, SOCK_DGRAM, 0); + if (s < 0) { + perror("socket"); + exit(2); + } + + /* be prepared to receive a lot of routes */ + for (bsize = 127*1024; ; bsize -= 1024) { + if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, + &bsize, sizeof(bsize)) == 0) + break; + if (bsize <= 4*1024) { + perror("setsockopt SO_RCVBUF"); + break; + } + } + + if (trace) + trace_loop(argv); + else + query_loop(argv, argc); + /* NOTREACHED */ +} + + +/* tell the target hosts about tracing + */ +static void +trace_loop(char *argv[]) +{ + struct sockaddr_in myaddr; + int res; + + if (geteuid() != 0) { + (void)fprintf(stderr, "-t requires UID 0\n"); + exit(1); + } + + if (ripv2) { + OMSG.rip_vers = RIPv2; + } else { + OMSG.rip_vers = RIPv1; + } + + bzero(&myaddr, sizeof(myaddr)); + myaddr.sin_family = AF_INET; +#ifdef _HAVE_SIN_LEN + myaddr.sin_len = sizeof(myaddr); +#endif + myaddr.sin_port = htons(IPPORT_RESERVED-1); + while (bind(s, (struct sockaddr *)&myaddr, sizeof(myaddr)) < 0) { + if (errno != EADDRINUSE + || myaddr.sin_port == 0) { + perror("bind"); + exit(2); + } + myaddr.sin_port = htons(ntohs(myaddr.sin_port)-1); + } + + res = 1; + while (*argv != 0) { + if (out(*argv++) <= 0) + res = 0; + } + exit(res); +} + + +/* query all of the listed hosts + */ +static void +query_loop(char *argv[], int argc) +{ + struct seen { + struct seen *next; + struct in_addr addr; + } *seen, *sp; + int answered = 0; + int cc; + fd_set bits; + struct timeval now, delay; + struct sockaddr_in from; + int fromlen; + + + OMSG.rip_cmd = (pflag) ? RIPCMD_POLL : RIPCMD_REQUEST; + if (ripv2) { + OMSG.rip_vers = RIPv2; + } else { + OMSG.rip_vers = RIPv1; + OMSG.rip_nets[0].n_mask = 0; + } + + /* ask the first (valid) host */ + seen = 0; + while (0 > out(*argv++)) { + if (*argv == 0) + exit(-1); + answered++; + } + + FD_ZERO(&bits); + for (;;) { + FD_SET(s, &bits); + delay.tv_sec = 0; + delay.tv_usec = STIME; + cc = select(s+1, &bits, 0,0, &delay); + if (cc > 0) { + fromlen = sizeof(from); + cc = recvfrom(s, imsg_buf.packet, + sizeof(imsg_buf.packet), 0, + (struct sockaddr *)&from, &fromlen); + if (cc < 0) { + perror("recvfrom"); + exit(1); + } + /* count the distinct responding hosts. + * You cannot match responding hosts with + * addresses to which queries were transmitted, + * because a router might respond with a + * different source address. + */ + for (sp = seen; sp != 0; sp = sp->next) { + if (sp->addr.s_addr == from.sin_addr.s_addr) + break; + } + if (sp == 0) { + sp = malloc(sizeof(*sp)); + sp->addr = from.sin_addr; + sp->next = seen; + seen = sp; + answered++; + } + + rip_input(&from, cc); + continue; + } + + if (cc < 0) { + if ( errno == EINTR) + continue; + perror("select"); + exit(1); + } + + /* After a pause in responses, probe another host. + * This reduces the intermingling of answers. + */ + while (*argv != 0 && 0 > out(*argv++)) + answered++; + + /* continue until no more packets arrive + * or we have heard from all hosts + */ + if (answered >= argc) + break; + + /* or until we have waited a long time + */ + if (gettimeofday(&now, 0) < 0) { + perror("gettimeofday(now)"); + exit(1); + } + if (sent.tv_sec + wtime <= now.tv_sec) + break; + } + + /* fail if there was no answer */ + exit (answered >= argc ? 0 : 1); +} + + +/* sent do one host + */ +static int +out(char *host) +{ + struct sockaddr_in router; + struct hostent *hp; + + if (gettimeofday(&sent, 0) < 0) { + perror("gettimeofday(sent)"); + return -1; + } + + bzero(&router, sizeof(router)); + router.sin_family = AF_INET; +#ifdef _HAVE_SIN_LEN + router.sin_len = sizeof(router); +#endif + if (!inet_aton(host, &router.sin_addr)) { + hp = gethostbyname(host); + if (hp == 0) { + herror(host); + return -1; + } + bcopy(hp->h_addr, &router.sin_addr, sizeof(router.sin_addr)); + } + router.sin_port = htons(RIP_PORT); + + if (sendto(s, &omsg_buf, omsg_len, 0, + (struct sockaddr *)&router, sizeof(router)) < 0) { + perror(host); + return -1; + } + + return 0; +} + + +/* + * Handle an incoming RIP packet. + */ +static void +rip_input(struct sockaddr_in *from, + int size) +{ + struct netinfo *n, *lim; + struct in_addr in; + char *name; + char net_buf[80]; + u_int mask, dmask; + char *sp; + int i; + struct hostent *hp; + struct netent *np; + struct netauth *a; + + + if (nflag) { + printf("%s:", inet_ntoa(from->sin_addr)); + } else { + hp = gethostbyaddr((char*)&from->sin_addr, + sizeof(struct in_addr), AF_INET); + if (hp == 0) { + printf("%s:", + inet_ntoa(from->sin_addr)); + } else { + printf("%s (%s):", hp->h_name, + inet_ntoa(from->sin_addr)); + } + } + if (IMSG.rip_cmd != RIPCMD_RESPONSE) { + printf("\n unexpected response type %d\n", IMSG.rip_cmd); + return; + } + printf(" RIPv%d%s %d bytes\n", IMSG.rip_vers, + (IMSG.rip_vers != RIPv1 && IMSG.rip_vers != RIPv2) ? " ?" : "", + size); + if (size > MAXPACKETSIZE) { + if (size > sizeof(imsg_buf) - sizeof(*n)) { + printf(" at least %d bytes too long\n", + size-MAXPACKETSIZE); + size = sizeof(imsg_buf) - sizeof(*n); + } else { + printf(" %d bytes too long\n", + size-MAXPACKETSIZE); + } + } else if (size%sizeof(*n) != sizeof(struct rip)%sizeof(*n)) { + printf(" response of bad length=%d\n", size); + } + + n = IMSG.rip_nets; + lim = (struct netinfo *)((char*)n + size) - 1; + for (; n <= lim; n++) { + name = ""; + if (n->n_family == RIP_AF_INET) { + in.s_addr = n->n_dst; + (void)strcpy(net_buf, inet_ntoa(in)); + + mask = ntohl(n->n_mask); + dmask = mask & -mask; + if (mask != 0) { + sp = &net_buf[strlen(net_buf)]; + if (IMSG.rip_vers == RIPv1) { + (void)sprintf(sp," mask=%#x ? ",mask); + mask = 0; + } else if (mask + dmask == 0) { + for (i = 0; + (i != 32 + && ((1<n_name; + else if (in.s_addr == 0) + name = "default"; + } + if (name[0] == '\0' + && ((in.s_addr & ~mask) != 0 + || mask == 0xffffffff)) { + hp = gethostbyaddr((char*)&in, + sizeof(in), + AF_INET); + if (hp != 0) + name = hp->h_name; + } + } + + } else if (n->n_family == RIP_AF_AUTH) { + a = (struct netauth*)n; + (void)printf(" authentication type %d: ", + a->a_type); + for (i = 0; i < sizeof(a->au.au_pw); i++) + (void)printf("%02x ", a->au.au_pw[i]); + putc('\n', stdout); + continue; + + } else { + (void)sprintf(net_buf, "(af %#x) %d.%d.%d.%d", + ntohs(n->n_family), + (char)(n->n_dst >> 24), + (char)(n->n_dst >> 16), + (char)(n->n_dst >> 8), + (char)n->n_dst); + } + + (void)printf(" %-18s metric %2d %-10s", + net_buf, ntohl(n->n_metric), name); + + if (n->n_nhop != 0) { + in.s_addr = n->n_nhop; + if (nflag) + hp = 0; + else + hp = gethostbyaddr((char*)&in, sizeof(in), + AF_INET); + (void)printf(" nhop=%-15s%s", + (hp != 0) ? hp->h_name : inet_ntoa(in), + (IMSG.rip_vers == RIPv1) ? " ?" : ""); + } + if (n->n_tag != 0) + (void)printf(" tag=%#x%s", n->n_tag, + (IMSG.rip_vers == RIPv1) ? " ?" : ""); + putc('\n', stdout); + } +} + + +/* Return the classical netmask for an IP address. + */ +static u_int +std_mask(u_int addr) /* in network order */ +{ + 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; +} + + +/* get a network number as a name or a number, with an optional "/xx" + * netmask. + */ +static int /* 0=bad */ +getnet(char *name, + struct netinfo *rt) +{ + int i; + struct netent *nentp; + u_int mask; + struct in_addr in; + char hname[MAXHOSTNAMELEN+1]; + char *mname, *p; + + + /* Detect and separate "1.2.3.4/24" + */ + if (0 != (mname = rindex(name,'/'))) { + i = (int)(mname - name); + if (i > sizeof(hname)-1) /* name too long */ + return 0; + bcopy(name, hname, i); + hname[i] = '\0'; + mname++; + name = hname; + } + + nentp = getnetbyname(name); + if (nentp != 0) { + in.s_addr = nentp->n_net; + } else if (inet_aton(name, &in) == 1) { + NTOHL(in.s_addr); + } else { + return 0; + } + + if (mname == 0) { + mask = std_mask(in.s_addr); + if ((~mask & in.s_addr) != 0) + mask = 0xffffffff; + } else { + mask = (u_int)strtoul(mname, &p, 0); + if (*p != '\0' || mask > 32) + return 0; + mask = 0xffffffff << (32-mask); + } + + rt->n_dst = htonl(in.s_addr); + rt->n_family = RIP_AF_INET; + rt->n_mask = htonl(mask); + return 1; +} diff --git a/sbin/routed/table.c b/sbin/routed/table.c new file mode 100644 index 0000000..6ad97f0 --- /dev/null +++ b/sbin/routed/table.c @@ -0,0 +1,1970 @@ +/* + * Copyright (c) 1983, 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if !defined(lint) && !defined(sgi) && !defined(__NetBSD__) +static char sccsid[] = "@(#)tables.c 8.1 (Berkeley) 6/5/93"; +#elif defined(__NetBSD__) +static char rcsid[] = "$NetBSD$"; +#endif +#ident "$Revision: 1.25 $" + +#include "defs.h" + +static struct rt_spare *rts_better(struct rt_entry *); + +struct radix_node_head *rhead; /* root of the radix tree */ + +int need_flash = 1; /* flash update needed + * start =1 to suppress the 1st + */ + +struct timeval age_timer; /* next check of old routes */ +struct timeval need_kern = { /* need to update kernel table */ + EPOCH+MIN_WAITTIME-1 +}; + +int stopint; + +int total_routes; + +naddr age_bad_gate; + + +/* It is desirable to "aggregate" routes, to combine differing routes of + * the same metric and next hop into a common route with a smaller netmask + * or to suppress redundant routes, routes that add no information to + * routes with smaller netmasks. + * + * A route is redundant if and only if any and all routes with smaller + * but matching netmasks and nets are the same. Since routes are + * kept sorted in the radix tree, redundant routes always come second. + * + * There are two kinds of aggregations. First, two routes of the same bit + * mask and differing only in the least significant bit of the network + * number can be combined into a single route with a coarser mask. + * + * Second, a route can be suppressed in favor of another route with a more + * coarse mask provided no incompatible routes with intermediate masks + * are present. The second kind of aggregation involves suppressing routes. + * A route must not be suppressed if an incompatible route exists with + * an intermediate mask, since the suppressed route would be covered + * by the intermediate. + * + * This code relies on the radix tree walk encountering routes + * sorted first by address, with the smallest address first. + */ + +struct ag_info ag_slots[NUM_AG_SLOTS], *ag_avail, *ag_corsest, *ag_finest; + +/* #define DEBUG_AG */ +#ifdef DEBUG_AG +#define CHECK_AG() {int acnt = 0; struct ag_info *cag; \ + for (cag = ag_avail; cag != 0; cag = cag->ag_fine) \ + acnt++; \ + for (cag = ag_corsest; cag != 0; cag = cag->ag_fine) \ + acnt++; \ + if (acnt != NUM_AG_SLOTS) { \ + (void)fflush(stderr); \ + abort(); \ + } \ +} +#else +#define CHECK_AG() +#endif + + +/* Output the contents of an aggregation table slot. + * This function must always be immediately followed with the deletion + * of the target slot. + */ +static void +ag_out(struct ag_info *ag, + void (*out)(struct ag_info *)) +{ + struct ag_info *ag_cors; + naddr bit; + + + /* If we output both the even and odd twins, then the immediate parent, + * if it is present, is redundant, unless the parent manages to + * aggregate into something coarser. + * On successive calls, this code detects the even and odd twins, + * and marks the parent. + * + * Note that the order in which the radix tree code emits routes + * ensures that the twins are seen before the parent is emitted. + */ + ag_cors = ag->ag_cors; + if (ag_cors != 0 + && ag_cors->ag_mask == ag->ag_mask<<1 + && ag_cors->ag_dst_h == (ag->ag_dst_h & ag_cors->ag_mask)) { + ag_cors->ag_state |= ((ag_cors->ag_dst_h == ag->ag_dst_h) + ? AGS_REDUN0 + : AGS_REDUN1); + } + + /* Skip it if this route is itself redundant. + * + * It is ok to change the contents of the slot here, since it is + * always deleted next. + */ + if (ag->ag_state & AGS_REDUN0) { + if (ag->ag_state & AGS_REDUN1) + return; + bit = (-ag->ag_mask) >> 1; + ag->ag_dst_h |= bit; + ag->ag_mask |= bit; + + } else if (ag->ag_state & AGS_REDUN1) { + bit = (-ag->ag_mask) >> 1; + ag->ag_mask |= bit; + } + out(ag); +} + + +static void +ag_del(struct ag_info *ag) +{ + CHECK_AG(); + + if (ag->ag_cors == 0) + ag_corsest = ag->ag_fine; + else + ag->ag_cors->ag_fine = ag->ag_fine; + + if (ag->ag_fine == 0) + ag_finest = ag->ag_cors; + else + ag->ag_fine->ag_cors = ag->ag_cors; + + ag->ag_fine = ag_avail; + ag_avail = ag; + + CHECK_AG(); +} + + +/* Flush routes waiting for aggretation. + * This must not suppress a route unless it is known that among all + * routes with coarser masks that match it, the one with the longest + * mask is appropriate. This is ensured by scanning the routes + * in lexical order, and with the most restritive mask first + * among routes to the same destination. + */ +void +ag_flush(naddr lim_dst_h, /* flush routes to here */ + naddr lim_mask, /* matching this mask */ + void (*out)(struct ag_info *)) +{ + struct ag_info *ag, *ag_cors; + naddr dst_h; + + + for (ag = ag_finest; + ag != 0 && ag->ag_mask >= lim_mask; + ag = ag_cors) { + ag_cors = ag->ag_cors; + + /* work on only the specified routes */ + dst_h = ag->ag_dst_h; + if ((dst_h & lim_mask) != lim_dst_h) + continue; + + if (!(ag->ag_state & AGS_SUPPRESS)) + ag_out(ag, out); + + else for ( ; ; ag_cors = ag_cors->ag_cors) { + /* Look for a route that can suppress the + * current route */ + if (ag_cors == 0) { + /* failed, so output it and look for + * another route to work on + */ + ag_out(ag, out); + break; + } + + if ((dst_h & ag_cors->ag_mask) == ag_cors->ag_dst_h) { + /* We found a route with a coarser mask that + * aggregates the current target. + * + * If it has a different next hop, it + * cannot replace the target, so output + * the target. + */ + if (ag->ag_gate != ag_cors->ag_gate + && !(ag->ag_state & AGS_FINE_GATE) + && !(ag_cors->ag_state & AGS_CORS_GATE)) { + ag_out(ag, out); + break; + } + + /* If the coarse route has a good enough + * metric, it suppresses the target. + */ + if (ag_cors->ag_pref <= ag->ag_pref) { + if (ag_cors->ag_seqno > ag->ag_seqno) + ag_cors->ag_seqno = ag->ag_seqno; + if (AG_IS_REDUN(ag->ag_state) + && ag_cors->ag_mask==ag->ag_mask<<1) { + if (ag_cors->ag_dst_h == dst_h) + ag_cors->ag_state |= AGS_REDUN0; + else + ag_cors->ag_state |= AGS_REDUN1; + } + if (ag->ag_tag != ag_cors->ag_tag) + ag_cors->ag_tag = 0; + if (ag->ag_nhop != ag_cors->ag_nhop) + ag_cors->ag_nhop = 0; + break; + } + } + } + + /* That route has either been output or suppressed */ + ag_cors = ag->ag_cors; + ag_del(ag); + } + + CHECK_AG(); +} + + +/* Try to aggregate a route with previous routes. + */ +void +ag_check(naddr dst, + naddr mask, + naddr gate, + naddr nhop, + char metric, + char pref, + u_int seqno, + u_short tag, + u_short state, + void (*out)(struct ag_info *)) /* output using this */ +{ + struct ag_info *ag, *nag, *ag_cors; + naddr xaddr; + int x; + + NTOHL(dst); + + /* Punt non-contiguous subnet masks. + * + * (X & -X) contains a single bit if and only if X is a power of 2. + * (X + (X & -X)) == 0 if and only if X is a power of 2. + */ + if ((mask & -mask) + mask != 0) { + struct ag_info nc_ag; + + nc_ag.ag_dst_h = dst; + nc_ag.ag_mask = mask; + nc_ag.ag_gate = gate; + nc_ag.ag_nhop = nhop; + nc_ag.ag_metric = metric; + nc_ag.ag_pref = pref; + nc_ag.ag_tag = tag; + nc_ag.ag_state = state; + nc_ag.ag_seqno = seqno; + out(&nc_ag); + return; + } + + /* Search for the right slot in the aggregation table. + */ + ag_cors = 0; + ag = ag_corsest; + while (ag != 0) { + if (ag->ag_mask >= mask) + break; + + /* Suppress old routes (i.e. combine with compatible routes + * with coarser masks) as we look for the right slot in the + * aggregation table for the new route. + * A route to an address less than the current destination + * will not be affected by the current route or any route + * seen hereafter. That means it is safe to suppress it. + * This check keeps poor routes (eg. with large hop counts) + * from preventing suppresion of finer routes. + */ + if (ag_cors != 0 + && ag->ag_dst_h < dst + && (ag->ag_state & AGS_SUPPRESS) + && ag_cors->ag_pref <= ag->ag_pref + && (ag->ag_dst_h & ag_cors->ag_mask) == ag_cors->ag_dst_h + && (ag_cors->ag_gate == ag->ag_gate + || (ag->ag_state & AGS_FINE_GATE) + || (ag_cors->ag_state & AGS_CORS_GATE))) { + if (ag_cors->ag_seqno > ag->ag_seqno) + ag_cors->ag_seqno = ag->ag_seqno; + if (AG_IS_REDUN(ag->ag_state) + && ag_cors->ag_mask==ag->ag_mask<<1) { + if (ag_cors->ag_dst_h == dst) + ag_cors->ag_state |= AGS_REDUN0; + else + ag_cors->ag_state |= AGS_REDUN1; + } + if (ag->ag_tag != ag_cors->ag_tag) + ag_cors->ag_tag = 0; + if (ag->ag_nhop != ag_cors->ag_nhop) + ag_cors->ag_nhop = 0; + ag_del(ag); + CHECK_AG(); + } else { + ag_cors = ag; + } + ag = ag_cors->ag_fine; + } + + /* If we find the even/odd twin of the new route, and if the + * masks and so forth are equal, we can aggregate them. + * We can probably promote one of the pair. + * + * Since the routes are encountered in lexical order, + * the new route must be odd. However, the second or later + * times around this loop, it could be the even twin promoted + * from the even/odd pair of twins of the finer route. + */ + while (ag != 0 + && ag->ag_mask == mask + && ((ag->ag_dst_h ^ dst) & (mask<<1)) == 0) { + + /* Here we know the target route and the route in the current + * slot have the same netmasks and differ by at most the + * last bit. They are either for the same destination, or + * for an even/odd pair of destinations. + */ + if (ag->ag_dst_h == dst) { + /* We have two routes to the same destination. + * Routes are encountered in lexical order, so a + * route is never promoted until the parent route is + * already present. So we know that the new route is + * a promoted pair and the route already in the slot + * is the explicit route. + * + * Prefer the best route if their metrics differ, + * or the promoted one if not, following a sort + * of longest-match rule. + */ + if (pref <= ag->ag_pref) { + ag->ag_gate = gate; + ag->ag_nhop = nhop; + ag->ag_tag = tag; + ag->ag_metric = metric; + ag->ag_pref = pref; + x = ag->ag_state; + ag->ag_state = state; + state = x; + } + + /* The sequence number controls flash updating, + * and should be the smaller of the two. + */ + if (ag->ag_seqno > seqno) + ag->ag_seqno = seqno; + + /* some bits are set if they are set on either route */ + ag->ag_state |= (state & (AGS_PROMOTE_EITHER + | AGS_REDUN0 | AGS_REDUN1)); + return; + } + + /* If one of the routes can be promoted and the other can + * be suppressed, it may be possible to combine them or + * worthwhile to promote one. + * + * Note that any route that can be promoted is always + * marked to be eligible to be suppressed. + */ + if (!((state & AGS_PROMOTE) + && (ag->ag_state & AGS_SUPPRESS)) + && !((ag->ag_state & AGS_PROMOTE) + && (state & AGS_SUPPRESS))) + break; + + /* A pair of even/odd twin routes can be combined + * if either is redundant, or if they are via the + * same gateway and have the same metric. + */ + if (AG_IS_REDUN(ag->ag_state) + || AG_IS_REDUN(state) + || (ag->ag_gate == gate + && ag->ag_pref == pref + && (state & ag->ag_state & AGS_PROMOTE) != 0)) { + + /* We have both the even and odd pairs. + * Since the routes are encountered in order, + * the route in the slot must be the even twin. + * + * Combine and promote the pair of routes. + */ + if (seqno > ag->ag_seqno) + seqno = ag->ag_seqno; + if (!AG_IS_REDUN(state)) + state &= ~AGS_REDUN1; + if (AG_IS_REDUN(ag->ag_state)) + state |= AGS_REDUN0; + else + state &= ~AGS_REDUN0; + state |= (ag->ag_state & AGS_PROMOTE_EITHER); + if (ag->ag_tag != tag) + tag = 0; + if (ag->ag_nhop != nhop) + nhop = 0; + + /* Get rid of the even twin that was already + * in the slot. + */ + ag_del(ag); + + } else if (ag->ag_pref >= pref + && (ag->ag_state & AGS_PROMOTE)) { + /* If we cannot combine the pair, maybe the route + * with the worse metric can be promoted. + * + * Promote the old, even twin, by giving its slot + * in the table to the new, odd twin. + */ + ag->ag_dst_h = dst; + + xaddr = ag->ag_gate; + ag->ag_gate = gate; + gate = xaddr; + + xaddr = ag->ag_nhop; + ag->ag_nhop = nhop; + nhop = xaddr; + + x = ag->ag_tag; + ag->ag_tag = tag; + tag = x; + + x = ag->ag_state; + ag->ag_state = state; + state = x; + if (!AG_IS_REDUN(state)) + state &= ~AGS_REDUN0; + + x = ag->ag_metric; + ag->ag_metric = metric; + metric = x; + + x = ag->ag_pref; + ag->ag_pref = pref; + pref = x; + + if (seqno >= ag->ag_seqno) + seqno = ag->ag_seqno; + else + ag->ag_seqno = seqno; + + } else { + if (!(state & AGS_PROMOTE)) + break; /* cannot promote either twin */ + + /* promote the new, odd twin by shaving its + * mask and address. + */ + if (seqno > ag->ag_seqno) + seqno = ag->ag_seqno; + else + ag->ag_seqno = seqno; + if (!AG_IS_REDUN(state)) + state &= ~AGS_REDUN1; + } + + mask <<= 1; + dst &= mask; + + if (ag_cors == 0) { + ag = ag_corsest; + break; + } + ag = ag_cors; + ag_cors = ag->ag_cors; + } + + /* When we can no longer promote and combine routes, + * flush the old route in the target slot. Also flush + * any finer routes that we know will never be aggregated by + * the new route. + * + * In case we moved toward coarser masks, + * get back where we belong + */ + if (ag != 0 + && ag->ag_mask < mask) { + ag_cors = ag; + ag = ag->ag_fine; + } + + /* Empty the target slot + */ + if (ag != 0 && ag->ag_mask == mask) { + ag_flush(ag->ag_dst_h, ag->ag_mask, out); + ag = (ag_cors == 0) ? ag_corsest : ag_cors->ag_fine; + } + +#ifdef DEBUG_AG + (void)fflush(stderr); + if (ag == 0 && ag_cors != ag_finest) + abort(); + if (ag_cors == 0 && ag != ag_corsest) + abort(); + if (ag != 0 && ag->ag_cors != ag_cors) + abort(); + if (ag_cors != 0 && ag_cors->ag_fine != ag) + abort(); + CHECK_AG(); +#endif + + /* Save the new route on the end of the table. + */ + nag = ag_avail; + ag_avail = nag->ag_fine; + + nag->ag_dst_h = dst; + nag->ag_mask = mask; + nag->ag_gate = gate; + nag->ag_nhop = nhop; + nag->ag_metric = metric; + nag->ag_pref = pref; + nag->ag_tag = tag; + nag->ag_state = state; + nag->ag_seqno = seqno; + + nag->ag_fine = ag; + if (ag != 0) + ag->ag_cors = nag; + else + ag_finest = nag; + nag->ag_cors = ag_cors; + if (ag_cors == 0) + ag_corsest = nag; + else + ag_cors->ag_fine = nag; + CHECK_AG(); +} + + +static char * +rtm_type_name(u_char type) +{ + static char *rtm_types[] = { + "RTM_ADD", + "RTM_DELETE", + "RTM_CHANGE", + "RTM_GET", + "RTM_LOSING", + "RTM_REDIRECT", + "RTM_MISS", + "RTM_LOCK", + "RTM_OLDADD", + "RTM_OLDDEL", + "RTM_RESOLVE", + "RTM_NEWADDR", + "RTM_DELADDR", + "RTM_IFINFO" + }; + static char name0[10]; + + + if (type > sizeof(rtm_types)/sizeof(rtm_types[0]) + || type == 0) { + sprintf(name0, "RTM type %#x", type); + return name0; + } else { + return rtm_types[type-1]; + } +} + + +/* Trim a mask in a sockaddr + * Produce a length of 0 for an address of 0. + * Otherwise produce the index of the first zero byte. + */ +void +#ifdef _HAVE_SIN_LEN +masktrim(struct sockaddr_in *ap) +#else +masktrim(struct sockaddr_in_new *ap) +#endif +{ + register char *cp; + + if (ap->sin_addr.s_addr == 0) { + ap->sin_len = 0; + return; + } + cp = (char *)(&ap->sin_addr.s_addr+1); + while (*--cp == 0) + continue; + ap->sin_len = cp - (char*)ap + 1; +} + + +/* Tell the kernel to add, delete or change a route + */ +static void +rtioctl(int action, /* RTM_DELETE, etc */ + naddr dst, + naddr gate, + naddr mask, + int metric, + int flags) +{ + struct { + struct rt_msghdr w_rtm; + struct sockaddr_in w_dst; + struct sockaddr_in w_gate; +#ifdef _HAVE_SA_LEN + struct sockaddr_in w_mask; +#else + struct sockaddr_in_new w_mask; +#endif + } w; + long cc; + +again: + bzero(&w, sizeof(w)); + w.w_rtm.rtm_msglen = sizeof(w); + w.w_rtm.rtm_version = RTM_VERSION; + w.w_rtm.rtm_type = action; + w.w_rtm.rtm_flags = flags; + w.w_rtm.rtm_seq = ++rt_sock_seqno; + w.w_rtm.rtm_addrs = RTA_DST|RTA_GATEWAY; + if (metric != 0) { + w.w_rtm.rtm_rmx.rmx_hopcount = metric; + w.w_rtm.rtm_inits |= RTV_HOPCOUNT; + } + w.w_dst.sin_family = AF_INET; + w.w_dst.sin_addr.s_addr = dst; + w.w_gate.sin_family = AF_INET; + w.w_gate.sin_addr.s_addr = gate; +#ifdef _HAVE_SA_LEN + w.w_dst.sin_len = sizeof(w.w_dst); + w.w_gate.sin_len = sizeof(w.w_gate); +#endif + if (mask == HOST_MASK) { + w.w_rtm.rtm_flags |= RTF_HOST; + w.w_rtm.rtm_msglen -= sizeof(w.w_mask); + } else { + w.w_rtm.rtm_addrs |= RTA_NETMASK; + w.w_mask.sin_addr.s_addr = htonl(mask); +#ifdef _HAVE_SA_LEN + masktrim(&w.w_mask); + if (w.w_mask.sin_len == 0) + w.w_mask.sin_len = sizeof(long); + w.w_rtm.rtm_msglen -= (sizeof(w.w_mask) - w.w_mask.sin_len); +#endif + } + + if (TRACEKERNEL) + trace_kernel("write kernel %s %s->%s metric=%d flags=%#x\n", + rtm_type_name(action), + addrname(dst, mask, 0), naddr_ntoa(gate), + metric, flags); + +#ifndef NO_INSTALL + cc = write(rt_sock, &w, w.w_rtm.rtm_msglen); + if (cc == w.w_rtm.rtm_msglen) + return; + if (cc < 0) { + if (errno == ESRCH + && (action == RTM_CHANGE || action == RTM_DELETE)) { + trace_act("route to %s disappeared before %s\n", + addrname(dst, mask, 0), + rtm_type_name(action)); + if (action == RTM_CHANGE) { + action = RTM_ADD; + goto again; + } + return; + } + msglog("write(rt_sock) %s %s --> %s: %s", + rtm_type_name(action), + addrname(dst, mask, 0), naddr_ntoa(gate), + strerror(errno)); + } else { + msglog("write(rt_sock) wrote %d instead of %d", + cc, w.w_rtm.rtm_msglen); + } +#endif +} + + +#define KHASH_SIZE 71 /* should be prime */ +#define KHASH(a,m) khash_bins[((a) ^ (m)) % KHASH_SIZE] +static struct khash { + struct khash *k_next; + naddr k_dst; + naddr k_mask; + naddr k_gate; + short k_metric; + u_short k_state; +#define KS_NEW 0x001 +#define KS_DELETE 0x002 +#define KS_ADD 0x004 /* add to the kernel */ +#define KS_CHANGE 0x008 /* tell kernel to change the route */ +#define KS_DEL_ADD 0x010 /* delete & add to change the kernel */ +#define KS_STATIC 0x020 /* Static flag in kernel */ +#define KS_GATEWAY 0x040 /* G flag in kernel */ +#define KS_DYNAMIC 0x080 /* result of redirect */ +#define KS_DELETED 0x100 /* already deleted */ + time_t k_keep; +#define K_KEEP_LIM 30 + time_t k_redirect_time; +} *khash_bins[KHASH_SIZE]; + + +static struct khash* +kern_find(naddr dst, naddr mask, struct khash ***ppk) +{ + struct khash *k, **pk; + + for (pk = &KHASH(dst,mask); (k = *pk) != 0; pk = &k->k_next) { + if (k->k_dst == dst && k->k_mask == mask) + break; + } + if (ppk != 0) + *ppk = pk; + return k; +} + + +static struct khash* +kern_add(naddr dst, naddr mask) +{ + struct khash *k, **pk; + + k = kern_find(dst, mask, &pk); + if (k != 0) + return k; + + k = (struct khash *)malloc(sizeof(*k)); + + bzero(k, sizeof(*k)); + k->k_dst = dst; + k->k_mask = mask; + k->k_state = KS_NEW; + k->k_keep = now.tv_sec; + *pk = k; + + return k; +} + + +/* If a kernel route has a non-zero metric, check that it is still in the + * daemon table, and not deleted by interfaces coming and going. + */ +static void +kern_check_static(struct khash *k, + struct interface *ifp) +{ + struct rt_entry *rt; + naddr int_addr; + + if (k->k_metric == 0) + return; + + int_addr = (ifp != 0) ? ifp->int_addr : loopaddr; + + rt = rtget(k->k_dst, k->k_mask); + if (rt != 0) { + if (!(rt->rt_state & RS_STATIC)) + rtchange(rt, rt->rt_state | RS_STATIC, + k->k_gate, int_addr, + k->k_metric, 0, ifp, now.tv_sec, 0); + } else { + rtadd(k->k_dst, k->k_mask, k->k_gate, int_addr, + k->k_metric, 0, RS_STATIC, ifp); + } +} + + +/* add a route the kernel told us + */ +static void +rtm_add(struct rt_msghdr *rtm, + struct rt_addrinfo *info, + time_t keep) +{ + struct khash *k; + struct interface *ifp; + naddr mask; + + + if (rtm->rtm_flags & RTF_HOST) { + mask = HOST_MASK; + } else if (INFO_MASK(info) != 0) { + mask = ntohl(S_ADDR(INFO_MASK(info))); + } else { + msglog("punt %s without mask", + rtm_type_name(rtm->rtm_type)); + return; + } + + if (INFO_GATE(info) == 0 + || INFO_GATE(info)->sa_family != AF_INET) { + msglog("punt %s without gateway", + rtm_type_name(rtm->rtm_type)); + return; + } + + k = kern_add(S_ADDR(INFO_DST(info)), mask); + if (k->k_state & KS_NEW) + k->k_keep = now.tv_sec+keep; + k->k_gate = S_ADDR(INFO_GATE(info)); + k->k_metric = rtm->rtm_rmx.rmx_hopcount; + if (k->k_metric < 0) + k->k_metric = 0; + else if (k->k_metric > HOPCNT_INFINITY) + k->k_metric = HOPCNT_INFINITY; + k->k_state &= ~(KS_DELETED | KS_GATEWAY | KS_STATIC | KS_NEW); + if (rtm->rtm_flags & RTF_GATEWAY) + k->k_state |= KS_GATEWAY; + if (rtm->rtm_flags & RTF_STATIC) + k->k_state |= KS_STATIC; + + if (0 != (rtm->rtm_flags & (RTF_DYNAMIC | RTF_MODIFIED))) { + if (supplier) { + /* Routers are not supposed to listen to redirects, + * so delete it. + */ + k->k_state &= ~KS_DYNAMIC; + k->k_state |= KS_DELETE; + LIM_SEC(need_kern, 0); + trace_act("mark redirected %s --> %s for deletion" + " since this is a router\n", + addrname(k->k_dst, k->k_mask, 0), + naddr_ntoa(k->k_gate)); + } else { + k->k_state |= KS_DYNAMIC; + k->k_redirect_time = now.tv_sec; + } + return; + } + + /* If it is not a static route, quit until the next comparison + * between the kernel and daemon tables, when it will be deleted. + */ + if (!(k->k_state & KS_STATIC)) { + k->k_state |= KS_DELETE; + LIM_SEC(need_kern, k->k_keep); + return; + } + + /* Put static routes with real metrics into the daemon table so + * they can be advertised. + * + * Find the interface concerned + */ + ifp = iflookup(k->k_gate); + if (ifp == 0) { + /* if there is no known interface, + * maybe there is a new interface + */ + ifinit(); + ifp = iflookup(k->k_gate); + if (ifp == 0) + msglog("static route %s --> %s impossibly lacks ifp", + addrname(S_ADDR(INFO_DST(info)), mask, 0), + naddr_ntoa(k->k_gate)); + } + + kern_check_static(k, ifp); +} + + +/* deal with packet loss + */ +static void +rtm_lose(struct rt_msghdr *rtm, + struct rt_addrinfo *info) +{ + if (INFO_GATE(info) == 0 + || INFO_GATE(info)->sa_family != AF_INET) { + msglog("punt %s without gateway", + rtm_type_name(rtm->rtm_type)); + return; + } + + if (!supplier) + rdisc_age(S_ADDR(INFO_GATE(info))); + + age(S_ADDR(INFO_GATE(info))); +} + + +/* Clean the kernel table by copying it to the daemon image. + * Eventually the daemon will delete any extra routes. + */ +void +flush_kern(void) +{ + size_t needed; + int mib[6]; + char *buf, *next, *lim; + struct rt_msghdr *rtm; + struct interface *ifp; + static struct sockaddr_in gate_sa; + struct rt_addrinfo info; + + + mib[0] = CTL_NET; + mib[1] = PF_ROUTE; + mib[2] = 0; /* protocol */ + mib[3] = 0; /* wildcard address family */ + mib[4] = NET_RT_DUMP; + mib[5] = 0; /* no flags */ + if (sysctl(mib, 6, 0, &needed, 0, 0) < 0) { + DBGERR(1,"RT_DUMP-sysctl-estimate"); + return; + } + buf = malloc(needed); + if (sysctl(mib, 6, buf, &needed, 0, 0) < 0) + BADERR(1,"RT_DUMP"); + lim = buf + needed; + for (next = buf; next < lim; next += rtm->rtm_msglen) { + rtm = (struct rt_msghdr *)next; + + rt_xaddrs(&info, + (struct sockaddr *)(rtm+1), + (struct sockaddr *)(next + rtm->rtm_msglen), + rtm->rtm_addrs); + + if (INFO_DST(&info) == 0 + || INFO_DST(&info)->sa_family != AF_INET) + continue; + + /* ignore ARP table entries on systems with a merged route + * and ARP table. + */ + if (rtm->rtm_flags & RTF_LLINFO) + continue; + + if (INFO_GATE(&info) == 0) + continue; + if (INFO_GATE(&info)->sa_family != AF_INET) { + if (INFO_GATE(&info)->sa_family != AF_LINK) + continue; + ifp = ifwithindex(((struct sockaddr_dl *) + INFO_GATE(&info))->sdl_index); + if (ifp == 0) + continue; + if ((ifp->int_if_flags & IFF_POINTOPOINT) + || S_ADDR(INFO_DST(&info)) == ifp->int_addr) + gate_sa.sin_addr.s_addr = ifp->int_addr; + else + gate_sa.sin_addr.s_addr = htonl(ifp->int_net); +#ifdef _HAVE_SA_LEN + gate_sa.sin_len = sizeof(gate_sa); +#endif + gate_sa.sin_family = AF_INET; + INFO_GATE(&info) = (struct sockaddr *)&gate_sa; + } + + /* ignore multicast addresses + */ + if (IN_MULTICAST(ntohl(S_ADDR(INFO_DST(&info))))) + continue; + + /* Note static routes and interface routes, and also + * preload the image of the kernel table so that + * we can later clean it, as well as avoid making + * unneeded changes. Keep the old kernel routes for a + * few seconds to allow a RIP or router-discovery + * response to be heard. + */ + rtm_add(rtm,&info,MIN_WAITTIME); + } + free(buf); +} + + +/* Listen to announcements from the kernel + */ +void +read_rt(void) +{ + long cc; + struct interface *ifp; + naddr mask; + union { + struct { + struct rt_msghdr rtm; + struct sockaddr addrs[RTAX_MAX]; + } r; + struct if_msghdr ifm; + } m; + char str[100], *strp; + struct rt_addrinfo info; + + + for (;;) { + cc = read(rt_sock, &m, sizeof(m)); + if (cc <= 0) { + if (cc < 0 && errno != EWOULDBLOCK) + LOGERR("read(rt_sock)"); + return; + } + + if (m.r.rtm.rtm_version != RTM_VERSION) { + msglog("bogus routing message version %d", + m.r.rtm.rtm_version); + continue; + } + + /* Ignore our own results. + */ + if (m.r.rtm.rtm_type <= RTM_CHANGE + && m.r.rtm.rtm_pid == mypid) { + static int complained = 0; + if (!complained) { + msglog("receiving our own change messages"); + complained = 1; + } + continue; + } + + if (m.r.rtm.rtm_type == RTM_IFINFO + || m.r.rtm.rtm_type == RTM_NEWADDR + || m.r.rtm.rtm_type == RTM_DELADDR) { + ifp = ifwithindex(m.ifm.ifm_index); + if (ifp == 0) + trace_act("note %s with flags %#x" + " for index #%d\n", + rtm_type_name(m.r.rtm.rtm_type), + m.ifm.ifm_flags, + m.ifm.ifm_index); + else + trace_act("note %s with flags %#x for %s\n", + rtm_type_name(m.r.rtm.rtm_type), + m.ifm.ifm_flags, + ifp->int_name); + + /* After being informed of a change to an interface, + * check them all now if the check would otherwise + * be a long time from now, if the interface is + * not known, or if the interface has been turned + * off or on. + */ + if (ifinit_timer.tv_sec-now.tv_sec>=CHECK_BAD_INTERVAL + || ifp == 0 + || ((ifp->int_if_flags ^ m.ifm.ifm_flags) + & IFF_UP_RUNNING) != 0) + ifinit_timer.tv_sec = now.tv_sec; + continue; + } + + strcpy(str, rtm_type_name(m.r.rtm.rtm_type)); + strp = &str[strlen(str)]; + if (m.r.rtm.rtm_type <= RTM_CHANGE) + strp += sprintf(strp," from pid %d",m.r.rtm.rtm_pid); + + rt_xaddrs(&info, m.r.addrs, &m.r.addrs[RTAX_MAX], + m.r.rtm.rtm_addrs); + + if (INFO_DST(&info) == 0) { + trace_act("ignore %s without dst\n", str); + continue; + } + + if (INFO_DST(&info)->sa_family != AF_INET) { + trace_act("ignore %s for AF %d\n", str, + INFO_DST(&info)->sa_family); + continue; + } + + mask = ((INFO_MASK(&info) != 0) + ? ntohl(S_ADDR(INFO_MASK(&info))) + : (m.r.rtm.rtm_flags & RTF_HOST) + ? HOST_MASK + : std_mask(S_ADDR(INFO_DST(&info)))); + + strp += sprintf(strp, ": %s", + addrname(S_ADDR(INFO_DST(&info)), mask, 0)); + + if (IN_MULTICAST(ntohl(S_ADDR(INFO_DST(&info))))) { + trace_act("ignore multicast %s\n", str); + continue; + } + + if (INFO_GATE(&info) != 0 + && INFO_GATE(&info)->sa_family == AF_INET) + strp += sprintf(strp, " --> %s", + saddr_ntoa(INFO_GATE(&info))); + + if (INFO_AUTHOR(&info) != 0) + strp += sprintf(strp, " by authority of %s", + saddr_ntoa(INFO_AUTHOR(&info))); + + switch (m.r.rtm.rtm_type) { + case RTM_ADD: + case RTM_CHANGE: + case RTM_REDIRECT: + if (m.r.rtm.rtm_errno != 0) { + trace_act("ignore %s with \"%s\" error\n", + str, strerror(m.r.rtm.rtm_errno)); + } else { + trace_act("%s\n", str); + rtm_add(&m.r.rtm,&info,0); + } + break; + + case RTM_DELETE: + if (m.r.rtm.rtm_errno != 0) { + trace_act("ignore %s with \"%s\" error\n", + str, strerror(m.r.rtm.rtm_errno)); + } else { + trace_act("%s\n", str); + del_static(S_ADDR(INFO_DST(&info)), mask, 1); + } + break; + + case RTM_LOSING: + trace_act("%s\n", str); + rtm_lose(&m.r.rtm,&info); + break; + + default: + trace_act("ignore %s\n", str); + break; + } + } +} + + +/* after aggregating, note routes that belong in the kernel + */ +static void +kern_out(struct ag_info *ag) +{ + struct khash *k; + + + /* Do not install bad routes if they are not already present. + * This includes routes that had RS_NET_SYN for interfaces that + * recently died. + */ + if (ag->ag_metric == HOPCNT_INFINITY) { + k = kern_find(htonl(ag->ag_dst_h), ag->ag_mask, 0); + if (k == 0) + return; + } else { + k = kern_add(htonl(ag->ag_dst_h), ag->ag_mask); + } + + if (k->k_state & KS_NEW) { + /* will need to add new entry to the kernel table */ + k->k_state = KS_ADD; + if (ag->ag_state & AGS_GATEWAY) + k->k_state |= KS_GATEWAY; + k->k_gate = ag->ag_gate; + k->k_metric = ag->ag_metric; + return; + } + + if (k->k_state & KS_STATIC) + return; + + /* modify existing kernel entry if necessary */ + if (k->k_gate != ag->ag_gate + || k->k_metric != ag->ag_metric) { + k->k_gate = ag->ag_gate; + k->k_metric = ag->ag_metric; + k->k_state |= KS_CHANGE; + } + + if (k->k_state & KS_DYNAMIC) { + k->k_state &= ~KS_DYNAMIC; + k->k_state |= (KS_ADD | KS_DEL_ADD); + } + + if ((k->k_state & KS_GATEWAY) + && !(ag->ag_state & AGS_GATEWAY)) { + k->k_state &= ~KS_GATEWAY; + k->k_state |= (KS_ADD | KS_DEL_ADD); + } else if (!(k->k_state & KS_GATEWAY) + && (ag->ag_state & AGS_GATEWAY)) { + k->k_state |= KS_GATEWAY; + k->k_state |= (KS_ADD | KS_DEL_ADD); + } + + /* Deleting-and-adding is necessary to change aspects of a route. + * Just delete instead of deleting and then adding a bad route. + * Otherwise, we want to keep the route in the kernel. + */ + if (k->k_metric == HOPCNT_INFINITY + && (k->k_state & KS_DEL_ADD)) + k->k_state |= KS_DELETE; + else + k->k_state &= ~KS_DELETE; +#undef RT +} + + +/* ARGSUSED */ +static int +walk_kern(struct radix_node *rn, + struct walkarg *w) +{ +#define RT ((struct rt_entry *)rn) + char metric, pref; + u_int ags = 0; + + + /* Do not install synthetic routes */ + if (RT->rt_state & RS_NET_SYN) + return 0; + + if (!(RT->rt_state & RS_IF)) { + ags |= (AGS_GATEWAY | AGS_SUPPRESS | AGS_PROMOTE); + + } else { + /* Do not install routes for "external" remote interfaces. + */ + if (RT->rt_ifp != 0 && (RT->rt_ifp->int_state & IS_EXTERNAL)) + return 0; + + ags |= AGS_IF; + + /* If it is not an interface, or an alias for an interface, + * it must be a "gateway." + * + * If it is a "remote" interface, it is also a "gateway" to + * the kernel if is not a alias. + */ + if (RT->rt_ifp == 0 + || ((RT->rt_ifp->int_state & IS_REMOTE) + && RT->rt_ifp->int_metric == 0)) + ags |= (AGS_GATEWAY | AGS_SUPPRESS | AGS_PROMOTE); + } + + if (RT->rt_state & RS_RDISC) + ags |= AGS_CORS_GATE; + + /* aggregate good routes without regard to their metric */ + pref = 1; + metric = RT->rt_metric; + if (metric == HOPCNT_INFINITY) { + /* if the route is dead, so try hard to aggregate. */ + pref = HOPCNT_INFINITY; + ags |= (AGS_FINE_GATE | AGS_SUPPRESS); + } + + ag_check(RT->rt_dst, RT->rt_mask, RT->rt_gate, 0, + metric,pref, 0, 0, ags, kern_out); + return 0; +#undef RT +} + + +/* Update the kernel table to match the daemon table. + */ +static void +fix_kern(void) +{ + int i, flags; + struct khash *k, **pk; + + + need_kern = age_timer; + + /* Walk daemon table, updating the copy of the kernel table. + */ + (void)rn_walktree(rhead, walk_kern, 0); + ag_flush(0,0,kern_out); + + for (i = 0; i < KHASH_SIZE; i++) { + for (pk = &khash_bins[i]; (k = *pk) != 0; ) { + /* Do not touch static routes */ + if (k->k_state & KS_STATIC) { + kern_check_static(k,0); + pk = &k->k_next; + continue; + } + + /* check hold on routes deleted by the operator */ + if (k->k_keep > now.tv_sec) { + LIM_SEC(need_kern, k->k_keep); + k->k_state |= KS_DELETE; + pk = &k->k_next; + continue; + } + + if ((k->k_state & (KS_DELETE | KS_DYNAMIC)) + == KS_DELETE) { + if (!(k->k_state & KS_DELETED)) + rtioctl(RTM_DELETE, + k->k_dst, k->k_gate, k->k_mask, + 0, 0); + *pk = k->k_next; + free(k); + continue; + } + + if (0 != (k->k_state&(KS_ADD|KS_CHANGE|KS_DEL_ADD))) { + if (k->k_state & KS_DEL_ADD) { + rtioctl(RTM_DELETE, + k->k_dst,k->k_gate,k->k_mask, + 0, 0); + k->k_state &= ~KS_DYNAMIC; + } + + flags = 0; + if (0 != (k->k_state&(KS_GATEWAY|KS_DYNAMIC))) + flags |= RTF_GATEWAY; + + if (k->k_state & KS_ADD) { + rtioctl(RTM_ADD, + k->k_dst, k->k_gate, k->k_mask, + k->k_metric, flags); + } else if (k->k_state & KS_CHANGE) { + rtioctl(RTM_CHANGE, + k->k_dst,k->k_gate,k->k_mask, + k->k_metric, flags); + } + k->k_state &= ~(KS_ADD|KS_CHANGE|KS_DEL_ADD); + } + + /* Mark this route to be deleted in the next cycle. + * This deletes routes that disappear from the + * daemon table, since the normal aging code + * will clear the bit for routes that have not + * disappeared from the daemon table. + */ + k->k_state |= KS_DELETE; + pk = &k->k_next; + } + } +} + + +/* Delete a static route in the image of the kernel table. + */ +void +del_static(naddr dst, + naddr mask, + int gone) +{ + struct khash *k; + struct rt_entry *rt; + + /* Just mark it in the table to be deleted next time the kernel + * table is updated. + * If it has already been deleted, mark it as such, and set its + * keep-timer so that it will not be deleted again for a while. + * This lets the operator delete a route added by the daemon + * and add a replacement. + */ + k = kern_find(dst, mask, 0); + if (k != 0) { + k->k_state &= ~(KS_STATIC | KS_DYNAMIC); + k->k_state |= KS_DELETE; + if (gone) { + k->k_state |= KS_DELETED; + k->k_keep = now.tv_sec + K_KEEP_LIM; + } + } + + rt = rtget(dst, mask); + if (rt != 0 && (rt->rt_state & RS_STATIC)) + rtbad(rt); +} + + +/* Delete all routes generated from ICMP Redirects that use a given gateway, + * as well as old redirected routes. + */ +void +del_redirects(naddr bad_gate, + time_t old) +{ + int i; + struct khash *k; + + + for (i = 0; i < KHASH_SIZE; i++) { + for (k = khash_bins[i]; k != 0; k = k->k_next) { + if (!(k->k_state & KS_DYNAMIC) + || (k->k_state & KS_STATIC)) + continue; + + if (k->k_gate != bad_gate + && k->k_redirect_time > old + && !supplier) + continue; + + k->k_state |= KS_DELETE; + k->k_state &= ~KS_DYNAMIC; + need_kern.tv_sec = now.tv_sec; + trace_act("mark redirected %s --> %s for deletion\n", + addrname(k->k_dst, k->k_mask, 0), + naddr_ntoa(k->k_gate)); + } + } +} + + +/* Start the daemon tables. + */ +void +rtinit(void) +{ + extern int max_keylen; + int i; + struct ag_info *ag; + + /* Initialize the radix trees */ + max_keylen = sizeof(struct sockaddr_in); + rn_init(); + rn_inithead((void**)&rhead, 32); + + /* mark all of the slots in the table free */ + ag_avail = ag_slots; + for (ag = ag_slots, i = 1; i < NUM_AG_SLOTS; i++) { + ag->ag_fine = ag+1; + ag++; + } +} + + +#ifdef _HAVE_SIN_LEN +static struct sockaddr_in dst_sock = {sizeof(dst_sock), AF_INET}; +static struct sockaddr_in mask_sock = {sizeof(mask_sock), AF_INET}; +#else +static struct sockaddr_in_new dst_sock = {_SIN_ADDR_SIZE, AF_INET}; +static struct sockaddr_in_new mask_sock = {_SIN_ADDR_SIZE, AF_INET}; +#endif + + +void +set_need_flash(void) +{ + if (!need_flash) { + need_flash = 1; + /* Do not send the flash update immediately. Wait a little + * while to hear from other routers. + */ + no_flash.tv_sec = now.tv_sec + MIN_WAITTIME; + } +} + + +/* Get a particular routing table entry + */ +struct rt_entry * +rtget(naddr dst, naddr mask) +{ + struct rt_entry *rt; + + dst_sock.sin_addr.s_addr = dst; + mask_sock.sin_addr.s_addr = mask; + masktrim(&mask_sock); + rt = (struct rt_entry *)rhead->rnh_lookup(&dst_sock,&mask_sock,rhead); + if (!rt + || rt->rt_dst != dst + || rt->rt_mask != mask) + return 0; + + return rt; +} + + +/* Find a route to dst as the kernel would. + */ +struct rt_entry * +rtfind(naddr dst) +{ + dst_sock.sin_addr.s_addr = dst; + return (struct rt_entry *)rhead->rnh_matchaddr(&dst_sock, rhead); +} + + +/* add a route to the table + */ +void +rtadd(naddr dst, + naddr mask, + naddr gate, /* forward packets here */ + naddr router, /* on the authority of this router */ + int metric, + u_short tag, + u_int state, /* rs_state for the entry */ + struct interface *ifp) +{ + struct rt_entry *rt; + naddr smask; + int i; + struct rt_spare *rts; + + rt = (struct rt_entry *)rtmalloc(sizeof (*rt), "rtadd"); + bzero(rt, sizeof(*rt)); + for (rts = rt->rt_spares, i = NUM_SPARES; i != 0; i--, rts++) + rts->rts_metric = HOPCNT_INFINITY; + + rt->rt_nodes->rn_key = (caddr_t)&rt->rt_dst_sock; + rt->rt_dst = dst; + rt->rt_dst_sock.sin_family = AF_INET; +#ifdef _HAVE_SIN_LEN + rt->rt_dst_sock.sin_len = dst_sock.sin_len; +#endif + if (mask != HOST_MASK) { + smask = std_mask(dst); + if ((smask & ~mask) == 0 && mask > smask) + state |= RS_SUBNET; + } + mask_sock.sin_addr.s_addr = mask; + masktrim(&mask_sock); + rt->rt_mask = mask; + rt->rt_state = state; + rt->rt_gate = gate; + rt->rt_router = router; + rt->rt_time = now.tv_sec; + rt->rt_metric = metric; + rt->rt_poison_metric = HOPCNT_INFINITY; + rt->rt_tag = tag; + rt->rt_ifp = ifp; + rt->rt_seqno = update_seqno; + + if (++total_routes == MAX_ROUTES) + msglog("have maximum (%d) routes", total_routes); + if (TRACEACTIONS) + trace_add_del("Add", rt); + + need_kern.tv_sec = now.tv_sec; + set_need_flash(); + + if (0 == rhead->rnh_addaddr(&rt->rt_dst_sock, &mask_sock, + rhead, rt->rt_nodes)) { + msglog("rnh_addaddr() failed for %s mask=%#x", + naddr_ntoa(dst), mask); + } +} + + +/* notice a changed route + */ +void +rtchange(struct rt_entry *rt, + u_int state, /* new state bits */ + naddr gate, /* now forward packets here */ + naddr router, /* on the authority of this router */ + int metric, /* new metric */ + u_short tag, + struct interface *ifp, + time_t new_time, + char *label) +{ + if (rt->rt_metric != metric) { + /* Fix the kernel immediately if it seems the route + * has gone bad, since there may be a working route that + * aggregates this route. + */ + if (metric == HOPCNT_INFINITY) { + need_kern.tv_sec = now.tv_sec; + if (new_time >= now.tv_sec - EXPIRE_TIME) + new_time = now.tv_sec - EXPIRE_TIME; + } + rt->rt_seqno = update_seqno; + set_need_flash(); + } + + if (rt->rt_gate != gate) { + need_kern.tv_sec = now.tv_sec; + rt->rt_seqno = update_seqno; + set_need_flash(); + } + + state |= (rt->rt_state & RS_SUBNET); + + /* Keep various things from deciding ageless routes are stale. + */ + if (!AGE_RT(state, ifp)) + new_time = now.tv_sec; + + if (TRACEACTIONS) + trace_change(rt, state, gate, router, metric, tag, ifp, + new_time, + label ? label : "Chg "); + + rt->rt_state = state; + rt->rt_gate = gate; + rt->rt_router = router; + rt->rt_metric = metric; + rt->rt_tag = tag; + rt->rt_ifp = ifp; + rt->rt_time = new_time; +} + + +/* check for a better route among the spares + */ +static struct rt_spare * +rts_better(struct rt_entry *rt) +{ + struct rt_spare *rts, *rts1; + int i; + + /* find the best alternative among the spares */ + rts = rt->rt_spares+1; + for (i = NUM_SPARES, rts1 = rts+1; i > 2; i--, rts1++) { + if (BETTER_LINK(rt,rts1,rts)) + rts = rts1; + } + + return rts; +} + + +/* switch to a backup route + */ +void +rtswitch(struct rt_entry *rt, + struct rt_spare *rts) +{ + struct rt_spare swap; + char label[10]; + + + /* Do not change permanent routes */ + if (0 != (rt->rt_state & (RS_MHOME | RS_STATIC | RS_RDISC + | RS_NET_SYN | RS_IF))) + return; + + /* find the best alternative among the spares */ + if (rts == 0) + rts = rts_better(rt); + + /* Do not bother if it is not worthwhile. + */ + if (!BETTER_LINK(rt, rts, rt->rt_spares)) + return; + + swap = rt->rt_spares[0]; + (void)sprintf(label, "Use #%d", rts - rt->rt_spares); + rtchange(rt, rt->rt_state & ~(RS_NET_SYN | RS_RDISC), + rts->rts_gate, rts->rts_router, rts->rts_metric, + rts->rts_tag, rts->rts_ifp, rts->rts_time, label); + *rts = swap; +} + + +void +rtdelete(struct rt_entry *rt) +{ + struct khash *k; + + + if (TRACEACTIONS) + trace_add_del("Del", rt); + + k = kern_find(rt->rt_dst, rt->rt_mask, 0); + if (k != 0) { + k->k_state |= KS_DELETE; + need_kern.tv_sec = now.tv_sec; + } + + dst_sock.sin_addr.s_addr = rt->rt_dst; + mask_sock.sin_addr.s_addr = rt->rt_mask; + masktrim(&mask_sock); + if (rt != (struct rt_entry *)rhead->rnh_deladdr(&dst_sock, &mask_sock, + rhead)) { + msglog("rnh_deladdr() failed"); + } else { + free(rt); + total_routes--; + } +} + + +/* Get rid of a bad route, and try to switch to a replacement. + */ +void +rtbad(struct rt_entry *rt) +{ + /* Poison the route */ + rtchange(rt, rt->rt_state & ~(RS_IF | RS_LOCAL | RS_STATIC), + rt->rt_gate, rt->rt_router, HOPCNT_INFINITY, rt->rt_tag, + 0, rt->rt_time, 0); + + rtswitch(rt, 0); +} + + +/* Junk a RS_NET_SYN or RS_LOCAL route, + * unless it is needed by another interface. + */ +void +rtbad_sub(struct rt_entry *rt) +{ + struct interface *ifp, *ifp1; + struct intnet *intnetp; + u_int state; + + + ifp1 = 0; + state = 0; + + if (rt->rt_state & RS_LOCAL) { + /* Is this the route through loopback for the interface? + * If so, see if it is used by any other interfaces, such + * as a point-to-point interface with the same local address. + */ + for (ifp = ifnet; ifp != 0; ifp = ifp->int_next) { + /* Retain it if another interface needs it. + */ + if (ifp->int_addr == rt->rt_ifp->int_addr) { + state |= RS_LOCAL; + ifp1 = ifp; + break; + } + } + + } + + if (!(state & RS_LOCAL)) { + /* Retain RIPv1 logical network route if there is another + * interface that justifies it. + */ + if (rt->rt_state & RS_NET_SYN) { + for (ifp = ifnet; ifp != 0; ifp = ifp->int_next) { + if ((ifp->int_state & IS_NEED_NET_SYN) + && rt->rt_mask == ifp->int_std_mask + && rt->rt_dst == ifp->int_std_addr) { + state |= RS_NET_SYN; + ifp1 = ifp; + break; + } + } + } + + /* or if there is an authority route that needs it. */ + for (intnetp = intnets; + intnetp != 0; + intnetp = intnetp->intnet_next) { + if (intnetp->intnet_addr == rt->rt_dst + && intnetp->intnet_mask == rt->rt_mask) { + state |= (RS_NET_SYN | RS_NET_INT); + break; + } + } + } + + if (ifp1 != 0 || (state & RS_NET_SYN)) { + rtchange(rt, ((rt->rt_state & ~(RS_NET_SYN | RS_LOCAL)) + | state), + rt->rt_gate, rt->rt_router, rt->rt_metric, + rt->rt_tag, ifp1, rt->rt_time, 0); + } else { + rtbad(rt); + } +} + + +/* Called while walking the table looking for sick interfaces + * or after a time change. + */ +/* ARGSUSED */ +int +walk_bad(struct radix_node *rn, + struct walkarg *w) +{ +#define RT ((struct rt_entry *)rn) + struct rt_spare *rts; + int i; + time_t new_time; + + + /* fix any spare routes through the interface + */ + rts = RT->rt_spares; + for (i = NUM_SPARES; i != 1; i--) { + rts++; + + if (rts->rts_ifp != 0 + && (rts->rts_ifp->int_state & IS_BROKE)) { + /* mark the spare route to be deleted immediately */ + new_time = rts->rts_time; + if (new_time >= now_garbage) + new_time = now_garbage-1; + trace_upslot(RT, rts, rts->rts_gate, + rts->rts_router, 0, + HOPCNT_INFINITY, rts->rts_tag, + new_time); + rts->rts_ifp = 0; + rts->rts_metric = HOPCNT_INFINITY; + rts->rts_time = new_time; + } + } + + /* Deal with the main route + */ + /* finished if it has been handled before or if its interface is ok + */ + if (RT->rt_ifp == 0 || !(RT->rt_ifp->int_state & IS_BROKE)) + return 0; + + /* Bad routes for other than interfaces are easy. + */ + if (0 == (RT->rt_state & (RS_IF | RS_NET_SYN | RS_LOCAL))) { + rtbad(RT); + return 0; + } + + rtbad_sub(RT); + return 0; +#undef RT +} + + +/* Check the age of an individual route. + */ +/* ARGSUSED */ +static int +walk_age(struct radix_node *rn, + struct walkarg *w) +{ +#define RT ((struct rt_entry *)rn) + struct interface *ifp; + struct rt_spare *rts; + int i; + + + /* age all of the spare routes, including the primary route + * currently in use + */ + rts = RT->rt_spares; + for (i = NUM_SPARES; i != 0; i--, rts++) { + + ifp = rts->rts_ifp; + if (i == NUM_SPARES) { + if (!AGE_RT(RT->rt_state, ifp)) { + /* Keep various things from deciding ageless + * routes are stale + */ + rts->rts_time = now.tv_sec; + continue; + } + + /* forget RIP routes after RIP has been turned off. + */ + if (rip_sock < 0) { + rtdelete(RT); + return 0; + } + } + + /* age failing routes + */ + if (age_bad_gate == rts->rts_gate + && rts->rts_time >= now_stale) { + rts->rts_time -= SUPPLY_INTERVAL; + } + + /* trash the spare routes when they go bad */ + if (rts->rts_metric < HOPCNT_INFINITY + && now_garbage > rts->rts_time) { + trace_upslot(RT, rts, rts->rts_gate, + rts->rts_router, rts->rts_ifp, + HOPCNT_INFINITY, rts->rts_tag, + rts->rts_time); + rts->rts_metric = HOPCNT_INFINITY; + } + } + + + /* finished if the active route is still fresh */ + if (now_stale <= RT->rt_time) + return 0; + + /* try to switch to an alternative */ + rtswitch(RT, 0); + + /* Delete a dead route after it has been publically mourned. */ + if (now_garbage > RT->rt_time) { + rtdelete(RT); + return 0; + } + + /* Start poisoning a bad route before deleting it. */ + if (now.tv_sec - RT->rt_time > EXPIRE_TIME) + rtchange(RT, RT->rt_state, RT->rt_gate, RT->rt_router, + HOPCNT_INFINITY, RT->rt_tag, RT->rt_ifp, + RT->rt_time, 0); + return 0; +} + + +/* Watch for dead routes and interfaces. + */ +void +age(naddr bad_gate) +{ + struct interface *ifp; + + + age_timer.tv_sec = now.tv_sec + (rip_sock < 0 + ? NEVER + : SUPPLY_INTERVAL); + + for (ifp = ifnet; ifp; ifp = ifp->int_next) { + /* Check for dead IS_REMOTE interfaces by timing their + * transmissions. + */ + if ((ifp->int_state & IS_REMOTE) + && !(ifp->int_state & IS_PASSIVE) + && (ifp->int_state & IS_ACTIVE)) { + LIM_SEC(age_timer, now.tv_sec+SUPPLY_INTERVAL); + + if (now.tv_sec - ifp->int_act_time > EXPIRE_TIME + && !(ifp->int_state & IS_BROKE)) { + msglog("remote interface %s to %s timed out" + "--turned off", + ifp->int_name, + naddr_ntoa(ifp->int_addr)); + if_bad(ifp); + } + } + } + + /* Age routes. */ + age_bad_gate = bad_gate; + (void)rn_walktree(rhead, walk_age, 0); + + /* Update the kernel routing table. */ + fix_kern(); +} diff --git a/sbin/routed/trace.c b/sbin/routed/trace.c new file mode 100644 index 0000000..8a0b59c --- /dev/null +++ b/sbin/routed/trace.c @@ -0,0 +1,887 @@ +/* + * Copyright (c) 1983, 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if !defined(lint) && !defined(sgi) && !defined(__NetBSD__) +static char sccsid[] = "@(#)trace.c 8.1 (Berkeley) 6/5/93"; +#elif defined(__NetBSD__) +static char rcsid[] = "$NetBSD$"; +#endif +#ident "$Revision: 1.13 $" + +#define RIPCMDS +#include "defs.h" +#include "pathnames.h" +#include +#include +#include + + +#ifdef sgi +/* use *stat64 for files on large filesystems */ +#define stat stat64 +#endif + +#define NRECORDS 50 /* size of circular trace buffer */ + +u_int tracelevel, new_tracelevel; +FILE *ftrace = stdout; /* output trace file */ +static char *tracelevel_pat = "%s\n"; + +char savetracename[MAXPATHLEN+1]; + +static void trace_dump(void); + + +/* convert IP address to a string, but not into a single buffer + */ +char * +naddr_ntoa(naddr a) +{ +#define NUM_BUFS 4 + static int bufno; + static struct { + char str[16]; /* xxx.xxx.xxx.xxx\0 */ + } bufs[NUM_BUFS]; + char *s; + struct in_addr addr; + + addr.s_addr = a; + s = strcpy(bufs[bufno].str, inet_ntoa(addr)); + bufno = (bufno+1) % NUM_BUFS; + return s; +#undef NUM_BUFS +} + + +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 + return s; +} + + +/* On each event, display a time stamp. + * This assumes that 'now' is update once for each event, and + * that at least now.tv_usec changes. + */ +void +lastlog(void) +{ + static struct timeval last; + + if (last.tv_sec != now.tv_sec + || last.tv_usec != now.tv_usec) { + (void)fprintf(ftrace, "-- %s --\n", ts(now.tv_sec)); + last = now; + } +} + + +static void +tmsg(char *p, ...) +{ + va_list args; + + if (ftrace != 0) { + lastlog(); + va_start(args, p); + vfprintf(ftrace, p, args); + fflush(ftrace); + } +} + + +static void +trace_close(void) +{ + int fd; + + + fflush(stdout); + fflush(stderr); + + if (ftrace != 0 + && 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; + } +} + + +void +trace_flush(void) +{ + if (ftrace != 0) { + fflush(ftrace); + if (ferror(ftrace)) + trace_off("tracing off: ", strerror(ferror(ftrace))); + } +} + + +void +trace_off(char *p, ...) +{ + va_list args; + + + if (ftrace != 0) { + lastlog(); + va_start(args, p); + vfprintf(ftrace, p, args); + fflush(ftrace); + } + trace_close(); + + new_tracelevel = tracelevel = 0; +} + + +void +trace_on(char *filename, + int trusted) +{ + struct stat stbuf; + FILE *n_ftrace; + + + /* Given a null filename when tracing is already on, increase the + * debugging level and re-open the file in case it has been unlinked. + */ + if (filename[0] == '\0') { + if (tracelevel != 0) { + new_tracelevel++; + tracelevel_pat = "trace command: %s\n"; + } else if (savetracename[0] == '\0') { + msglog("missing trace file name"); + return; + } + filename = savetracename; + + } else if (!strcmp(filename,"dump/../table")) { + trace_dump(); + return; + + } else { + 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 +#ifdef _PATH_TRACE + && (strncmp(filename, _PATH_TRACE, sizeof(_PATH_TRACE)-1) + || strstr(filename,"../") + || 0 > stat(_PATH_TRACE, &stbuf)) +#endif + && strcmp(filename, savetracename)) { + 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; + } + + tmsg("switch to trace file %s\n", filename); + trace_close(); + 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) + new_tracelevel = 1; + set_tracelevel(); +} + + +/* ARGSUSED */ +void +sigtrace_on(int s) +{ + new_tracelevel++; + tracelevel_pat = "SIGUSR1: %s\n"; +} + + +/* ARGSUSED */ +void +sigtrace_off(int s) +{ + new_tracelevel--; + tracelevel_pat = "SIGUSR2: %s\n"; +} + + +/* Move to next higher level of tracing when -t option processed or + * SIGUSR1 is received. Successive levels are: + * actions + * actions + packets + * actions + packets + contents + */ +void +set_tracelevel(void) +{ + static char *off_msgs[MAX_TRACELEVEL] = { + "Tracing actions stopped", + "Tracing packets stopped", + "Tracing packet contents stopped", + "Tracing kernel changes stopped", + }; + static char *on_msgs[MAX_TRACELEVEL] = { + "Tracing actions started", + "Tracing packets started", + "Tracing packet contents started", + "Tracing kernel changes started", + }; + + + if (new_tracelevel > MAX_TRACELEVEL) { + new_tracelevel = MAX_TRACELEVEL; + if (new_tracelevel == tracelevel) { + tmsg(tracelevel_pat, on_msgs[tracelevel-1]); + return; + } + } + while (new_tracelevel != tracelevel) { + if (new_tracelevel < tracelevel) { + if (--tracelevel == 0) + trace_off(tracelevel_pat, off_msgs[0]); + else + tmsg(tracelevel_pat, off_msgs[tracelevel]); + } else { + if (ftrace == 0) { + if (savetracename[0] != '\0') + trace_on(savetracename, 1); + else + ftrace = stdout; + } + tmsg(tracelevel_pat, on_msgs[tracelevel++]); + } + } + tracelevel_pat = "%s\n"; +} + + +/* 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 */ +#define NUM_BUFS 4 + static int bufno; + static struct { + char str[15+20]; + } bufs[NUM_BUFS]; + char *s, *sp; + naddr dmask; + int i; + + s = strcpy(bufs[bufno].str, naddr_ntoa(addr)); + bufno = (bufno+1) % NUM_BUFS; + + 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<bits_mask) != 0) { + if ((b & field) == b) { + if (tbl->bits_name[0] != '\0') { + if (c) + (void)putc(c, ftrace); + (void)fprintf(ftrace, "%s", tbl->bits_name); + c = '|'; + } + if (0 == (field &= ~(b | tbl->bits_clear))) + break; + } + tbl++; + } + if (field != 0 && tbl->bits_name != 0) { + if (c) + (void)putc(c, ftrace); + (void)fprintf(ftrace, tbl->bits_name, field); + c = '|'; + } + + if (c != '<' || force) + (void)fputs("> ", ftrace); +} + + +static char * +trace_pair(naddr dst, + naddr mask, + char *gate) +{ + static char buf[3*4+3+1+2+3 /* "xxx.xxx.xxx.xxx/xx-->" */ + +3*4+3+1]; /* "xxx.xxx.xxx.xxx" */ + int i; + + i = sprintf(buf, "%-16s-->", addrname(dst, mask, 0)); + (void)sprintf(&buf[i], "%-*s", 15+20-MAX(20,i), gate); + return buf; +} + + +void +trace_if(char *act, + struct interface *ifp) +{ + if (!TRACEACTIONS || ftrace == 0) + return; + + lastlog(); + (void)fprintf(ftrace, "%s interface %-4s ", act, ifp->int_name); + (void)fprintf(ftrace, "%-15s-->%-15s ", + naddr_ntoa(ifp->int_addr), + addrname(htonl((ifp->int_if_flags & IFF_POINTOPOINT) + ? ifp->int_dstaddr + : ifp->int_net), + ifp->int_mask, 1)); + if (ifp->int_metric != 0) + (void)fprintf(ftrace, "metric=%d ", ifp->int_metric); + trace_bits(if_bits, ifp->int_if_flags, 0); + trace_bits(is_bits, ifp->int_state, 0); + (void)fputc('\n',ftrace); +} + + +void +trace_upslot(struct rt_entry *rt, + struct rt_spare *rts, + naddr gate, + naddr router, + struct interface *ifp, + int metric, + u_short tag, + time_t new_time) +{ + if (!TRACEACTIONS || ftrace == 0) + return; + if (rts->rts_gate == gate + && rts->rts_router == router + && rts->rts_metric == metric + && rts->rts_tag == tag) + return; + + lastlog(); + if (rts->rts_gate != RIP_DEFAULT) { + (void)fprintf(ftrace, "Chg #%d %-35s ", + rts - rt->rt_spares, + trace_pair(rt->rt_dst, rt->rt_mask, + 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 ", ntohs(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, " %19s%-16s ", + "", + 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 ", ntohs(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 %-35s ", + rts - rt->rt_spares, + trace_pair(rt->rt_dst, rt->rt_mask, + naddr_ntoa(gate))); + if (gate != router) + (void)fprintf(ftrace, "router=%s ", naddr_ntoa(gate)); + if (tag != 0) + (void)fprintf(ftrace, "tag=%#x ", ntohs(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)); + } +} + + +/* talk about a change made to the kernel table + */ +void +trace_kernel(char *p, ...) +{ + va_list args; + + if (!TRACEKERNEL || ftrace == 0) + return; + + lastlog(); + va_start(args, p); + vfprintf(ftrace, p, args); +} + + +/* display a message if tracing actions + */ +void +trace_act(char *p, ...) +{ + va_list args; + + if (!TRACEACTIONS || ftrace == 0) + return; + + lastlog(); + va_start(args, p); + vfprintf(ftrace, p, args); +} + + +/* display a message if tracing packets + */ +void +trace_pkt(char *p, ...) +{ + va_list args; + + if (!TRACEPACKETS || ftrace == 0) + return; + + lastlog(); + va_start(args, p); + vfprintf(ftrace, p, args); +} + + +void +trace_change(struct rt_entry *rt, + u_int state, + naddr gate, /* forward packets here */ + naddr router, /* on the authority of this router */ + int metric, + u_short tag, + struct interface *ifp, + time_t new_time, + char *label) +{ + if (ftrace == 0) + return; + + if (rt->rt_metric == metric + && rt->rt_gate == gate + && rt->rt_router == router + && rt->rt_state == state + && rt->rt_tag == tag) + return; + + lastlog(); + (void)fprintf(ftrace, "%s %-35s metric=%-2d ", + label, + trace_pair(rt->rt_dst, rt->rt_mask, + 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 ", ntohs(rt->rt_tag)); + trace_bits(rs_bits, rt->rt_state, rt->rt_state != state); + (void)fprintf(ftrace, "%s ", + rt->rt_ifp == 0 ? "?" : rt->rt_ifp->int_name); + (void)fprintf(ftrace, "%s\n", + AGE_RT(rt->rt_state, rt->rt_ifp) ? ts(rt->rt_time) : ""); + + (void)fprintf(ftrace, "%*s %19s%-16s ", + strlen(label), "", "", + 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 ", ntohs(tag)); + if (rt->rt_state != state) + trace_bits(rs_bits, state, 1); + if (rt->rt_ifp != ifp) + (void)fprintf(ftrace, "%s ", + ifp != 0 ? ifp->int_name : "?"); + (void)fprintf(ftrace, "%s\n", + ((rt->rt_time == new_time || !AGE_RT(rt->rt_state, ifp)) + ? "" : ts(new_time))); +} + + +void +trace_add_del(char * action, struct rt_entry *rt) +{ + u_int state = rt->rt_state; + + if (ftrace == 0) + return; + + lastlog(); + (void)fprintf(ftrace, "%s %-35s metric=%-2d ", + action, + trace_pair(rt->rt_dst, rt->rt_mask, + 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 ", ntohs(rt->rt_tag)); + trace_bits(rs_bits, state, 0); + (void)fprintf(ftrace, "%s ", + rt->rt_ifp != 0 ? rt->rt_ifp->int_name : "?"); + (void)fprintf(ftrace, "%s\n", ts(rt->rt_time)); +} + + +/* ARGSUSED */ +static int +walk_trace(struct radix_node *rn, + struct walkarg *w) +{ +#define RT ((struct rt_entry *)rn) + struct rt_spare *rts; + int i, age; + + (void)fprintf(ftrace, " %-35s metric=%-2d ", + trace_pair(RT->rt_dst, RT->rt_mask, + 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 ", + ntohs(RT->rt_tag)); + trace_bits(rs_bits, RT->rt_state, 0); + (void)fprintf(ftrace, "%s ", + RT->rt_ifp == 0 ? "?" : RT->rt_ifp->int_name); + age = AGE_RT(RT->rt_state, RT->rt_ifp); + if (age) + (void)fprintf(ftrace, "%s", ts(RT->rt_time)); + + rts = &RT->rt_spares[1]; + for (i = 1; i < NUM_SPARES; i++, rts++) { + if (rts->rts_metric != HOPCNT_INFINITY) { + (void)fprintf(ftrace,"\n #%d%15s%-16s metric=%-2d ", + i, "", naddr_ntoa(rts->rts_gate), + rts->rts_metric); + if (rts->rts_router != rts->rts_gate) + (void)fprintf(ftrace, "router=%s ", + naddr_ntoa(rts->rts_router)); + if (rts->rts_tag != 0) + (void)fprintf(ftrace, "tag=%#x ", + ntohs(rts->rts_tag)); + (void)fprintf(ftrace, "%s ", + (rts->rts_ifp == 0 + ? "?" : rts->rts_ifp->int_name)); + if (age) + (void)fprintf(ftrace, "%s", ts(rts->rts_time)); + } + } + (void)fputc('\n',ftrace); + + return 0; +} + + +static void +trace_dump(void) +{ + if (ftrace == 0) + return; + lastlog(); + + (void)rn_walktree(rhead, walk_trace, 0); +} + + +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 (!TRACEPACKETS || ftrace == 0) + return; + + lastlog(); + if (msg->rip_cmd >= RIPCMD_MAX + || msg->rip_vers == 0) { + (void)fprintf(ftrace, "%s bad RIPv%d cmd=%d %s" + " %s.%d size=%d\n", + dir1, msg->rip_vers, msg->rip_cmd, dir2, + naddr_ntoa(who->sin_addr.s_addr), + ntohs(who->sin_port), + size); + return; + } + + (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: + n = msg->rip_nets; + 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 ", + (u_int)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", + ntohs(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 (n->n_family != RIP_AF_INET) { + (void)fprintf(ftrace, + "\t(af %d) %-18s mask=%#x", + ntohs(n->n_family), + naddr_ntoa(n->n_dst), + (u_int)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 ", + (u_int)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", + ntohs(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(ftrace, "\tfile=%*s\n", size-4, msg->rip_tracefile); + break; + + case RIPCMD_TRACEOFF: + break; + } +} -- cgit v1.1