From cb4ae035de08615af55c2a8d5b44227eb1807fa5 Mon Sep 17 00:00:00 2001 From: Garrett Wollman Date: Thu, 30 May 1996 16:19:16 +0000 Subject: [PATCH 1/5] SGI's version of routed(8), including support for router discovery, RIP version 2, and better configuration. Thanks to Vernon Schryver at SGI for doing the work to make this available to the free software community. This import is mostly conflicts because of the trailing whitespace issue. Obtained from: Vernon Schryver --- usr.sbin/routed/Makefile | 8 + usr.sbin/routed/Makefile.inc | 1 + usr.sbin/routed/defs.h | 542 +++++++++ usr.sbin/routed/if.c | 1075 ++++++++++++++++ usr.sbin/routed/input.c | 706 +++++++++++ usr.sbin/routed/main.c | 817 +++++++++++++ usr.sbin/routed/output.c | 748 ++++++++++++ usr.sbin/routed/parms.c | 563 +++++++++ usr.sbin/routed/pathnames.h | 43 + usr.sbin/routed/radix.c | 894 ++++++++++++++ usr.sbin/routed/rdisc.c | 965 +++++++++++++++ usr.sbin/routed/routed.8 | 517 ++++++++ usr.sbin/routed/rtquery/Makefile | 6 + usr.sbin/routed/rtquery/rtquery.8 | 79 ++ usr.sbin/routed/rtquery/rtquery.c | 516 ++++++++ usr.sbin/routed/rttrace/Makefile | 6 + usr.sbin/routed/rttrace/rttrace.c | 146 +++ usr.sbin/routed/table.c | 1888 +++++++++++++++++++++++++++++ usr.sbin/routed/trace.c | 713 +++++++++++ 19 files changed, 10233 insertions(+) create mode 100644 usr.sbin/routed/Makefile create mode 100644 usr.sbin/routed/Makefile.inc create mode 100644 usr.sbin/routed/defs.h create mode 100644 usr.sbin/routed/if.c create mode 100644 usr.sbin/routed/input.c create mode 100644 usr.sbin/routed/main.c create mode 100644 usr.sbin/routed/output.c create mode 100644 usr.sbin/routed/parms.c create mode 100644 usr.sbin/routed/pathnames.h create mode 100644 usr.sbin/routed/radix.c create mode 100644 usr.sbin/routed/rdisc.c create mode 100644 usr.sbin/routed/routed.8 create mode 100644 usr.sbin/routed/rtquery/Makefile create mode 100644 usr.sbin/routed/rtquery/rtquery.8 create mode 100644 usr.sbin/routed/rtquery/rtquery.c create mode 100644 usr.sbin/routed/rttrace/Makefile create mode 100644 usr.sbin/routed/rttrace/rttrace.c create mode 100644 usr.sbin/routed/table.c create mode 100644 usr.sbin/routed/trace.c diff --git a/usr.sbin/routed/Makefile b/usr.sbin/routed/Makefile new file mode 100644 index 00000000000..7436a2896d1 --- /dev/null +++ b/usr.sbin/routed/Makefile @@ -0,0 +1,8 @@ +# @(#)Makefile 8.1 (Berkeley) 6/19/93 + +PROG= routed +SRCS= if.c input.c main.c output.c parms.c radix.c rdisc.c table.c trace.c +MAN8= routed.8 +SUBDIR= rtquery rttrace + +.include diff --git a/usr.sbin/routed/Makefile.inc b/usr.sbin/routed/Makefile.inc new file mode 100644 index 00000000000..10fa13f1ed8 --- /dev/null +++ b/usr.sbin/routed/Makefile.inc @@ -0,0 +1 @@ +.include "../../Makefile.inc" diff --git a/usr.sbin/routed/defs.h b/usr.sbin/routed/defs.h new file mode 100644 index 00000000000..02ac4c249f7 --- /dev/null +++ b/usr.sbin/routed/defs.h @@ -0,0 +1,542 @@ +/* + * 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 + */ + +#ident "$Revision: 1.1 $" + +/* Definitions for RIPv2 routing process. + * + * This code is based on the 4.4BSD `routed` daemon, with extensions to + * support: + * RIPv2, including variable length subnet masks. + * Router Discovery + * aggregate routes in the kernel tables. + * aggregate advertised routes. + * maintain spare routes for faster selection of another gateway + * when the current gateway dies. + * timers on routes with second granularity so that selection + * of a new route does not wait 30-60 seconds. + * tolerance of static routes. + * tell the kernel hop counts + * do not advertise if ipforwarding=0 + * + * The vestigual support for other protocols has been removed. There + * is no likelihood that IETF RIPv1 or RIPv2 will ever be used with + * other protocols. The result is far smaller, faster, cleaner, and + * perhaps understandable. + * + * The accumulation of special flags and kludges added over the many + * years have been simplified and integrated. + */ + +#include +#include +#include +#include +#include +#include +#ifdef sgi +#include +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef sgi +struct walkarg; +#endif +#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 +#define naddr u_long +#define _HAVE_SA_LEN +#define _HAVE_SIN_LEN +#endif + +#ifdef sgi +/* Turn on if IP_DROP_MEMBERSHIP and IP_ADD_MEMBERSHIP do not look at + * the dstaddr of point-to-point interfaces. + */ +#define MCAST_PPP_BUG +#endif + +#define NEVER (24*60*60) /* a long time */ +#define EPOCH NEVER /* bias time by this to avoid <0 */ + +/* Scan the kernel regularly to see if any interfaces have appeared or been + * turned off. These must be less than STALE_TIME. + */ +#define CHECK_BAD_INTERVAL 5 /* when an interface is known bad */ +#define CHECK_ACT_INTERVAL 30 /* when advertising */ +#define CHECK_QUIET_INTERVAL 300 /* when not */ + + +/* set times to this to continue poisoning a route */ +#define POISON_SECS (GARBAGE_TIME - POISON_TIME) + +#define NET_S_METRIC 1 /* metric used on synthetic routes */ + +#define LIM_SEC(s,l) ((s).tv_sec = MIN((s).tv_sec, (l))) + + +/* Router Discovery parameters */ +#ifndef sgi +#define INADDR_ALLROUTERS_GROUP 0xe0000002 /* 224.0.0.2 */ +#endif +#define MaxMaxAdvertiseInterval 1800 +#define MinMaxAdvertiseInterval 4 +#define DefMaxAdvertiseInterval 600 +#define DEF_PreferenceLevel 0 +#define MIN_PreferenceLevel 0x80000000 + +#define MAX_INITIAL_ADVERT_INTERVAL 16 +#define MAX_INITIAL_ADVERTS 3 +#define MAX_RESPONSE_DELAY 2 + +#define MAX_SOLICITATION_DELAY 1 +#define SOLICITATION_INTERVAL 3 +#define MAX_SOLICITATIONS 3 + + +/* typical packet buffers */ +union pkt_buf { + char packet[MAXPACKETSIZE+1]; + struct rip rip; +}; + + +/* Main, daemon routing table structure + */ +struct rt_entry { + struct radix_node rt_nodes[2]; /* radix tree glue */ + u_int rt_state; +# define RS_IF 0x001 /* for network interface */ +# define RS_NET_SUB 0x002 /* fake net route for subnet */ +# define RS_NET_HOST 0x004 /* fake net route for host */ +# define RS_NET_INT 0x008 /* authority route */ +# define RS_NET_S (RS_NET_SUB | RS_NET_HOST | RS_NET_INT) +# define RS_SUBNET 0x010 /* subnet route from any source */ +# define RS_LOCAL 0x020 /* loopback for pt-to-pt */ +# define RS_MHOME 0x040 /* from -m */ +# define RS_GW 0x080 /* from -g */ +# define RS_STATIC 0x100 /* from the kernel */ +# define RS_RDISC 0x200 /* from router discovery */ + struct sockaddr_in rt_dst_sock; + naddr rt_mask; + struct rt_spare { + struct interface *rts_ifp; + naddr rts_gate; /* forward packets here */ + naddr rts_router; /* on the authority of this router */ + char rts_metric; + u_short rts_tag; + time_t rts_time; /* timer to junk stale routes */ +#define NUM_SPARES 4 + } rt_spares[NUM_SPARES]; + u_int rt_seqno; /* when last changed */ + char rt_hold_metric; + time_t rt_hold_down; +}; +#define rt_dst rt_dst_sock.sin_addr.s_addr +#define rt_ifp rt_spares[0].rts_ifp +#define rt_gate rt_spares[0].rts_gate +#define rt_router rt_spares[0].rts_router +#define rt_metric rt_spares[0].rts_metric +#define rt_tag rt_spares[0].rts_tag +#define rt_time rt_spares[0].rts_time + +#define HOST_MASK 0xffffffff +#define RT_ISHOST(rt) ((rt)->rt_mask == HOST_MASK) + +/* age all routes that + * are not from -g, -m, or static routes from the kernel + * not unbroken interface routes + * but not broken interfaces + * nor non-passive, remote interfaces that are not aliases + * (i.e. remote & metric=0) + */ +#define AGE_RT(rt,ifp) (0 == ((rt)->rt_state & (RS_GW | RS_MHOME | RS_STATIC \ + | RS_NET_SUB | RS_NET_HOST \ + | RS_RDISC)) \ + && (!((rt)->rt_state & RS_IF) \ + || (ifp) == 0 \ + || (((ifp)->int_state & IS_REMOTE) \ + && !((ifp)->int_state & IS_PASSIVE)))) + +/* true if A is better than B + * Better if + * - A is not a poisoned route + * - and A is not stale + * - and A has a shorter path + * - or is the router speaking for itself + * - or the current route is equal but stale + */ +#define BETTER_LINK(A, B) ((A)->rts_metric != HOPCNT_INFINITY \ + && now_stale <= (A)->rts_time \ + && ((A)->rts_metric < (B)->rts_metric \ + || ((A)->rts_gate == (A)->rts_router \ + && (B)->rts_gate != (B)->rts_router) \ + || ((A)->rts_metric == (B)->rts_metric \ + && now_stale > (B)->rts_time))) + + +/* An "interface" is similar to a kernel ifnet structure, except it also + * handles "logical" or "IS_REMOTE" interfaces (remote gateways). + */ +struct interface { + struct interface *int_next, *int_prev; + char int_name[IFNAMSIZ+15+1]; /* big enough for IS_REMOTE */ + u_short int_index; + naddr int_addr; /* address on this host (net order) */ + naddr int_brdaddr; /* broadcast address (n) */ + naddr int_dstaddr; /* other end of pt-to-pt link (n) */ + naddr int_net; /* working network # (host order)*/ + naddr int_mask; /* working net mask (host order) */ + naddr int_std_addr; /* class A/B/C address (n) */ + naddr int_std_net; /* class A/B/C network (h) */ + naddr int_std_mask; /* class A/B/C netmask (h) */ + naddr int_host_addr; /* RIPv1 net for pt-to-pt link (h) */ + naddr int_host_mask; /* RIPv1 mask for pt-to-pt (h) */ + int int_rip_sock; /* for queries */ + int int_if_flags; /* copied from kernel */ + u_int int_state; + time_t int_act_time; /* last thought healthy */ + time_t int_quiet_time; /* last inactive */ + u_short int_transitions; /* times gone up-down */ + char int_metric; + char int_d_metric; /* for faked default route */ + u_int int_data_ipackets; /* previous network stats */ + u_int int_data_ierrors; + u_int int_data_opackets; + u_int int_data_oerrors; +#ifdef sgi + u_int int_data_odrops; +#endif + time_t int_data_ts; /* timestamp on network stats */ + char int_passwd[RIP_AUTH_PW_LEN]; /* RIPv2 password */ + int int_rdisc_pref; /* advertised rdisc preference */ + int int_rdisc_int; /* MaxAdvertiseInterval */ + int int_rdisc_cnt; + struct timeval int_rdisc_timer; +}; + +#define IS_ALIAS 0x0000001 /* interface alias */ +#define IS_SUBNET 0x0000002 /* interface on subnetted network */ +#define IS_REMOTE 0x0000004 /* interface is not on this machine */ +#define IS_PASSIVE 0x0000008 /* remote and does not do RIP */ +#define IS_EXTERNAL 0x0000010 /* handled by EGP or something */ +#define IS_CHECKED 0x0000020 /* still exists */ +#define IS_ALL_HOSTS 0x0000040 /* in INADDR_ALLHOSTS_GROUP */ +#define IS_ALL_ROUTERS 0x0000080 /* in INADDR_ALLROUTERS_GROUP */ +#define IS_RIP_QUERIED 0x0000100 /* query broadcast */ +#define IS_BROKE 0x0000200 /* seems to be broken */ +#define IS_ACTIVE 0x0000400 /* heard from it at least once */ +#define IS_QUIET 0x0000800 /* have not heard from it recently */ +#define IS_NEED_NET_SUB 0x0001000 /* need RS_NET_SUB route */ +#define IS_NO_AG 0x0002000 /* do not aggregate subnets */ +#define IS_NO_SUPER_AG 0x0004000 /* do not aggregate networks */ +#define IS_NO_RIPV1_IN 0x0008000 /* no RIPv1 input at all */ +#define IS_NO_RIPV2_IN 0x0010000 /* no RIPv2 input at all */ +#define IS_NO_RIP_IN (IS_NO_RIPV2_IN | IS_NO_RIPV2_IN) +#define IS_NO_RIPV1_OUT 0x0020000 /* no RIPv1 output at all */ +#define IS_NO_RIPV2_OUT 0x0040000 /* no RIPv2 output at all */ +#define IS_NO_RIP_OUT (IS_NO_RIPV1_OUT | IS_NO_RIPV2_OUT) +#define IS_NO_ADV_IN 0x0080000 +#define IS_NO_SOL_OUT 0x0100000 /* no solicitations */ +#define IS_SOL_OUT 0x0200000 /* send solicitations */ +#define GROUP_IS_SOL (IS_NO_ADV_IN|IS_NO_SOL_OUT) +#define IS_NO_ADV_OUT 0x0400000 /* do not advertise rdisc */ +#define IS_ADV_OUT 0x0800000 /* advertise rdisc */ +#define GROUP_IS_ADV (IS_NO_ADV_OUT|IS_ADV_OUT) +#define IS_BCAST_RDISC 0x1000000 /* broadcast instead of multicast */ + +#ifdef sgi +#define IFF_UP_RUNNING (IFF_RUNNING|IFF_UP) +#else +#define IFF_UP_RUNNING IFF_UP +#endif +#define iff_alive(f) (((f) & IFF_UP_RUNNING) == IFF_UP_RUNNING) + + +/* Information for aggregating routes */ +#define NUM_AG_SLOTS 32 +struct ag_info { + struct ag_info *ag_fine; /* slot with finer netmask */ + struct ag_info *ag_cors; /* more coarse netmask */ + naddr ag_dst_h; /* destination in host byte order */ + naddr ag_mask; + naddr ag_gate; + char ag_metric; /* metric to be advertised */ + char ag_pref; /* aggregate based on this */ + u_int ag_seqno; + u_short ag_tag; + u_short ag_state; +#define AGS_SUPPRESS 0x01 /* combine with coaser mask */ +#define AGS_PROMOTE 0x002 /* synthesize combined routes */ +#define AGS_REDUN0 0x004 /* redundant, finer routes output */ +#define AGS_REDUN1 0x008 +#define AG_IS_REDUN(state) (((state) & (AGS_REDUN0 | AGS_REDUN1)) \ + == (AGS_REDUN0 | AGS_REDUN1)) +#define AGS_GATEWAY 0x010 /* tell kernel RTF_GATEWAY */ +#define AGS_RIPV2 0x020 /* send only as RIPv2 */ +#define AGS_DEAD 0x080 /* dead--ignore differing gate */ +#define AGS_RDISC 0x100 /* suppresses most routes */ +}; + + +/* parameters for interfaces */ +extern struct parm { + struct parm *parm_next; + char parm_name[IFNAMSIZ+1]; + naddr parm_a_h; + naddr parm_m; + + char parm_d_metric; + u_int parm_int_state; + int parm_rdisc_pref; + int parm_rdisc_int; + char parm_passwd[RIP_AUTH_PW_LEN+1]; +} *parms; + +/* authority for internal networks */ +extern struct intnet { + struct intnet *intnet_next; + naddr intnet_addr; + naddr intnet_mask; +} *intnets; + + + +extern pid_t mypid; +extern naddr myaddr; /* main address of this system */ + +extern int stopint; /* !=0 to stop */ + +extern int sock_max; +extern int rip_sock; /* RIP socket */ +extern struct interface *rip_sock_mcast; /* current multicast interface */ +extern int rt_sock; /* routing socket */ +extern int rt_sock_seqno; +extern int rdisc_sock; /* router-discovery raw socket */ + +extern int seqno; /* sequence number for messages */ +extern int supplier; /* process should supply updates */ +extern int default_gateway; /* 1=advertise default */ +extern int lookforinterfaces; /* 1=probe for new up interfaces */ +extern int supplier_set; /* -s or -q requested */ +extern int ridhosts; /* 1=reduce host routes */ +extern int ppp_noage; /* 1=do not age quiet link routes */ +extern int mhome; /* 1=want multi-homed host route */ +extern int advertise_mhome; /* 1=must continue adverising it */ +extern int auth_ok; /* 1=ignore auth if we do not care */ + +extern struct timeval epoch; /* when started */ +extern struct timeval now; /* current idea of time */ +extern time_t now_stale; +extern time_t now_garbage; + +extern struct timeval next_bcast; /* next general broadcast */ +extern struct timeval age_timer; /* next check of old routes */ +extern struct timeval no_flash; /* inhibit flash update until then */ +extern struct timeval rdisc_timer; /* next advert. or solicitation */ +extern int rdisc_ok; /* using solicited route */ + +extern struct timeval ifinit_timer; /* time to check interfaces */ + +extern naddr loopaddr; /* our address on loopback */ +extern int tot_interfaces; /* # of remote and local interfaces */ +extern int rip_interfaces; /* # of interfaces doing RIP */ +extern struct interface *ifnet; /* all interfaces */ +extern int have_ripv1; /* have a RIPv1 interface */ +extern int need_flash; /* flash update needed */ +extern struct timeval need_kern; /* need to update kernel table */ +extern int update_seqno; /* a route has changed */ + +extern u_int tracelevel, new_tracelevel; +#define MAX_TRACELEVEL 3 +#define TRACEPACKETS (tracelevel >= 2) /* note packets */ +#define TRACECONTENTS (tracelevel >= 3) /* display packet contents */ +#define TRACEACTIONS (tracelevel != 0) +extern FILE *ftrace; /* output trace file */ + +extern struct radix_node_head *rhead; + + +#ifdef sgi +/* Fix conflicts */ +#define dup2(x,y) BSDdup2(x,y) +#endif /* sgi */ + +extern void fix_sock(int, char *); +extern void fix_select(void); +extern void rip_off(void); +extern void rip_on(struct interface *); + +enum output_type {OUT_QUERY, OUT_UNICAST, OUT_BROADCAST, OUT_MULTICAST}; +extern int output(enum output_type, struct sockaddr_in *, + struct interface *, struct rip *, int); +extern void rip_query(void); +extern void rip_bcast(int); +extern void supply(struct sockaddr_in *, struct interface *, + enum output_type, int, int); + +extern void msglog(char *, ...); +#define LOGERR(msg) msglog(msg ": %s", strerror(errno)) +extern void logbad(int, char *, ...); +#define BADERR(dump,msg) logbad(dump,msg ": %s", strerror(errno)) +#ifdef DEBUG +#define DBGERR(dump,msg) BADERR(dump,msg) +#else +#define DBGERR(dump,msg) LOGERR(msg) +#endif +#ifdef MCAST_PPP_BUG +extern void mcasterr(struct interface *, int, char *); +#define MCASTERR(ifp,dump,msg) mcasterr(ifp, dump, "setsockopt(IP_"msg")") +#else +#define MCASTERR(ifp, dump,msg) DBGERR(dump,"setsockopt(IP_" msg ")") +#endif +extern char *naddr_ntoa(naddr); +extern char *saddr_ntoa(struct sockaddr *); + +extern void timevaladd(struct timeval *, struct timeval *); +extern void intvl_random(struct timeval *, u_long, u_long); +extern int getnet(char *, naddr *, naddr *); +extern int gethost(char *, naddr *); +extern void gwkludge(void); +extern char *parse_parms(char *); +extern void get_parms(struct interface *); + +extern void lastlog(void); +extern void trace_on(char *, int); +extern void trace_off(char*, char*); +extern void trace_flush(void); +extern void set_tracelevel(void); +extern void trace_msg(char *, ...); +extern void trace_add_del(char *, struct rt_entry *); +extern void trace_change(struct rt_entry *, u_int, naddr, naddr, int, + u_short, struct interface *, time_t, char *); +extern void trace_if(char *, struct interface *); +extern void trace_upslot(struct rt_entry *, struct rt_spare *, + naddr, naddr, + struct interface *, int, u_short, time_t); +extern void trace_rip(char*, char*, struct sockaddr_in *, + struct interface *, struct rip *, int); +extern char *addrname(naddr, naddr, int); + +extern void rdisc_age(naddr); +extern void set_rdisc_mg(struct interface *, int); +extern void set_supplier(void); +extern void ifbad_rdisc(struct interface *); +extern void ifok_rdisc(struct interface *); +extern void read_rip(int, struct interface *); +extern void read_rt(void); +extern void read_d(void); +extern void rdisc_adv(void); +extern void rdisc_sol(void); + +extern void sigalrm(int); +extern void sigterm(int); + +extern void sigtrace_on(int); +extern void sigtrace_off(int); + +extern void fix_kern(void); +extern void flush_kern(void); +extern void age(naddr); + +extern void ag_flush(naddr, naddr, void (*)(struct ag_info *)); +extern void ag_check(naddr, naddr, naddr, char, char, u_int, + u_short, u_short, void (*)(struct ag_info *)); +extern void del_static(naddr, naddr, int); +extern void del_redirects(naddr, time_t); +extern struct rt_entry *rtget(naddr, naddr); +extern struct rt_entry *rtfind(naddr); +extern void rtinit(void); +extern void rtadd(naddr, naddr, naddr, naddr, + int, u_short, u_int, struct interface *); +extern void rtchange(struct rt_entry *, u_int, naddr,naddr, int, u_short, + struct interface *ifp, time_t, char *); +extern void rtdelete(struct rt_entry *); +extern void rtbad_sub(struct rt_entry *); +extern void rtswitch(struct rt_entry *, struct rt_spare *); +extern void rtbad(struct rt_entry *); + + +extern struct rt_addrinfo rtinfo; +#define S_ADDR(x) (((struct sockaddr_in *)(x))->sin_addr.s_addr) +#define RTINFO_DST rtinfo.rti_info[RTAX_DST] +#define RTINFO_GATE rtinfo.rti_info[RTAX_GATEWAY] +#define RTINFO_NETMASK rtinfo.rti_info[RTAX_NETMASK] +#define RTINFO_IFA rtinfo.rti_info[RTAX_IFA] +#define RTINFO_AUTHOR rtinfo.rti_info[RTAX_AUTHOR] +#define RTINFO_BRD rtinfo.rti_info[RTAX_BRD] +#define RTINFO_IFP ((struct sockaddr_dl *)rtinfo.rti_info[RTAX_IFP]) +void rt_xaddrs(struct sockaddr *, struct sockaddr *, int); + +extern naddr std_mask(naddr); +extern naddr ripv1_mask_net(naddr, struct interface *, struct interface *); +extern naddr ripv1_mask_host(naddr,struct interface *, struct interface *); +#define on_net(tgt, net, mask) ((ntohl(tgt) & mask) == (net & mask)) +extern int check_dst(naddr); +#ifdef sgi +extern int sysctl(int *, u_int, void *, size_t *, void *, size_t); +#endif +extern void addrouteforif(register struct interface *); +extern void ifinit(void); +extern int walk_bad(struct radix_node *, struct walkarg *); +extern int ifok(struct interface *, char *); +extern void ifbad(struct interface *, char *); +extern struct interface *ifwithaddr(naddr, int, int); +extern struct interface *ifwithname(char *, naddr); +extern struct interface *ifwithindex(u_short); +extern struct interface *iflookup(naddr); diff --git a/usr.sbin/routed/if.c b/usr.sbin/routed/if.c new file mode 100644 index 00000000000..a102b2e156c --- /dev/null +++ b/usr.sbin/routed/if.c @@ -0,0 +1,1075 @@ +/* + * 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. + */ + +#ifndef lint +static char sccsid[] = "@(#)if.c 8.1 (Berkeley) 6/5/93"; +#endif /* not lint */ + +#ident "$Revision: 1.1 $" + +#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; /* have a RIPv1 interface */ + + +/* 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_state & IS_REMOTE) && !remote) + continue; + + if (ifp->int_addr == addr + || ((ifp->int_if_flags & IFF_BROADCAST) + && ifp->int_brdaddr == addr + && bcast)) { + if (!(ifp->int_state & IS_BROKE)) + return ifp; + possible = ifp; + } + } + + return possible; +} + + +/* find the interface with a name + */ +struct interface * +ifwithname(char *name, /* enp0 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) + && ((addr == 0 && !(ifp->int_state & IS_ALIAS) + || ifp->int_addr == addr))) + 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; + int twice; + + twice = 0; + maybe = 0; + do { + for (ifp = ifnet; ifp; ifp = ifp->int_next) { + /* finished with an exact match */ + if (ifp->int_addr == addr) + return ifp; + + if ((ifp->int_if_flags & IFF_BROADCAST) + && ifp->int_brdaddr == addr) + return ifp; + + if ((ifp->int_if_flags & IFF_POINTOPOINT) + && ifp->int_dstaddr == addr) + return ifp; + + /* Look for the longest approximate match. + */ + if (on_net(addr, + ifp->int_net, ifp->int_mask) + && (maybe == 0 + || ifp->int_mask > maybe->int_mask)) + maybe = ifp; + } + + if (maybe != 0) + return maybe; + + /* See if an interface has come up since we checked. + */ + ifinit(); + } while (twice++ == 0); + + return 0; +} + + +/* Return the classical netmask for an IP address. + */ +naddr +std_mask(naddr addr) +{ + NTOHL(addr); /* was a host, not a network */ + + if (addr == 0) /* default route has mask 0 */ + return 0; + if (IN_CLASSA(addr)) + return IN_CLASSA_NET; + if (IN_CLASSB(addr)) + return IN_CLASSB_NET; + return IN_CLASSC_NET; +} + + +/* find the netmask that would be inferred by RIPv1 listeners + * on the given interface + */ +naddr +ripv1_mask_net(naddr addr, /* in network byte order */ + struct interface *ifp1, /* as seen on this interface */ + struct interface *ifp2) /* but not this interface */ +{ + naddr mask = 0; + + if (addr == 0) /* default always has 0 mask */ + return mask; + + if (ifp1 != 0) { + /* If the target is that of the associated interface on which + * it arrived, then use the netmask of the interface. + */ + if (on_net(addr, ifp1->int_net, ifp1->int_std_mask)) + mask = ifp1->int_mask; + + } else { + /* Examine all interfaces, and if it the target seems + * to have the same network number of an interface, use the + * netmask of that interface. If there is more than one + * such interface, prefer the interface with the longest + * match. + */ + for (ifp1 = ifnet; ifp1 != 0; ifp1 = ifp1->int_next) { + if (ifp1 != ifp2 + && !(ifp1->int_if_flags & IFF_POINTOPOINT) + && on_net(addr, + ifp1->int_std_net, ifp1->int_std_mask) + && ifp1->int_mask > mask) + mask = ifp1->int_mask; + } + } + + /* Otherwise, make the classic A/B/C guess. + */ + if (mask == 0) + mask = std_mask(addr); + + return mask; +} + + +naddr +ripv1_mask_host(naddr addr, /* in network byte order */ + struct interface *ifp1, /* as seen on this interface */ + struct interface *ifp2) /* but not this interface */ +{ + naddr mask = ripv1_mask_net(addr, ifp1, ifp2); + + + /* If the computed netmask does not mask the address, + * then assume it is a host address + */ + if ((ntohl(addr) & ~mask) != 0) + mask = HOST_MASK; + return mask; +} + + +/* See if a IP address looks reasonable as a destination + */ +int /* 0=bad */ +check_dst(naddr addr) +{ + NTOHL(addr); + + if (IN_CLASSA(addr)) { + if (addr == 0) + return 1; /* default */ + + addr >>= IN_CLASSA_NSHIFT; + return (addr != 0 && addr != IN_LOOPBACKNET); + } + + return (IN_CLASSB(addr) || IN_CLASSC(addr)); +} + + +/* Delete an interface. + */ +static void +ifdel(struct interface *ifp) +{ + struct ip_mreq m; + struct interface *ifp1; + + + if (TRACEACTIONS) + trace_if("Del", ifp); + + if (!(ifp->int_state & IS_ALIAS)) { + if ((ifp->int_if_flags & IFF_MULTICAST) +#ifdef MCAST_PPP_BUG + && !(ifp->int_if_flags & IFF_POINTOPOINT) +#endif + && rip_sock >= 0) { + m.imr_multiaddr.s_addr = htonl(INADDR_RIP_GROUP); + m.imr_interface.s_addr = ((ifp->int_if_flags + & IFF_POINTOPOINT) + ? ifp->int_dstaddr + : ifp->int_addr); + if (setsockopt(rip_sock,IPPROTO_IP,IP_DROP_MEMBERSHIP, + &m, sizeof(m)) < 0) + DBGERR(1,"setsockopt(IP_DROP_MEMBERSHIP" + " RIP)"); + } + if (ifp->int_rip_sock >= 0) { + (void)close(ifp->int_rip_sock); + ifp->int_rip_sock = -1; + fix_select(); + } + set_rdisc_mg(ifp, 0); + } + + /* Zap all routes associated with this interface. + * Assume routes just using gateways beyond this interface will + * timeout naturally, and have probably already died. + */ + ifp->int_state |= IS_BROKE; + (void)rn_walktree(rhead, walk_bad, 0); + ifbad_rdisc(ifp); + + if (!(ifp->int_state & IS_ALIAS)) { + tot_interfaces--; + if (0 == (ifp->int_state & (IS_NO_RIP_IN|IS_PASSIVE))) + rip_interfaces--; + } + + /* unlink and forget the interface */ + if (rip_sock_mcast == ifp) + rip_sock_mcast = 0; + if (ifp->int_next != 0) + ifp->int_next->int_prev = ifp->int_prev; + if (ifp->int_prev != 0) + ifp->int_prev->int_next = ifp->int_next; + else + ifnet = ifp->int_next; + + if (!(ifp->int_state & IS_ALIAS)) { + /* delete aliases of primary interface */ + for (ifp1 = ifnet; 0 != ifp1; ifp1 = ifp1->int_next) { + if (!strcmp(ifp->int_name, ifp1->int_name)) + ifdel(ifp1); + } + } + + free(ifp); +} + + +/* Mark an interface dead. + */ +void +ifbad(struct interface *ifp, + char *pat) +{ + if (ifp->int_state & IS_BROKE) + return; + + if (pat) + msglog(pat, ifp->int_name, naddr_ntoa(ifp->int_addr)); + + LIM_SEC(ifinit_timer, now.tv_sec+CHECK_BAD_INTERVAL); + + ifp->int_state |= IS_BROKE; + ifp->int_state &= ~(IS_RIP_QUERIED | IS_ACTIVE | IS_QUIET); + ifp->int_quiet_time = now.tv_sec - MaxMaxAdvertiseInterval; + ifp->int_data_ts = 0; + + trace_if("Chg", ifp); + + (void)rn_walktree(rhead, walk_bad, 0); + + ifbad_rdisc(ifp); +} + + +/* Mark an interface alive + */ +int /* 1=it was dead */ +ifok(struct interface *ifp, + char *type) +{ + if (!(ifp->int_state & IS_BROKE)) + return 0; + + msglog("%sinterface %s to %s restored", + type, ifp->int_name, naddr_ntoa(ifp->int_addr)); + ifp->int_state &= ~IS_BROKE; + ifp->int_data_ts = 0; + + ifok_rdisc(ifp); + return 1; +} + + +struct rt_addrinfo rtinfo; + +/* disassemble routing message + */ +void +rt_xaddrs(struct sockaddr *sa, + struct sockaddr *lim, + int addrs) +{ + int i; +#ifdef _HAVE_SA_LEN + static struct sockaddr sa_zero; +#endif +#ifdef sgi +#define ROUNDUP(a) ((a) > 0 ? (1 + (((a) - 1) | (sizeof(__uint64_t) - 1))) \ + : sizeof(__uint64_t)) +#else +#define ROUNDUP(a) ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) \ + : sizeof(long)) +#endif + + + bzero(rtinfo.rti_info, sizeof(rtinfo.rti_info)); + rtinfo.rti_addrs = addrs; + + for (i = 0; i < RTAX_MAX && sa < lim; i++) { + if ((addrs & (1 << i)) == 0) + continue; +#ifdef _HAVE_SA_LEN + rtinfo.rti_info[i] = (sa->sa_len != 0) ? sa : &sa_zero; + sa = (struct sockaddr *)((char*)(sa) + + ROUNDUP(sa->sa_len)); +#else + rtinfo.rti_info[i] = sa; + sa = (struct sockaddr *)((char*)(sa) + + ROUNDUP(_FAKE_SA_LEN_DST(sa))); +#endif + } +} + + +/* Find the network interfaces which have configured themselves. + * This must be done regularly, if only for extra addresses + * that come and go on interfaces. + */ +void +ifinit(void) +{ + static char *sysctl_buf; + static size_t sysctl_buf_size = 0; + uint complaints = 0; + static u_int prev_complaints = 0; +# define COMP_NOT_INET 0x01 +# define COMP_WIERD 0x02 +# define COMP_NOADDR 0x04 +# define COMP_NODST 0x08 +# define COMP_NOBADR 0x10 +# define COMP_NOMASK 0x20 +# define COMP_DUP 0x40 + + struct interface ifs, ifs0, *ifp, *ifp1; + struct rt_entry *rt; + size_t needed; + int mib[6]; + struct if_msghdr *ifm; + struct ifa_msghdr *ifam, *ifam_lim, *ifam2; + struct sockaddr_dl *sdl; + int in, ierr, out, oerr; + struct intnet *intnetp; +#ifdef SIOCGIFMETRIC + struct ifreq ifr; +#endif + + + ifinit_timer.tv_sec = now.tv_sec + (supplier + ? CHECK_ACT_INTERVAL + : CHECK_QUIET_INTERVAL); + + /* mark all interfaces so we can get rid of thost that disappear */ + for (ifp = ifnet; 0 != ifp; ifp = ifp->int_next) + ifp->int_state &= ~IS_CHECKED; + + /* Fetch the interface list, without too many system calls + * since we do it repeatedly. + */ + mib[0] = CTL_NET; + mib[1] = PF_ROUTE; + mib[2] = 0; + mib[3] = AF_INET; + mib[4] = NET_RT_IFLIST; + mib[5] = 0; + for (;;) { + if ((needed = sysctl_buf_size) != 0) { + if (sysctl(mib, 6, sysctl_buf,&needed, 0, 0) >= 0) + break; + + if (errno != ENOMEM && errno != EFAULT) + BADERR(1, "ifinit: get interface table"); + free(sysctl_buf); + needed = 0; + } + if (sysctl(mib, 6, 0, &needed, 0, 0) < 0) + BADERR(1,"ifinit: route-sysctl-estimate"); + if ((sysctl_buf = malloc(sysctl_buf_size = needed)) == 0) + BADERR(1,"ifinit: malloc"); + } + + ifam_lim = (struct ifa_msghdr *)(sysctl_buf + needed); + for (ifam = (struct ifa_msghdr *)sysctl_buf; + ifam < ifam_lim; + ifam = ifam2) { + + ifam2 = (struct ifa_msghdr*)((char*)ifam + ifam->ifam_msglen); + + if (ifam->ifam_type == RTM_IFINFO) { + ifm = (struct if_msghdr *)ifam; + bzero(&ifs0, sizeof(ifs0)); + ifs0.int_rip_sock = -1; + ifs0.int_index = ifm->ifm_index; + ifs0.int_if_flags = ifm->ifm_flags; + ifs0.int_state = IS_CHECKED; + ifs0.int_act_time = now.tv_sec; + ifs0.int_quiet_time = (now.tv_sec + - MaxMaxAdvertiseInterval); + ifs0.int_data_ts = now.tv_sec; + ifs0.int_data_ipackets = ifm->ifm_data.ifi_ipackets; + ifs0.int_data_ierrors = ifm->ifm_data.ifi_ierrors; + ifs0.int_data_opackets = ifm->ifm_data.ifi_opackets; + ifs0.int_data_oerrors = ifm->ifm_data.ifi_oerrors; +#ifdef sgi + ifs0.int_data_odrops = ifm->ifm_data.ifi_odrops; +#endif + sdl = (struct sockaddr_dl *)(ifm + 1); + sdl->sdl_data[sdl->sdl_nlen] = 0; + continue; + } + if (ifam->ifam_type != RTM_NEWADDR) { + DBGERR(1,"ifinit: out of sync"); + continue; + } + + rt_xaddrs((struct sockaddr *)(ifam+1), + (struct sockaddr *)ifam2, + ifam->ifam_addrs); + + if (RTINFO_IFA == 0) { + if (iff_alive(ifs.int_if_flags)) { + if (!(prev_complaints & COMP_NOADDR)) + msglog("%s has a bad address", + sdl->sdl_data); + complaints |= COMP_NOADDR; + } + continue; + } + if (RTINFO_IFA->sa_family != AF_INET) { + if (iff_alive(ifs.int_if_flags)) { + if (!(prev_complaints & COMP_NOT_INET)) + trace_msg("%s: not AF_INET", + sdl->sdl_data); + complaints |= COMP_NOT_INET; + } + continue; + } + + bcopy(&ifs0, &ifs, sizeof(ifs0)); + ifs0.int_state |= IS_ALIAS; /* next will be an alias */ + + ifs.int_addr = S_ADDR(RTINFO_IFA); + + if (ifs.int_if_flags & IFF_BROADCAST) { + if (RTINFO_NETMASK == 0) { + if (iff_alive(ifs.int_if_flags)) { + if (!(prev_complaints & COMP_NOMASK)) + msglog("%s has no netmask", + sdl->sdl_data); + complaints |= COMP_NOMASK; + } + continue; + } + ifs.int_mask = ntohl(S_ADDR(RTINFO_NETMASK)); + ifs.int_net = ntohl(ifs.int_addr) & ifs.int_mask; + ifs.int_std_mask = std_mask(ifs.int_addr); + if (ifs.int_mask != ifs.int_std_mask) + ifs.int_state |= IS_SUBNET; + + if (RTINFO_BRD == 0) { + if (iff_alive(ifs.int_if_flags)) { + if (!(prev_complaints & COMP_NOBADR)) + msglog("%s has no" + " broadcast address", + sdl->sdl_data); + complaints |= COMP_NOBADR; + } + continue; + } + ifs.int_brdaddr = S_ADDR(RTINFO_BRD); + + } else if (ifs.int_if_flags & IFF_POINTOPOINT) { + if (RTINFO_BRD == 0 + || RTINFO_BRD->sa_family != AF_INET) { + if (iff_alive(ifs.int_if_flags)) { + if (!(prev_complaints & COMP_NODST)) + msglog("%s has a bad" + " destination address", + sdl->sdl_data); + complaints |= COMP_NODST; + } + continue; + } + ifs.int_dstaddr = S_ADDR(RTINFO_BRD); + ifs.int_net = ntohl(ifs.int_dstaddr); + ifs.int_mask = HOST_MASK; + ifs.int_std_mask = std_mask(ifs.int_dstaddr); + + } else if (ifs.int_if_flags & IFF_LOOPBACK) { + ifs.int_state |= IS_PASSIVE; + ifs.int_dstaddr = ifs.int_addr; + ifs.int_net = ntohl(ifs.int_dstaddr); + ifs.int_mask = HOST_MASK; + ifs.int_std_mask = std_mask(ifs.int_dstaddr); + if (!foundloopback) { + foundloopback = 1; + loopaddr = ifs.int_addr; + } + + } else { + if (TRACEACTIONS + && !(prev_complaints & COMP_WIERD)) + msglog("%s is neither broadcast" + " nor point-to-point nor loopback", + sdl->sdl_data); + complaints |= COMP_WIERD; + continue; + } + ifs.int_std_net = ifs.int_net & ifs.int_std_mask; + ifs.int_std_addr = htonl(ifs.int_std_net); + + /* Use a minimum metric of one. Treat the interface metric + * (default 0) as an increment to the hop count of one. + * + * The metric obtained from the routing socket dump of + * interface addresses is wrong. It is not set by the + * SIOCSIFMETRIC ioctl. + */ +#ifdef SIOCGIFMETRIC + strncpy(ifr.ifr_name, sdl->sdl_data, sizeof(ifr.ifr_name)); + if (ioctl(rt_sock, SIOCGIFMETRIC, &ifr) < 0) { + DBGERR(1, "ioctl(SIOCGIFMETRIC)"); + ifs.int_metric = HOPCNT_INFINITY; + } else { + ifs.int_metric = ifr.ifr_metric+1; + } +#else + ifs.int_metric = ifam->ifam_metric+1; +#endif + if (ifs.int_metric >= HOPCNT_INFINITY) + ifs.int_metric = HOPCNT_INFINITY; + + /* See if this is a familiar interface. + * If so, stop worrying about it if it is the same. + * Start it over if it now is to somewhere else, as happens + * frequently with PPP and SLIP. + */ + ifp = ifwithname(sdl->sdl_data, ((ifs.int_state & IS_ALIAS) + ? ifs.int_addr + : 0)); + if (ifp != 0) { + ifp->int_state |= IS_CHECKED; + + if (0 != ((ifp->int_if_flags ^ ifs.int_if_flags) + & (IFF_BROADCAST + | IFF_LOOPBACK + | IFF_POINTOPOINT + | IFF_MULTICAST)) + || 0 != ((ifp->int_state ^ ifs.int_state) + & IS_ALIAS) + || ifp->int_addr != ifs.int_addr + || ifp->int_brdaddr != ifs.int_brdaddr + || ifp->int_dstaddr != ifs.int_dstaddr + || ifp->int_mask != ifs.int_mask + || ifp->int_metric != ifs.int_metric) { + /* Forget old information about + * a changed interface. + */ + trace_msg("interface %s has changed", + ifp->int_name); + ifdel(ifp); + ifp = 0; + } + } + + if (ifp != 0) { + /* note interfaces that have been turned off + */ + if (!iff_alive(ifs.int_if_flags)) { + if (iff_alive(ifp->int_if_flags)) + ifbad(ifp, "interface %s to %s" + " turned off"); + ifp->int_if_flags &= ~IFF_UP_RUNNING; + continue; + } + /* or that were off and are now ok */ + if (!iff_alive(ifp->int_if_flags)) { + ifp->int_if_flags |= IFF_UP_RUNNING; + (void)ifok(ifp, ""); + } + + /* If it has been long enough, + * see if the interface is broken. + */ + if (now.tv_sec < ifp->int_data_ts+CHECK_BAD_INTERVAL) + continue; + + in = ifs.int_data_ipackets - ifp->int_data_ipackets; + ierr = ifs.int_data_ierrors - ifp->int_data_ierrors; + out = ifs.int_data_opackets - ifp->int_data_opackets; +#ifdef sgi + oerr = (ifs.int_data_oerrors - ifp->int_data_oerrors + + ifs.int_data_odrops - ifp->int_data_odrops); +#else + oerr = ifs.int_data_oerrors - ifp->int_data_oerrors; +#endif + + ifp->int_data_ipackets = ifs.int_data_ipackets; + ifp->int_data_ierrors = ifs.int_data_ierrors; + ifp->int_data_opackets = ifs.int_data_opackets; + ifp->int_data_oerrors = ifs.int_data_oerrors; +#ifdef sgi + ifp->int_data_odrops = ifs.int_data_odrops; +#endif + + /* If the interface just awoke, restart the counters. + */ + if (ifp->int_data_ts == 0) { + ifp->int_data_ts = now.tv_sec; + continue; + } + ifp->int_data_ts = now.tv_sec; + + /* Withhold judgement when the short error + * counters wrap or the interface is reset. + */ + if (ierr < 0 || in < 0 || oerr < 0 || out < 0) + continue; + + /* Withhold judgement when there is no traffic + */ + if (in == 0 && out == 0 && ierr == 0 && oerr == 0) { + if (!(ifp->int_state & IS_QUIET)) { + ifp->int_state |= IS_QUIET; + ifp->int_quiet_time = now.tv_sec; + } + continue; + } + + /* It is bad if input or output is not working + */ + if ((in <= ierr && ierr > 0) + || (out <= oerr && oerr > 0)) { + ifbad(ifp,"interface %s to %s not working"); + continue; + } + + /* otherwise, it is active and healthy + */ + ifp->int_act_time = now.tv_sec; + ifp->int_state &= ~IS_QUIET; + if (ifok(ifp, "")) + addrouteforif(ifp); + continue; + } + + /* See if this new interface duplicates an existing + * interface. + */ + for (ifp = ifnet; 0 != ifp; ifp = ifp->int_next) { + if (ifp->int_addr == ifs.int_addr + && ifp->int_mask == ifs.int_mask) + break; + } + if (ifp != 0) { + if (iff_alive(ifs.int_if_flags)) { + if (!(prev_complaints & COMP_DUP)) + msglog("%s is duplicated by %s at %s", + sdl->sdl_data, ifp->int_name, + naddr_ntoa(ifp->int_addr)); + complaints |= COMP_DUP; + } + continue; + } + + strncpy(ifs.int_name, sdl->sdl_data, + MIN(sizeof(ifs.int_name)-1, sdl->sdl_nlen)); + + get_parms(&ifs); + + ifok_rdisc(&ifs); + + /* create the interface */ + ifp = (struct interface *)malloc(sizeof(*ifp)); + if (ifp == 0) + BADERR(1,"ifinit: out of memory"); + bcopy(&ifs, ifp, sizeof(*ifp)); + if (ifnet != 0) { + ifp->int_next = ifnet; + ifnet->int_prev = ifp; + } + ifnet = ifp; + + /* Count the # of directly connected networks. + */ + if (!(ifp->int_state & IS_ALIAS)) { + if (!(ifp->int_if_flags & IFF_LOOPBACK)) + tot_interfaces++; + if (0 == (ifp->int_state & (IS_NO_RIP_IN|IS_PASSIVE))) + rip_interfaces++; + } + + /* note dead interfaces */ + if (iff_alive(ifs.int_if_flags)) { + set_rdisc_mg(ifp, 1); + } else { + LIM_SEC(ifinit_timer, now.tv_sec+CHECK_BAD_INTERVAL); + ifp->int_state |= IS_BROKE; + } + + if (TRACEACTIONS) + trace_if("Add", ifp); + + rip_on(ifp); + } + + /* If we are multi-homed and have at least one interface + * listening to RIP, then output by default. + */ + if (!supplier_set && rip_interfaces > 1) + set_supplier(); + + /* If we are multi-homed, optionally advertise a route to + * our main address. + */ + if (advertise_mhome + || (tot_interfaces > 1 + && mhome + && (ifp = ifwithaddr(myaddr, 0, 0)) != 0 + && foundloopback)) { + advertise_mhome = 1; + del_static(myaddr, HOST_MASK, 0); + rt = rtget(myaddr, HOST_MASK); + if (rt != 0) { + if (rt->rt_ifp != ifp + || rt->rt_router != loopaddr) { + rtdelete(rt); + rt = 0; + } else { + rtchange(rt, rt->rt_state | RS_MHOME, + loopaddr, loopaddr, + ifp->int_metric, 0, + ifp, rt->rt_time, 0); + } + } + if (rt == 0) + rtadd(myaddr, HOST_MASK, loopaddr, loopaddr, + ifp->int_metric, 0, RS_MHOME, ifp); + } + + for (ifp = ifnet; ifp != 0; ifp = ifp1) { + ifp1 = ifp->int_next; /* because we may delete it */ + + /* Forget any interfaces that have disappeared. + */ + if (!(ifp->int_state & (IS_CHECKED | IS_REMOTE))) { + trace_msg("interface %s has disappeared", + ifp->int_name); + ifdel(ifp); + continue; + } + + if (ifp->int_state & IS_BROKE) + LIM_SEC(ifinit_timer, now.tv_sec+CHECK_BAD_INTERVAL); + + /* If we ever have a RIPv1 interface, assume we always will. + * It might come back if it ever goes away. + */ + if ((ifp->int_state & IS_NO_RIPV2_OUT) + && !(ifp->int_if_flags & IFF_LOOPBACK)) + have_ripv1 = 1; + } + + /* add the authority interfaces */ + for (intnetp = intnets; intnetp!=0; intnetp = intnetp->intnet_next) { + rt = rtget(intnetp->intnet_addr, intnetp->intnet_mask); + if (rt != 0 + && !(rt->rt_state & RS_IF) + && !(rt->rt_state & RS_NET_INT)) { + rtdelete(rt); + rt = 0; + } + if (rt == 0) + rtadd(intnetp->intnet_addr, intnetp->intnet_mask, + loopaddr, loopaddr, + 1, 0, RS_NET_INT, 0); + } + + for (ifp = ifnet; ifp != 0; ifp = ifp->int_next) { + /* Ensure there is always a network route for interfaces, + * after any dead interfaces have been deleted, which + * might affect routes for point-to-point links. + */ + addrouteforif(ifp); + + /* Add routes to the local end of point-to-point interfaces + * using loopback. + */ + if ((ifp->int_if_flags & IFF_POINTOPOINT) + && !(ifp->int_state & IS_REMOTE) + && foundloopback) { + /* Delete any routes to the network address through + * foreign routers. Remove even static routes. + */ + del_static(ifp->int_addr, HOST_MASK, 0); + rt = rtget(ifp->int_addr, HOST_MASK); + if (rt != 0 && rt->rt_router != loopaddr) { + rtdelete(rt); + rt = 0; + } + if (rt != 0) { + if (!(rt->rt_state & RS_LOCAL) + || rt->rt_metric > ifp->int_metric) { + ifp1 = ifp; + } else { + ifp1 = rt->rt_ifp; + } + rtchange(rt,((rt->rt_state | (RS_IF|RS_LOCAL)) + & ~RS_NET_S), + loopaddr, loopaddr, + ifp1->int_metric, 0, + ifp1, rt->rt_time, 0); + } else { + rtadd(ifp->int_addr, HOST_MASK, + loopaddr, loopaddr, + ifp->int_metric, 0, + (RS_IF | RS_LOCAL), ifp); + } + } + } + + prev_complaints = complaints; +} + + +static void +add_net_sub(struct interface *ifp, + naddr dst, + naddr mask, + u_int state) +{ + struct rt_entry *rt; + + rt = rtget(dst, mask); + if (rt != 0) { + if (0 != (rt->rt_state & (RS_STATIC | RS_LOCAL + | RS_MHOME | RS_GW))) + return; + + if ((rt->rt_state & state) != state + || rt->rt_metric != NET_S_METRIC) { + rtchange(rt, rt->rt_state | state, + rt->rt_gate, rt->rt_router, + NET_S_METRIC, rt->rt_tag, + rt->rt_ifp, rt->rt_time, 0); + } + return; + } + + rtadd(dst, mask, ifp->int_addr, ifp->int_addr, + NET_S_METRIC, 0, state, ifp); +} + + +static void +check_net_sub(struct interface *ifp) +{ + struct interface *ifp2; + struct rt_entry *rt; + + /* See if there are any RIPv1 listeners, to determine if + * we need to synthesize a network route for an interface + * on a subnet. + */ + for (ifp2 = ifnet; ifp2; ifp2 = ifp2->int_next) { + if (ifp2 != ifp + && !(ifp->int_state & IS_PASSIVE) + && !(ifp->int_state & IS_NO_RIPV1_OUT) + && !on_net(ifp->int_addr, + ifp2->int_std_net, + ifp2->int_std_mask)) + break; + } + + /* only if running RIPv1 somewhere */ + if (ifp2 != 0) { + ifp->int_state |= IS_NEED_NET_SUB; + add_net_sub(ifp, ifp->int_std_addr, ifp->int_std_mask, + RS_IF | RS_NET_SUB); + + } else { + ifp->int_state &= ~IS_NEED_NET_SUB; + + rt = rtget(ifp->int_std_addr, + ifp->int_std_mask); + if (rt != 0 + && 0 != (rt->rt_state & RS_NET_S) + && rt->rt_ifp == ifp) + rtbad_sub(rt); + } +} + + +/* Add route for interface if not currently installed. + * Create route to other end if a point-to-point link, + * otherwise a route to this (sub)network. + */ +void +addrouteforif(struct interface *ifp) +{ + struct rt_entry *rt; + naddr dst, mask; + + + /* skip sick interfaces + */ + if (ifp->int_state & IS_BROKE) + return; + + /* If the interface on a subnet, then install a RIPv1 route to + * the network as well (unless it is sick). + */ + if (ifp->int_metric != HOPCNT_INFINITY + && !(ifp->int_state & IS_PASSIVE)) { + if (ifp->int_state & IS_SUBNET) { + check_net_sub(ifp); + + } else if ((ifp->int_if_flags & IFF_POINTOPOINT) + && ridhosts) { + + /* The (dis)appearance of other interfaces can change + * the parent (sub)net. + */ + mask = ripv1_mask_net(ifp->int_dstaddr,0,ifp); + if (mask != ifp->int_host_mask) { + rt = rtget(ifp->int_host_addr, + ifp->int_host_mask); + ifp->int_host_addr = htonl(ntohl(ifp->int_dstaddr) + & mask); + ifp->int_host_mask = mask; + if (rt != 0 + && (rt->rt_state & RS_NET_S) + && rt->rt_ifp == ifp) + rtbad_sub(rt); + } + + add_net_sub(ifp, ifp->int_host_addr, + ifp->int_host_mask, + RS_IF | RS_NET_HOST); + } + } + + dst = (0 != (ifp->int_if_flags & (IFF_POINTOPOINT | IFF_LOOPBACK)) + ? ifp->int_dstaddr + : htonl(ifp->int_net)); + + /* We are finished if the right, main interface route exists. + * The right route must be for the right interface, not synthesized + * from a subnet, be a "gateway" or not as appropriate, and so forth. + */ + del_static(dst, ifp->int_mask, 0); + rt = rtget(dst, ifp->int_mask); + if (rt != 0) { + if (rt->rt_ifp != ifp + || rt->rt_router != ifp->int_addr) { + rtdelete(rt); + rt = 0; + } else { + rtchange(rt, ((rt->rt_state | RS_IF) + & ~(RS_NET_S | RS_LOCAL)), + ifp->int_addr, ifp->int_addr, + ifp->int_metric, 0, ifp, now.tv_sec, 0); + } + } + if (rt == 0) { + if (ifp->int_transitions++ > 0) + trace_msg("re-install interface %s", ifp->int_name); + + rtadd(dst, ifp->int_mask, ifp->int_addr, ifp->int_addr, + ifp->int_metric, 0, RS_IF, ifp); + } +} diff --git a/usr.sbin/routed/input.c b/usr.sbin/routed/input.c new file mode 100644 index 00000000000..c80d93f0809 --- /dev/null +++ b/usr.sbin/routed/input.c @@ -0,0 +1,706 @@ +/* + * 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. + */ + +#ifndef lint +static char sccsid[] = "@(#)input.c 8.1 (Berkeley) 6/5/93"; +#endif /* not lint */ + +#ident "$Revision: 1.1 $" + +#include "defs.h" + +static void input(struct sockaddr_in *, struct interface*, struct rip *, int); +static void input_route(struct interface *, naddr, + naddr, naddr, naddr, int, u_short); + + +/* process RIP input + */ +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 != 0) ? ifp : iflookup(from.sin_addr.s_addr), + &inbuf.rip, cc); + } +} + + +/* Process a RIP packet + */ +static void +input(struct sockaddr_in *from, /* received from this IP address */ + struct interface *ifp, + struct rip *rip, + int size) +{ +# define FROM_NADDR from->sin_addr.s_addr + static naddr use_auth, bad_len, bad_mask; + static naddr unk_router, bad_router, bad_nhop; + + struct rt_entry *rt; + struct netinfo *n, *lim; + struct interface *ifp1; + naddr gate, mask, v1_mask, dst, ddst_h; + int i; + + + if (ifp != 0) + ifp->int_state |= IS_ACTIVE; + + if (TRACEPACKETS) + trace_rip("Recv", "from", from, ifp, rip, size); + + if (rip->rip_vers == 0) { + if (from->sin_addr.s_addr != bad_router) + msglog("RIP version 0, cmd %d, packet received" + " from %s", + rip->rip_cmd, naddr_ntoa(FROM_NADDR)); + bad_router = from->sin_addr.s_addr; + return; + } + if (size > MAXPACKETSIZE) { + if (from->sin_addr.s_addr != bad_router) + msglog("packet at least %d bytes too long received" + " from %s", + size-MAXPACKETSIZE, naddr_ntoa(FROM_NADDR)); + bad_router = from->sin_addr.s_addr; + return; + } + + n = rip->rip_nets; + lim = (struct netinfo *)((char*)rip + size); + + /* Notice authentication. + * As required by section 4.2 in RFC 1723, discard authenticated + * RIPv2 messages, but only if configured for that silliness. + * + * RIPv2 authentication is lame, since snooping on the wire makes + * its simple passwords evident. Also, why authenticate queries? + * Why should a RIPv2 implementation with authentication disabled + * not be able to listen to RIPv2 packets with authenication, while + * RIPv1 systems will listen? Crazy! + */ + if (!auth_ok + && rip->rip_vers >= RIPv2 + && n < lim && n->n_family == RIP_AF_AUTH) { + if (from->sin_addr.s_addr != use_auth) + msglog("RIPv2 message with authentication" + " from %s discarded", + naddr_ntoa(FROM_NADDR)); + use_auth = from->sin_addr.s_addr; + if (TRACEPACKETS) + trace_msg("discard authenticated RIPv2 message\n"); + return; + } + + switch (rip->rip_cmd) { + case RIPCMD_REQUEST: + /* did the request come from a router? + */ + if (from->sin_port == htons(RIP_PORT)) { + /* yes, ignore it if RIP is off + */ + if (rip_sock < 0) { + trace_msg("ignore request while RIP off"); + return; + } + + /* Ignore the request if we talking to ourself + * (and not a remote gateway). + */ + ifp1 = ifwithaddr(FROM_NADDR, 0, 0); + if (ifp1 != 0 + && (!(ifp1->int_state & IS_REMOTE) + || ifp->int_metric != 0)) { + if (TRACEPACKETS) + trace_msg("discard our own packet\n"); + return; + } + } + + /* According to RFC 1723, we should ignore unathenticated + * queries. That is too silly to bother with. + */ + + if (n >= lim + || size%sizeof(*n) != sizeof(struct rip)%sizeof(*n)) { + if (from->sin_addr.s_addr != bad_len) + msglog("request of bad length (%d) from %s", + size, naddr_ntoa(FROM_NADDR)); + bad_len = from->sin_addr.s_addr; + } + for (; n < lim; n++) { + n->n_metric = ntohl(n->n_metric); + + /* A single entry with family RIP_AF_UNSPEC and + * metric HOPCNT_INFINITY means "all routes". + * We respond to routers only if we are acting + * as a supplier, or to anyone other than a router + * (i.e. a query). + * + * Answer a query from a stray program with all + * we know. Filter the answer to a query from a + * router in the about same way broadcasts are + * filtered. + * + * Only answer a router if we are a supplier + * to keep an unwary host that is just starting + * from picking us an a router. + */ + if (n->n_family == RIP_AF_UNSPEC + && n->n_metric == HOPCNT_INFINITY + && n == rip->rip_nets + && n+1 == lim) { + if (from->sin_port != htons(RIP_PORT)) { + /* query */ + supply(from, ifp, + OUT_QUERY, 0, rip->rip_vers); + } else if (supplier) { + supply(from, ifp, + OUT_UNICAST, 0, rip->rip_vers); + } + return; + } + + if (n->n_family != RIP_AF_INET) { + if (from->sin_addr.s_addr != bad_router) + msglog("request from %s" + " for unsupported (af %d) %s", + naddr_ntoa(FROM_NADDR), + ntohs(n->n_family), + naddr_ntoa(n->n_dst)); + bad_router = from->sin_addr.s_addr; + return; + } + + dst = n->n_dst; + if (!check_dst(dst)) { + if (from->sin_addr.s_addr != bad_router) + msglog("bad queried destination" + " %s from %s", + naddr_ntoa(dst), + naddr_ntoa(FROM_NADDR)); + bad_router = from->sin_addr.s_addr; + return; + } + + if (rip->rip_vers == RIPv1) { + mask = ripv1_mask_host(dst,ifp,0); + } else { + mask = ntohl(n->n_mask); + if (mask == 0) + mask = ripv1_mask_host(dst,ifp,0); + } + rt = rtget(dst, mask); + if (!rt) + rt = rtfind(n->n_dst); + + n->n_tag = 0; + n->n_nhop = 0; + if (!rt) { + n->n_metric = HOPCNT_INFINITY; + } else { + n->n_metric = (rt->rt_metric + + (ifp ? ifp->int_metric : 1)); + if (n->n_metric > HOPCNT_INFINITY) + n->n_metric = HOPCNT_INFINITY; + if (rip->rip_vers == RIPv1) { + n->n_mask = 0; + } else { + n->n_tag = rt->rt_tag; + if (!ifp + || !on_net(rt->rt_gate, + ifp->int_net, + ifp->int_mask) + || rt->rt_gate != ifp->int_addr) + n->n_nhop = 0; + else + n->n_nhop = rt->rt_gate; + } + } + HTONL(n->n_metric); + } + /* Answer about specific routes. + * Only answer a router if we are a supplier + * to keep an unwary host that is just starting + * from picking us an a router. + */ + rip->rip_cmd = RIPCMD_RESPONSE; + rip->rip_res1 = 0; + if (rip->rip_vers != RIPv1) + rip->rip_vers = RIPv2; + if (from->sin_port != htons(RIP_PORT)) { + /* query */ + (void)output(OUT_QUERY, from, ifp, rip, size); + } else if (supplier) { + (void)output(OUT_UNICAST, from, ifp, rip, size); + } + return; + + case RIPCMD_TRACEON: + case RIPCMD_TRACEOFF: + /* verify message came from a privileged port */ + if (ntohs(from->sin_port) > IPPORT_RESERVED) { + msglog("trace command from untrusted port on %s", + naddr_ntoa(FROM_NADDR)); + return; + } + if (ifp == 0) { + msglog("trace command from unknown router %s", + naddr_ntoa(FROM_NADDR)); + return; + } + if (rip->rip_cmd == RIPCMD_TRACEON) { + rip->rip_tracefile[size-4] = '\0'; + trace_on(rip->rip_tracefile, 0); + } else { + trace_off("tracing turned off by ", + naddr_ntoa(FROM_NADDR)); + } + return; + + case RIPCMD_RESPONSE: + if (size%sizeof(*n) != sizeof(struct rip)%sizeof(*n)) { + if (from->sin_addr.s_addr != bad_len) + msglog("response of bad length (%d) from %s", + size, naddr_ntoa(FROM_NADDR)); + bad_len = from->sin_addr.s_addr; + } + + /* verify message came from a router */ + if (from->sin_port != ntohs(RIP_PORT)) { + if (TRACEPACKETS) + trace_msg("discard response" + " from unknown port\n"); + return; + } + + if (rip_sock < 0) { + if (TRACEPACKETS) + trace_msg("discard response while RIP off"); + return; + } + + /* Are we talking to ourself or a remote gateway? + */ + ifp1 = ifwithaddr(FROM_NADDR, 0, 1); + if (ifp1) { + if (ifp1->int_state & IS_PASSIVE) { + msglog("bogus input from %s on supposedly" + " passive interface %s", + naddr_ntoa(FROM_NADDR), + ifp1->int_name); + + } else if (ifp1->int_state & IS_REMOTE) { + ifp1->int_act_time = now.tv_sec; + if (ifok(ifp1, "remote ")) + addrouteforif(ifp1); + } else if (TRACEPACKETS) { + trace_msg("discard our own packet\n"); + } + return; + } + + /* Check the router from which message originated. We accept + * routing packets from routers directly connected via + * broadcast or point-to-point networks, and from + * those listed in /etc/gateways. + */ + if (!ifp || (ifp->int_state & IS_PASSIVE)) { + if (from->sin_addr.s_addr != unk_router) + msglog("packet from unknown router %s", + naddr_ntoa(FROM_NADDR)); + unk_router = from->sin_addr.s_addr; + return; + } + + /* Check required version + */ + if (((ifp->int_state & IS_NO_RIPV1_IN) + && rip->rip_vers == RIPv1) + || ((ifp->int_state & IS_NO_RIPV2_IN) + && rip->rip_vers != RIPv1)) { + if (TRACEPACKETS) + trace_msg("discard RIPv%d response\n", + rip->rip_vers); + return; + } + + /* Ignore routes via dead interface. + */ + if (ifp->int_state & IS_BROKE) { + if (TRACEPACKETS) + trace_msg("discard response via" + " broken interface %s\n", + ifp->int_name); + return; + } + + /* Authenticate the packet. + */ + if (ifp->int_passwd[0] != '\0' + && (n >= lim + || n->n_family != RIP_AF_AUTH + || ((struct netauth*)n)->a_type != RIP_AUTH_PW + || 0 != bcmp(((struct netauth*)n)->au.au_pw, + ifp->int_passwd, + sizeof(ifp->int_passwd)))) { + if (from->sin_addr.s_addr != use_auth) + msglog("missing authentication from %s", + naddr_ntoa(FROM_NADDR)); + use_auth = from->sin_addr.s_addr; + return; + } + + for (; n < lim; n++) { + if (n->n_family == RIP_AF_AUTH) + continue; + + NTOHL(n->n_metric); + dst = n->n_dst; + if (n->n_family != RIP_AF_INET + && (n->n_family != RIP_AF_UNSPEC + || dst != RIP_DEFAULT)) { + if (from->sin_addr.s_addr != bad_router) + msglog("route from %s to unsupported" + " address family %d," + " destination %s", + naddr_ntoa(FROM_NADDR), + n->n_family, + naddr_ntoa(dst)); + bad_router = from->sin_addr.s_addr; + continue; + } + if (!check_dst(dst)) { + if (from->sin_addr.s_addr != bad_router) + msglog("bad destination %s from %s", + naddr_ntoa(dst), + naddr_ntoa(FROM_NADDR)); + bad_router = from->sin_addr.s_addr; + return; + } + 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 + && rip->rip_vers == RIPv2) { + /* Ignore the route if it points to us */ + if (0 != ifwithaddr(n->n_nhop, 1, 0)) + continue; + + /* Use it only if it is valid. */ + if (on_net(n->n_nhop, + ifp->int_net, ifp->int_mask) + && check_dst(n->n_nhop)) { + gate = n->n_nhop; + } else { + if (bad_nhop != from->sin_addr.s_addr) + msglog("router %s to %s has" + " bad next hop %s", + naddr_ntoa(FROM_NADDR), + naddr_ntoa(dst), + naddr_ntoa(n->n_nhop)); + bad_nhop = from->sin_addr.s_addr; + } + } + + mask = ntohl(n->n_mask); + if (rip->rip_vers == RIPv1 || mask == 0) { + mask = ripv1_mask_host(dst,ifp,0); + } else if ((ntohl(dst) & ~mask) != 0) { + if (bad_mask != from->sin_addr.s_addr) { + msglog("router %s sent bad netmask" + " %#x with %s", + naddr_ntoa(FROM_NADDR), + mask, + naddr_ntoa(dst)); + bad_mask = from->sin_addr.s_addr; + } + continue; + } + v1_mask = (have_ripv1 + ? ripv1_mask_host(dst,0,0) + : mask); + + if (rip->rip_vers == RIPv1) + n->n_tag = 0; + + /* Adjust metric according to incoming interface. + */ + n->n_metric += ifp->int_metric; + if (n->n_metric > HOPCNT_INFINITY) + n->n_metric = HOPCNT_INFINITY; + + /* Recognize and ignore a default route we faked + * which is being sent back to us by a machine with + * broken split-horizon. + */ + if (ifp->int_d_metric != 0 + && dst == RIP_DEFAULT + && n->n_family == RIP_AF_UNSPEC + && n->n_metric > ifp->int_d_metric) + continue; + + /* We can receive aggregated RIPv2 routes via one + * interface that must be broken down before + * they are transmitted by RIPv1 via an interface + * on a subnet. We might receive the same routes + * aggregated otherwise via other RIPv2 interfaces. + * This could cause duplicate routes to be sent on + * the RIPv1 interfaces. "Longest matching variable + * length netmasks" lets RIPv2 listeners understand, + * but breaking down the aggregated routes for RIPv1 + * listeners can produce duplicate routes. + * + * Breaking down aggregated routes here bloats + * the daemon table, but does not hurt the kernel + * table, since routes are always aggregated for + * the kernel. + * + * Notice that this does not break down network + * routes corresponding to subnets. This is part + * of the defense against RS_NET_SUB. + */ + if (0 != (ntohl(dst) & (v1_mask & ~mask))) { + ddst_h = v1_mask & -v1_mask; + i = (v1_mask & ~mask)/ddst_h; + if (i >= 1024) { + /* Punt if we would have to generate + * an unreasonable number of routes. + */ +#ifdef DEBUG + msglog("accept %s from %s as-is" + " instead of as %d routes", + addrname(dst,mask,0), + naddr_ntoa(FROM_NADDR), i); +#endif + i = 0; + } else { + mask = v1_mask; + } + } else { + i = 0; + } + + for (;;) { + input_route(ifp, FROM_NADDR, + dst, mask, gate, + n->n_metric, n->n_tag); + if (i-- == 0) + break; + dst = htonl(ntohl(dst) + ddst_h); + } + } + break; + } +} + + +/* Process a single input route. + */ +static void +input_route(struct interface *ifp, + naddr from, + naddr dst, + naddr mask, + naddr gate, + int metric, + u_short tag) +{ + int i; + struct rt_entry *rt; + struct rt_spare *rts, *rts0; + struct interface *ifp1; + time_t new_time; + + + /* See if the other guy is telling us to send our packets to him. + * Sometimes network routes arrive over a point-to-point link for + * the network containing the address(es) of the link. + * + * If our interface is broken, switch to using the other guy. + */ + ifp1 = ifwithaddr(dst, 1, 1); + if (ifp1 != 0 + && !(ifp1->int_state & IS_BROKE)) + return; + + /* Look for the route in our table. + */ + rt = rtget(dst, mask); + + /* Consider adding the route if we do not already have it. + */ + if (rt == 0) { + /* Usually ignore routes being poisoned. + */ + if (metric == HOPCNT_INFINITY) + return; + + rtadd(dst, mask, gate, from, metric, tag, 0, ifp); + return; + } + + /* We already know about the route. Consider + * this update. + * + * If (rt->rt_state & RS_NET_SUB), then this route + * is the same as a network route we have inferred + * for subnets we know, in order to tell RIPv1 routers + * about the subnets. + * + * It is impossible to tell if the route is coming + * from a distant RIPv2 router with the standard + * netmask because that router knows about the entire + * network, or if it is a round-about echo of a + * synthetic, RIPv1 network route of our own. + * The worst is that both kinds of routes might be + * received, and the bad one might have the smaller + * metric. Partly solve this problem by faking the + * RIPv1 route with a metric that reflects the most + * distant part of the subnet. Also never + * aggregate into such a route. Also keep it + * around as long as the interface exists. + */ + + rts0 = rt->rt_spares; + for (rts = rts0, i = NUM_SPARES; i != 0; i--, rts++) { + if (rts->rts_router == from) + break; + /* Note the worst slot to reuse, + * other than the current slot. + */ + if (rts0 == rt->rt_spares + || BETTER_LINK(rts0, rts)) + rts0 = rts; + } + if (i != 0) { + /* Found the router + */ + int old_metric = rts->rts_metric; + + if (old_metric < HOPCNT_INFINITY) { + new_time = now.tv_sec; + } else { + /* Keep poisoned routes around only long + * enough to pass the poison on. + */ + new_time = rts->rts_time; + if (new_time > now.tv_sec-POISON_SECS) + new_time = now.tv_sec-POISON_SECS; + } + + /* If this is an update for the router we currently prefer, + * then note it. + */ + if (i == NUM_SPARES) { + rtchange(rt,rt->rt_state, gate,rt->rt_router, + metric, tag, ifp, new_time, 0); + /* If the route got worse, check for something better. + */ + if (metric > old_metric) + rtswitch(rt, 0); + return; + } + + /* This is an update for a spare route. + * Finished if the route is unchanged. + */ + if (rts->rts_gate == gate + && old_metric == metric + && rts->rts_tag == tag) { + rts->rts_time = new_time; + return; + } + + } else { + /* The update is for a route we know about, + * but not from a familiar router. + */ + rts = rts0; + + /* Save the route as a spare only if it has + * a better metric than our worst spare. + * This also ignores poisoned routes (those + * with metric HOPCNT_INFINITY). + */ + if (metric >= rts->rts_metric) + return; + + new_time = now.tv_sec; + } + + if (TRACEACTIONS) + trace_upslot(rt, rts, gate, from, ifp, metric, tag, new_time); + rts->rts_gate = gate; + rts->rts_router = from; + rts->rts_metric = metric; + rts->rts_tag = tag; + rts->rts_time = new_time; + rts->rts_ifp = ifp; + + /* try to switch to a better route */ + rtswitch(rt, rts); +} diff --git a/usr.sbin/routed/main.c b/usr.sbin/routed/main.c new file mode 100644 index 00000000000..6776a66c430 --- /dev/null +++ b/usr.sbin/routed/main.c @@ -0,0 +1,817 @@ +/* + * 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. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1983, 1988, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; + +static char sccsid[] = "@(#)main.c 8.1 (Berkeley) 6/5/93"; +#endif /* not lint */ + +#ident "$Revision: 1.2 $" + +#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 ppp_noage; /* do not age routes on quiet links */ +int mhome; /* 1=want multi-homed host route */ +int advertise_mhome; /* 1=must continue adverising it */ +int auth_ok = 1; /* 1=ignore auth if we do not care */ + +struct timeval epoch; /* when started */ +struct timeval clk, prev_clk; +struct timeval now; /* current idea of time */ +time_t now_stale; +time_t now_garbage; + +struct timeval next_bcast; /* next general broadcast */ +struct timeval no_flash = {EPOCH+SUPPLY_INTERVAL}; /* inhibit flash update */ + +fd_set fdbits; +int sock_max; +int rip_sock = -1; /* RIP socket */ +struct interface *rip_sock_mcast; /* current multicast interface */ +int rt_sock; /* routing socket */ +int rt_sock_seqno; + + +static int get_rip_sock(naddr, int); +static void timevalsub(struct timeval *, struct timeval *, struct timeval *); + +int +main(int argc, + char *argv[]) +{ + int n, mib[4], off; + size_t len; + char *p, *q; + struct timeval wtime, wtime2; + time_t dt; + fd_set ibits; + naddr p_addr_h, p_mask; + struct parm *parmp; + struct interface *ifp; + 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_garbage = EPOCH - GARBAGE_TIME; + wtime.tv_sec = 0; + + (void)gethostname(myname, sizeof(myname)-1); + (void)gethost(myname, &myaddr); + + while ((n = getopt(argc, argv, "sqdghmpAtT:F:P:")) != EOF) { + switch (n) { + case 's': + supplier = 1; + supplier_set = 1; + break; + + case 'q': + supplier = 0; + supplier_set = 1; + break; + + case 'd': + background = 0; + break; + + case 'g': + default_gateway = 1; + break; + + case 'h': /* suppress extra host routes */ + ridhosts = 1; + break; + + case 'm': /* advertise host route */ + mhome = 1; /* on multi-homed hosts */ + break; + + case 'p': /* do not age routes on quiet */ + ppp_noage = 1; /* point-to-point links */ + break; + + case 'A': + /* Ignore authentication if we do not care. + * Crazy as it is, that is what RFC 1723 requires. + */ + auth_ok = 0; + break; + + case 't': + new_tracelevel++; + break; + + case 'T': + tracename = optarg; + break; + + case 'F': /* minimal routes for SLIP */ + n = HOPCNT_INFINITY-2; + p = strchr(optarg,','); + if (p && *p != '\0') { + n = (int)strtoul(p+1, &q, 0); + if (*q == '\0' + && n <= HOPCNT_INFINITY-2 + && n >= 1) + *p = '\0'; + } + if (!getnet(optarg, &p_addr_h, &p_mask)) { + msglog("routed: bad network;" + " \"-F %s\" ignored", + optarg); + break; + } + parmp = (struct parm*)malloc(sizeof(*parmp)); + bzero(parmp, sizeof(*parmp)); + parmp->parm_next = parms; + parms = parmp; + parmp->parm_a_h = p_addr_h; + parmp->parm_m = p_mask; + parmp->parm_d_metric = n; + break; + + case 'P': + /* handle arbirary, (usually) per-interface + * parameters. + */ + p = parse_parms(optarg); + if (p != 0) { + msglog("routed: bad \"%s\" in \"%s\"", + p, optarg); + } + break; + + default: + goto usage; + } + } + argc -= optind; + argv += optind; + + if (tracename == 0 && argc >= 1) { + tracename = *argv++; + argc--; + } + if (argc != 0) { +usage: + logbad(0, "usage: routed [-sqdghmpAt] [-T /tracefile]" + " [-F net[,metric]] [-P parms]"); + } + + mib[0] = CTL_NET; + mib[1] = PF_INET; + mib[2] = IPPROTO_IP; + mib[3] = IPCTL_FORWARDING; + len = sizeof(ipforwarding); + if (sysctl(mib, 4, &ipforwarding, &len, 0, 0) < 0) + LOGERR("sysctl(IPCTL_FORWARDING)"); + + if (!ipforwarding) { + if (supplier) + msglog("-s incompatible with ipforwarding=0"); + if (default_gateway) { + msglog("-g incompatible with ipforwarding=0"); + default_gateway = 0; + } + supplier = 0; + supplier_set = 1; + } + if (default_gateway) { + if (supplier_set && !supplier) { + msglog("-g and -q incompatible"); + } else { + supplier = 1; + supplier_set = 1; + } + } + + + /* get into the background */ + if (background) { +#ifdef sgi + if (_daemonize(_DF_NOCHDIR,STDOUT_FILENO,STDERR_FILENO,-1)<0) + BADERR(0, "_daemonize()"); +#else + 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)"); + + /* prepare Router Discovery socket. + */ + rdisc_sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); + if (rdisc_sock < 0) + BADERR(1,"rdisc_sock = socket()"); + fix_sock(rdisc_sock,"rdisc_sock"); + + fix_select(); + + + if (background && new_tracelevel == 0) + ftrace = 0; + if (tracename != 0) { + trace_on(tracename, 1); + if (new_tracelevel == 0) + new_tracelevel = 1; + } + set_tracelevel(); + + /* initialize radix tree */ + rtinit(); + + /* Pick a random part of the second for our output to minimize + * collisions. + * + * Start broadcasting after hearing from other routers, and + * at a random time so a bunch of systems do not get synchronized + * after a power failure. + */ + intvl_random(&next_bcast, EPOCH+MIN_WAITTIME, EPOCH+SUPPLY_INTERVAL); + age_timer.tv_usec = next_bcast.tv_usec; + age_timer.tv_sec = EPOCH+MIN_WAITTIME; + rdisc_timer = next_bcast; + ifinit_timer.tv_usec = next_bcast.tv_usec; + + signal(SIGALRM, sigalrm); + signal(SIGHUP, sigterm); + signal(SIGTERM, sigterm); + signal(SIGINT, sigterm); + signal(SIGUSR1, sigtrace_on); + signal(SIGUSR2, sigtrace_off); + + /* If we have an interface to the wide, wide world, add an entry for + * an Internet default route to the internal tables and advertise it. + * This route is not added to the kernel routes, but this entry + * prevents us from listening to default routes from other + * systems and installing them in the kernel. + */ + if (default_gateway > 0) + rtadd(RIP_DEFAULT, 0, myaddr, myaddr, 1, 0, RS_GW, 0); + + /* Collect an initial view of the world by checking the interface + * configuration and the kludge file. + */ + gwkludge(); + ifinit(); + flush_kern(); + + /* Ask for routes */ + rip_query(); + if (!supplier) + rdisc_sol(); + + /* Loop forever, listening and broadcasting. + */ + for (;;) { + prev_clk = clk; + gettimeofday(&clk, 0); + timevalsub(&wtime2, &clk, &prev_clk); + if (wtime2.tv_sec < 0 + || wtime2.tv_sec > wtime.tv_sec + 5) { + /* Deal with time changes before other housekeeping to + * keep everything straight. + */ + dt = wtime2.tv_sec; + if (dt > 0) + dt -= wtime.tv_sec; + trace_msg("time changed by %d sec\n", dt); + epoch.tv_sec += dt; + } + timevalsub(&now, &clk, &epoch); + now_stale = now.tv_sec - STALE_TIME; + now_garbage = now.tv_sec - GARBAGE_TIME; + + /* deal with interrupts that should affect tracing */ + set_tracelevel(); + + if (stopint != 0) { + if (supplier) { + rip_bcast(0); + rdisc_adv(); + } + trace_off("exiting",""); + exit(stopint | 128); + } + + /* look for new or dead interfaces */ + timevalsub(&wtime, &ifinit_timer, &now); + if (wtime.tv_sec <= 0) { + ifinit(); + rip_query(); + continue; + } + + /* If it is time, then broadcast our routes. + */ + if (supplier || advertise_mhome) { + timevalsub(&wtime2, &next_bcast, &now); + if (wtime2.tv_sec <= 0) { + /* Synchronize the aging and broadcast + * timers to minimize awakenings + */ + age(0); + + rip_bcast(0); + + /* It is desirable to send routing updates + * regularly. So schedule the next update + * 30 seconds after the previous one was + * secheduled, instead of 30 seconds after + * the previous update was finished. + * Even if we just started after discovering + * a 2nd interface or were otherwise delayed, + * pick a 30-second aniversary of the + * original broadcast time. + */ + n = 1 + (0-wtime2.tv_sec)/SUPPLY_INTERVAL; + next_bcast.tv_sec += n*SUPPLY_INTERVAL; + + continue; + } + + if (timercmp(&wtime2, &wtime, <)) + wtime = wtime2; + } + + /* If we need a flash update, either do it now or + * set the delay to end when it is time. + * + * If we are within MIN_WAITTIME seconds of a full update, + * do not bother. + */ + if (need_flash + && supplier + && no_flash.tv_sec+MIN_WAITTIME < next_bcast.tv_sec) { + /* accurate to the millisecond */ + if (!timercmp(&no_flash, &now, >)) + rip_bcast(1); + timevalsub(&wtime2, &no_flash, &now); + if (timercmp(&wtime2, &wtime, <)) + wtime = wtime2; + } + + /* trigger the main aging timer. + */ + timevalsub(&wtime2, &age_timer, &now); + if (wtime2.tv_sec <= 0) { + age(0); + continue; + } + if (timercmp(&wtime2, &wtime, <)) + wtime = wtime2; + + /* update the kernel routing table + */ + timevalsub(&wtime2, &need_kern, &now); + if (wtime2.tv_sec <= 0) { + age(0); + continue; + } + if (timercmp(&wtime2, &wtime, <)) + wtime = wtime2; + + /* take care of router discovery, + * but do it to the millisecond + */ + if (!timercmp(&rdisc_timer, &now, >)) { + rdisc_age(0); + continue; + } + timevalsub(&wtime2, &rdisc_timer, &now); + if (timercmp(&wtime2, &wtime, <)) + wtime = wtime2; + + + /* wait for input or a timer to expire. + */ + ibits = fdbits; + trace_flush(); + n = select(sock_max, &ibits, 0, 0, &wtime); + if (n <= 0) { + if (n < 0 && errno != EINTR && errno != EAGAIN) + BADERR(1,"select"); + continue; + } + + 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; +} + + +/* watch for fatal signals */ +void +sigterm(int sig) +{ + stopint = sig; + (void)signal(sig, SIG_DFL); /* catch it only once */ +} + + +void +fix_select(void) +{ + struct interface *ifp; + + + FD_ZERO(&fdbits); + sock_max = 0; + + FD_SET(rt_sock, &fdbits); + if (sock_max <= rt_sock) + sock_max = rt_sock+1; + if (rip_sock >= 0) { + FD_SET(rip_sock, &fdbits); + if (sock_max <= rip_sock) + sock_max = rip_sock+1; + } + for (ifp = ifnet; 0 != ifp; ifp = ifp->int_next) { + if (ifp->int_rip_sock >= 0) { + FD_SET(ifp->int_rip_sock, &fdbits); + if (sock_max <= ifp->int_rip_sock) + sock_max = ifp->int_rip_sock+1; + } + } + if (rdisc_sock >= 0) { + FD_SET(rdisc_sock, &fdbits); + if (sock_max <= rdisc_sock) + sock_max = rdisc_sock+1; + } +} + + +void +fix_sock(int sock, + char *name) +{ + int on; +#define MIN_SOCKBUF (4*1024) + static int rbuf; + + if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1) + logbad(1, "fcntl(%s) O_NONBLOCK: %s", + name, strerror(errno)); + on = 1; + if (setsockopt(sock, SOL_SOCKET,SO_BROADCAST, + &on,sizeof(on)) < 0) + msglog("setsockopt(%s,SO_BROADCAST): %s", + name, strerror(errno)); + if (rbuf >= MIN_SOCKBUF) { + if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, + &rbuf, sizeof(rbuf)) < 0) + msglog("setsockopt(%s,SO_RCVBUF=%d): %s", + name, rbuf, strerror(errno)); + } else { + for (rbuf = 60*1024; ; rbuf -= 4096) { + if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, + &rbuf, sizeof(rbuf)) == 0) { + trace_msg("RCVBUF=%d\n", rbuf); + break; + } + if (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) { + trace_msg("turn off RIP\n"); + + (void)close(rip_sock); + rip_sock = -1; + + /* get non-broadcast sockets to listen to queries. + */ + for (ifp = ifnet; ifp != 0; ifp = ifp->int_next) { + if (ifp->int_rip_sock < 0 + && !(ifp->int_state & IS_ALIAS)) { + addr = ((ifp->int_if_flags & IFF_POINTOPOINT) + ? ifp->int_dstaddr + : ifp->int_addr); + ifp->int_rip_sock = get_rip_sock(addr, 0); + } + } + + fix_select(); + + age(0); + } +} + + +/* Prepare socket used for RIP. + */ +void +rip_on(struct interface *ifp) +{ + struct ip_mreq m; + + + if (rip_sock >= 0) { + if (ifp != 0 + && 0 == (ifp->int_state & (IS_NO_RIP_IN|IS_PASSIVE)) + && (ifp->int_if_flags & IFF_MULTICAST) +#ifdef MCAST_PPP_BUG + && !(ifp->int_if_flags & IFF_POINTOPOINT) +#endif + && !(ifp->int_state & IS_ALIAS)) { + m.imr_multiaddr.s_addr = htonl(INADDR_RIP_GROUP); + m.imr_interface.s_addr = ((ifp->int_if_flags + & IFF_POINTOPOINT) + ? ifp->int_dstaddr + : ifp->int_addr); + if (setsockopt(rip_sock,IPPROTO_IP, IP_ADD_MEMBERSHIP, + &m, sizeof(m)) < 0) + DBGERR(1,"setsockopt(IP_ADD_MEMBERSHIP RIP)"); + } + return; + } + + if (rip_interfaces > 0 && !rdisc_ok) { + trace_msg("turn on RIP\n"); + + /* Close all of the query sockets so that we can open + * the main socket. SO_REUSEPORT is not a solution, + * since that would let two daemons bind to the broadcast + * socket. + */ + for (ifp = ifnet; ifp != 0; ifp = ifp->int_next) { + if (ifp->int_rip_sock >= 0) { + (void)close(ifp->int_rip_sock); + ifp->int_rip_sock = -1; + } + } + + rip_sock = get_rip_sock(INADDR_ANY, 1); + rip_sock_mcast = 0; + + /* Do not advertise anything until we have heard something + */ + if (next_bcast.tv_sec < now.tv_sec+MIN_WAITTIME) + next_bcast.tv_sec = now.tv_sec+MIN_WAITTIME; + + for (ifp = ifnet; ifp != 0; ifp = ifp->int_next) { + if ((ifp->int_state & IS_NO_RIP_IN) != IS_NO_RIP_IN) + ifp->int_state &= ~IS_RIP_QUERIED; + + if ((ifp->int_if_flags & IFF_MULTICAST) + && !(ifp->int_state & IS_ALIAS)) { + m.imr_multiaddr.s_addr = htonl(INADDR_RIP_GROUP); + m.imr_interface.s_addr = ifp->int_addr; + if (setsockopt(rip_sock, IPPROTO_IP, + IP_ADD_MEMBERSHIP, + &m, sizeof(m)) < 0) + DBGERR(1,"setsockopt(IP_ADD_MEMBERSHIP RIP)"); + } + } + + ifinit_timer.tv_sec = now.tv_sec; + + fix_select(); + + } else if (ifp != 0 + && ifp->int_rip_sock < 0 + && !(ifp->int_state & IS_ALIAS)) { + /* RIP is off, so ensure there are sockets on which + * to listen for queries. + */ + ifp->int_rip_sock = get_rip_sock(ifp->int_addr, 0); + + fix_select(); + } +} + + +/* 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/usr.sbin/routed/output.c b/usr.sbin/routed/output.c new file mode 100644 index 00000000000..59c46842ddc --- /dev/null +++ b/usr.sbin/routed/output.c @@ -0,0 +1,748 @@ +/* + * 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. + */ + +#ifndef lint +static char sccsid[] = "@(#)output.c 8.1 (Berkeley) 6/5/93"; +#endif /* not lint */ + +#ident "$Revision: 1.1 $" + +#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 { /* for each buffer */ + struct rip *buf; + struct netinfo *n; + struct netinfo *base; + struct netinfo *lim; + enum output_type type; + } v2, mcast; + char metric; /* adjust metrics by interface */ + int npackets; + int state; +#define WS_ST_FLASH 0x01 /* send only changed routes */ +#define WS_ST_RIP2_SAFE 0x02 /* send RIPv2 safe for RIPv1 */ +#define WS_ST_RIP2_ALL 0x04 /* full featured RIPv2 */ +#define WS_ST_AG 0x08 /* ok to aggregate subnets */ +#define WS_ST_SUPER_AG 0x10 /* ok to aggregate networks */ +#define WS_ST_QUERY 0x20 /* responding to a query */ +#define WS_ST_TO_ON_NET 0x40 /* sending onto one of our nets */ +#define WS_ST_DEFAULT 0x80 /* faking a default */ +} ws; + +/* A buffer for what can be heard by both RIPv1 and RIPv2 listeners */ +union pkt_buf ripv2_buf; + +/* Another for only RIPv2 listeners */ +union pkt_buf rip_mcast_buf; + + + +/* Send the contents of the global buffer via the non-multicast socket + */ +int /* <0 on failure */ +output(enum output_type type, + struct sockaddr_in *dst, /* send to here */ + struct interface *ifp, + struct rip *buf, + int size) /* this many bytes */ +{ + struct sockaddr_in sin; + int flags; + char *msg; + int res, serrno; + naddr tgt_mcast; + int soc; + + sin = *dst; + if (sin.sin_port == 0) + sin.sin_port = htons(RIP_PORT); +#ifdef _HAVE_SIN_LEN + if (sin.sin_len == 0) + sin.sin_len = sizeof(sin); +#endif + + soc = rip_sock; + flags = 0; + + switch (type) { + case OUT_QUERY: + msg = "Answer Query"; + if (soc < 0) + soc = ifp->int_rip_sock; + break; + case OUT_UNICAST: + msg = "Send"; + if (soc < 0) + soc = ifp->int_rip_sock; + flags = MSG_DONTROUTE; + break; + case OUT_BROADCAST: + if (ifp->int_if_flags & IFF_POINTOPOINT) { + msg = "Send pt-to-pt"; + } else { + msg = "Send"; + } + flags = MSG_DONTROUTE; + break; + case OUT_MULTICAST: + if (ifp->int_if_flags & IFF_POINTOPOINT) { + msg = "Send pt-to-pt"; + } else { + msg = "Send mcast"; + if (rip_sock_mcast != ifp) { +#ifdef MCAST_PPP_BUG + /* Do not specifiy the primary interface + * explicitly if we have the multicast + * point-to-point kernel bug, since the + * kernel will do the wrong thing if the + * local address of a point-to-point link + * is the same as the address of an ordinary + * interface. + */ + if (ifp->int_addr == myaddr) { + tgt_mcast = 0; + } else +#endif + tgt_mcast = ifp->int_addr; + if (setsockopt(rip_sock, + IPPROTO_IP, IP_MULTICAST_IF, + &tgt_mcast, sizeof(tgt_mcast))) + BADERR(1,"setsockopt(rip_sock," + "IP_MULTICAST_IF)"); + rip_sock_mcast = ifp; + } + sin.sin_addr.s_addr = htonl(INADDR_RIP_GROUP); + } + } + + if (TRACEPACKETS) + trace_rip(msg, "to", &sin, ifp, buf, size); + + res = sendto(soc, buf, size, flags, + (struct sockaddr *)&sin, sizeof(sin)); + if (res < 0) { + serrno = errno; + msglog("sendto(%s%s%s.%d): %s", + ifp != 0 ? ifp->int_name : "", + ifp != 0 ? ", " : "", + inet_ntoa(sin.sin_addr), + ntohs(sin.sin_port), + strerror(errno)); + errno = serrno; + } + + return res; +} + + +/* install authentication if appropriate + */ +static void +set_auth(struct ws_buf *w) +{ + if (ws.ifp != 0 + && ws.ifp->int_passwd[0] != '\0' + && (ws.state & WS_ST_RIP2_SAFE)) { + w->n->n_family = RIP_AF_AUTH; + ((struct netauth*)w->n)->a_type = RIP_AUTH_PW; + bcopy(ws.ifp->int_passwd, ((struct netauth*)w->n)->au.au_pw, + sizeof(((struct netauth*)w->n)->au.au_pw)); + w->n++; + } +} + + +/* Send the buffer + */ +static void +supply_write(struct ws_buf *w) +{ + /* Output multicast only if legal. + * If we would multcast and it would be illegal, then discard the + * packet. + */ + if (w != &ws.mcast + || ((ws.state & WS_ST_RIP2_SAFE) + && (ws.ifp == 0 + || (ws.ifp->int_if_flags & IFF_MULTICAST)))) { + if (output(w->type, &ws.to, ws.ifp, w->buf, + ((char *)w->n - (char*)w->buf)) < 0 + && ws.ifp != 0) + ifbad(ws.ifp, 0); + ws.npackets++; + } + + bzero(w->n = w->base, sizeof(*w->n)*NETS_LEN); + + if (w->buf->rip_vers == RIPv2) + set_auth(w); +} + + +/* put an entry into the packet + */ +static void +supply_out(struct ag_info *ag) +{ + int i; + naddr mask, v1_mask, s_mask, dst_h, ddst_h; + struct ws_buf *w; + + + /* Skip this route if doing a flash update and it and the routes + * it aggregates have not changed recently. + */ + if (ag->ag_seqno <= update_seqno + && (ws.state & WS_ST_FLASH)) + return; + + dst_h = ag->ag_dst_h; + mask = ag->ag_mask; + v1_mask = ripv1_mask_host(htonl(dst_h), + (ws.state & WS_ST_TO_ON_NET) ? ws.ifp : 0, + 0); + s_mask = std_mask(htonl(dst_h)); + i = 0; + + /* If we are sending RIPv2 packets that cannot (or must not) be + * heard by RIPv1 listeners, do not worry about sub- or supernets. + * Subnets (from other networks) can only be sent via multicast. + */ + if ((ws.state & WS_ST_RIP2_ALL) + || ((ag->ag_state & AGS_RIPV2) + && v1_mask != mask)) { + w = &ws.mcast; /* use the multicast-only buffer */ + + } else { + w = &ws.v2; + + /* Convert supernet route into corresponding set of network + * routes for RIPv1, but leave non-contiguous netmasks + * to ag_check(). + */ + if (v1_mask > mask + && mask + (mask & -mask) == 0) { + ddst_h = v1_mask & -v1_mask; + i = (v1_mask & ~mask)/ddst_h; + + if (i >= 1024) { + /* Punt if we would have to generate an + * unreasonable number of routes. + */ +#ifdef DEBUG + msglog("sending %s to %s as-is instead" + " of as %d routes", + addrname(htonl(dst_h),mask,0), + naddr_ntoa(ws.to.sin_addr.s_addr), i); +#endif + i = 0; + + } else { + mask = v1_mask; + } + } + } + + do { + w->n->n_family = RIP_AF_INET; + w->n->n_dst = htonl(dst_h); + w->n->n_metric = stopint ? HOPCNT_INFINITY : ag->ag_metric; + HTONL(w->n->n_metric); + if (w->buf->rip_vers == RIPv2) { + w->n->n_nhop = ag->ag_gate; + if ((ws.state & WS_ST_RIP2_ALL) + || mask != s_mask) + w->n->n_mask = htonl(mask); + w->n->n_tag = ag->ag_tag; + } + dst_h += ddst_h; + + if (++w->n >= w->lim) + supply_write(w); + } while (i-- != 0); +} + + +/* supply one route from the table + */ +/* ARGSUSED */ +static int +walk_supply(struct radix_node *rn, + struct walkarg *w) +{ +#define RT ((struct rt_entry *)rn) + u_short ags; + char metric, pref; + naddr dst, gate; + + /* Do not advertise the loopback interface + * or external remote interfaces + */ + if (RT->rt_ifp != 0 + && ((RT->rt_ifp->int_if_flags & IFF_LOOPBACK) + || (RT->rt_ifp->int_state & IS_EXTERNAL))) + return 0; + + /* Do not send a route back to where it came from, except in + * response to a query. This is "split-horizon". + * + * That means not advertising back to the same network + * and so via the same interface. + */ + if (RT->rt_ifp == ws.ifp && ws.ifp != 0 + && !(ws.state & WS_ST_QUERY) + && (ws.state & WS_ST_TO_ON_NET) + && !(RT->rt_state & RS_IF)) + return 0; + + dst = RT->rt_dst; + + /* If being quiet about our ability to forward, then + * do not say anything except our own host number, + * unless responding to a query. + */ + if (!supplier + && (!mhome || myaddr != dst) + && !(ws.state & WS_ST_QUERY)) + return 0; + + ags = 0; + + /* do not override the fake default route */ + if (dst == RIP_DEFAULT + && (ws.state & WS_ST_DEFAULT)) + return 0; + + if (RT_ISHOST(RT)) { + /* We should always aggregate the host routes + * for the local end of our point-to-point links. + * If we are suppressing host routes, then do so. + */ + if ((RT->rt_state & RS_LOCAL) + || ridhosts) + ags |= AGS_SUPPRESS; + + } else if (ws.state & WS_ST_AG) { + /* Aggregate network routes, if we are allowed. + */ + ags |= AGS_SUPPRESS; + + /* Generate supernets if allowed. + * If we can be heard by RIPv1 systems, we will + * later convert back to ordinary nets. This unifies + * dealing with received supernets. + */ + if ((RT->rt_state & RS_SUBNET) + || (ws.state & WS_ST_SUPER_AG)) + ags |= AGS_PROMOTE; + } + + /* Never aggregate our own interfaces, + * or the host route for multi-homed servers. + */ + if (0 != (RT->rt_state & (RS_IF | RS_MHOME))) + ags &= ~(AGS_SUPPRESS | AGS_PROMOTE); + + + if (RT->rt_state & RS_SUBNET) { + /* Do not send authority routes into the subnet, + * or when RIP is off. + */ + if ((RT->rt_state & RS_NET_INT) + && (on_net(dst, ws.to_net, ws.to_mask) + || rip_sock < 0)) + return 0; + + /* Do not send RIPv1 advertisements of subnets to + * other networks. + * + * If possible, multicast them by RIPv2. + */ + if (!(ws.state & WS_ST_RIP2_ALL) + && !on_net(dst, ws.to_std_net, ws.to_std_mask)) + ags |= AGS_RIPV2; + + } else if (RT->rt_state & RS_NET_SUB) { + /* do not send synthetic network routes if no RIPv1 + * listeners might hear. + */ + if (ws.state & WS_ST_RIP2_ALL) + return 0; + + /* Do not send synthetic network routes on the real subnet */ + if (on_net(dst, ws.to_std_net, ws.to_std_mask)) + return 0; + } + + /* forget synthetic routes when RIP is off */ + if (rip_sock < 0 && 0 != (RT->rt_state & RS_NET_S)) + return 0; + + + /* Adjust outgoing metric by the cost of the link. + * Interface routes have already been adjusted. + */ + pref = metric = RT->rt_metric + ws.metric; + if (metric >= HOPCNT_INFINITY) { + metric = HOPCNT_INFINITY; + pref = ((RT->rt_hold_down > now.tv_sec) + ? RT->rt_hold_metric + : metric); + } + + /* Advertise the next hop if this is not a route for one + * of our interfaces and the next hop is on the same + * network as the target. + */ + if ((ws.state & WS_ST_RIP2_SAFE) + && !(RT->rt_state & RS_IF) + && ((ws.state & WS_ST_QUERY) + || (on_net(RT->rt_gate, ws.ifp->int_net, ws.ifp->int_mask) + && RT->rt_gate != ws.ifp->int_addr))) { + gate = RT->rt_gate; + } else { + gate = 0; + } + + ag_check(dst, RT->rt_mask, gate, metric, pref, + RT->rt_seqno, RT->rt_tag, ags, supply_out); + return 0; +#undef RT +} + + +/* Supply dst with the contents of the routing tables. + * If this won't fit in one packet, chop it up into several. + */ +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; + int metric; + + + ws.state = 0; + + ws.to = *dst; + ws.to_std_mask = std_mask(ws.to.sin_addr.s_addr); + ws.to_std_net = ntohl(ws.to.sin_addr.s_addr) & ws.to_std_mask; + + if (ifp != 0) { + ws.to_mask = ifp->int_mask; + ws.to_net = ifp->int_net; + if (on_net(ws.to.sin_addr.s_addr, ws.to_net, ws.to_mask)) + ws.state |= WS_ST_TO_ON_NET; + + } else { + ws.to_mask = ripv1_mask_net(ws.to.sin_addr.s_addr, 0, 0); + ws.to_net = ntohl(ws.to.sin_addr.s_addr) & ws.to_mask; + rt = rtfind(dst->sin_addr.s_addr); + if (rt) + ifp = rt->rt_ifp; + } + + ws.npackets = 0; + if (flash) + ws.state |= WS_ST_FLASH; + if (type == OUT_QUERY) + ws.state |= WS_ST_QUERY; + + if ((ws.ifp = ifp) == 0) { + ws.metric = 0; + } else { + /* Adjust the advertised metric by the outgoing interface + * metric, but reduced by 1 to avoid counting this hop + * twice. + */ + ws.metric = ifp->int_metric; + if (ws.metric > 0) + ws.metric--; + } + + if (init) { + init = 0; + + bzero(&ripv2_buf, sizeof(ripv2_buf)); + ripv2_buf.rip.rip_cmd = RIPCMD_RESPONSE; + ws.v2.buf = &ripv2_buf.rip; + ws.v2.base = &ws.v2.buf->rip_nets[0]; + ws.v2.lim = ws.v2.base + NETS_LEN; + + bzero(&rip_mcast_buf, sizeof(rip_mcast_buf)); + rip_mcast_buf.rip.rip_cmd = RIPCMD_RESPONSE; + rip_mcast_buf.rip.rip_vers = RIPv2; + ws.mcast.buf = &rip_mcast_buf.rip; + ws.mcast.base = &ws.mcast.buf->rip_nets[0]; + ws.mcast.lim = ws.mcast.base + NETS_LEN; + } + ripv2_buf.rip.rip_vers = vers; + + ws.v2.type = type; + ws.v2.n = ws.v2.base; + set_auth(&ws.v2); + + ws.mcast.type = (type == OUT_BROADCAST) ? OUT_MULTICAST : type; + ws.mcast.n = ws.mcast.base; + set_auth(&ws.mcast); + + if (vers == RIPv2) { + ws.state |= WS_ST_RIP2_SAFE; + /* full RIPv2 only if cannot be heard by RIPv1 listeners */ + if (type != OUT_BROADCAST) + ws.state |= WS_ST_RIP2_ALL; + if (!(ws.state & WS_ST_TO_ON_NET)) { + ws.state |= (WS_ST_AG | WS_ST_SUPER_AG); + } else if (ws.ifp == 0 || !(ws.ifp->int_state & IS_NO_AG)) { + ws.state |= WS_ST_AG; + if (type != OUT_BROADCAST + && (ws.ifp == 0 + || !(ws.ifp->int_state & IS_NO_SUPER_AG))) + ws.state |= WS_ST_SUPER_AG; + } + } + + /* send the routes + */ + if ((metric = ifp->int_d_metric) != 0) { + /* Fake a default route if asked */ + ws.state |= WS_ST_DEFAULT; + + /* Use the metric of a real default, if there is one. + */ + rt = rtget(RIP_DEFAULT, 0); + if (rt != 0 + && rt->rt_metric+ws.metric < metric) + metric = rt->rt_metric+ws.metric; + + if (metric < HOPCNT_INFINITY) + ag_check(0, 0, 0, metric,metric, 0, 0, 0, supply_out); + } + (void)rn_walktree(rhead, walk_supply, 0); + ag_flush(0,0,supply_out); + + /* Flush the packet buffers */ + if (ws.v2.n != ws.v2.base + && (ws.v2.n > ws.v2.base+1 + || ws.v2.n->n_family != RIP_AF_AUTH)) + supply_write(&ws.v2); + if (ws.mcast.n != ws.mcast.base + && (ws.mcast.n > ws.mcast.base+1 + || ws.mcast.n->n_family != RIP_AF_AUTH)) + supply_write(&ws.mcast); + + /* If we sent nothing and this is an answer to a query, send + * an empty buffer. + */ + if (ws.npackets == 0 + && (ws.state & WS_ST_QUERY)) + supply_write(&ws.v2); +} + + +/* send all of the routing table or just do a flash update + */ +void +rip_bcast(int flash) +{ +#ifdef _HAVE_SIN_LEN + static struct sockaddr_in dst = {sizeof(dst), AF_INET}; +#else + static struct sockaddr_in dst = {AF_INET}; +#endif + struct interface *ifp; + enum output_type type; + int vers; + struct timeval rtime; + + + need_flash = 0; + intvl_random(&rtime, MIN_WAITTIME, MAX_WAITTIME); + no_flash = rtime; + timevaladd(&no_flash, &now); + + if (rip_sock < 0) + return; + + trace_msg("send %s and inhibit dynamic updates for %.3f sec\n", + flash ? "dynamic update" : "all routes", + rtime.tv_sec + ((float)rtime.tv_usec)/1000000.0); + + for (ifp = ifnet; ifp != 0; ifp = ifp->int_next) { + /* skip interfaces not doing RIP, those already queried, + * and aliases. Do try broken interfaces to see + * if they have healed. + */ + if (0 != (ifp->int_state & (IS_PASSIVE + | IS_ALIAS))) + continue; + + /* skip turned off interfaces */ + if (!iff_alive(ifp->int_if_flags)) + continue; + + /* Prefer RIPv1 announcements unless RIPv2 is on and + * RIPv2 is off. + */ + if (ifp->int_state & IS_NO_RIPV1_OUT) { + if (ifp->int_state & IS_NO_RIPV2_OUT) + continue; + vers = RIPv2; + } else { + vers = RIPv1; + } + + if (ifp->int_if_flags & IFF_BROADCAST) { + /* ordinary, hardware interface */ + dst.sin_addr.s_addr = ifp->int_brdaddr; + /* if RIPv1 is not turned off, then broadcast so + * that RIPv1 listeners can hear. + */ + if (!(ifp->int_state & IS_NO_RIPV1_OUT)) { + type = OUT_BROADCAST; + } else { + type = OUT_MULTICAST; + } + + } else if (ifp->int_if_flags & IFF_POINTOPOINT) { + /* point-to-point hardware interface */ + dst.sin_addr.s_addr = ifp->int_dstaddr; + type = OUT_UNICAST; + + } else { + /* remote interface */ + dst.sin_addr.s_addr = ifp->int_addr; + type = OUT_UNICAST; + } + + supply(&dst, ifp, type, flash, vers); + } + + update_seqno++; /* all routes are up to date */ +} + + +/* Ask for routes + * Do it only once to an interface, and not even after the interface + * was broken and recovered. + */ +void +rip_query(void) +{ +#ifdef _HAVE_SIN_LEN + static struct sockaddr_in dst = {sizeof(dst), AF_INET}; +#else + static struct sockaddr_in dst = {AF_INET}; +#endif + struct interface *ifp; + struct rip buf; + enum output_type type; + + + if (rip_sock < 0) + return; + + bzero(&buf, sizeof(buf)); + + for (ifp = ifnet; ifp; ifp = ifp->int_next) { + /* skip interfaces not doing RIP, those already queried, + * and aliases. Do try broken interfaces to see + * if they have healed. + */ + if (0 != (ifp->int_state & (IS_RIP_QUERIED + | IS_PASSIVE + | IS_ALIAS))) + continue; + + /* skip turned off interfaces */ + if (!iff_alive(ifp->int_if_flags)) + continue; + + /* prefer RIPv2 queries */ + if (ifp->int_state & IS_NO_RIPV2_OUT) { + if (ifp->int_state & IS_NO_RIPV1_OUT) + continue; + buf.rip_vers = RIPv1; + } else { + buf.rip_vers = RIPv2; + } + + buf.rip_cmd = RIPCMD_REQUEST; + buf.rip_nets[0].n_family = RIP_AF_UNSPEC; + buf.rip_nets[0].n_metric = htonl(HOPCNT_INFINITY); + + if (ifp->int_if_flags & IFF_BROADCAST) { + /* ordinary, hardware interface */ + dst.sin_addr.s_addr = ifp->int_brdaddr; + /* if RIPv1 is not turned off, then broadcast so + * that RIPv1 listeners can hear. + */ + if (!(ifp->int_state & IS_NO_RIPV1_OUT)) { + type = OUT_BROADCAST; + } else { + type = OUT_MULTICAST; + } + + } else if (ifp->int_if_flags & IFF_POINTOPOINT) { + /* point-to-point hardware interface */ + dst.sin_addr.s_addr = ifp->int_dstaddr; + type = OUT_UNICAST; + + } else { + /* remote interface */ + dst.sin_addr.s_addr = ifp->int_addr; + type = OUT_UNICAST; + } + + ifp->int_state |= IS_RIP_QUERIED; + if (output(type, &dst, ifp, &buf, sizeof(buf)) < 0) + ifbad(ifp,0); + } +} diff --git a/usr.sbin/routed/parms.c b/usr.sbin/routed/parms.c new file mode 100644 index 00000000000..1fc0b857b58 --- /dev/null +++ b/usr.sbin/routed/parms.c @@ -0,0 +1,563 @@ +/* + * 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. + */ + +#ifndef lint +static char sccsid[] = "@(#)if.c 8.1 (Berkeley) 6/5/93"; +#endif /* not lint */ + +#ident "$Revision: 1.1 $" + +#include "defs.h" +#include "pathnames.h" + + +struct parm *parms; +struct intnet *intnets; + + +/* parse a set of parameters for an interface + */ +char * /* 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, *parmp; + 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)); + if (!getnet(&line[7], &intnetp->intnet_addr, + &intnetp->intnet_mask)) { + free(intnetp); + return line; + } + HTONL(intnetp->intnet_addr); + 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; + + } else if (PARS("no_super_ag")) { + parm.parm_int_state |= IS_NO_SUPER_AG; + + } else if (PARS("no_rip")) { + parm.parm_int_state |= (IS_NO_RIPV1_IN + | IS_NO_RIPV2_IN + | IS_NO_RIPV1_OUT + | IS_NO_RIPV2_OUT); + + } 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("no_ripv2_out")) { + parm.parm_int_state |= IS_NO_RIPV2_OUT; + + } 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_rdisc")) { + CKF((GROUP_IS_SOL|GROUP_IS_ADV), + IS_NO_ADV_IN | IS_NO_SOL_OUT | IS_NO_ADV_OUT); + + } 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 (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 (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-2) + break; + + } else { + tgt = tok; + break; + } + } + if (tgt != 0) + return tgt; + + if (parm.parm_int_state & IS_NO_ADV_IN) + parm.parm_int_state |= IS_NO_SOL_OUT; + + /* check for duplicate specification */ + for (parmp = parms; parmp != 0; parmp = parmp->parm_next) { + if (strcmp(parm.parm_name, parmp->parm_name)) + continue; + if (parmp->parm_a_h != (parm.parm_a_h & parmp->parm_m) + && parm.parm_a_h != (parmp->parm_a_h & parm.parm_m)) + continue; + + if (strcmp(parmp->parm_passwd, parm.parm_passwd) + || (0 != (parm.parm_int_state & GROUP_IS_SOL) + && 0 != (parmp->parm_int_state & GROUP_IS_SOL) + && 0 != ((parm.parm_int_state ^ parmp->parm_int_state) + && GROUP_IS_SOL)) + || (0 != (parm.parm_int_state & GROUP_IS_ADV) + && 0 != (parmp->parm_int_state & GROUP_IS_ADV) + && 0 != ((parm.parm_int_state ^ parmp->parm_int_state) + && GROUP_IS_ADV)) + || (parm.parm_rdisc_pref != 0 + && parmp->parm_rdisc_pref != 0 + && parm.parm_rdisc_pref != parmp->parm_rdisc_pref) + || (parm.parm_rdisc_int != 0 + && parmp->parm_rdisc_int != 0 + && parm.parm_rdisc_int != parmp->parm_rdisc_int) + || (parm.parm_d_metric != 0 + && parmp->parm_d_metric != 0 + && parm.parm_d_metric != parmp->parm_d_metric)) + return "duplicate"; + } + + parmp = (struct parm*)malloc(sizeof(*parmp)); + bcopy(&parm, parmp, sizeof(*parmp)); + parmp->parm_next = parms; + parms = parmp; + + return 0; +#undef DELIMS +#undef PARS +#undef PARSE +} + + +/* use configured parameters + */ +void +get_parms(struct interface *ifp) +{ + struct parm *parmp; + + for (parmp = parms; parmp != 0; parmp = parmp->parm_next) { + if ((parmp->parm_a_h == (ntohl(ifp->int_addr) + & parmp->parm_m) + && parmp->parm_name[0] == '\0') + || (parmp->parm_name[0] != '\0' + && !strcmp(ifp->int_name, parmp->parm_name))) { + ifp->int_state |= parmp->parm_int_state; + bcopy(parmp->parm_passwd, ifp->int_passwd, + sizeof(ifp->int_passwd)); + ifp->int_rdisc_pref = parmp->parm_rdisc_pref; + ifp->int_rdisc_int = parmp->parm_rdisc_int; + ifp->int_d_metric = parmp->parm_d_metric; + } + } + + if ((ifp->int_state & IS_NO_RIP_IN) == IS_NO_RIP_IN) + ifp->int_state |= IS_NO_RIP_OUT; + + if (ifp->int_rdisc_int == 0) + ifp->int_rdisc_int = DefMaxAdvertiseInterval; + + if ((ifp->int_state & IS_PASSIVE) + || (ifp->int_state & IS_REMOTE)) + ifp->int_state |= IS_NO_ADV_IN|IS_NO_SOL_OUT|IS_NO_ADV_OUT; + + + if (!(ifp->int_state & IS_PASSIVE)) { + 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; + /* point-to-point links should be passive 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; + } +} + + +/* 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; + + /* notice parameter lines */ + if (strncasecmp("net", lptr, 3) + && strncasecmp("host", lptr, 4)) { + p = parse_parms(lptr); + if (p != 0) + msglog("bad \"%s\" in "_PATH_GATEWAYS + " entry %s", lptr, p); + continue; + } + +/* {net | host} XX[/M] XX gateway XX metric DD [passive | external]\n */ + n = sscanf(lptr, "%4s %129[^ ] gateway" + " %64[^ / ] 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; + } + HTONL(dst); + } 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; + } + + if (!(state & IS_EXTERNAL)) { + /* If we are going to send packets to the gateway, + * it must be reachable using our physical interfaces + */ + if (!rtfind(gate)) { + msglog("unreachable gateway %s in " + _PATH_GATEWAYS" entry %s", + gname, lptr); + continue; + } + + /* Remember to advertise the corresponding logical + * network. + */ + if (netmask != std_mask(dst)) + state |= IS_SUBNET; + } + + parmp = (struct parm*)malloc(sizeof(*parmp)); + bzero(parmp, sizeof(*parmp)); + parmp->parm_next = parms; + parms = parmp; + parmp->parm_a_h = ntohl(dst); + parmp->parm_m = -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_addr == dst + && ifp->int_mask == netmask) + 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); + + if (TRACEACTIONS) + trace_if("Add", ifp); + } +} + + +/* get a network number as a name or a number, with an optional "/xx" + * netmask. + */ +int /* 0=bad */ +getnet(char *name, + naddr *addr_hp, + naddr *maskp) +{ + int i; + struct netent *nentp; + 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; + } + + nentp = getnetbyname(name); + if (nentp != 0) { + in.s_addr = (naddr)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); + } else { + mask = (naddr)strtoul(mname, &p, 0); + if (*p != '\0' || mask > 32) + return 0; + mask = HOST_MASK << (32-mask); + } + + *addr_hp = 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/usr.sbin/routed/pathnames.h b/usr.sbin/routed/pathnames.h new file mode 100644 index 00000000000..9b2c28c0152 --- /dev/null +++ b/usr.sbin/routed/pathnames.h @@ -0,0 +1,43 @@ +/* + * 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 + */ + +#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. + */ +#define _PATH_TRACE "/tmp" diff --git a/usr.sbin/routed/radix.c b/usr.sbin/routed/radix.c new file mode 100644 index 00000000000..7552e0832a5 --- /dev/null +++ b/usr.sbin/routed/radix.c @@ -0,0 +1,894 @@ +/* + * 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. + */ +#include +#include +#include +#include +#include +#include +#define min(a,b) (((a)<(b))?(a):(b)) +#define log(x, msg) syslog(x, msg) +#define panic(s) {log(LOG_ERR,s); exit(1);} + + +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(v_arg, head) + 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(v_arg, head, m_arg) + struct radix_node *head; + void *v_arg, *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(m_arg, n_arg) + void *m_arg, *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(v_arg, head) + 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! + */ + if ((t->rn_flags & RNF_ROOT) && t->rn_dupedkey) + t = t->rn_dupedkey; + return t; +on1: + test = (*cp ^ *cp2) & 0xff; /* find first bit that differs */ + for (b = 7; (test >>= 1) > 0;) + b--; + 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(v, b, nodes) + 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(v_arg, head, dupentry, nodes) + 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; + *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(n_arg, search, skip) + int search, skip; + void *n_arg; +{ + 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(v_arg, n_arg, head, treenodes) + void *v_arg, *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, *tt; + struct radix_node *saved_tt, *top = head->rnh_treetop; + short b = 0, b_leaf; + 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(v_arg, netmask_arg, head) + void *v_arg, *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) +#ifdef _KERNEL + printf("%s %x at %x\n", + "rn_delete: Orphaned Mask", m, x); +#else + syslog(LOG_ERR, "%s %x at %x\n", + "rn_delete: Orphaned Mask", m, x); +#endif + } + } + /* + * 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(h, f, w) + struct radix_node_head *h; + register int (*f)(); + void *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(head, off) + 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() +{ + char *cp, *cplim; +#ifdef KERNEL + struct domain *dom; + + for (dom = domains; dom; dom = dom->dom_next) + if (dom->dom_maxrtkey > max_keylen) + max_keylen = dom->dom_maxrtkey; +#endif + 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/usr.sbin/routed/rdisc.c b/usr.sbin/routed/rdisc.c new file mode 100644 index 00000000000..93c5f8b30d4 --- /dev/null +++ b/usr.sbin/routed/rdisc.c @@ -0,0 +1,965 @@ +/* + * 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. + */ + +#ifndef lint +static char sccsid[] = "@(#)rdisc.c 8.1 (Berkeley) x/y/95"; +#endif /* not lint */ + +#ident "$Revision: 1.1 $" + +#include "defs.h" +#include +#include +#include + +/* router advertisement ICMP packet */ +struct icmp_ad { + u_char icmp_type; /* type of message */ + u_char icmp_code; /* type sub code */ + u_short icmp_cksum; /* ones complement cksum of struct */ + u_char icmp_ad_num; /* # of following router addresses */ + u_char icmp_ad_asize; /* 2--words in each advertisement */ + u_short 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_char icmp_type; /* type of message */ + u_char icmp_code; /* type sub code */ + u_short 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-1)) + +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 (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 : "?", + 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]), ntohl(wp[1])); + wp += p->ad.icmp_ad_asize; + } + (void)fputc('\n',ftrace); + + } else { + trace_msg("%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)); + } +} + + +/* 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 == -1 + || !(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) + DBGERR(1,"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) + DBGERR(1,"IP_ADD_MEMBERSHIP ALLHOSTS"); + 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) + DBGERR(1,"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) + DBGERR(1,"IP_ADD_MEMBERSHIP ALLROUTERS"); + ifp->int_state |= IS_ALL_ROUTERS; + } +} + + +/* start supplying routes + */ +void +set_supplier(void) +{ + struct interface *ifp; + struct dr *drp; + + if (supplier_set) + return; + + trace_msg("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); + } +} + + +/* age discovered routes and find the best one + */ +void +rdisc_age(naddr bad_gate) +{ + time_t sec; + struct dr *drp; + + + if (supplier) { + /* If only adverising, then do only that. */ + 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_msg("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 + */ +void +ifbad_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 +ifok_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_msg("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; + time_t sec; + + + /* 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 routes. + * Routes received over PPP links do not die until + * the link has been active long enough to be certain + * we should have heard from the router. + */ + if (drp->dr_ts + drp->dr_life <= now.tv_sec) { + if (drp->dr_recv_pref == 0 + || !ppp_noage + || !(ifp->int_if_flags & IFF_POINTOPOINT) + || !(ifp->int_state & IS_QUIET) + || (ifp->int_quiet_time + + (sec = MIN(MaxMaxAdvertiseInterval, + drp->dr_life)) <= now.tv_sec)) { + del_rdisc(drp); + continue; + } + + /* If the PPP link is quiet, keep checking + * in case the link becomes active. + * After the link is active, the timer on the + * discovered route might force its deletion. + */ + sec += now.tv_sec+1; + } else { + sec = drp->dr_ts+drp->dr_life+1; + } + LIM_SEC(rdisc_timer, sec); + + /* 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. + */ + if (new_drp == 0 + || new_drp->dr_pref < drp->dr_pref + || (new_drp->dr_pref == drp->dr_pref + && (drp == cur_drp + || (new_drp != cur_drp + && new_drp->dr_life > drp->dr_life)))) + new_drp = drp; + } + + /* 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_msg("turn off Router Discovery\n"); + rdisc_ok = 0; + + if (rt != 0 + && (rt->rt_state & RS_RDISC)) { + rtchange(rt, rt->rt_state, + 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_msg("turn on Router Discovery using" + " %s via %s\n", + naddr_ntoa(new_drp->dr_gate), + new_drp->dr_ifp->int_name); + + rdisc_ok = 1; + rip_off(); + + } else { + trace_msg("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); + } + } + + cur_drp = new_drp; + } +} + + +/* handle a single address in an advertisement + */ +static void +parse_ad(naddr from, + naddr gate, + n_long pref, + int life, + struct interface *ifp) +{ + static naddr bad_gate; + struct dr *drp, *new_drp; + + + NTOHL(gate); + 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) { + if (TRACEPACKETS) + trace_msg("discard our own packet\n"); + return; + } + if (!on_net(gate, ifp->int_net, ifp->int_mask)) { + if (TRACEPACKETS) + trace_msg("discard packet from unreachable net\n"); + return; + } + + /* Convert preference to an unsigned value + * and bias it by the metric of the interface. + */ + pref = ntohl(pref) ^ MIN_PreferenceLevel; + + for (new_drp = drs, drp = drs; drp < &drs[MAX_ADS]; drp++) { + if (drp->dr_ts == 0) { + new_drp = drp; + continue; + } + + if (drp->dr_gate == gate) { + /* Zap an entry we are being told is kaput */ + if (pref == 0 || life == 0) { + drp->dr_recv_pref = 0; + drp->dr_life = 0; + return; + } + new_drp = drp; + break; + } + + /* look for least valueable entry */ + if (new_drp->dr_pref > drp->dr_pref) + new_drp = drp; + } + + /* ignore zap of an entry we do not know about. */ + if (pref == 0 || life == 0) + return; + + new_drp->dr_ifp = ifp; + new_drp->dr_gate = gate; + new_drp->dr_ts = now.tv_sec; + new_drp->dr_life = ntohl(life); + new_drp->dr_recv_pref = pref; + new_drp->dr_pref = PREF(pref,ifp); + + 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; + 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 = "Broadcast"; + sin.sin_addr.s_addr = ifp->int_brdaddr; + } + break; + + case 2: /* multicast */ + msg = "Multicast"; + 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 (setsockopt(rdisc_sock, + IPPROTO_IP, IP_MULTICAST_IF, + &tgt_mcast, sizeof(tgt_mcast))) { + DBGERR(1,"setsockopt(rdisc_sock," + "IP_MULTICAST_IF)"); + return; + } + rdisc_sock_mcast = ifp; + } + flags = 0; + break; + } + + if (TRACEPACKETS) + 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))) { + msglog("sendto(%s%s%s): %s", + ifp != 0 ? ifp->int_name : "", + ifp != 0 ? ", " : "", + inet_ntoa(sin.sin_addr), + strerror(errno)); + if (ifp != 0) + ifbad(ifp, 0); + } +} + + +/* 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 : htonl(ifp->int_rdisc_int*3); + + u.ad.icmp_ad_life = stopint ? 0 : htonl(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, INADDR_ALLHOSTS_GROUP, + (ifp->int_if_flags&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, + INADDR_ALLROUTERS_GROUP, + ((ifp->int_if_flags & 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) { + if (TRACEPACKETS) + msglog("unrecognized ICMP Router" + " %s code=%d from %s to %s\n", + type, p->icmp.icmp_code, + naddr_ntoa(from), naddr_ntoa(to)); + return 0; + } + + if (TRACEPACKETS) + trace_rdisc(act, from, to, ifp, p, len); + + if (ifp == 0 && TRACEPACKETS) + msglog("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_msg("\tdiscard our own packet\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) { + if (TRACEPACKETS) + trace_msg("\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], + 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/usr.sbin/routed/routed.8 b/usr.sbin/routed/routed.8 new file mode 100644 index 00000000000..eac9d5e9657 --- /dev/null +++ b/usr.sbin/routed/routed.8 @@ -0,0 +1,517 @@ +.\" 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 March 1, 1996 +.Dt ROUTED 8 +.Os BSD 4.4 +.Sh NAME +.Nm routed +.Nd network 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 version of the RIPv1 protocol implemented +is based on the RIPv1 protocol implemented in 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 an router, +.Nm +periodically supplies copies +of its routing tables to any directly connected hosts and networks. +It also advertise or solicits default routes using Router Discovery +ICMP messages. +.Pp +When 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 resquesting +network to implement +.Em split-horizon . +Requests from query programs +such as +.Xr rtquery 8 +are answered with the complete table. +.Pp +The routing table maintained by the daemon +includes space for several gateways for each destination +to speed recovery from a failing router. +RIP +.Em response +packets received are used to update the routing tables provided they are +from one of the several currently recognized gateways or +advertise a better metric than at least one of the existing +gateways. +.Pp +When an update is applied, +.Nm +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 minimze +.Em black-holes . +When a TCP connection suffers a timeout, +the kernel tells +.Nm routed , +which deletes all redirected routes +through the gateway involved, advances the age of all RIP routes through +the gateway to allow an alternate to be chosen, and advances of the +age of any relevant Router Discovery Protocol default routes. +.Pp +Hosts acting as internetwork routers gratuitously supply their +routing tables every 30 seconds to all directly connected hosts +and networks. +The response is sent to the broadcast address on nets 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 +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. +This is typically used on a gateway to the Internet, +or on a gateway that uses another routing protocol whose routes +are not reported to other local routers. +.It Fl h +This causes host or point-to-point routes to not be advertised, +provided there is a network route going the same direction. +That is a limited kind of aggregation. +This option is useful on gateways to ethernets that have other gateway +machines connected with point-to-point links such as SLIP. +.It Fl m +This causes the machine to advertise a host or point-to-point route to +its primary interface. +It is useful on multi-homed machines such as NFS servers. +This option should not be used except when the cost of +the host routes it generates is justified by the popularity of +the server. +It is effective only when the machine is supplying +routing information, because there is more than one interface. +The +.Fl m +option overrides the +.Fl q +option to the limited extent of advertising the host route. +.It Fl p +causes routes received over point-to-point links to not be timed +out while the link is idle. +This is handy for "demand dialed" PPP links that filter routing packets. +.It Fl A +do not ignore RIPv2 authentication if we do not care about RIPv2 +authentication. +This option is required for conformance wiht RFC 1723, +but it makes little sense and breaks using RIP as a discovery protocol +to ignore all RIPv2 packets that carry authentication when this machine +does not care about authentication. +.It Fl T Ar tracefile +increases the debugging level to at least 1 and +causes debugging information to be appended to the file. +.It Fl t +increases the debugging level, which causes more information to be logged +on the tracefile specified with +.Fl T +or standard out. +The debugging level can be increased or decreased +with the +.Em SIGUSR1 +or +.Em SIGUSR2 +signals. +.It Fl F Ar net[/mask][,metric] +minimize routes in transmissions to network +.Em net/mask , +and synthesizes a default route to this machine with the +.Em metric . +The intent is to reduce RIP traffic on slow, point-to-point links +such as PPP links by replacing many large UDP packets of RIP information +with a single, small packet containing a "fake" default route. +If +.Em metric +is absent, a value of 14 is assumed to limit +the spread of the "fake" default route. +.It Fl P Ar parms +is equivalent to adding the parameter +line +.Em parms +to the +.Pa /etc/gateways +file. +.El +.Pp +Any other argument supplied is interpreted as the name +of 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 and if no responses are received +in turn for a period of the time, the associated route deleted from +the kernel table and RIP responses advertised via other interfaces. +If the distant gateway resumes sending RIP responses, the associated +route is restored. +.Pp +Such gateways can be useful on media that do not support broadcasts +or multicasts but otherwise act like classic shared media like +Ethernets such as some ATM networks. +One can list all RIP routers reachable on the ATM network in +.Pa /etc/gateways +with a series of +"host" lines. +.Pp +Gateways marked +.Em external +are also passive, but are not placed in the kernel +routing table nor are they included in routing updates. +The function of external entries is to 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 +.Em passive +or +.Em active +(as described above), +or whether the gateway is +.Em 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: +.Bl -tag -width Ds +.It Cm if Ns \&= Ns Ar ifname +indicates that the other parameters on the line apply to the interface +name +.Ar ifname . +.It Cm subnet Ns \&= Ns Ar nname[/mask] +causes other routes to be aggregated as if a compatible route to +Ar nname/mask +had been received. +This is useful for filling "holes" in CIDR allocations. +This parameter must appear by itself on a line. +.It Cm passwd Ns \&= Ns Ar XXX +specifies a RIPv2 password that will be included on all RIPv2 +responses sent and checked on all RIPv2 responses received. +.It Cm no_ag +turns off aggregation of subnets in RIPv1 and RIPv2 responses. +.It Cm no_super_ag +turns off aggregation of networks into supernets in RIPv2 responses. +.It Cm no_rip +disables all RIP processing on the specified interface. +If no interfaces are allowed to process RIP packets, +.Nm +acts purely as a router discovery daemon. +.Ar " No_rip " +is equivalent to +.Ar " no_ripv1_in no_ripv2_in no_ripv1_out no_ripv2_out ." +.It Cm no_ripv1_in +causes RIPv1 received responses to be ignored. +.It Cm no_ripv2_in +causes RIPv2 received responses to be ignored. +.It Cm ripv2_out +disables the RIPv2 responses that are otherwise multicast containing +information that cannot be sent in RIPv2 packets. +.It Cm no_rdisc +disables the Internet Router Discovery Protocol. +.It Cm no_solicit +disables the tranmission of Router Discovery Solicitations. +.It Cm send_solicit +specifies that Router Discovery solicitations should be sent, +even on point-to-point links, +which by default only listen to Router Discovery messages. +.It Cm no_rdisc_adv +disables the transmission of Router Discovery Advertisements +.It Cm rdisc_adv +specifies that Router Discovery advertisements should be sent, +even on point-to-point links, +which by default only listen to Router Discovery messages +.It Cm bcast_rdisc +specifies that Router Discovery packets should be broadcast instead of +multicast. +.It Cm rdisc_pref Ns \&= Ns Ar N +sets the preference in Router Discovery Advertisements to the integer +.Ar N . +.It Cm rdisc_interval Ns \&= Ns Ar N +sets the nominal interval with which Router Discovery Advertisements +are transmitted to N seconds and their lifetime to 3*N. +.It Cm fake_default Ns \&= Ns Ar metric +has an identical effect to +.Fl F Ar net[/mask][,metric] +with the network and mask coming from the affected interface. +.El +.Pp +.Sh FILES +.Bl -tag -width /etc/gateways -compact +.It Pa /etc/gateways +for distant gateways +.El +.Sh SEE ALSO +.Xr gated 8 , +.Xr udp 4 , +.Xr icmp 4 , +.Xr 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/usr.sbin/routed/rtquery/Makefile b/usr.sbin/routed/rtquery/Makefile new file mode 100644 index 00000000000..dd2419b9941 --- /dev/null +++ b/usr.sbin/routed/rtquery/Makefile @@ -0,0 +1,6 @@ +# @(#)Makefile 8.1 (Berkeley) 6/5/93 + +PROG= rtquery +MAN8= rtquery.8 + +.include diff --git a/usr.sbin/routed/rtquery/rtquery.8 b/usr.sbin/routed/rtquery/rtquery.8 new file mode 100644 index 00000000000..3c59efd6644 --- /dev/null +++ b/usr.sbin/routed/rtquery/rtquery.8 @@ -0,0 +1,79 @@ +.Dd April 9, 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 1 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 +.B \-p +option is specified, +.Nm rtquery +uses the +.Em poll +command, which is an +undocumented extension to the RIP specification supported by +.IR gated (1M). +When querying +.IR gated (1M), +the +.I poll +command is preferred over the +.I request +command because the response is not subject to Split Horizon and/or +Poisioned Reverse. +.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 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 parms +.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/usr.sbin/routed/rtquery/rtquery.c b/usr.sbin/routed/rtquery/rtquery.c new file mode 100644 index 00000000000..97899c97098 --- /dev/null +++ b/usr.sbin/routed/rtquery/rtquery.c @@ -0,0 +1,516 @@ +/*- + * 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. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1982, 1986, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)query.c 8.1 (Berkeley) 6/5/93"; +#endif /* not lint */ + +#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 pkt_buf { + char packet[MAXPACKETSIZE+4096]; + struct rip rip; +} msg_buf; +#define MSG msg_buf.rip +#define MSG_LIM ((struct rip*)(&msg_buf.packet[MAXPACKETSIZE \ + - sizeof(struct netinfo)])) + +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 */ + +struct timeval start; /* when query sent */ + +static void rip_input(struct sockaddr_in*, int); +static int query(char *, struct netinfo *); +static int getnet(char *, struct netinfo *); +static u_int std_mask(u_int); + + +int +main(int argc, + char *argv[]) +{ + char *p; + struct seen { + struct seen *next; + struct in_addr addr; + } *seen, *sp; + int answered = 0; + int ch, cc, bsize; + fd_set bits; + struct timeval now, delay; + struct sockaddr_in from; + int fromlen; + struct netinfo rt; + + + bzero(&rt, sizeof(rt)); + + pgmname = argv[0]; + while ((ch = getopt(argc, argv, "np1w:r:")) != EOF) + switch (ch) { + case 'n': + nflag = 1; + break; + case 'p': + pflag = 1; + break; + case '1': + ripv2 = 0; + break; + case 'w': + wtime = (int)strtoul(optarg, &p, 0); + if (*p != '\0' + || wtime <= 0) + goto usage; + break; + case 'r': + if (rflag) + goto usage; + rflag = getnet(optarg, &rt); + break; + case '?': + default: + goto usage; + } + argv += optind; + argc -= optind; + if (argc == 0) { +usage: printf("usage: query [-np1v] [-w wtime] host1 [host2 ...]\n"); + exit(1); + } + + if (!rflag) { + rt.n_dst = RIP_DEFAULT; + rt.n_family = RIP_AF_UNSPEC; + rt.n_metric = htonl(HOPCNT_INFINITY); + } + + s = socket(AF_INET, SOCK_DGRAM, 0); + if (s < 0) { + perror("socket"); + exit(2); + } + 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; + } + } + + /* ask the first host */ + seen = 0; + while (0 > query(*argv++, &rt) && *argv != 0) + 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, msg_buf.packet, + sizeof(msg_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 > query(*argv++, &rt)) + 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 (start.tv_sec + wtime <= now.tv_sec) + break; + } + + /* fail if there was no answer */ + exit (answered >= argc ? 0 : 1); + /* NOTREACHED */ +} + + +/* + * Poll one host. + */ +static int +query(char *host, + struct netinfo *rt) +{ + struct sockaddr_in router; + struct hostent *hp; + + if (gettimeofday(&start, 0) < 0) { + perror("gettimeofday(start)"); + return -1; + } + + bzero(&router, sizeof(router)); + router.sin_family = AF_INET; +#ifdef _HAVE_SIN_LEN + router.sin_len = sizeof(router); +#endif + router.sin_addr.s_addr = inet_addr(host); + if (router.sin_addr.s_addr == -1) { + hp = gethostbyname(host); + if (hp == 0) { + fprintf(stderr,"%s: %s:", pgmname, host); + herror(0); + return -1; + } + bcopy(hp->h_addr, &router.sin_addr, hp->h_length); + } + + router.sin_port = htons(RIP_PORT); + + MSG.rip_cmd = (pflag)? RIPCMD_POLL : RIPCMD_REQUEST; + MSG.rip_nets[0] = *rt; + if (ripv2) { + MSG.rip_vers = RIPv2; + } else { + MSG.rip_vers = RIPv1; + MSG.rip_nets[0].n_mask = 0; + } + + if (sendto(s, msg_buf.packet, sizeof(struct rip), 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 (MSG.rip_cmd != RIPCMD_RESPONSE) { + printf("\n unexpected response type %d\n", MSG.rip_cmd); + return; + } + printf(" RIPv%d%s %d bytes\n", MSG.rip_vers, + (MSG.rip_vers != RIPv1 && MSG.rip_vers != RIPv2) ? " ?" : "", + size); + if (size > MAXPACKETSIZE) { + if (size > sizeof(msg_buf) - sizeof(*n)) { + printf(" at least %d bytes too long\n", + size-MAXPACKETSIZE); + size = sizeof(msg_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 = MSG.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 (MSG.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) { + 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", + 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 %8s", + 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), + (MSG.rip_vers == RIPv1) ? " ?" : ""); + } + if (n->n_tag != 0) + (void)printf(" tag=%#x%s", n->n_tag, + (MSG.rip_vers == RIPv1) ? " ?" : ""); + putc('\n', stdout); + } +} + + +/* Return the classical netmask for an IP address. + */ +static u_int +std_mask(u_int addr) +{ + NTOHL(addr); + + if (addr == 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); + } else { + mask = (u_int)strtoul(mname, &p, 0); + if (*p != '\0' || mask > 32) + return 0; + mask = 0xffffffff << (32-mask); + } + + rt->n_dst = in.s_addr; + rt->n_family = AF_INET; + rt->n_mask = htonl(mask); + return 1; +} diff --git a/usr.sbin/routed/rttrace/Makefile b/usr.sbin/routed/rttrace/Makefile new file mode 100644 index 00000000000..df19d5c61b4 --- /dev/null +++ b/usr.sbin/routed/rttrace/Makefile @@ -0,0 +1,6 @@ +# @(#)Makefile 8.1 (Berkeley) 6/5/93 + +PROG= rttrace +NOMAN= noman + +.include diff --git a/usr.sbin/routed/rttrace/rttrace.c b/usr.sbin/routed/rttrace/rttrace.c new file mode 100644 index 00000000000..0d8ac6224ce --- /dev/null +++ b/usr.sbin/routed/rttrace/rttrace.c @@ -0,0 +1,146 @@ +/*- + * 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. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1983, 1988, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)trace.c 8.1 (Berkeley) 6/5/93"; +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#ifdef sgi +#include +#endif +#include +#include +#include +#include +#include +#include + +#ifndef sgi +#define _HAVE_SIN_LEN +#endif + +struct sockaddr_in myaddr; +char packet[MAXPACKETSIZE]; + +int +main(int argc, + char **argv) +{ + int size, s; + struct sockaddr_in router; + char *tgt; + register struct rip *msg = (struct rip *)packet; + struct hostent *hp; + + if (argc < 2) { +usage: + printf("usage: on filename host1 host2 ...\n" + " or: off host1 host2 ...\n"); + exit(1); + } + s = socket(AF_INET, SOCK_DGRAM, 0); + if (s < 0) { + perror("socket"); + exit(2); + } + 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); + } + + msg->rip_vers = RIPVERSION; + size = sizeof(int); + + argv++, argc--; + if (!strcmp(*argv, "on")) { + msg->rip_cmd = RIPCMD_TRACEON; + if (--argc <= 1) + goto usage; + strcpy(msg->rip_tracefile, *++argv); + size += strlen(msg->rip_tracefile); + + } else if (!strcmp(*argv, "off")) { + msg->rip_cmd = RIPCMD_TRACEOFF; + + } else { + goto usage; + } + argv++, argc--; + + bzero(&router, sizeof(router)); + router.sin_family = AF_INET; +#ifdef _HAVE_SIN_LEN + router.sin_len = sizeof(router); +#endif + router.sin_port = htons(RIP_PORT); + + do { + tgt = argc > 0 ? *argv++ : "localhost"; + router.sin_family = AF_INET; + router.sin_addr.s_addr = inet_addr(tgt); + if (router.sin_addr.s_addr == -1) { + hp = gethostbyname(tgt); + if (hp == 0) { + herror(tgt); + continue; + } + bcopy(hp->h_addr, &router.sin_addr, hp->h_length); + } + if (sendto(s, packet, size, 0, + (struct sockaddr *)&router, sizeof(router)) < 0) + perror(*argv); + } while (--argc > 0); + + return 0; +} diff --git a/usr.sbin/routed/table.c b/usr.sbin/routed/table.c new file mode 100644 index 00000000000..110656378b8 --- /dev/null +++ b/usr.sbin/routed/table.c @@ -0,0 +1,1888 @@ +/* + * 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. + */ + +#ifndef lint +static char sccsid[] = "@(#)tables.c 8.1 (Berkeley) 6/5/93"; +#endif /* not lint */ + +#ident "$Revision: 1.2 $" + +#include "defs.h" + + +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; + +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 have both the even and odd twins, then the immediate parent, + * if it is present is redundant, unless it manages to aggregate + * something. 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_DEAD) + && !(ag_cors->ag_state & AGS_RDISC)) { + ag_out(ag, out); + break; + } + + /* If it has a good enough metric, it replaces + * 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; + } + 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, + 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_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 routes as we look. + * 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_DEAD) + || (ag_cors->ag_state & AGS_RDISC))) { + 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; + } + 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) { + + /* When a promoted route encounters the same but explicit + * route, assume the new one has been promoted, and + * so its gateway, metric and tag are right. + * + * Routes are encountered in lexical order, so an even/odd + * pair 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 that was made redundant by + * the pair. + * + * The sequence number only controls flash updating, and + * so should be the smaller of the two. + */ + if (ag->ag_dst_h == dst) { + ag->ag_metric = metric; + ag->ag_pref = pref; + ag->ag_gate = gate; + ag->ag_tag = tag; + if (ag->ag_seqno > seqno) + ag->ag_seqno = seqno; + + /* some bits are set only if both routes have them */ + ag->ag_state &= ~(~state & (AGS_PROMOTE | AGS_RIPV2)); + /* others are set if they are set on either route */ + ag->ag_state |= (state & (AGS_REDUN0 | AGS_REDUN1 + | AGS_GATEWAY + | AGS_SUPPRESS)); + return; + } + + /* If one of the routes can be promoted and suppressed + * and the other can at least be suppressed, they + * can be combined. + * 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. + * Except that the kernel does not care about the + * 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 + && ag->ag_tag == tag)) { + + /* 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_RIPV2); + + /* 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; + + 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_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 + } +#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) { + trace_msg("route to %s disappeared before CHANGE", + addrname(dst, mask, 0)); + action = RTM_ADD; + goto again; + } + msglog("write(rt_sock) %s %s: %s", + rtm_type_name(action), addrname(dst, mask, 0), + 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 +#define KS_CHANGE 0x008 +#define KS_DEL_ADD 0x010 +#define KS_STATIC 0x020 +#define KS_GATEWAY 0x040 +#define KS_DYNAMIC 0x080 +#define KS_DELETED 0x100 /* already deleted */ + time_t k_hold; + time_t k_time; +#define K_HOLD_LIM 30 +} *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_time = now.tv_sec; + k->k_hold = now.tv_sec; + *pk = k; + + return k; +} + + +/* add a route the kernel told us + * rt_xaddrs() must have already been called. + */ +static void +rtm_add(struct rt_msghdr *rtm) +{ + struct khash *k; + struct interface *ifp; + struct rt_entry *rt; + naddr mask; + + + if (rtm->rtm_flags & RTF_HOST) { + mask = HOST_MASK; + } else if (RTINFO_NETMASK != 0) { + mask = ntohl(S_ADDR(RTINFO_NETMASK)); + } else { + msglog("punt %s without mask", + rtm_type_name(rtm->rtm_type)); + return; + } + + if (RTINFO_GATE == 0 + || RTINFO_GATE->sa_family != AF_INET) { + msglog("punt %s without gateway", + rtm_type_name(rtm->rtm_type)); + return; + } + + k = kern_add(S_ADDR(RTINFO_DST), mask); + k->k_gate = S_ADDR(RTINFO_GATE); + 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_NEW | KS_DELETED | KS_GATEWAY | KS_STATIC); + if (rtm->rtm_flags & RTF_GATEWAY) + k->k_state |= KS_GATEWAY; + if (rtm->rtm_flags & RTF_STATIC) + k->k_state |= KS_STATIC; + if (rtm->rtm_flags & RTF_DYNAMIC) + k->k_state |= KS_DYNAMIC; + k->k_time = now.tv_sec; + k->k_hold = now.tv_sec; + + /* Put static routes with real metrics into the daemon table so + * they can be advertised. + */ + if (!(k->k_state & KS_STATIC)) + return; + + if (RTINFO_IFP != 0 + && RTINFO_IFP->sdl_nlen != 0) { + RTINFO_IFP->sdl_data[RTINFO_IFP->sdl_nlen] = '\0'; + ifp = ifwithname(RTINFO_IFP->sdl_data, k->k_gate); + } else { + ifp = iflookup(k->k_gate); + } + if (ifp == 0) { + msglog("static route %s --> %s impossibly lacks ifp", + addrname(S_ADDR(RTINFO_DST), mask, 0), + naddr_ntoa(k->k_gate)); + return; + } + if (k->k_metric == 0) + return; + + rt = rtget(k->k_dst, k->k_mask); + if (rt != 0) { + if (rt->rt_ifp != ifp + || 0 != (rt->rt_state & RS_NET_S)) { + rtdelete(rt); + rt = 0; + } else if (!(rt->rt_state & (RS_IF + | RS_LOCAL + | RS_MHOME + | RS_GW))) { + rtchange(rt, RS_STATIC, + k->k_gate, ifp->int_addr, + k->k_metric, 0, ifp, + now.tv_sec, 0); + } + } + if (rt == 0) + rtadd(k->k_dst, k->k_mask, k->k_gate, + ifp->int_addr, k->k_metric, + 0, RS_STATIC, ifp); +} + + +/* deal with packet loss + */ +static void +rtm_lose(struct rt_msghdr *rtm) +{ + if (RTINFO_GATE == 0 + || RTINFO_GATE->sa_family != AF_INET) { + msglog("punt %s without gateway", + rtm_type_name(rtm->rtm_type)); + return; + } + + if (!supplier) + rdisc_age(S_ADDR(RTINFO_GATE)); + + age(S_ADDR(RTINFO_GATE)); +} + + +/* 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; + + + 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((struct sockaddr *)(rtm+1), + (struct sockaddr *)(next + rtm->rtm_msglen), + rtm->rtm_addrs); + + if (RTINFO_DST == 0 + || RTINFO_DST->sa_family != AF_INET) + continue; + + if (RTINFO_GATE == 0) + continue; + if (RTINFO_GATE->sa_family != AF_INET) { + if (RTINFO_GATE->sa_family != AF_LINK) + continue; + ifp = ifwithindex(((struct sockaddr_dl *) + RTINFO_GATE)->sdl_index); + if (ifp == 0) + continue; + gate_sa.sin_addr.s_addr = ifp->int_addr; +#ifdef _HAVE_SA_LEN + gate_sa.sin_len = sizeof(gate_sa); +#endif + gate_sa.sin_family = AF_INET; + RTINFO_GATE = (struct sockaddr *)&gate_sa; + } + + /* ignore multicast addresses + */ + if (IN_MULTICAST(ntohl(S_ADDR(RTINFO_DST)))) + continue; + + /* Note static routes and interface routes. + */ + rtm_add(rtm); + } + 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 pid_str[10+19+1]; + + + 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) { + ifp = ifwithindex(m.ifm.ifm_index); + if (ifp == 0) + trace_msg("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_msg("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; + } + + if (m.r.rtm.rtm_type <= RTM_CHANGE) + (void)sprintf(pid_str," from pid %d",m.r.rtm.rtm_pid); + else + pid_str[0] = '\0'; + + rt_xaddrs(m.r.addrs, &m.r.addrs[RTAX_MAX], + m.r.rtm.rtm_addrs); + + if (RTINFO_DST == 0) { + trace_msg("ignore %s%s without dst\n", + rtm_type_name(m.r.rtm.rtm_type), pid_str); + continue; + } + + if (RTINFO_DST->sa_family != AF_INET) { + trace_msg("ignore %s%s for AF %d\n", + rtm_type_name(m.r.rtm.rtm_type), pid_str, + RTINFO_DST->sa_family); + continue; + } + + mask = ((RTINFO_NETMASK != 0) + ? ntohl(S_ADDR(RTINFO_NETMASK)) + : (m.r.rtm.rtm_flags & RTF_HOST) + ? HOST_MASK + : std_mask(S_ADDR(RTINFO_DST))); + + if (RTINFO_GATE == 0 + || RTINFO_GATE->sa_family != AF_INET) { + trace_msg("%s for %s%s\n", + rtm_type_name(m.r.rtm.rtm_type), + addrname(S_ADDR(RTINFO_DST), mask, 0), + pid_str); + } else { + trace_msg("%s %s --> %s%s\n", + rtm_type_name(m.r.rtm.rtm_type), + addrname(S_ADDR(RTINFO_DST), mask, 0), + saddr_ntoa(RTINFO_GATE), + pid_str); + } + + switch (m.r.rtm.rtm_type) { + case RTM_ADD: + case RTM_CHANGE: + if (m.r.rtm.rtm_errno != 0) { + trace_msg("ignore %s%s with \"%s\" error\n", + rtm_type_name(m.r.rtm.rtm_type), + pid_str, + strerror(m.r.rtm.rtm_errno)); + } else { + rtm_add(&m.r.rtm); + } + break; + + case RTM_REDIRECT: + if (m.r.rtm.rtm_errno != 0) { + trace_msg("ignore %s with \"%s\" from %s" + " for %s-->%s\n", + rtm_type_name(m.r.rtm.rtm_type), + strerror(m.r.rtm.rtm_errno), + saddr_ntoa(RTINFO_AUTHOR), + saddr_ntoa(RTINFO_GATE), + addrname(S_ADDR(RTINFO_DST), + mask, 0)); + } else { + rtm_add(&m.r.rtm); + } + break; + + case RTM_DELETE: + if (m.r.rtm.rtm_errno != 0) { + trace_msg("ignore %s%s with \"%s\" error\n", + rtm_type_name(m.r.rtm.rtm_type), + pid_str, + strerror(m.r.rtm.rtm_errno)); + } else { + del_static(S_ADDR(RTINFO_DST), mask, 1); + } + break; + + case RTM_LOSING: + rtm_lose(&m.r.rtm); + break; + default: + 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_S for interfaces that + * recently died. + */ + if (ag->ag_metric == HOPCNT_INFINITY + && 0 == kern_find(htonl(ag->ag_dst_h), ag->ag_mask, 0)) + return; + + k = kern_add(htonl(ag->ag_dst_h), ag->ag_mask); + + /* will need to add new entry */ + if (k->k_state & KS_NEW) { + 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; + } + + /* modify existing kernel entry if necessary */ + k->k_state &= ~(KS_DELETE | KS_DYNAMIC); + 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_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); + } +#undef RT +} + + +/* ARGSUSED */ +static int +walk_kern(struct radix_node *rn, + struct walkarg *w) +{ +#define RT ((struct rt_entry *)rn) + char pref; + u_int ags = 0; + + /* Do not install synthetic routes */ + if (0 != (RT->rt_state & RS_NET_S)) + return 0; + + /* Do not install routes for "external" remote interfaces. + */ + if ((RT->rt_state & RS_IF) + && RT->rt_ifp != 0 + && (RT->rt_ifp->int_state & IS_EXTERNAL)) + return 0; + + /* 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_state & RS_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_metric == HOPCNT_INFINITY) { + pref = HOPCNT_INFINITY; + ags |= (AGS_DEAD | AGS_SUPPRESS); + } else { + pref = 1; + } + + if (RT->rt_state & RS_RDISC) + ags |= AGS_RDISC; + + ag_check(RT->rt_dst, RT->rt_mask, RT->rt_gate, + RT->rt_metric, pref, + 0, 0, ags, kern_out); + return 0; +#undef RT +} + + +/* Update the kernel table to match the daemon table. + */ +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) { + pk = &k->k_next; + continue; + } + + /* check hold on routes deleted by the operator */ + if (k->k_hold > now.tv_sec) { + LIM_SEC(need_kern, k->k_hold); + pk = &k->k_next; + continue; + } + + if (k->k_state & 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 (k->k_state & KS_DEL_ADD) + rtioctl(RTM_DELETE, + k->k_dst,k->k_gate,k->k_mask, 0, 0); + + flags = (k->k_state & KS_GATEWAY) ? RTF_GATEWAY : 0; + 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); + + /* Unless it seems something else is handling the + * routes in the kernel, 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 + * disappeard from the daemon table. + */ + if (now.tv_sec >= EPOCH+MIN_WAITTIME-1 + && (rip_interfaces != 0 || !supplier)) + 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 + * hold 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; + k->k_state |= KS_DELETE; + if (gone) { + k->k_state |= KS_DELETED; + k->k_hold = now.tv_sec + K_HOLD_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. + */ +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) + || 0 != (k->k_state & (KS_STATIC | KS_DELETE))) + continue; + + if (k->k_gate != bad_gate + && k->k_time > old) + continue; + + k->k_state |= KS_DELETE; + need_kern.tv_sec = now.tv_sec; + if (TRACEACTIONS) + trace_msg("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_ for our table */ + struct interface *ifp) +{ + struct rt_entry *rt; + naddr smask; + int i; + struct rt_spare *rts; + + rt = (struct rt_entry *)malloc(sizeof (*rt)); + if (rt == 0) { + BADERR(1,"rtadd malloc"); + return; + } + 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; + if (metric == HOPCNT_INFINITY) { + rt->rt_time -= POISON_SECS; + rt->rt_hold_down = now.tv_sec+HOLD_TIME; + } + rt->rt_metric = metric; + if ((rt->rt_state & RS_NET_S) == 0) + rt->rt_hold_metric = metric; + else + rt->rt_hold_metric = HOPCNT_INFINITY; + rt->rt_tag = tag; + rt->rt_ifp = ifp; + rt->rt_seqno = update_seqno+1; + + 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) { + /* Hold down the route if it is bad, but only long enough + * for neighors that do not implement poison-reverse or + * split horizon to hear the bad news. + */ + if (metric == HOPCNT_INFINITY) { + if (new_time > now.tv_sec - POISON_SECS) + new_time = now.tv_sec - POISON_SECS; + if (!(rt->rt_state & RS_RDISC) + && rt->rt_hold_down < now.tv_sec+HOLD_TIME) + rt->rt_hold_down = now.tv_sec+HOLD_TIME; + if (now.tv_sec < rt->rt_hold_down) + LIM_SEC(age_timer, rt->rt_hold_down+1); + } else { + rt->rt_hold_down = 0; + if ((rt->rt_state & RS_NET_S) == 0) + rt->rt_hold_metric = metric; + } + + rt->rt_seqno = update_seqno+1; + set_need_flash(); + } + + if (rt->rt_gate != gate) { + need_kern.tv_sec = now.tv_sec; + rt->rt_seqno = update_seqno+1; + set_need_flash(); + } + + state |= (rt->rt_state & RS_SUBNET); + + 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; +} + + +/* switch to a backup route + */ +void +rtswitch(struct rt_entry *rt, + struct rt_spare *rts) +{ + struct rt_spare *rts1, swap; + char label[10]; + int i; + + + /* Do not change permanent routes */ + if (0 != (rt->rt_state & (RS_GW | RS_MHOME | RS_STATIC | RS_IF))) + return; + + /* Do not discard synthetic routes until they go bad */ + if (0 != (rt->rt_state & RS_NET_S) + && rt->rt_metric < HOPCNT_INFINITY) + return; + + if (rts == 0) { + /* 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(rts1,rts)) + rts = rts1; + } + } + + /* Do not bother if it is not worthwhile. + */ + if (!BETTER_LINK(rts, rt->rt_spares)) + return; + + /* Do not change the route if it is being held down. + * Honor the hold-down to counter systems that do not support + * split horizon or for other causes of counting to infinity, + * and so only for routes worse than our last good route. + */ + if (now.tv_sec < rt->rt_hold_down + && rts->rts_metric > rt->rt_hold_metric) { + LIM_SEC(age_timer, rt->rt_hold_down+1); + return; + } + + swap = rt->rt_spares[0]; + + (void)sprintf(label, "Use #%d", rts - rt->rt_spares); + rtchange(rt, rt->rt_state & ~(RS_NET_S | 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); + } +} + + +/* 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_S route, but save if if 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, a + * point-to-point interface with the same local address. + */ + for (ifp = ifnet; ifp != 0; ifp = ifp->int_next) { + if (ifp->int_metric == HOPCNT_INFINITY) + continue; + + /* Save 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) + && (rt->rt_state & RS_NET_S)) { + for (ifp = ifnet; ifp != 0; ifp = ifp->int_next) { + if (ifp->int_metric == HOPCNT_INFINITY) + continue; + + /* Retain RIPv1 logical network route if + * there is another interface that justifies + * it. + */ + if ((ifp->int_state & IS_NEED_NET_SUB) + && rt->rt_mask == ifp->int_std_mask + && rt->rt_dst == ifp->int_std_addr) { + state |= RS_NET_SUB; + ifp1 = ifp; + + } else if ((ifp->int_if_flags & IFF_POINTOPOINT) + && rt->rt_mask == ifp->int_host_mask + && rt->rt_dst == ifp->int_host_addr + && ridhosts) { + state |= RS_NET_HOST; + ifp1 = ifp; + } + } + + if (ifp1 == 0) { + 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_SUB; + break; + } + } + } + } + + + if (ifp1 != 0) { + rtchange(rt, (rt->rt_state & ~(RS_NET_S | RS_LOCAL)) | state, + rt->rt_gate, rt->rt_router, NET_S_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)) { + new_time = rts->rts_time; + if (new_time >= now_garbage) + new_time = now_garbage-1; + if (TRACEACTIONS) + 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 (!(RT->rt_state & RS_IF)) { + 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 the spare routes */ + rts = RT->rt_spares; + for (i = NUM_SPARES; i != 0; i--, rts++) { + + ifp = rts->rts_ifp; + if (i == NUM_SPARES) { + if (!AGE_RT(RT, 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 && !(RT->rt_state & RS_RDISC)) { + rtdelete(RT); + return 0; + } + } + + if (age_bad_gate == rts->rts_gate + && rts->rts_time >= now_stale) { + /* age failing routes + */ + rts->rts_time -= SUPPLY_INTERVAL; + + } else if (ppp_noage + && ifp != 0 + && (ifp->int_if_flags & IFF_POINTOPOINT) + && (ifp->int_state & IS_QUIET)) { + /* optionally do not age routes through quiet + * point-to-point interfaces + */ + rts->rts_time = now.tv_sec; + continue; + } + + /* trash the spare routes when they go bad */ + if (rts->rts_metric < HOPCNT_INFINITY + && now_garbage > rts->rts_time) { + if (TRACEACTIONS) + 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 */ + if (now.tv_sec < RT->rt_hold_down) { + LIM_SEC(age_timer, RT->rt_hold_down+1); + return 0; + } else { + 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) + ifbad(ifp, + "remote interface %s to %s timed out"); + } + } + + /* Age routes. */ + age_bad_gate = bad_gate; + (void)rn_walktree(rhead, walk_age, 0); + + /* Update the kernel routing table. */ + fix_kern(); +} diff --git a/usr.sbin/routed/trace.c b/usr.sbin/routed/trace.c new file mode 100644 index 00000000000..5f4fc7d1a94 --- /dev/null +++ b/usr.sbin/routed/trace.c @@ -0,0 +1,713 @@ +/* + * 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. + */ + +#ifndef lint +static char sccsid[] = "@(#)trace.c 8.1 (Berkeley) 6/5/93"; +#endif /* not lint */ + +#ident "$Revision: 1.1 $" + +#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 */ +char *tracelevel_msg = ""; + +char savetracename[MAXPATHLEN+1]; + + +char * +naddr_ntoa(naddr a) +{ +#define NUM_BUFS 4 + static int i; + static struct { + char str[16]; /* xxx.xxx.xxx.xxx\0 */ + } bufs[NUM_BUFS]; + struct in_addr addr; + char *s; + + addr.s_addr = a; + s = strcpy(bufs[i].str, inet_ntoa(addr)); + i = (i+1) % NUM_BUFS; + return s; +} + + +char * +saddr_ntoa(struct sockaddr *sa) +{ + return (sa == 0) ? "?" : naddr_ntoa(S_ADDR(sa)); +} + + +static char * +ts(time_t secs) { + static char s[20]; + + secs += epoch.tv_sec; +#ifdef sgi + (void)cftime(s, "%T", &secs); +#else + bcopy(ctime(&secs)+11, s, 8); + s[8] = '\0'; +#endif + 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 *msg1, char* msg2) +{ + if (ftrace != 0) { + lastlog(); + (void)fprintf(ftrace, "%s%s\n", msg1,msg2); + } +} + + +static void +trace_close(char *msg1, char *msg2) +{ + int fd; + + fflush(stdout); + fflush(stderr); + + if (ftrace != 0) { + tmsg(msg1,msg2); + fflush(ftrace); + + if (savetracename[0] != '\0') { + fd = open(_PATH_DEVNULL, O_RDWR); + (void)dup2(fd, STDOUT_FILENO); + (void)dup2(fd, STDERR_FILENO); + (void)close(fd); + fclose(ftrace); + ftrace = 0; + } + } +} + + +void +trace_flush(void) +{ + if (ftrace != 0) { + fflush(ftrace); + if (ferror(ftrace)) + trace_off("tracing off: ", strerror(ferror(ftrace))); + } +} + + +void +trace_off(char *msg1, char *msg2) +{ + trace_close(msg1, msg2); + + new_tracelevel = tracelevel = 0; +} + + +void +trace_on(char *filename, + int trusted) +{ + struct stat stbuf; + FILE *n_ftrace; + + + if (stat(filename, &stbuf) >= 0 && + (stbuf.st_mode & S_IFMT) != S_IFREG) { + msglog("wrong type (%#x) of trace file \"%s\"", + stbuf.st_mode, filename); + return; + } + if (!trusted + && strcmp(filename, savetracename) + && strncmp(filename, _PATH_TRACE, sizeof(_PATH_TRACE)-1)) { + msglog("wrong directory for trace file %s", filename); + return; + } + n_ftrace = fopen(filename, "a"); + if (n_ftrace == 0) { + msglog("failed to open trace file \"%s\": %s", + filename, strerror(errno)); + return; + } + + trace_close("switch to trace file ", filename); + if (filename != savetracename) + strncpy(savetracename, filename, sizeof(savetracename)-1); + ftrace = n_ftrace; + + fflush(stdout); + fflush(stderr); + dup2(fileno(ftrace), STDOUT_FILENO); + dup2(fileno(ftrace), STDERR_FILENO); + + if (new_tracelevel == 0) { + tracelevel_msg = "trace command: "; + new_tracelevel = 1; + } else { + tmsg("trace command",""); + } +} + + +/* ARGSUSED */ +void +sigtrace_on(int s) +{ + new_tracelevel++; + tracelevel_msg = "SIGUSR1: "; +} + + +/* ARGSUSED */ +void +sigtrace_off(int s) +{ + new_tracelevel--; + tracelevel_msg = "SIGUSR2: "; +} + + +/* Move to next higher level of tracing when -t option processed or + * SIGUSR1 is received. Successive levels are: + * actions + * actions + packets + * actions + packets + contents + */ +void +set_tracelevel(void) +{ + static char *off_msgs[MAX_TRACELEVEL] = { + "Tracing actions stopped", + "Tracing packets stopped", + "Tracing packet contents stopped", + }; + static char *on_msgs[MAX_TRACELEVEL] = { + "Tracing actions started", + "Tracing packets started", + "Tracing packet contents started", + }; + + + if (new_tracelevel > MAX_TRACELEVEL) + new_tracelevel = MAX_TRACELEVEL; + while (new_tracelevel != tracelevel) { + if (new_tracelevel < tracelevel) { + if (--tracelevel == 0) + trace_off(tracelevel_msg, off_msgs[0]); + else + tmsg(tracelevel_msg, off_msgs[tracelevel]); + } else { + if (ftrace == 0) { + if (savetracename[0] != '\0') + trace_on(savetracename, 1); + else + ftrace = stdout; + } + tmsg(tracelevel_msg, on_msgs[tracelevel++]); + } + } +} + + +/* display an address + */ +char * +addrname(naddr addr, /* in network byte order */ + naddr mask, + int force) /* 0=show mask if nonstandard, */ +{ /* 1=always show mask, 2=never */ + static char s[15+20]; + char *sp; + naddr dmask; + int i; + + (void)strcpy(s, naddr_ntoa(addr)); + + if (force == 1 || (force == 0 && mask != std_mask(addr))) { + sp = &s[strlen(s)]; + + dmask = mask & -mask; + if (mask + dmask == 0) { + for (i = 0; i != 32 && ((1<bits_mask; + if (b == 0) + break; + if ((b & field) == b + && tbl->bits_name[0] != '\0') { + (void)fprintf(ftrace, first ? "<%s" : "|%s", + tbl->bits_name); + first = 0; + } + field &= ~b; + tbl++; + } + if (field != 0) { + (void)fputc(first ? '<' : '|', ftrace); + (void)fprintf(ftrace, "%#x", field); + first = 0; + } + + if (!first) + (void)fputs("> ", ftrace); +} + + +void +trace_if(char *act, + struct interface *ifp) +{ + if (ftrace == 0) + return; + + lastlog(); + (void)fprintf(ftrace, "%s interface %-4s ", act, ifp->int_name); + (void)fprintf(ftrace, "%-15s --> %s ", + naddr_ntoa(ifp->int_addr), + ((ifp->int_if_flags & IFF_POINTOPOINT) + ? naddr_ntoa(ifp->int_dstaddr) + : addrname(htonl(ifp->int_net), ifp->int_mask, 0))); + (void)fprintf(ftrace, "metric=%d ", ifp->int_metric); + trace_bits(if_bits, ifp->int_if_flags); + trace_bits(is_bits, ifp->int_state); + (void)fputc('\n',ftrace); +} + + +void +trace_upslot(struct rt_entry *rt, + struct rt_spare *rts, + naddr gate, + naddr router, + struct interface *ifp, + int metric, + u_short tag, + time_t new_time) +{ + if (ftrace == 0) + return; + if (rts->rts_gate == gate + && rts->rts_router == router + && rts->rts_metric == metric + && rts->rts_tag == tag) + return; + + lastlog(); + if (rts->rts_gate != RIP_DEFAULT) { + (void)fprintf(ftrace, "Chg #%d %-16s--> ", + rts - rt->rt_spares, + addrname(rt->rt_dst, rt->rt_mask, 0)); + (void)fprintf(ftrace, "%-15s ", + naddr_ntoa(rts->rts_gate)); + if (rts->rts_gate != rts->rts_gate) + (void)fprintf(ftrace, "router=%s ", + naddr_ntoa(rts->rts_gate)); + if (rts->rts_tag != 0) + (void)fprintf(ftrace, "tag=%#x ", rts->rts_tag); + (void)fprintf(ftrace, "metric=%-2d ", rts->rts_metric); + if (rts->rts_ifp != 0) + (void)fprintf(ftrace, "%s ", + rts->rts_ifp->int_name); + (void)fprintf(ftrace, "%s\n", ts(rts->rts_time)); + + (void)fprintf(ftrace, " %-16s--> ", + addrname(rt->rt_dst, rt->rt_mask, 0)); + (void)fprintf(ftrace, "%-15s ", + gate != rts->rts_gate ? naddr_ntoa(gate) : ""); + if (gate != router) + (void)fprintf(ftrace,"router=%s ",naddr_ntoa(router)); + if (tag != rts->rts_tag) + (void)fprintf(ftrace, "tag=%#x ", tag); + if (metric != rts->rts_metric) + (void)fprintf(ftrace, "metric=%-2d ", metric); + if (ifp != rts->rts_ifp && ifp != 0 ) + (void)fprintf(ftrace, "%s ", ifp->int_name); + (void)fprintf(ftrace, "%s\n", + new_time != rts->rts_time ? ts(new_time) : ""); + + } else { + (void)fprintf(ftrace, "Add #%d %-16s--> ", + rts - rt->rt_spares, + addrname(rt->rt_dst, rt->rt_mask, 0)); + (void)fprintf(ftrace, "%-15s ", naddr_ntoa(gate)); + if (gate != router) + (void)fprintf(ftrace, "router=%s ", naddr_ntoa(gate)); + if (tag != 0) + (void)fprintf(ftrace, "tag=%#x ", tag); + (void)fprintf(ftrace, "metric=%-2d ", metric); + if (ifp != 0) + (void)fprintf(ftrace, "%s ", ifp->int_name); + (void)fprintf(ftrace, "%s\n", ts(new_time)); + } +} + + +void +trace_msg(char *p, ...) +{ + va_list args; + + if (!TRACEACTIONS || 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 %-16s--> %-15s metric=%-2d ", + label, + addrname(rt->rt_dst, rt->rt_mask, 0), + naddr_ntoa(rt->rt_gate), rt->rt_metric); + if (rt->rt_router != rt->rt_gate) + (void)fprintf(ftrace, "router=%s ", + naddr_ntoa(rt->rt_router)); + if (rt->rt_tag != 0) + (void)fprintf(ftrace, "tag=%#x ", rt->rt_tag); + trace_bits(rs_bits, rt->rt_state); + (void)fprintf(ftrace, "%s ", + rt->rt_ifp == 0 ? "-" : rt->rt_ifp->int_name); + (void)fprintf(ftrace, "%s\n", + AGE_RT(rt, rt->rt_ifp) ? ts(rt->rt_time) : ""); + + (void)fprintf(ftrace, "%*s %-16s--> %-15s ", + strlen(label), "", + addrname(rt->rt_dst, rt->rt_mask, 0), + (rt->rt_gate != gate) ? naddr_ntoa(gate) : ""); + if (rt->rt_metric != metric) + (void)fprintf(ftrace, "metric=%-2d ", metric); + if (router != gate) + (void)fprintf(ftrace, "router=%s ", naddr_ntoa(router)); + if (rt->rt_tag != tag) + (void)fprintf(ftrace, "tag=%#x ", tag); + if (rt->rt_state != state) + trace_bits(rs_bits, state); + if (rt->rt_ifp != ifp) + (void)fprintf(ftrace, "%s ", + ifp != 0 ? ifp->int_name : "-"); + if (rt->rt_hold_down > now.tv_sec) + (void)fprintf(ftrace, "hold-down=%d ", + rt->rt_hold_down - now.tv_sec); + (void)fprintf(ftrace, "%s\n", + ((rt->rt_time == new_time || !AGE_RT(rt, ifp)) + ? "" : ts(new_time))); +} + + +void +trace_add_del(char * action, struct rt_entry *rt) +{ + u_int state = rt->rt_state; + + if (ftrace == 0) + return; + + lastlog(); + (void)fprintf(ftrace, "%s %-16s--> %-15s metric=%-2d ", + action, + addrname(rt->rt_dst, rt->rt_mask, 0), + naddr_ntoa(rt->rt_gate), rt->rt_metric); + if (rt->rt_router != rt->rt_gate) + (void)fprintf(ftrace, "router=%s ", + naddr_ntoa(rt->rt_router)); + if (rt->rt_tag != 0) + (void)fprintf(ftrace, "tag=%#x ", rt->rt_tag); + trace_bits(rs_bits, state); + if (rt->rt_ifp != 0) + (void)fprintf(ftrace, "%s ", rt->rt_ifp->int_name); + (void)fprintf(ftrace, "%s\n", ts(rt->rt_time)); +} + + +void +trace_rip(char *dir1, char *dir2, + struct sockaddr_in *who, + struct interface *ifp, + struct rip *msg, + int size) /* total size of message */ +{ + struct netinfo *n, *lim; + struct netauth *a; + int i; + + if (ftrace == 0) + return; + + lastlog(); + if (msg->rip_cmd >= RIPCMD_MAX + || msg->rip_vers == 0) { + (void)fprintf(ftrace, "%s bad RIPv%d cmd=%d %s %s.%d%s%s" + " size=%d msg=%#x\n", + dir1, msg->rip_vers, msg->rip_cmd, dir2, + naddr_ntoa(who->sin_addr.s_addr), + ntohs(who->sin_port), + size, msg); + return; + } + + (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 ", + ntohl(n->n_mask)); + if (n->n_nhop != 0) + (void)fprintf(ftrace, " nhop=%s ", + naddr_ntoa(n->n_nhop)); + if (n->n_tag != 0) + (void)fprintf(ftrace, "tag=%#x", + n->n_tag); + (void)fputc('\n',ftrace); + continue; + } + + if (n->n_family == RIP_AF_AUTH) { + a = (struct netauth*)n; + (void)fprintf(ftrace, + "\tAuthentication type %d: ", + ntohs(a->a_type)); + for (i = 0; + i < sizeof(a->au.au_pw); + i++) + (void)fprintf(ftrace, "%02x ", + a->au.au_pw[i]); + (void)fputc('\n',ftrace); + continue; + } + + if (n->n_family != RIP_AF_INET) { + (void)fprintf(ftrace, + "\t(af %d) %-18s mask=%#x", + ntohs(n->n_family), + naddr_ntoa(n->n_dst), + ntohl(n->n_mask)); + } else if (msg->rip_vers == RIPv1) { + (void)fprintf(ftrace, "\t%-18s ", + addrname(n->n_dst, + ntohl(n->n_mask), + n->n_mask==0 ? 2 : 1)); + } else { + (void)fprintf(ftrace, "\t%-18s ", + addrname(n->n_dst, + ntohl(n->n_mask), + n->n_mask==0 ? 2 : 0)); + } + (void)fprintf(ftrace, "metric=%-2d ", + ntohl(n->n_metric)); + if (n->n_nhop != 0) + (void)fprintf(ftrace, " nhop=%s ", + naddr_ntoa(n->n_nhop)); + if (n->n_tag != 0) + (void)fprintf(ftrace, "tag=%#x", + n->n_tag); + (void)fputc('\n',ftrace); + } + if (size != (char *)n - (char *)msg) + (void)fprintf(ftrace, "truncated record, len %d\n", + size); + break; + + case RIPCMD_TRACEON: + fprintf(ftrace, "\tfile=%*s\n", size-4, msg->rip_tracefile); + break; + + case RIPCMD_TRACEOFF: + break; + } +} From 9979ce1560e78706bb3ab5bbed7e8d0c91ee5743 Mon Sep 17 00:00:00 2001 From: cvs2svn Date: Mon, 22 Jul 1996 20:56:39 +0000 Subject: [PATCH 2/5] This commit was manufactured by cvs2svn to create branch 'SGI'. --- usr.sbin/routed/routed.h | 159 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 159 insertions(+) create mode 100644 usr.sbin/routed/routed.h diff --git a/usr.sbin/routed/routed.h b/usr.sbin/routed/routed.h new file mode 100644 index 00000000000..2ab50f64f12 --- /dev/null +++ b/usr.sbin/routed/routed.h @@ -0,0 +1,159 @@ +/*- + * Copyright (c) 1983, 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. + * + * @(#)routed.h 8.1 (Berkeley) 6/2/93 + */ + +#ifndef _ROUTED_H_ +#define _ROUTED_H_ +#ifdef __cplusplus +extern "C" { +#endif +#ident "$Revision: 1.8 $" + +/* + * Routing Information Protocol + * + * Derived from Xerox NS Routing Information Protocol + * by changing 32-bit net numbers to sockaddr's and + * padding stuff to 32-bit boundaries. + */ + +#define RIPv1 1 +#define RIPv2 2 +#ifndef RIPVERSION +#define RIPVERSION RIPv1 +#endif + +#define RIP_PORT 520 + +#if RIPVERSION == 1 +/* Note that this so called sockaddr has a 2-byte sa_family and no sa_len. + * It is not a UNIX sockaddr, but the shape of an address as defined + * in RIPv1. + */ +struct netinfo { + struct sockaddr rip_dst; /* destination net/host */ + int rip_metric; /* cost of route */ +}; +#else +struct netinfo { + u_short n_family; +#define RIP_AF_INET htons(AF_INET) +#define RIP_AF_UNSPEC 0 +#define RIP_AF_AUTH 0xffff + u_short n_tag; /* optional in RIPv2 */ + u_int n_dst; /* destination net or host */ +#define RIP_DEFAULT 0 + u_int n_mask; /* netmask in RIPv2 */ + u_int n_nhop; /* optional next hop in RIPv2 */ + u_int n_metric; /* cost of route */ +}; +#endif + +/* RIPv2 authentication */ +struct netauth { + u_short a_type; +#define RIP_AUTH_PW htons(2) /* password type */ + union { +#define RIP_AUTH_PW_LEN 16 + char au_pw[RIP_AUTH_PW_LEN]; + } au; +}; + +struct rip { + u_char rip_cmd; /* request/response */ + u_char rip_vers; /* protocol version # */ + u_short rip_res1; /* pad to 32-bit boundary */ + union { /* variable length... */ + struct netinfo ru_nets[1]; + char ru_tracefile[1]; + struct netauth ru_auth[1]; + } ripun; +#define rip_nets ripun.ru_nets +#define rip_tracefile ripun.ru_tracefile +}; + +/* Packet types. + */ +#define RIPCMD_REQUEST 1 /* want info */ +#define RIPCMD_RESPONSE 2 /* responding to request */ +#define RIPCMD_TRACEON 3 /* turn tracing on */ +#define RIPCMD_TRACEOFF 4 /* turn it off */ + +/* Gated extended RIP to include a "poll" command instead of using + * RIPCMD_REQUEST with (RIP_AF_UNSPEC, RIP_DEFAULT). RFC 1058 says + * command 5 is used by Sun Microsystems for its own purposes. + */ +#define RIPCMD_POLL 5 + +#define RIPCMD_MAX 6 + +#ifdef RIPCMDS +char *ripcmds[RIPCMD_MAX] = { + "#0", "REQUEST", "RESPONSE", "TRACEON", "TRACEOFF" +}; +#endif + +#define HOPCNT_INFINITY 16 +#define MAXPACKETSIZE 512 /* max broadcast size */ +#define NETS_LEN ((MAXPACKETSIZE-sizeof(struct rip)) \ + / sizeof(struct netinfo) +1) + +#define INADDR_RIP_GROUP (u_long)0xe0000009 /* 224.0.0.9 */ + + +/* Timer values used in managing the routing table. + * + * Complete tables are broadcast every SUPPLY_INTERVAL seconds. + * If changes occur between updates, dynamic updates containing only changes + * may be sent. When these are sent, a timer is set for a random value + * between MIN_WAITTIME and MAX_WAITTIME, and no additional dynamic updates + * are sent until the timer expires. + * + * Every update of a routing entry forces an entry's timer to be reset. + * After EXPIRE_TIME without updates, the entry is marked invalid, + * but held onto until GARBAGE_TIME so that others may see it, to + * "poison" the bad route. + */ +#define SUPPLY_INTERVAL 30 /* time to supply tables */ +#define MIN_WAITTIME 2 /* min sec until next flash updates */ +#define MAX_WAITTIME 5 /* max sec until flash update */ + +#define STALE_TIME 90 /* switch to a new gateway */ +#define EXPIRE_TIME 180 /* time to mark entry invalid */ +#define GARBAGE_TIME 240 /* time to garbage collect */ + +#ifdef __cplusplus +} +#endif +#endif /* !_ROUTED_H_ */ From 1823680765faa251d4ecd9229988ba71fce9c175 Mon Sep 17 00:00:00 2001 From: Garrett Wollman Date: Mon, 22 Jul 1996 20:56:54 +0000 Subject: [PATCH 3/5] The latest and greatest routed from Vern Schryver. This version includes many fixes to router discovery, including a number of endianness bug fixes. (Router discovery appears to actually work now.) This should be the end of it for the Sun `rdisc' program. (People currently using rdisc(8) should be able to go back to routed again.) Obtained from: Vernon J. Schryver --- usr.sbin/routed/Makefile | 2 +- usr.sbin/routed/defs.h | 196 ++++---- usr.sbin/routed/if.c | 701 ++++++++++++++------------- usr.sbin/routed/input.c | 189 ++++---- usr.sbin/routed/main.c | 196 ++++---- usr.sbin/routed/output.c | 517 ++++++++++++-------- usr.sbin/routed/parms.c | 538 +++++++++++---------- usr.sbin/routed/pathnames.h | 6 +- usr.sbin/routed/rdisc.c | 280 ++++++----- usr.sbin/routed/routed.8 | 151 ++++-- usr.sbin/routed/rtquery/Makefile | 1 + usr.sbin/routed/rtquery/rtquery.8 | 56 ++- usr.sbin/routed/rtquery/rtquery.c | 286 +++++++---- usr.sbin/routed/table.c | 766 ++++++++++++++++-------------- usr.sbin/routed/trace.c | 359 ++++++++------ 15 files changed, 2411 insertions(+), 1833 deletions(-) diff --git a/usr.sbin/routed/Makefile b/usr.sbin/routed/Makefile index 7436a2896d1..594a05059d7 100644 --- a/usr.sbin/routed/Makefile +++ b/usr.sbin/routed/Makefile @@ -3,6 +3,6 @@ PROG= routed SRCS= if.c input.c main.c output.c parms.c radix.c rdisc.c table.c trace.c MAN8= routed.8 -SUBDIR= rtquery rttrace +SUBDIR= rtquery .include diff --git a/usr.sbin/routed/defs.h b/usr.sbin/routed/defs.h index 02ac4c249f7..85eb3dad517 100644 --- a/usr.sbin/routed/defs.h +++ b/usr.sbin/routed/defs.h @@ -33,7 +33,7 @@ * @(#)defs.h 8.1 (Berkeley) 6/5/93 */ -#ident "$Revision: 1.1 $" +#ident "$Revision: 1.11 $" /* Definitions for RIPv2 routing process. * @@ -104,12 +104,10 @@ struct walkarg; #define _HAVE_SIN_LEN #endif -#ifdef sgi /* Turn on if IP_DROP_MEMBERSHIP and IP_ADD_MEMBERSHIP do not look at * the dstaddr of point-to-point interfaces. */ -#define MCAST_PPP_BUG -#endif +/* #define MCAST_PPP_BUG */ #define NEVER (24*60*60) /* a long time */ #define EPOCH NEVER /* bias time by this to avoid <0 */ @@ -121,12 +119,6 @@ struct walkarg; #define CHECK_ACT_INTERVAL 30 /* when advertising */ #define CHECK_QUIET_INTERVAL 300 /* when not */ - -/* set times to this to continue poisoning a route */ -#define POISON_SECS (GARBAGE_TIME - POISON_TIME) - -#define NET_S_METRIC 1 /* metric used on synthetic routes */ - #define LIM_SEC(s,l) ((s).tv_sec = MIN((s).tv_sec, (l))) @@ -162,16 +154,15 @@ struct rt_entry { struct radix_node rt_nodes[2]; /* radix tree glue */ u_int rt_state; # define RS_IF 0x001 /* for network interface */ -# define RS_NET_SUB 0x002 /* fake net route for subnet */ -# define RS_NET_HOST 0x004 /* fake net route for host */ -# define RS_NET_INT 0x008 /* authority route */ -# define RS_NET_S (RS_NET_SUB | RS_NET_HOST | RS_NET_INT) -# define RS_SUBNET 0x010 /* subnet route from any source */ -# define RS_LOCAL 0x020 /* loopback for pt-to-pt */ -# define RS_MHOME 0x040 /* from -m */ -# define RS_GW 0x080 /* from -g */ -# define RS_STATIC 0x100 /* from the kernel */ -# define RS_RDISC 0x200 /* from router discovery */ +# 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 */ +# define RS_PERMANENT (RS_MHOME | RS_STATIC | RS_NET_SYN | RS_RDISC) struct sockaddr_in rt_dst_sock; naddr rt_mask; struct rt_spare { @@ -184,8 +175,8 @@ struct rt_entry { #define NUM_SPARES 4 } rt_spares[NUM_SPARES]; u_int rt_seqno; /* when last changed */ - char rt_hold_metric; - time_t rt_hold_down; + 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 @@ -205,9 +196,7 @@ struct rt_entry { * nor non-passive, remote interfaces that are not aliases * (i.e. remote & metric=0) */ -#define AGE_RT(rt,ifp) (0 == ((rt)->rt_state & (RS_GW | RS_MHOME | RS_STATIC \ - | RS_NET_SUB | RS_NET_HOST \ - | RS_RDISC)) \ +#define AGE_RT(rt,ifp) (0 == ((rt)->rt_state & RS_PERMANENT) \ && (!((rt)->rt_state & RS_IF) \ || (ifp) == 0 \ || (((ifp)->int_state & IS_REMOTE) \ @@ -220,14 +209,18 @@ struct rt_entry { * - 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(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))) +#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 @@ -242,27 +235,27 @@ struct interface { 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) */ - naddr int_host_addr; /* RIPv1 net for pt-to-pt link (h) */ - naddr int_host_mask; /* RIPv1 mask for pt-to-pt (h) */ int int_rip_sock; /* for queries */ int int_if_flags; /* copied from kernel */ u_int int_state; time_t int_act_time; /* last thought healthy */ - time_t int_quiet_time; /* last inactive */ u_short int_transitions; /* times gone up-down */ char int_metric; char int_d_metric; /* for faked default route */ - u_int int_data_ipackets; /* previous network stats */ - u_int int_data_ierrors; - u_int int_data_opackets; - u_int int_data_oerrors; + struct int_data { + u_int ipackets; /* previous network stats */ + u_int ierrors; + u_int opackets; + u_int oerrors; #ifdef sgi - u_int int_data_odrops; + u_int odrops; #endif - time_t int_data_ts; /* timestamp on network stats */ + 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 */ @@ -280,25 +273,32 @@ struct interface { #define IS_ALL_ROUTERS 0x0000080 /* in INADDR_ALLROUTERS_GROUP */ #define IS_RIP_QUERIED 0x0000100 /* query broadcast */ #define IS_BROKE 0x0000200 /* seems to be broken */ -#define IS_ACTIVE 0x0000400 /* heard from it at least once */ -#define IS_QUIET 0x0000800 /* have not heard from it recently */ -#define IS_NEED_NET_SUB 0x0001000 /* need RS_NET_SUB route */ -#define IS_NO_AG 0x0002000 /* do not aggregate subnets */ -#define IS_NO_SUPER_AG 0x0004000 /* do not aggregate networks */ -#define IS_NO_RIPV1_IN 0x0008000 /* no RIPv1 input at all */ -#define IS_NO_RIPV2_IN 0x0010000 /* no RIPv2 input at all */ -#define IS_NO_RIP_IN (IS_NO_RIPV2_IN | IS_NO_RIPV2_IN) -#define IS_NO_RIPV1_OUT 0x0020000 /* no RIPv1 output at all */ -#define IS_NO_RIPV2_OUT 0x0040000 /* no RIPv2 output at all */ +#define IS_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_ADV_IN 0x0080000 -#define IS_NO_SOL_OUT 0x0100000 /* no solicitations */ -#define IS_SOL_OUT 0x0200000 /* send solicitations */ +#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 0x0400000 /* do not advertise rdisc */ -#define IS_ADV_OUT 0x0800000 /* advertise rdisc */ +#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 0x1000000 /* broadcast instead of multicast */ +#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) @@ -316,21 +316,30 @@ struct ag_info { 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 0x01 /* combine with coaser mask */ -#define AGS_PROMOTE 0x002 /* synthesize combined routes */ -#define AGS_REDUN0 0x004 /* redundant, finer routes output */ -#define AGS_REDUN1 0x008 +#define 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_RIPV2 0x020 /* send only as RIPv2 */ -#define AGS_DEAD 0x080 /* dead--ignore differing gate */ -#define AGS_RDISC 0x100 /* suppresses most routes */ +#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) }; @@ -338,8 +347,8 @@ struct ag_info { extern struct parm { struct parm *parm_next; char parm_name[IFNAMSIZ+1]; - naddr parm_a_h; - naddr parm_m; + naddr parm_addr_h; + naddr parm_mask; char parm_d_metric; u_int parm_int_state; @@ -353,6 +362,7 @@ extern struct intnet { struct intnet *intnet_next; naddr intnet_addr; naddr intnet_mask; + char intnet_metric; } *intnets; @@ -371,11 +381,9 @@ extern int rdisc_sock; /* router-discovery raw socket */ extern int seqno; /* sequence number for messages */ extern int supplier; /* process should supply updates */ -extern int default_gateway; /* 1=advertise default */ extern int lookforinterfaces; /* 1=probe for new up interfaces */ extern int supplier_set; /* -s or -q requested */ extern int ridhosts; /* 1=reduce host routes */ -extern int ppp_noage; /* 1=do not age quiet link routes */ extern int mhome; /* 1=want multi-homed host route */ extern int advertise_mhome; /* 1=must continue adverising it */ extern int auth_ok; /* 1=ignore auth if we do not care */ @@ -397,15 +405,16 @@ extern naddr loopaddr; /* our address on loopback */ extern int tot_interfaces; /* # of remote and local interfaces */ extern int rip_interfaces; /* # of interfaces doing RIP */ extern struct interface *ifnet; /* all interfaces */ -extern int have_ripv1; /* have a RIPv1 interface */ +extern int 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 3 -#define TRACEPACKETS (tracelevel >= 2) /* note packets */ #define TRACECONTENTS (tracelevel >= 3) /* display packet contents */ +#define TRACEPACKETS (tracelevel >= 2) /* note packets */ #define TRACEACTIONS (tracelevel != 0) extern FILE *ftrace; /* output trace file */ @@ -422,7 +431,8 @@ 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}; +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); @@ -454,14 +464,16 @@ 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*, char*); +extern void trace_off(char*, ...); extern void trace_flush(void); extern void set_tracelevel(void); -extern void trace_msg(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 *); @@ -476,8 +488,8 @@ extern char *addrname(naddr, naddr, int); extern void rdisc_age(naddr); extern void set_rdisc_mg(struct interface *, int); extern void set_supplier(void); -extern void ifbad_rdisc(struct interface *); -extern void ifok_rdisc(struct interface *); +extern void 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); @@ -490,12 +502,11 @@ extern void sigterm(int); extern void sigtrace_on(int); extern void sigtrace_off(int); -extern void fix_kern(void); extern void flush_kern(void); extern void age(naddr); extern void ag_flush(naddr, naddr, void (*)(struct ag_info *)); -extern void ag_check(naddr, naddr, naddr, char, char, u_int, +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); @@ -512,21 +523,21 @@ extern void rtswitch(struct rt_entry *, struct rt_spare *); extern void rtbad(struct rt_entry *); -extern struct rt_addrinfo rtinfo; #define S_ADDR(x) (((struct sockaddr_in *)(x))->sin_addr.s_addr) -#define RTINFO_DST rtinfo.rti_info[RTAX_DST] -#define RTINFO_GATE rtinfo.rti_info[RTAX_GATEWAY] -#define RTINFO_NETMASK rtinfo.rti_info[RTAX_NETMASK] -#define RTINFO_IFA rtinfo.rti_info[RTAX_IFA] -#define RTINFO_AUTHOR rtinfo.rti_info[RTAX_AUTHOR] -#define RTINFO_BRD rtinfo.rti_info[RTAX_BRD] -#define RTINFO_IFP ((struct sockaddr_dl *)rtinfo.rti_info[RTAX_IFP]) -void rt_xaddrs(struct sockaddr *, struct sockaddr *, int); +#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 *, struct interface *); -extern naddr ripv1_mask_host(naddr,struct interface *, struct interface *); -#define on_net(tgt, net, mask) ((ntohl(tgt) & mask) == (net & mask)) +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); #ifdef sgi extern int sysctl(int *, u_int, void *, size_t *, void *, size_t); @@ -534,8 +545,9 @@ extern int sysctl(int *, u_int, void *, size_t *, void *, size_t); extern void addrouteforif(register struct interface *); extern void ifinit(void); extern int walk_bad(struct radix_node *, struct walkarg *); -extern int ifok(struct interface *, char *); -extern void ifbad(struct interface *, char *); +extern 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); diff --git a/usr.sbin/routed/if.c b/usr.sbin/routed/if.c index a102b2e156c..38d344ca049 100644 --- a/usr.sbin/routed/if.c +++ b/usr.sbin/routed/if.c @@ -31,11 +31,11 @@ * SUCH DAMAGE. */ -#ifndef lint +#if !defined(lint) && !defined(sgi) static char sccsid[] = "@(#)if.c 8.1 (Berkeley) 6/5/93"; #endif /* not lint */ -#ident "$Revision: 1.1 $" +#ident "$Revision: 1.15 $" #include "defs.h" #include "pathnames.h" @@ -48,7 +48,8 @@ naddr loopaddr; /* our address on loopback */ struct timeval ifinit_timer; -int have_ripv1; /* have a RIPv1 interface */ +int have_ripv1_out; /* have a RIPv1 interface */ +int have_ripv1_in; /* Find the interface with an address @@ -61,15 +62,20 @@ ifwithaddr(naddr addr, struct interface *ifp, *possible = 0; for (ifp = ifnet; ifp; ifp = ifp->int_next) { - if ((ifp->int_state & IS_REMOTE) && !remote) - continue; - - if (ifp->int_addr == addr + if ((ifp->int_addr == addr + && !(ifp->int_if_flags & IFF_POINTOPOINT)) + || (ifp->int_dstaddr == addr + && (ifp->int_if_flags & IFF_POINTOPOINT)) || ((ifp->int_if_flags & IFF_BROADCAST) && ifp->int_brdaddr == addr && bcast)) { - if (!(ifp->int_state & IS_BROKE)) + if ((ifp->int_state & IS_REMOTE) && !remote) + continue; + + if (!(ifp->int_state & IS_BROKE) + && !(ifp->int_state & IS_PASSIVE)) return ifp; + possible = ifp; } } @@ -81,7 +87,7 @@ ifwithaddr(naddr addr, /* find the interface with a name */ struct interface * -ifwithname(char *name, /* enp0 or whatever */ +ifwithname(char *name, /* "ec0" or whatever */ naddr addr) /* 0 or network address */ { struct interface *ifp; @@ -89,8 +95,8 @@ ifwithname(char *name, /* enp0 or whatever */ for (ifp = ifnet; 0 != ifp; ifp = ifp->int_next) { if (!strcmp(ifp->int_name, name) - && ((addr == 0 && !(ifp->int_state & IS_ALIAS) - || ifp->int_addr == addr))) + && (ifp->int_addr == addr + || (addr == 0 && !(ifp->int_state & IS_ALIAS)))) return ifp; } return 0; @@ -119,49 +125,39 @@ struct interface * iflookup(naddr addr) { struct interface *ifp, *maybe; - int twice; - twice = 0; maybe = 0; - do { - for (ifp = ifnet; ifp; ifp = ifp->int_next) { + 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; - if ((ifp->int_if_flags & IFF_POINTOPOINT) - && ifp->int_dstaddr == addr) - return ifp; - /* Look for the longest approximate match. */ - if (on_net(addr, - ifp->int_net, ifp->int_mask) + if (on_net(addr, ifp->int_net, ifp->int_mask) && (maybe == 0 || ifp->int_mask > maybe->int_mask)) maybe = ifp; } + } - if (maybe != 0) - return maybe; - - /* See if an interface has come up since we checked. - */ - ifinit(); - } while (twice++ == 0); - - return 0; + return maybe; } /* Return the classical netmask for an IP address. */ naddr -std_mask(naddr addr) +std_mask(naddr addr) /* in network order */ { NTOHL(addr); /* was a host, not a network */ @@ -175,25 +171,25 @@ std_mask(naddr addr) } -/* find the netmask that would be inferred by RIPv1 listeners - * on the given interface +/* 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 *ifp1, /* as seen on this interface */ - struct interface *ifp2) /* but not this interface */ + struct interface *ifp) /* as seen on this interface */ { naddr mask = 0; if (addr == 0) /* default always has 0 mask */ return mask; - if (ifp1 != 0) { - /* If the target is that of the associated interface on which - * it arrived, then use the netmask of the interface. + if (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, ifp1->int_net, ifp1->int_std_mask)) - mask = ifp1->int_mask; + 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 @@ -202,13 +198,10 @@ ripv1_mask_net(naddr addr, /* in network byte order */ * such interface, prefer the interface with the longest * match. */ - for (ifp1 = ifnet; ifp1 != 0; ifp1 = ifp1->int_next) { - if (ifp1 != ifp2 - && !(ifp1->int_if_flags & IFF_POINTOPOINT) - && on_net(addr, - ifp1->int_std_net, ifp1->int_std_mask) - && ifp1->int_mask > mask) - mask = ifp1->int_mask; + 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; } } @@ -223,10 +216,9 @@ ripv1_mask_net(naddr addr, /* in network byte order */ naddr ripv1_mask_host(naddr addr, /* in network byte order */ - struct interface *ifp1, /* as seen on this interface */ - struct interface *ifp2) /* but not this interface */ + struct interface *ifp) /* as seen on this interface */ { - naddr mask = ripv1_mask_net(addr, ifp1, ifp2); + naddr mask = ripv1_mask_net(addr, ifp); /* If the computed netmask does not mask the address, @@ -266,10 +258,30 @@ ifdel(struct interface *ifp) struct interface *ifp1; - if (TRACEACTIONS) - trace_if("Del", ifp); + 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) @@ -281,106 +293,121 @@ ifdel(struct interface *ifp) ? ifp->int_dstaddr : ifp->int_addr); if (setsockopt(rip_sock,IPPROTO_IP,IP_DROP_MEMBERSHIP, - &m, sizeof(m)) < 0) - DBGERR(1,"setsockopt(IP_DROP_MEMBERSHIP" - " RIP)"); + &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(); } - set_rdisc_mg(ifp, 0); - } - /* Zap all routes associated with this interface. - * Assume routes just using gateways beyond this interface will - * timeout naturally, and have probably already died. - */ - ifp->int_state |= IS_BROKE; - (void)rn_walktree(rhead, walk_bad, 0); - ifbad_rdisc(ifp); - - if (!(ifp->int_state & IS_ALIAS)) { tot_interfaces--; - if (0 == (ifp->int_state & (IS_NO_RIP_IN|IS_PASSIVE))) + if (!IS_RIP_OFF(ifp->int_state)) rip_interfaces--; - } - /* unlink and forget the interface */ - if (rip_sock_mcast == ifp) - rip_sock_mcast = 0; - if (ifp->int_next != 0) - ifp->int_next->int_prev = ifp->int_prev; - if (ifp->int_prev != 0) - ifp->int_prev->int_next = ifp->int_next; - else - ifnet = ifp->int_next; + /* 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); - if (!(ifp->int_state & IS_ALIAS)) { - /* delete aliases of primary interface */ - for (ifp1 = ifnet; 0 != ifp1; ifp1 = ifp1->int_next) { - if (!strcmp(ifp->int_name, ifp1->int_name)) - ifdel(ifp1); - } + 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 -ifbad(struct interface *ifp, - char *pat) +if_bad(struct interface *ifp) { + struct interface *ifp1; + + if (ifp->int_state & IS_BROKE) return; - if (pat) - msglog(pat, ifp->int_name, naddr_ntoa(ifp->int_addr)); - LIM_SEC(ifinit_timer, now.tv_sec+CHECK_BAD_INTERVAL); - ifp->int_state |= IS_BROKE; - ifp->int_state &= ~(IS_RIP_QUERIED | IS_ACTIVE | IS_QUIET); - ifp->int_quiet_time = now.tv_sec - MaxMaxAdvertiseInterval; - ifp->int_data_ts = 0; + ifp->int_state |= (IS_BROKE | IS_SICK); + ifp->int_state &= ~(IS_RIP_QUERIED | IS_ACTIVE); + ifp->int_data.ts = 0; trace_if("Chg", ifp); - (void)rn_walktree(rhead, walk_bad, 0); - - ifbad_rdisc(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 */ -ifok(struct interface *ifp, - char *type) +if_ok(struct interface *ifp, + char *type) { - if (!(ifp->int_state & IS_BROKE)) + 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; - ifp->int_data_ts = 0; + ifp->int_state &= ~(IS_BROKE | IS_SICK); + ifp->int_data.ts = 0; - ifok_rdisc(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_ok(ifp1, type); + } + if_ok_rdisc(ifp); + } return 1; } -struct rt_addrinfo rtinfo; - /* disassemble routing message */ void -rt_xaddrs(struct sockaddr *sa, +rt_xaddrs(struct rt_addrinfo *info, + struct sockaddr *sa, struct sockaddr *lim, int addrs) { @@ -397,18 +424,17 @@ rt_xaddrs(struct sockaddr *sa, #endif - bzero(rtinfo.rti_info, sizeof(rtinfo.rti_info)); - rtinfo.rti_addrs = addrs; - + 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 - rtinfo.rti_info[i] = (sa->sa_len != 0) ? sa : &sa_zero; + info->rti_info[i] = (sa->sa_len != 0) ? sa : &sa_zero; sa = (struct sockaddr *)((char*)(sa) + ROUNDUP(sa->sa_len)); #else - rtinfo.rti_info[i] = sa; + info->rti_info[i] = sa; sa = (struct sockaddr *)((char*)(sa) + ROUNDUP(_FAKE_SA_LEN_DST(sa))); #endif @@ -434,6 +460,7 @@ ifinit(void) # define COMP_NOBADR 0x10 # define COMP_NOMASK 0x20 # define COMP_DUP 0x40 +# define COMP_BAD_METRIC 0x80 struct interface ifs, ifs0, *ifp, *ifp1; struct rt_entry *rt; @@ -444,6 +471,7 @@ ifinit(void) struct sockaddr_dl *sdl; int in, ierr, out, oerr; struct intnet *intnetp; + struct rt_addrinfo info; #ifdef SIOCGIFMETRIC struct ifreq ifr; #endif @@ -455,7 +483,7 @@ ifinit(void) /* 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; + ifp->int_state &= ~(IS_CHECKED | IS_DUP); /* Fetch the interface list, without too many system calls * since we do it repeatedly. @@ -491,21 +519,21 @@ ifinit(void) 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_quiet_time = (now.tv_sec - - MaxMaxAdvertiseInterval); - ifs0.int_data_ts = now.tv_sec; - ifs0.int_data_ipackets = ifm->ifm_data.ifi_ipackets; - ifs0.int_data_ierrors = ifm->ifm_data.ifi_ierrors; - ifs0.int_data_opackets = ifm->ifm_data.ifi_opackets; - ifs0.int_data_oerrors = ifm->ifm_data.ifi_oerrors; + 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; + ifs0.int_data.odrops = ifm->ifm_data.ifi_odrops; #endif sdl = (struct sockaddr_dl *)(ifm + 1); sdl->sdl_data[sdl->sdl_nlen] = 0; @@ -516,11 +544,11 @@ ifinit(void) continue; } - rt_xaddrs((struct sockaddr *)(ifam+1), + rt_xaddrs(&info, (struct sockaddr *)(ifam+1), (struct sockaddr *)ifam2, ifam->ifam_addrs); - if (RTINFO_IFA == 0) { + if (INFO_IFA(&info) == 0) { if (iff_alive(ifs.int_if_flags)) { if (!(prev_complaints & COMP_NOADDR)) msglog("%s has a bad address", @@ -529,10 +557,10 @@ ifinit(void) } continue; } - if (RTINFO_IFA->sa_family != AF_INET) { + if (INFO_IFA(&info)->sa_family != AF_INET) { if (iff_alive(ifs.int_if_flags)) { if (!(prev_complaints & COMP_NOT_INET)) - trace_msg("%s: not AF_INET", + trace_act("%s: not AF_INET\n", sdl->sdl_data); complaints |= COMP_NOT_INET; } @@ -540,12 +568,12 @@ ifinit(void) } bcopy(&ifs0, &ifs, sizeof(ifs0)); - ifs0.int_state |= IS_ALIAS; /* next will be an alias */ + ifs0.int_state |= IS_ALIAS; /* next will be an alias */ - ifs.int_addr = S_ADDR(RTINFO_IFA); + ifs.int_addr = S_ADDR(INFO_IFA(&info)); if (ifs.int_if_flags & IFF_BROADCAST) { - if (RTINFO_NETMASK == 0) { + if (INFO_MASK(&info) == 0) { if (iff_alive(ifs.int_if_flags)) { if (!(prev_complaints & COMP_NOMASK)) msglog("%s has no netmask", @@ -554,13 +582,15 @@ ifinit(void) } continue; } - ifs.int_mask = ntohl(S_ADDR(RTINFO_NETMASK)); + 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 (RTINFO_BRD == 0) { + if (INFO_BRD(&info) == 0) { if (iff_alive(ifs.int_if_flags)) { if (!(prev_complaints & COMP_NOBADR)) msglog("%s has no" @@ -570,11 +600,11 @@ ifinit(void) } continue; } - ifs.int_brdaddr = S_ADDR(RTINFO_BRD); + ifs.int_brdaddr = S_ADDR(INFO_BRD(&info)); } else if (ifs.int_if_flags & IFF_POINTOPOINT) { - if (RTINFO_BRD == 0 - || RTINFO_BRD->sa_family != AF_INET) { + 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" @@ -584,16 +614,18 @@ ifinit(void) } continue; } - ifs.int_dstaddr = S_ADDR(RTINFO_BRD); - ifs.int_net = ntohl(ifs.int_dstaddr); + ifs.int_dstaddr = S_ADDR(INFO_BRD(&info)); 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; + ifs.int_state |= IS_PASSIVE | IS_NO_RIP; ifs.int_dstaddr = ifs.int_addr; - ifs.int_net = ntohl(ifs.int_dstaddr); 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; @@ -601,11 +633,10 @@ ifinit(void) } } else { - if (TRACEACTIONS - && !(prev_complaints & COMP_WIERD)) - msglog("%s is neither broadcast" - " nor point-to-point nor loopback", - sdl->sdl_data); + if (!(prev_complaints & COMP_WIERD)) + trace_act("%s is neither broadcast" + " nor point-to-point nor loopback", + sdl->sdl_data); complaints |= COMP_WIERD; continue; } @@ -623,15 +654,22 @@ ifinit(void) strncpy(ifr.ifr_name, sdl->sdl_data, sizeof(ifr.ifr_name)); if (ioctl(rt_sock, SIOCGIFMETRIC, &ifr) < 0) { DBGERR(1, "ioctl(SIOCGIFMETRIC)"); - ifs.int_metric = HOPCNT_INFINITY; + ifs.int_metric = 0; } else { - ifs.int_metric = ifr.ifr_metric+1; + ifs.int_metric = ifr.ifr_metric; } #else - ifs.int_metric = ifam->ifam_metric+1; + ifs.int_metric = ifam->ifam_metric; #endif - if (ifs.int_metric >= HOPCNT_INFINITY) - ifs.int_metric = HOPCNT_INFINITY; + 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. @@ -659,7 +697,7 @@ ifinit(void) /* Forget old information about * a changed interface. */ - trace_msg("interface %s has changed", + trace_act("interface %s has changed\n", ifp->int_name); ifdel(ifp); ifp = 0; @@ -667,113 +705,151 @@ ifinit(void) } if (ifp != 0) { + 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)) - ifbad(ifp, "interface %s to %s" - " turned off"); - ifp->int_if_flags &= ~IFF_UP_RUNNING; + 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)ifok(ifp, ""); + (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) + 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; + 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 - oerr = (ifs.int_data_oerrors - ifp->int_data_oerrors - + ifs.int_data_odrops - ifp->int_data_odrops); -#else - oerr = ifs.int_data_oerrors - ifp->int_data_oerrors; + /* 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 - - ifp->int_data_ipackets = ifs.int_data_ipackets; - ifp->int_data_ierrors = ifs.int_data_ierrors; - ifp->int_data_opackets = ifs.int_data_opackets; - ifp->int_data_oerrors = ifs.int_data_oerrors; -#ifdef sgi - ifp->int_data_odrops = ifs.int_data_odrops; -#endif - /* If the interface just awoke, restart the counters. */ - if (ifp->int_data_ts == 0) { - ifp->int_data_ts = now.tv_sec; + if (ifp->int_data.ts == 0) { + ifp->int_data = ifs.int_data; continue; } - ifp->int_data_ts = now.tv_sec; + 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) - continue; - - /* Withhold judgement when there is no traffic - */ - if (in == 0 && out == 0 && ierr == 0 && oerr == 0) { - if (!(ifp->int_state & IS_QUIET)) { - ifp->int_state |= IS_QUIET; - ifp->int_quiet_time = now.tv_sec; - } + if (ierr < 0 || in < 0 || oerr < 0 || out < 0) { + LIM_SEC(ifinit_timer, + now.tv_sec+CHECK_BAD_INTERVAL); continue; } - /* It is bad if input or output is not working + /* 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)) { - ifbad(ifp,"interface %s to %s not working"); + 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; - ifp->int_state &= ~IS_QUIET; - if (ifok(ifp, "")) - addrouteforif(ifp); + (void)if_ok(ifp, ""); continue; } - /* See if this new interface duplicates an existing - * interface. + /* 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_addr == ifs.int_addr - && ifp->int_mask == ifs.int_mask) - break; - } - if (ifp != 0) { - if (iff_alive(ifs.int_if_flags)) { - if (!(prev_complaints & COMP_DUP)) - msglog("%s is duplicated by %s at %s", - sdl->sdl_data, ifp->int_name, - naddr_ntoa(ifp->int_addr)); - complaints |= COMP_DUP; - } - continue; - } + 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); - ifok_rdisc(&ifs); - - /* create the interface */ + /* Add it to the list of interfaces + */ ifp = (struct interface *)malloc(sizeof(*ifp)); if (ifp == 0) BADERR(1,"ifinit: out of memory"); @@ -783,27 +859,18 @@ ifinit(void) ifnet->int_prev = ifp; } ifnet = ifp; + trace_if("Add", ifp); /* Count the # of directly connected networks. */ if (!(ifp->int_state & IS_ALIAS)) { if (!(ifp->int_if_flags & IFF_LOOPBACK)) tot_interfaces++; - if (0 == (ifp->int_state & (IS_NO_RIP_IN|IS_PASSIVE))) + if (!IS_RIP_OFF(ifp->int_state)) rip_interfaces++; } - /* note dead interfaces */ - if (iff_alive(ifs.int_if_flags)) { - set_rdisc_mg(ifp, 1); - } else { - LIM_SEC(ifinit_timer, now.tv_sec+CHECK_BAD_INTERVAL); - ifp->int_state |= IS_BROKE; - } - - if (TRACEACTIONS) - trace_if("Add", ifp); - + if_ok_rdisc(ifp); rip_on(ifp); } @@ -822,7 +889,6 @@ ifinit(void) && (ifp = ifwithaddr(myaddr, 0, 0)) != 0 && foundloopback)) { advertise_mhome = 1; - del_static(myaddr, HOST_MASK, 0); rt = rtget(myaddr, HOST_MASK); if (rt != 0) { if (rt->rt_ifp != ifp @@ -832,13 +898,12 @@ ifinit(void) } else { rtchange(rt, rt->rt_state | RS_MHOME, loopaddr, loopaddr, - ifp->int_metric, 0, - ifp, rt->rt_time, 0); + 0, 0, ifp, rt->rt_time, 0); } } if (rt == 0) rtadd(myaddr, HOST_MASK, loopaddr, loopaddr, - ifp->int_metric, 0, RS_MHOME, ifp); + 0, 0, RS_MHOME, ifp); } for (ifp = ifnet; ifp != 0; ifp = ifp1) { @@ -847,36 +912,25 @@ ifinit(void) /* Forget any interfaces that have disappeared. */ if (!(ifp->int_state & (IS_CHECKED | IS_REMOTE))) { - trace_msg("interface %s has disappeared", + trace_act("interface %s has disappeared\n", ifp->int_name); ifdel(ifp); continue; } - if (ifp->int_state & IS_BROKE) + 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_RIPV2_OUT) - && !(ifp->int_if_flags & IFF_LOOPBACK)) - have_ripv1 = 1; - } - - /* add the authority interfaces */ - for (intnetp = intnets; intnetp!=0; intnetp = intnetp->intnet_next) { - rt = rtget(intnetp->intnet_addr, intnetp->intnet_mask); - if (rt != 0 - && !(rt->rt_state & RS_IF) - && !(rt->rt_state & RS_NET_INT)) { - rtdelete(rt); - rt = 0; + if (!(ifp->int_if_flags & IFF_LOOPBACK)) { + if (!(ifp->int_state & IS_NO_RIPV1_OUT)) + have_ripv1_out = 1; + if (!(ifp->int_state & IS_NO_RIPV1_IN)) + have_ripv1_in = 1; } - if (rt == 0) - rtadd(intnetp->intnet_addr, intnetp->intnet_mask, - loopaddr, loopaddr, - 1, 0, RS_NET_INT, 0); } for (ifp = ifnet; ifp != 0; ifp = ifp->int_next) { @@ -908,86 +962,69 @@ ifinit(void) } else { ifp1 = rt->rt_ifp; } - rtchange(rt,((rt->rt_state | (RS_IF|RS_LOCAL)) - & ~RS_NET_S), + rtchange(rt,((rt->rt_state & ~RS_NET_SYN) + | (RS_IF|RS_LOCAL)), loopaddr, loopaddr, - ifp1->int_metric, 0, - ifp1, rt->rt_time, 0); + 0, 0, ifp1, rt->rt_time, 0); } else { rtadd(ifp->int_addr, HOST_MASK, loopaddr, loopaddr, - ifp->int_metric, 0, - (RS_IF | RS_LOCAL), ifp); + 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 -add_net_sub(struct interface *ifp, - naddr dst, - naddr mask, - u_int state) +check_net_syn(struct interface *ifp) { struct rt_entry *rt; - rt = rtget(dst, mask); - if (rt != 0) { - if (0 != (rt->rt_state & (RS_STATIC | RS_LOCAL - | RS_MHOME | RS_GW))) - return; - if ((rt->rt_state & state) != state - || rt->rt_metric != NET_S_METRIC) { - rtchange(rt, rt->rt_state | state, - rt->rt_gate, rt->rt_router, - NET_S_METRIC, rt->rt_tag, - rt->rt_ifp, rt->rt_time, 0); - } - return; - } - - rtadd(dst, mask, ifp->int_addr, ifp->int_addr, - NET_S_METRIC, 0, state, ifp); -} - - -static void -check_net_sub(struct interface *ifp) -{ - struct interface *ifp2; - struct rt_entry *rt; - - /* See if there are any RIPv1 listeners, to determine if - * we need to synthesize a network route for an interface - * on a subnet. + /* 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. */ - for (ifp2 = ifnet; ifp2; ifp2 = ifp2->int_next) { - if (ifp2 != ifp - && !(ifp->int_state & IS_PASSIVE) - && !(ifp->int_state & IS_NO_RIPV1_OUT) - && !on_net(ifp->int_addr, - ifp2->int_std_net, - ifp2->int_std_mask)) - break; - } - - /* only if running RIPv1 somewhere */ - if (ifp2 != 0) { - ifp->int_state |= IS_NEED_NET_SUB; - add_net_sub(ifp, ifp->int_std_addr, ifp->int_std_mask, - RS_IF | RS_NET_SUB); + 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_SUB; + 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_NET_S) + && (rt->rt_state & RS_NET_SYN) && rt->rt_ifp == ifp) rtbad_sub(rt); } @@ -1002,7 +1039,7 @@ void addrouteforif(struct interface *ifp) { struct rt_entry *rt; - naddr dst, mask; + naddr dst, gate; /* skip sick interfaces @@ -1013,63 +1050,59 @@ addrouteforif(struct interface *ifp) /* If the interface on a subnet, then install a RIPv1 route to * the network as well (unless it is sick). */ - if (ifp->int_metric != HOPCNT_INFINITY - && !(ifp->int_state & IS_PASSIVE)) { - if (ifp->int_state & IS_SUBNET) { - check_net_sub(ifp); + if (ifp->int_state & IS_SUBNET) + check_net_syn(ifp); - } else if ((ifp->int_if_flags & IFF_POINTOPOINT) - && ridhosts) { - - /* The (dis)appearance of other interfaces can change - * the parent (sub)net. - */ - mask = ripv1_mask_net(ifp->int_dstaddr,0,ifp); - if (mask != ifp->int_host_mask) { - rt = rtget(ifp->int_host_addr, - ifp->int_host_mask); - ifp->int_host_addr = htonl(ntohl(ifp->int_dstaddr) - & mask); - ifp->int_host_mask = mask; - if (rt != 0 - && (rt->rt_state & RS_NET_S) - && rt->rt_ifp == ifp) - rtbad_sub(rt); - } - - add_net_sub(ifp, ifp->int_host_addr, - ifp->int_host_mask, - RS_IF | RS_NET_HOST); + 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; } - dst = (0 != (ifp->int_if_flags & (IFF_POINTOPOINT | IFF_LOOPBACK)) - ? ifp->int_dstaddr - : htonl(ifp->int_net)); - - /* We are finished if the right, main interface route exists. + /* 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) { + 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_S | RS_LOCAL)), + & ~(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_msg("re-install interface %s", ifp->int_name); + trace_act("re-install interface %s\n", + ifp->int_name); - rtadd(dst, ifp->int_mask, ifp->int_addr, ifp->int_addr, + rtadd(dst, ifp->int_mask, gate, gate, ifp->int_metric, 0, RS_IF, ifp); } } diff --git a/usr.sbin/routed/input.c b/usr.sbin/routed/input.c index c80d93f0809..aab1b0adc84 100644 --- a/usr.sbin/routed/input.c +++ b/usr.sbin/routed/input.c @@ -31,17 +31,17 @@ * SUCH DAMAGE. */ -#ifndef lint +#if !defined(lint) && !defined(sgi) static char sccsid[] = "@(#)input.c 8.1 (Berkeley) 6/5/93"; #endif /* not lint */ -#ident "$Revision: 1.1 $" +#ident "$Revision: 1.10 $" #include "defs.h" static void input(struct sockaddr_in *, struct interface*, struct rip *, int); static void input_route(struct interface *, naddr, - naddr, naddr, naddr, int, u_short); + naddr, naddr, naddr, struct netinfo *); /* process RIP input @@ -97,8 +97,7 @@ input(struct sockaddr_in *from, /* received from this IP address */ if (ifp != 0) ifp->int_state |= IS_ACTIVE; - if (TRACEPACKETS) - trace_rip("Recv", "from", from, ifp, rip, size); + trace_rip("Recv", "from", from, ifp, rip, size); if (rip->rip_vers == 0) { if (from->sin_addr.s_addr != bad_router) @@ -138,8 +137,7 @@ input(struct sockaddr_in *from, /* received from this IP address */ " from %s discarded", naddr_ntoa(FROM_NADDR)); use_auth = from->sin_addr.s_addr; - if (TRACEPACKETS) - trace_msg("discard authenticated RIPv2 message\n"); + trace_pkt("discard authenticated RIPv2 message\n"); return; } @@ -148,28 +146,29 @@ input(struct sockaddr_in *from, /* received from this IP address */ /* did the request come from a router? */ if (from->sin_port == htons(RIP_PORT)) { - /* yes, ignore it if RIP is off + /* yes, ignore it if RIP is off so that it does not + * depend on us. */ if (rip_sock < 0) { - trace_msg("ignore request while RIP off"); + trace_pkt("ignore request while RIP off\n"); return; } /* Ignore the request if we talking to ourself * (and not a remote gateway). */ - ifp1 = ifwithaddr(FROM_NADDR, 0, 0); - if (ifp1 != 0 - && (!(ifp1->int_state & IS_REMOTE) - || ifp->int_metric != 0)) { - if (TRACEPACKETS) - trace_msg("discard our own packet\n"); + 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. + * 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 @@ -234,13 +233,10 @@ input(struct sockaddr_in *from, /* received from this IP address */ return; } - if (rip->rip_vers == RIPv1) { - mask = ripv1_mask_host(dst,ifp,0); - } else { - mask = ntohl(n->n_mask); - if (mask == 0) - mask = ripv1_mask_host(dst,ifp,0); - } + if (rip->rip_vers == RIPv1 + || 0 == (mask = ntohl(n->n_mask))) + mask = ripv1_mask_host(dst,ifp); + rt = rtget(dst, mask); if (!rt) rt = rtfind(n->n_dst); @@ -250,8 +246,9 @@ input(struct sockaddr_in *from, /* received from this IP address */ if (!rt) { n->n_metric = HOPCNT_INFINITY; } else { - n->n_metric = (rt->rt_metric - + (ifp ? ifp->int_metric : 1)); + n->n_metric = rt->rt_metric+1; + if (ifp != 0) + n->n_metric += ifp->int_metric; if (n->n_metric > HOPCNT_INFINITY) n->n_metric = HOPCNT_INFINITY; if (rip->rip_vers == RIPv1) { @@ -304,7 +301,7 @@ input(struct sockaddr_in *from, /* received from this IP address */ rip->rip_tracefile[size-4] = '\0'; trace_on(rip->rip_tracefile, 0); } else { - trace_off("tracing turned off by ", + trace_off("tracing turned off by %s\n", naddr_ntoa(FROM_NADDR)); } return; @@ -319,15 +316,12 @@ input(struct sockaddr_in *from, /* received from this IP address */ /* verify message came from a router */ if (from->sin_port != ntohs(RIP_PORT)) { - if (TRACEPACKETS) - trace_msg("discard response" - " from unknown port\n"); + trace_pkt("discard RIP response from unknown port\n"); return; } if (rip_sock < 0) { - if (TRACEPACKETS) - trace_msg("discard response while RIP off"); + trace_pkt("discard response while RIP off\n"); return; } @@ -335,18 +329,19 @@ input(struct sockaddr_in *from, /* received from this IP address */ */ ifp1 = ifwithaddr(FROM_NADDR, 0, 1); if (ifp1) { - if (ifp1->int_state & IS_PASSIVE) { - msglog("bogus input from %s on supposedly" - " passive interface %s", - naddr_ntoa(FROM_NADDR), - ifp1->int_name); - - } else if (ifp1->int_state & IS_REMOTE) { - ifp1->int_act_time = now.tv_sec; - if (ifok(ifp1, "remote ")) - addrouteforif(ifp1); - } else if (TRACEPACKETS) { - trace_msg("discard our own packet\n"); + 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; } @@ -356,13 +351,20 @@ input(struct sockaddr_in *from, /* received from this IP address */ * broadcast or point-to-point networks, and from * those listed in /etc/gateways. */ - if (!ifp || (ifp->int_state & IS_PASSIVE)) { + if (!ifp) { if (from->sin_addr.s_addr != unk_router) - msglog("packet from unknown router %s", + msglog("packet from unknown router %s" + " or via unidentified interface", naddr_ntoa(FROM_NADDR)); unk_router = from->sin_addr.s_addr; return; } + if (ifp->int_state & IS_PASSIVE) { + trace_act("packet from %s via passive interface %s\n", + naddr_ntoa(FROM_NADDR), + ifp->int_name); + return; + } /* Check required version */ @@ -370,19 +372,16 @@ input(struct sockaddr_in *from, /* received from this IP address */ && rip->rip_vers == RIPv1) || ((ifp->int_state & IS_NO_RIPV2_IN) && rip->rip_vers != RIPv1)) { - if (TRACEPACKETS) - trace_msg("discard RIPv%d response\n", - rip->rip_vers); + trace_pkt("discard RIPv%d response\n", + rip->rip_vers); return; } /* Ignore routes via dead interface. */ if (ifp->int_state & IS_BROKE) { - if (TRACEPACKETS) - trace_msg("discard response via" - " broken interface %s\n", - ifp->int_name); + trace_pkt("discard response via broken interface %s\n", + ifp->int_name); return; } @@ -466,9 +465,9 @@ input(struct sockaddr_in *from, /* received from this IP address */ } } - mask = ntohl(n->n_mask); - if (rip->rip_vers == RIPv1 || mask == 0) { - mask = ripv1_mask_host(dst,ifp,0); + if (rip->rip_vers == RIPv1 + || 0 == (mask = ntohl(n->n_mask))) { + mask = ripv1_mask_host(dst,ifp); } else if ((ntohl(dst) & ~mask) != 0) { if (bad_mask != from->sin_addr.s_addr) { msglog("router %s sent bad netmask" @@ -480,14 +479,10 @@ input(struct sockaddr_in *from, /* received from this IP address */ } continue; } - v1_mask = (have_ripv1 - ? ripv1_mask_host(dst,0,0) - : mask); - if (rip->rip_vers == RIPv1) n->n_tag = 0; - /* Adjust metric according to incoming interface. + /* Adjust metric according to incoming interface.. */ n->n_metric += ifp->int_metric; if (n->n_metric > HOPCNT_INFINITY) @@ -496,18 +491,19 @@ input(struct sockaddr_in *from, /* received from this IP address */ /* 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 (ifp->int_d_metric != 0 && dst == RIP_DEFAULT - && n->n_family == RIP_AF_UNSPEC - && n->n_metric > ifp->int_d_metric) + && n->n_metric >= ifp->int_d_metric) continue; - /* We can receive aggregated RIPv2 routes via one - * interface that must be broken down before - * they are transmitted by RIPv1 via an interface - * on a subnet. We might receive the same routes - * aggregated otherwise via other RIPv2 interfaces. + /* 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, @@ -521,9 +517,12 @@ input(struct sockaddr_in *from, /* received from this IP address */ * * Notice that this does not break down network * routes corresponding to subnets. This is part - * of the defense against RS_NET_SUB. + * of the defense against RS_NET_SYN. */ - if (0 != (ntohl(dst) & (v1_mask & ~mask))) { + 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 >= 1024) { @@ -546,8 +545,7 @@ input(struct sockaddr_in *from, /* received from this IP address */ for (;;) { input_route(ifp, FROM_NADDR, - dst, mask, gate, - n->n_metric, n->n_tag); + dst, mask, gate, n); if (i-- == 0) break; dst = htonl(ntohl(dst) + ddst_h); @@ -566,8 +564,7 @@ input_route(struct interface *ifp, naddr dst, naddr mask, naddr gate, - int metric, - u_short tag) + struct netinfo *n) { int i; struct rt_entry *rt; @@ -594,19 +591,18 @@ input_route(struct interface *ifp, /* Consider adding the route if we do not already have it. */ if (rt == 0) { - /* Usually ignore routes being poisoned. + /* Ignore unknown routes being poisoned. */ - if (metric == HOPCNT_INFINITY) + if (n->n_metric == HOPCNT_INFINITY) return; - rtadd(dst, mask, gate, from, metric, tag, 0, ifp); + rtadd(dst, mask, gate, from, n->n_metric, n->n_tag, 0, ifp); return; } - /* We already know about the route. Consider - * this update. + /* We already know about the route. Consider this update. * - * If (rt->rt_state & RS_NET_SUB), then this route + * 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. @@ -633,7 +629,7 @@ input_route(struct interface *ifp, * other than the current slot. */ if (rts0 == rt->rt_spares - || BETTER_LINK(rts0, rts)) + || BETTER_LINK(rt, rts0, rts)) rts0 = rts; } if (i != 0) { @@ -641,26 +637,21 @@ input_route(struct interface *ifp, */ int old_metric = rts->rts_metric; - if (old_metric < HOPCNT_INFINITY) { + /* Keep poisoned routes around only long + * enough to pass the poison on. + */ + if (old_metric < HOPCNT_INFINITY) new_time = now.tv_sec; - } else { - /* Keep poisoned routes around only long - * enough to pass the poison on. - */ - new_time = rts->rts_time; - if (new_time > now.tv_sec-POISON_SECS) - new_time = now.tv_sec-POISON_SECS; - } /* If this is an update for the router we currently prefer, * then note it. */ if (i == NUM_SPARES) { rtchange(rt,rt->rt_state, gate,rt->rt_router, - metric, tag, ifp, new_time, 0); + n->n_metric, n->n_tag, ifp, new_time, 0); /* If the route got worse, check for something better. */ - if (metric > old_metric) + if (n->n_metric > old_metric) rtswitch(rt, 0); return; } @@ -669,8 +660,8 @@ input_route(struct interface *ifp, * Finished if the route is unchanged. */ if (rts->rts_gate == gate - && old_metric == metric - && rts->rts_tag == tag) { + && old_metric == n->n_metric + && rts->rts_tag == n->n_tag) { rts->rts_time = new_time; return; } @@ -684,20 +675,20 @@ input_route(struct interface *ifp, /* Save the route as a spare only if it has * a better metric than our worst spare. * This also ignores poisoned routes (those - * with metric HOPCNT_INFINITY). + * received with metric HOPCNT_INFINITY). */ - if (metric >= rts->rts_metric) + if (n->n_metric >= rts->rts_metric) return; new_time = now.tv_sec; } - if (TRACEACTIONS) - trace_upslot(rt, rts, gate, from, ifp, metric, tag, new_time); + 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 = metric; - rts->rts_tag = tag; + rts->rts_metric = n->n_metric; + rts->rts_tag = n->n_tag; rts->rts_time = new_time; rts->rts_ifp = ifp; diff --git a/usr.sbin/routed/main.c b/usr.sbin/routed/main.c index 6776a66c430..96afecc25e0 100644 --- a/usr.sbin/routed/main.c +++ b/usr.sbin/routed/main.c @@ -31,15 +31,14 @@ * SUCH DAMAGE. */ -#ifndef lint -static char copyright[] = +char copyright[] = "@(#) Copyright (c) 1983, 1988, 1993\n\ The Regents of the University of California. All rights reserved.\n"; - +#if !defined(lint) && !defined(sgi) static char sccsid[] = "@(#)main.c 8.1 (Berkeley) 6/5/93"; #endif /* not lint */ -#ident "$Revision: 1.2 $" +#ident "$Revision: 1.13 $" #include "defs.h" #include "pathnames.h" @@ -62,7 +61,6 @@ int ipforwarding = 1; /* kernel forwarding on */ int default_gateway; /* 1=advertise default */ int background = 1; int ridhosts; /* 1=reduce host routes */ -int ppp_noage; /* do not age routes on quiet links */ int mhome; /* 1=want multi-homed host route */ int advertise_mhome; /* 1=must continue adverising it */ int auth_ok = 1; /* 1=ignore auth if we do not care */ @@ -94,12 +92,12 @@ main(int argc, int n, mib[4], off; size_t len; char *p, *q; - struct timeval wtime, wtime2; + struct timeval wtime, t2; time_t dt; fd_set ibits; - naddr p_addr_h, p_mask; - struct parm *parmp; + naddr p_addr, p_mask; struct interface *ifp; + struct parm parm; char *tracename = 0; @@ -135,7 +133,13 @@ main(int argc, break; case 'g': - default_gateway = 1; + 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 */ @@ -146,10 +150,6 @@ main(int argc, mhome = 1; /* on multi-homed hosts */ break; - case 'p': /* do not age routes on quiet */ - ppp_noage = 1; /* point-to-point links */ - break; - case 'A': /* Ignore authentication if we do not care. * Crazy as it is, that is what RFC 1723 requires. @@ -171,23 +171,22 @@ main(int argc, if (p && *p != '\0') { n = (int)strtoul(p+1, &q, 0); if (*q == '\0' - && n <= HOPCNT_INFINITY-2 + && n <= HOPCNT_INFINITY-1 && n >= 1) *p = '\0'; } - if (!getnet(optarg, &p_addr_h, &p_mask)) { - msglog("routed: bad network;" - " \"-F %s\" ignored", + if (!getnet(optarg, &p_addr, &p_mask)) { + msglog("bad network; \"-F %s\"", optarg); break; } - parmp = (struct parm*)malloc(sizeof(*parmp)); - bzero(parmp, sizeof(*parmp)); - parmp->parm_next = parms; - parms = parmp; - parmp->parm_a_h = p_addr_h; - parmp->parm_m = p_mask; - parmp->parm_d_metric = n; + 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': @@ -195,10 +194,9 @@ main(int argc, * parameters. */ p = parse_parms(optarg); - if (p != 0) { - msglog("routed: bad \"%s\" in \"%s\"", + if (p != 0) + msglog("bad \"%s\" in \"%s\"", p, optarg); - } break; default: @@ -217,6 +215,8 @@ 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; @@ -249,7 +249,10 @@ usage: /* get into the background */ if (background) { #ifdef sgi - if (_daemonize(_DF_NOCHDIR,STDOUT_FILENO,STDERR_FILENO,-1)<0) + 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) @@ -286,7 +289,7 @@ usage: ftrace = 0; if (tracename != 0) { trace_on(tracename, 1); - if (new_tracelevel == 0) + if (new_tracelevel == 0) /* use stdout if file is bad */ new_tracelevel = 1; } set_tracelevel(); @@ -314,15 +317,6 @@ usage: signal(SIGUSR1, sigtrace_on); signal(SIGUSR2, sigtrace_off); - /* If we have an interface to the wide, wide world, add an entry for - * an Internet default route to the internal tables and advertise it. - * This route is not added to the kernel routes, but this entry - * prevents us from listening to default routes from other - * systems and installing them in the kernel. - */ - if (default_gateway > 0) - rtadd(RIP_DEFAULT, 0, myaddr, myaddr, 1, 0, RS_GW, 0); - /* Collect an initial view of the world by checking the interface * configuration and the kludge file. */ @@ -340,16 +334,16 @@ usage: for (;;) { prev_clk = clk; gettimeofday(&clk, 0); - timevalsub(&wtime2, &clk, &prev_clk); - if (wtime2.tv_sec < 0 - || wtime2.tv_sec > wtime.tv_sec + 5) { + 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 = wtime2.tv_sec; + dt = t2.tv_sec; if (dt > 0) dt -= wtime.tv_sec; - trace_msg("time changed by %d sec\n", dt); + trace_act("time changed by %d sec\n", dt); epoch.tv_sec += dt; } timevalsub(&now, &clk, &epoch); @@ -364,13 +358,14 @@ usage: rip_bcast(0); rdisc_adv(); } - trace_off("exiting",""); + 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; @@ -379,8 +374,8 @@ usage: /* If it is time, then broadcast our routes. */ if (supplier || advertise_mhome) { - timevalsub(&wtime2, &next_bcast, &now); - if (wtime2.tv_sec <= 0) { + timevalsub(&t2, &next_bcast, &now); + if (t2.tv_sec <= 0) { /* Synchronize the aging and broadcast * timers to minimize awakenings */ @@ -398,14 +393,14 @@ usage: * pick a 30-second aniversary of the * original broadcast time. */ - n = 1 + (0-wtime2.tv_sec)/SUPPLY_INTERVAL; + n = 1 + (0-t2.tv_sec)/SUPPLY_INTERVAL; next_bcast.tv_sec += n*SUPPLY_INTERVAL; continue; } - if (timercmp(&wtime2, &wtime, <)) - wtime = wtime2; + if (timercmp(&t2, &wtime, <)) + wtime = t2; } /* If we need a flash update, either do it now or @@ -420,30 +415,30 @@ usage: /* accurate to the millisecond */ if (!timercmp(&no_flash, &now, >)) rip_bcast(1); - timevalsub(&wtime2, &no_flash, &now); - if (timercmp(&wtime2, &wtime, <)) - wtime = wtime2; + timevalsub(&t2, &no_flash, &now); + if (timercmp(&t2, &wtime, <)) + wtime = t2; } /* trigger the main aging timer. */ - timevalsub(&wtime2, &age_timer, &now); - if (wtime2.tv_sec <= 0) { + timevalsub(&t2, &age_timer, &now); + if (t2.tv_sec <= 0) { age(0); continue; } - if (timercmp(&wtime2, &wtime, <)) - wtime = wtime2; + if (timercmp(&t2, &wtime, <)) + wtime = t2; /* update the kernel routing table */ - timevalsub(&wtime2, &need_kern, &now); - if (wtime2.tv_sec <= 0) { + timevalsub(&t2, &need_kern, &now); + if (t2.tv_sec <= 0) { age(0); continue; } - if (timercmp(&wtime2, &wtime, <)) - wtime = wtime2; + if (timercmp(&t2, &wtime, <)) + wtime = t2; /* take care of router discovery, * but do it to the millisecond @@ -452,15 +447,15 @@ usage: rdisc_age(0); continue; } - timevalsub(&wtime2, &rdisc_timer, &now); - if (timercmp(&wtime2, &wtime, <)) - wtime = wtime2; + timevalsub(&t2, &rdisc_timer, &now); + if (timercmp(&t2, &wtime, <)) + wtime = t2; /* wait for input or a timer to expire. */ - ibits = fdbits; trace_flush(); + ibits = fdbits; n = select(sock_max, &ibits, 0, 0, &wtime); if (n <= 0) { if (n < 0 && errno != EINTR && errno != EAGAIN) @@ -500,6 +495,7 @@ sigalrm(int sig) * new and broken interfaces. */ ifinit_timer.tv_sec = now.tv_sec; + trace_act("SIGALRM\n"); } @@ -569,7 +565,7 @@ fix_sock(int sock, for (rbuf = 60*1024; ; rbuf -= 4096) { if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &rbuf, sizeof(rbuf)) == 0) { - trace_msg("RCVBUF=%d\n", rbuf); + trace_act("RCVBUF=%d\n", rbuf); break; } if (rbuf < MIN_SOCKBUF) { @@ -627,8 +623,8 @@ rip_off(void) register naddr addr; - if (rip_sock >= 0) { - trace_msg("turn off RIP\n"); + if (rip_sock >= 0 && !mhome) { + trace_act("turn off RIP\n"); (void)close(rip_sock); rip_sock = -1; @@ -652,36 +648,49 @@ rip_off(void) } +/* 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) { - struct ip_mreq m; - - + /* If the main RIP socket is already alive, only start receiving + * multicasts for this interface. + */ if (rip_sock >= 0) { - if (ifp != 0 - && 0 == (ifp->int_state & (IS_NO_RIP_IN|IS_PASSIVE)) - && (ifp->int_if_flags & IFF_MULTICAST) -#ifdef MCAST_PPP_BUG - && !(ifp->int_if_flags & IFF_POINTOPOINT) -#endif - && !(ifp->int_state & IS_ALIAS)) { - m.imr_multiaddr.s_addr = htonl(INADDR_RIP_GROUP); - m.imr_interface.s_addr = ((ifp->int_if_flags - & IFF_POINTOPOINT) - ? ifp->int_dstaddr - : ifp->int_addr); - if (setsockopt(rip_sock,IPPROTO_IP, IP_ADD_MEMBERSHIP, - &m, sizeof(m)) < 0) - DBGERR(1,"setsockopt(IP_ADD_MEMBERSHIP RIP)"); - } + 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_msg("turn on RIP\n"); + 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, @@ -704,18 +713,9 @@ rip_on(struct interface *ifp) next_bcast.tv_sec = now.tv_sec+MIN_WAITTIME; for (ifp = ifnet; ifp != 0; ifp = ifp->int_next) { - if ((ifp->int_state & IS_NO_RIP_IN) != IS_NO_RIP_IN) + if (!IS_RIP_IN_OFF(ifp->int_state)) ifp->int_state &= ~IS_RIP_QUERIED; - - if ((ifp->int_if_flags & IFF_MULTICAST) - && !(ifp->int_state & IS_ALIAS)) { - m.imr_multiaddr.s_addr = htonl(INADDR_RIP_GROUP); - m.imr_interface.s_addr = ifp->int_addr; - if (setsockopt(rip_sock, IPPROTO_IP, - IP_ADD_MEMBERSHIP, - &m, sizeof(m)) < 0) - DBGERR(1,"setsockopt(IP_ADD_MEMBERSHIP RIP)"); - } + rip_mcast_on(ifp); } ifinit_timer.tv_sec = now.tv_sec; diff --git a/usr.sbin/routed/output.c b/usr.sbin/routed/output.c index 59c46842ddc..8750c412dfd 100644 --- a/usr.sbin/routed/output.c +++ b/usr.sbin/routed/output.c @@ -31,11 +31,11 @@ * SUCH DAMAGE. */ -#ifndef lint +#if !defined(lint) && !defined(sgi) static char sccsid[] = "@(#)output.c 8.1 (Berkeley) 6/5/93"; #endif /* not lint */ -#ident "$Revision: 1.1 $" +#ident "$Revision: 1.14 $" #include "defs.h" @@ -52,31 +52,33 @@ struct { naddr to_std_mask; naddr to_std_net; struct interface *ifp; /* usually output interface */ - struct ws_buf { /* for each buffer */ + struct ws_buf { /* info for each buffer */ struct rip *buf; struct netinfo *n; struct netinfo *base; struct netinfo *lim; enum output_type type; - } v2, mcast; + } v12, v2; char metric; /* adjust metrics by interface */ int npackets; - int state; -#define WS_ST_FLASH 0x01 /* send only changed routes */ -#define WS_ST_RIP2_SAFE 0x02 /* send RIPv2 safe for RIPv1 */ -#define WS_ST_RIP2_ALL 0x04 /* full featured RIPv2 */ -#define WS_ST_AG 0x08 /* ok to aggregate subnets */ -#define WS_ST_SUPER_AG 0x10 /* ok to aggregate networks */ -#define WS_ST_QUERY 0x20 /* responding to a query */ -#define WS_ST_TO_ON_NET 0x40 /* sending onto one of our nets */ -#define WS_ST_DEFAULT 0x80 /* faking a default */ + 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 ripv2_buf; +union pkt_buf ripv12_buf; /* Another for only RIPv2 listeners */ -union pkt_buf rip_mcast_buf; +union pkt_buf rip_v2_buf; @@ -92,9 +94,10 @@ output(enum output_type type, struct sockaddr_in sin; int flags; char *msg; - int res, serrno; + int res; naddr tgt_mcast; int soc; + int serrno; sin = *dst; if (sin.sin_port == 0) @@ -106,7 +109,7 @@ output(enum output_type type, soc = rip_sock; flags = 0; - + switch (type) { case OUT_QUERY: msg = "Answer Query"; @@ -121,15 +124,20 @@ output(enum output_type type, break; case OUT_BROADCAST: if (ifp->int_if_flags & IFF_POINTOPOINT) { - msg = "Send pt-to-pt"; - } else { 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) { @@ -147,25 +155,31 @@ output(enum output_type type, } else #endif tgt_mcast = ifp->int_addr; - if (setsockopt(rip_sock, - IPPROTO_IP, IP_MULTICAST_IF, - &tgt_mcast, sizeof(tgt_mcast))) - BADERR(1,"setsockopt(rip_sock," + 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); } } - if (TRACEPACKETS) - trace_rip(msg, "to", &sin, ifp, buf, size); + trace_rip(msg, "to", &sin, ifp, buf, size); res = sendto(soc, buf, size, flags, (struct sockaddr *)&sin, sizeof(sin)); - if (res < 0) { + if (res < 0 + && (ifp == 0 || !(ifp->int_state & IS_BROKE))) { serrno = errno; - msglog("sendto(%s%s%s.%d): %s", + msglog("%s sendto(%s%s%s.%d): %s", msg, ifp != 0 ? ifp->int_name : "", ifp != 0 ? ", " : "", inet_ntoa(sin.sin_addr), @@ -198,27 +212,31 @@ set_auth(struct ws_buf *w) /* Send the buffer */ static void -supply_write(struct ws_buf *w) +supply_write(struct ws_buf *wb) { /* Output multicast only if legal. * If we would multcast and it would be illegal, then discard the * packet. */ - if (w != &ws.mcast - || ((ws.state & WS_ST_RIP2_SAFE) - && (ws.ifp == 0 - || (ws.ifp->int_if_flags & IFF_MULTICAST)))) { - if (output(w->type, &ws.to, ws.ifp, w->buf, - ((char *)w->n - (char*)w->buf)) < 0 + 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) - ifbad(ws.ifp, 0); + if_sick(ws.ifp); ws.npackets++; + break; } - bzero(w->n = w->base, sizeof(*w->n)*NETS_LEN); - - if (w->buf->rip_vers == RIPv2) - set_auth(w); + bzero(wb->n = wb->base, sizeof(*wb->n)*NETS_LEN); + if (wb->buf->rip_vers == RIPv2) + set_auth(wb); } @@ -229,35 +247,46 @@ supply_out(struct ag_info *ag) { int i; naddr mask, v1_mask, s_mask, dst_h, ddst_h; - struct ws_buf *w; + 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 + 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, - 0); + (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) - || ((ag->ag_state & AGS_RIPV2) - && v1_mask != mask)) { - w = &ws.mcast; /* use the multicast-only buffer */ + 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 { - w = &ws.v2; + /* 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 @@ -287,21 +316,33 @@ supply_out(struct ag_info *ag) } do { - w->n->n_family = RIP_AF_INET; - w->n->n_dst = htonl(dst_h); - w->n->n_metric = stopint ? HOPCNT_INFINITY : ag->ag_metric; - HTONL(w->n->n_metric); - if (w->buf->rip_vers == RIPv2) { - w->n->n_nhop = ag->ag_gate; + 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) - w->n->n_mask = htonl(mask); - w->n->n_tag = ag->ag_tag; + wb->n->n_mask = htonl(mask); + wb->n->n_tag = ag->ag_tag; } dst_h += ddst_h; - if (++w->n >= w->lim) - supply_write(w); + if (++wb->n >= wb->lim) + supply_write(wb); } while (i-- != 0); } @@ -314,57 +355,119 @@ walk_supply(struct radix_node *rn, struct walkarg *w) { #define RT ((struct rt_entry *)rn) - u_short ags; + u_short ags = 0; char metric, pref; - naddr dst, gate; + naddr dst, nhop; + /* Do not advertise the loopback interface * or external remote interfaces */ if (RT->rt_ifp != 0 && ((RT->rt_ifp->int_if_flags & IFF_LOOPBACK) - || (RT->rt_ifp->int_state & IS_EXTERNAL))) + || (RT->rt_ifp->int_state & IS_EXTERNAL)) + && !(RT->rt_state & RS_MHOME)) return 0; - /* Do not send a route back to where it came from, except in - * response to a query. This is "split-horizon". - * - * That means not advertising back to the same network - * and so via the same interface. + /* If being quiet about our ability to forward, then + * do not say anything unless responding to a query. */ - 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)) + if (!supplier && !(ws.state & WS_ST_QUERY)) return 0; dst = RT->rt_dst; - /* If being quiet about our ability to forward, then - * do not say anything except our own host number, - * unless responding to a query. - */ - if (!supplier - && (!mhome || myaddr != dst) - && !(ws.state & WS_ST_QUERY)) - return 0; - - ags = 0; - - /* do not override the fake default route */ + /* do not collide with the fake default route */ if (dst == RIP_DEFAULT && (ws.state & WS_ST_DEFAULT)) return 0; - if (RT_ISHOST(RT)) { + 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; + } + + /* Adjust the outgoing metric by the cost of the link. + */ + pref = metric = RT->rt_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_garbage) { + RT->rt_poison_time = now.tv_sec; + RT->rt_poison_metric = RT->rt_metric; + } + + } 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) + return 0; + + metric = HOPCNT_INFINITY; + } + + 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, then do so. + * 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) + || 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. */ @@ -372,82 +475,49 @@ walk_supply(struct radix_node *rn, /* 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. + * later convert back to ordinary nets. + * This unifies dealing with received supernets. */ if ((RT->rt_state & RS_SUBNET) || (ws.state & WS_ST_SUPER_AG)) ags |= AGS_PROMOTE; + } - /* Never aggregate our own interfaces, - * or the host route for multi-homed servers. + /* Do not send RIPv1 advertisements of subnets to other + * networks. If possible, multicast them by RIPv2. */ - if (0 != (RT->rt_state & (RS_IF | RS_MHOME))) - ags &= ~(AGS_SUPPRESS | AGS_PROMOTE); - - - if (RT->rt_state & RS_SUBNET) { - /* Do not send authority routes into the subnet, - * or when RIP is off. - */ - if ((RT->rt_state & RS_NET_INT) - && (on_net(dst, ws.to_net, ws.to_mask) - || rip_sock < 0)) - return 0; - - /* Do not send RIPv1 advertisements of subnets to - * other networks. - * - * If possible, multicast them by RIPv2. - */ - if (!(ws.state & WS_ST_RIP2_ALL) - && !on_net(dst, ws.to_std_net, ws.to_std_mask)) - ags |= AGS_RIPV2; - - } else if (RT->rt_state & RS_NET_SUB) { - /* do not send synthetic network routes if no RIPv1 - * listeners might hear. - */ - if (ws.state & WS_ST_RIP2_ALL) - return 0; - - /* Do not send synthetic network routes on the real subnet */ - if (on_net(dst, ws.to_std_net, ws.to_std_mask)) - return 0; + 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; } - /* forget synthetic routes when RIP is off */ - if (rip_sock < 0 && 0 != (RT->rt_state & RS_NET_S)) - return 0; - - - /* Adjust outgoing metric by the cost of the link. - * Interface routes have already been adjusted. + /* 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. */ - pref = metric = RT->rt_metric + ws.metric; - if (metric >= HOPCNT_INFINITY) { - metric = HOPCNT_INFINITY; - pref = ((RT->rt_hold_down > now.tv_sec) - ? RT->rt_hold_metric - : metric); + 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)) { + ags |= AGS_SPLIT_HZ; + ags &= ~(AGS_PROMOTE | AGS_SUPPRESS); } - /* Advertise the next hop if this is not a route for one - * of our interfaces and the next hop is on the same - * network as the target. - */ - if ((ws.state & WS_ST_RIP2_SAFE) - && !(RT->rt_state & RS_IF) - && ((ws.state & WS_ST_QUERY) - || (on_net(RT->rt_gate, ws.ifp->int_net, ws.ifp->int_mask) - && RT->rt_gate != ws.ifp->int_addr))) { - gate = RT->rt_gate; - } else { - gate = 0; - } - - ag_check(dst, RT->rt_mask, gate, metric, pref, + ag_check(dst, RT->rt_mask, 0, nhop, metric, pref, RT->rt_seqno, RT->rt_tag, ags, supply_out); return 0; #undef RT @@ -466,7 +536,6 @@ supply(struct sockaddr_in *dst, { static int init = 1; struct rt_entry *rt; - int metric; ws.state = 0; @@ -482,7 +551,7 @@ supply(struct sockaddr_in *dst, ws.state |= WS_ST_TO_ON_NET; } else { - ws.to_mask = ripv1_mask_net(ws.to.sin_addr.s_addr, 0, 0); + 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) @@ -496,45 +565,69 @@ supply(struct sockaddr_in *dst, ws.state |= WS_ST_QUERY; if ((ws.ifp = ifp) == 0) { - ws.metric = 0; + ws.metric = 1; } else { /* Adjust the advertised metric by the outgoing interface - * metric, but reduced by 1 to avoid counting this hop - * twice. + * metric. */ - ws.metric = ifp->int_metric; - if (ws.metric > 0) - ws.metric--; + ws.metric = ifp->int_metric+1; } if (init) { init = 0; - bzero(&ripv2_buf, sizeof(ripv2_buf)); - ripv2_buf.rip.rip_cmd = RIPCMD_RESPONSE; - ws.v2.buf = &ripv2_buf.rip; + 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; - - bzero(&rip_mcast_buf, sizeof(rip_mcast_buf)); - rip_mcast_buf.rip.rip_cmd = RIPCMD_RESPONSE; - rip_mcast_buf.rip.rip_vers = RIPv2; - ws.mcast.buf = &rip_mcast_buf.rip; - ws.mcast.base = &ws.mcast.buf->rip_nets[0]; - ws.mcast.lim = ws.mcast.base + NETS_LEN; } - ripv2_buf.rip.rip_vers = vers; + ripv12_buf.rip.rip_vers = vers; - ws.v2.type = type; + ws.v12.n = ws.v12.base; + set_auth(&ws.v12); ws.v2.n = ws.v2.base; set_auth(&ws.v2); - ws.mcast.type = (type == OUT_BROADCAST) ? OUT_MULTICAST : type; - ws.mcast.n = ws.mcast.base; - set_auth(&ws.mcast); + 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; @@ -547,43 +640,51 @@ supply(struct sockaddr_in *dst, || !(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; } - /* send the routes - */ - if ((metric = ifp->int_d_metric) != 0) { - /* Fake a default route if asked */ - ws.state |= WS_ST_DEFAULT; - - /* Use the metric of a real default, if there is one. + if (supplier) { + /* Fake a default route if asked, and if there is not + * a better, real default route. */ - rt = rtget(RIP_DEFAULT, 0); - if (rt != 0 - && rt->rt_metric+ws.metric < metric) - metric = rt->rt_metric+ws.metric; - - if (metric < HOPCNT_INFINITY) - ag_check(0, 0, 0, metric,metric, 0, 0, 0, supply_out); + 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 */ + /* 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 (ws.mcast.n != ws.mcast.base - && (ws.mcast.n > ws.mcast.base+1 - || ws.mcast.n->n_family != RIP_AF_AUTH)) - supply_write(&ws.mcast); /* If we sent nothing and this is an answer to a query, send * an empty buffer. */ if (ws.npackets == 0 && (ws.state & WS_ST_QUERY)) - supply_write(&ws.v2); + supply_write(&ws.v12); } @@ -611,7 +712,7 @@ rip_bcast(int flash) if (rip_sock < 0) return; - trace_msg("send %s and inhibit dynamic updates for %.3f sec\n", + 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); @@ -620,18 +721,16 @@ rip_bcast(int flash) * and aliases. Do try broken interfaces to see * if they have healed. */ - if (0 != (ifp->int_state & (IS_PASSIVE - | IS_ALIAS))) + if (0 != (ifp->int_state & (IS_PASSIVE | IS_ALIAS))) continue; /* skip turned off interfaces */ if (!iff_alive(ifp->int_if_flags)) continue; - /* Prefer RIPv1 announcements unless RIPv2 is on and - * RIPv2 is off. - */ + /* 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; @@ -645,10 +744,11 @@ rip_bcast(int flash) /* if RIPv1 is not turned off, then broadcast so * that RIPv1 listeners can hear. */ - if (!(ifp->int_state & IS_NO_RIPV1_OUT)) { - type = OUT_BROADCAST; - } else { + 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) { @@ -697,16 +797,16 @@ rip_query(void) * if they have healed. */ if (0 != (ifp->int_state & (IS_RIP_QUERIED - | IS_PASSIVE - | IS_ALIAS))) + | IS_PASSIVE | IS_ALIAS))) continue; /* skip turned off interfaces */ if (!iff_alive(ifp->int_if_flags)) continue; - /* prefer RIPv2 queries */ + /* 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; @@ -724,10 +824,11 @@ rip_query(void) /* if RIPv1 is not turned off, then broadcast so * that RIPv1 listeners can hear. */ - if (!(ifp->int_state & IS_NO_RIPV1_OUT)) { - type = OUT_BROADCAST; - } else { + 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) { @@ -743,6 +844,6 @@ rip_query(void) ifp->int_state |= IS_RIP_QUERIED; if (output(type, &dst, ifp, &buf, sizeof(buf)) < 0) - ifbad(ifp,0); + if_sick(ifp); } } diff --git a/usr.sbin/routed/parms.c b/usr.sbin/routed/parms.c index 1fc0b857b58..e5c9ac6f3f1 100644 --- a/usr.sbin/routed/parms.c +++ b/usr.sbin/routed/parms.c @@ -31,11 +31,11 @@ * SUCH DAMAGE. */ -#ifndef lint +#if !defined(lint) && !defined(sgi) static char sccsid[] = "@(#)if.c 8.1 (Berkeley) 6/5/93"; #endif /* not lint */ -#ident "$Revision: 1.1 $" +#ident "$Revision: 1.7 $" #include "defs.h" #include "pathnames.h" @@ -45,177 +45,6 @@ struct parm *parms; struct intnet *intnets; -/* parse a set of parameters for an interface - */ -char * /* 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, *parmp; - 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)); - if (!getnet(&line[7], &intnetp->intnet_addr, - &intnetp->intnet_mask)) { - free(intnetp); - return line; - } - HTONL(intnetp->intnet_addr); - 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; - - } else if (PARS("no_super_ag")) { - parm.parm_int_state |= IS_NO_SUPER_AG; - - } else if (PARS("no_rip")) { - parm.parm_int_state |= (IS_NO_RIPV1_IN - | IS_NO_RIPV2_IN - | IS_NO_RIPV1_OUT - | IS_NO_RIPV2_OUT); - - } 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("no_ripv2_out")) { - parm.parm_int_state |= IS_NO_RIPV2_OUT; - - } 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_rdisc")) { - CKF((GROUP_IS_SOL|GROUP_IS_ADV), - IS_NO_ADV_IN | IS_NO_SOL_OUT | IS_NO_ADV_OUT); - - } 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 (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 (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-2) - break; - - } else { - tgt = tok; - break; - } - } - if (tgt != 0) - return tgt; - - if (parm.parm_int_state & IS_NO_ADV_IN) - parm.parm_int_state |= IS_NO_SOL_OUT; - - /* check for duplicate specification */ - for (parmp = parms; parmp != 0; parmp = parmp->parm_next) { - if (strcmp(parm.parm_name, parmp->parm_name)) - continue; - if (parmp->parm_a_h != (parm.parm_a_h & parmp->parm_m) - && parm.parm_a_h != (parmp->parm_a_h & parm.parm_m)) - continue; - - if (strcmp(parmp->parm_passwd, parm.parm_passwd) - || (0 != (parm.parm_int_state & GROUP_IS_SOL) - && 0 != (parmp->parm_int_state & GROUP_IS_SOL) - && 0 != ((parm.parm_int_state ^ parmp->parm_int_state) - && GROUP_IS_SOL)) - || (0 != (parm.parm_int_state & GROUP_IS_ADV) - && 0 != (parmp->parm_int_state & GROUP_IS_ADV) - && 0 != ((parm.parm_int_state ^ parmp->parm_int_state) - && GROUP_IS_ADV)) - || (parm.parm_rdisc_pref != 0 - && parmp->parm_rdisc_pref != 0 - && parm.parm_rdisc_pref != parmp->parm_rdisc_pref) - || (parm.parm_rdisc_int != 0 - && parmp->parm_rdisc_int != 0 - && parm.parm_rdisc_int != parmp->parm_rdisc_int) - || (parm.parm_d_metric != 0 - && parmp->parm_d_metric != 0 - && parm.parm_d_metric != parmp->parm_d_metric)) - return "duplicate"; - } - - parmp = (struct parm*)malloc(sizeof(*parmp)); - bcopy(&parm, parmp, sizeof(*parmp)); - parmp->parm_next = parms; - parms = parmp; - - return 0; -#undef DELIMS -#undef PARS -#undef PARSE -} - - /* use configured parameters */ void @@ -223,51 +52,66 @@ get_parms(struct interface *ifp) { struct parm *parmp; + /* get all relevant parameters + */ for (parmp = parms; parmp != 0; parmp = parmp->parm_next) { - if ((parmp->parm_a_h == (ntohl(ifp->int_addr) - & parmp->parm_m) - && parmp->parm_name[0] == '\0') + 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; - bcopy(parmp->parm_passwd, ifp->int_passwd, - sizeof(ifp->int_passwd)); - ifp->int_rdisc_pref = parmp->parm_rdisc_pref; - ifp->int_rdisc_int = parmp->parm_rdisc_int; - ifp->int_d_metric = parmp->parm_d_metric; - } + 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 ((ifp->int_state & IS_NO_RIP_IN) == IS_NO_RIP_IN) + 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_state & IS_PASSIVE) - || (ifp->int_state & IS_REMOTE)) - ifp->int_state |= IS_NO_ADV_IN|IS_NO_SOL_OUT|IS_NO_ADV_OUT; - - - if (!(ifp->int_state & IS_PASSIVE)) { - 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_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; - /* point-to-point links should be passive for the sake - * of demand-dialing + /* 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; } @@ -314,53 +158,62 @@ gwkludge(void) if (*lptr == '\n' /* ignore null and comment lines */ || *lptr == '#') continue; + p = lptr+strlen(lptr)-1; + while (*p == '\n' + || *p == ' ') + *p-- = '\0'; - /* notice parameter lines */ + /* notice newfangled parameter lines + */ if (strncasecmp("net", lptr, 3) && strncasecmp("host", lptr, 4)) { p = parse_parms(lptr); - if (p != 0) - msglog("bad \"%s\" in "_PATH_GATEWAYS - " entry %s", lptr, p); + 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[^ ] gateway" - " %64[^ / ] metric %d %8s\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); + msglog("bad "_PATH_GATEWAYS" entry \"%s\"", lptr); continue; } if (metric < 0 || metric >= HOPCNT_INFINITY) { - msglog("bad metric in "_PATH_GATEWAYS" entry %s", + 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); + 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); + msglog("bad net \"%s\" in "_PATH_GATEWAYS + " entry \"%s\"", dname, lptr); continue; } - HTONL(dst); } else { msglog("bad \"%s\" in "_PATH_GATEWAYS - " entry %s", lptr); + " entry \"%s\"", lptr); continue; } if (!gethost(gname, &gate)) { - msglog("bad gateway %s in "_PATH_GATEWAYS - " entry %s", gname, lptr); + msglog("bad gateway \"%s\" in "_PATH_GATEWAYS + " entry \"%s\"", gname, lptr); continue; } @@ -403,34 +256,29 @@ gwkludge(void) } } else { - msglog("bad "_PATH_GATEWAYS" entry %s", lptr); + msglog("bad "_PATH_GATEWAYS" entry \"%s\"", lptr); continue; } - if (!(state & IS_EXTERNAL)) { - /* If we are going to send packets to the gateway, - * it must be reachable using our physical interfaces - */ - if (!rtfind(gate)) { - msglog("unreachable gateway %s in " - _PATH_GATEWAYS" entry %s", - gname, lptr); - continue; - } + /* Remember to advertise the corresponding logical network. + */ + if (!(state & IS_EXTERNAL) + && netmask != std_mask(dst)) + state |= IS_SUBNET; - /* Remember to advertise the corresponding logical - * network. - */ - if (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_a_h = ntohl(dst); - parmp->parm_m = -1; + parmp->parm_addr_h = ntohl(dst); + parmp->parm_mask = -1; parmp->parm_d_metric = 0; parmp->parm_int_state = state; @@ -438,8 +286,11 @@ gwkludge(void) * interface. */ for (ifp = ifnet; 0 != ifp; ifp = ifp->int_next) { - if (ifp->int_addr == dst - && ifp->int_mask == netmask) + if (ifp->int_mask == netmask + && ((ifp->int_addr == dst + && netmask != HOST_MASK) + || (ifp->int_dstaddr == dst + && netmask == HOST_MASK))) break; } if (ifp != 0) { @@ -478,22 +329,218 @@ gwkludge(void) get_parms(ifp); - if (TRACEACTIONS) - trace_if("Add", 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; + + if (parm.parm_int_state & IS_NO_ADV_IN) + parm.parm_int_state |= IS_NO_SOL_OUT; + + if ((parm.parm_int_state & (IS_NO_RIP | IS_NO_RDISC)) + == (IS_NO_RIP | IS_NO_RDISC)) + parm.parm_int_state |= IS_PASSIVE; + + 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; + + + 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 *addr_hp, + naddr *addrp, /* host byte order */ naddr *maskp) { int i; - struct netent *nentp; + struct netent *np; naddr mask; struct in_addr in; char hname[MAXHOSTNAMELEN+1]; @@ -512,25 +559,34 @@ getnet(char *name, name = hname; } - nentp = getnetbyname(name); - if (nentp != 0) { - in.s_addr = (naddr)nentp->n_net; + np = getnetbyname(name); + if (np != 0) { + in.s_addr = (naddr)np->n_net; } else if (inet_aton(name, &in) == 1) { - NTOHL(in.s_addr); + 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; - *addr_hp = in.s_addr; + *addrp = in.s_addr; *maskp = mask; return 1; } diff --git a/usr.sbin/routed/pathnames.h b/usr.sbin/routed/pathnames.h index 9b2c28c0152..d539eb51dab 100644 --- a/usr.sbin/routed/pathnames.h +++ b/usr.sbin/routed/pathnames.h @@ -37,7 +37,9 @@ #define _PATH_GATEWAYS "/etc/gateways" -/* all remotely requested trace files must either start with this prefix +/* 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. */ -#define _PATH_TRACE "/tmp" +#define _PATH_TRACE "/tmp/routed.log" diff --git a/usr.sbin/routed/rdisc.c b/usr.sbin/routed/rdisc.c index 93c5f8b30d4..5a4809475bc 100644 --- a/usr.sbin/routed/rdisc.c +++ b/usr.sbin/routed/rdisc.c @@ -31,11 +31,11 @@ * SUCH DAMAGE. */ -#ifndef lint +#if !defined(lint) && !defined(sgi) static char sccsid[] = "@(#)rdisc.c 8.1 (Berkeley) x/y/95"; #endif /* not lint */ -#ident "$Revision: 1.1 $" +#ident "$Revision: 1.14 $" #include "defs.h" #include @@ -89,8 +89,8 @@ struct dr { /* accumulated advertisements */ } *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-1)) +#define PREF(p, ifp) ((p) <= (ifp)->int_metric ? ((p) != 0 ? 1 : 0) \ + : (p) - ((ifp)->int_metric)) static void rdisc_sort(void); @@ -109,7 +109,7 @@ trace_rdisc(char *act, n_long *wp, *lim; - if (ftrace == 0) + if (!TRACEPACKETS || ftrace == 0) return; lastlog(); @@ -119,7 +119,7 @@ trace_rdisc(char *act, " from %s to %s via %s life=%d\n", act, naddr_ntoa(from), naddr_ntoa(to), ifp ? ifp->int_name : "?", - p->ad.icmp_ad_life); + ntohs(p->ad.icmp_ad_life)); if (!TRACECONTENTS) return; @@ -133,7 +133,7 @@ trace_rdisc(char *act, (void)fputc('\n',ftrace); } else { - trace_msg("%s Router Solic. from %s to %s via %s" + 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 : "?", @@ -173,7 +173,7 @@ set_rdisc_mg(struct interface *ifp, if (setsockopt(rdisc_sock, IPPROTO_IP, IP_DROP_MEMBERSHIP, &m, sizeof(m)) < 0) - DBGERR(1,"IP_DROP_MEMBERSHIP ALLHOSTS"); + LOGERR("IP_DROP_MEMBERSHIP ALLHOSTS"); ifp->int_state &= ~IS_ALL_HOSTS; } @@ -181,9 +181,11 @@ set_rdisc_mg(struct interface *ifp, /* 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) - DBGERR(1,"IP_ADD_MEMBERSHIP ALLHOSTS"); - ifp->int_state |= IS_ALL_HOSTS; + &m, sizeof(m)) < 0) { + LOGERR("IP_ADD_MEMBERSHIP ALLHOSTS"); + } else { + ifp->int_state |= IS_ALL_HOSTS; + } } if (!supplier @@ -195,7 +197,7 @@ set_rdisc_mg(struct interface *ifp, if (setsockopt(rdisc_sock, IPPROTO_IP, IP_DROP_MEMBERSHIP, &m, sizeof(m)) < 0) - DBGERR(1,"IP_DROP_MEMBERSHIP ALLROUTERS"); + LOGERR("IP_DROP_MEMBERSHIP ALLROUTERS"); ifp->int_state &= ~IS_ALL_ROUTERS; } @@ -203,9 +205,11 @@ set_rdisc_mg(struct interface *ifp, /* 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) - DBGERR(1,"IP_ADD_MEMBERSHIP ALLROUTERS"); - ifp->int_state |= IS_ALL_ROUTERS; + &m, sizeof(m)) < 0) { + LOGERR("IP_ADD_MEMBERSHIP ALLROUTERS"); + } else { + ifp->int_state |= IS_ALL_ROUTERS; + } } } @@ -221,7 +225,7 @@ set_supplier(void) if (supplier_set) return; - trace_msg("start suppying routes\n"); + trace_act("start suppying routes\n"); /* Forget discovered routes. */ @@ -248,6 +252,9 @@ set_supplier(void) 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); } @@ -260,8 +267,13 @@ rdisc_age(naddr bad_gate) struct dr *drp; + /* If only adverising, then do only that. */ if (supplier) { - /* If only adverising, then do only that. */ + /* if switching from client to server, get rid of old + * default routes. + */ + if (cur_drp != 0) + rdisc_sort(); rdisc_adv(); return; } @@ -287,7 +299,7 @@ rdisc_age(naddr bad_gate) sec = (now.tv_sec - drp->dr_life + SUPPLY_INTERVAL); if (drp->dr_ts > sec) { - trace_msg("age 0.0.0.0 --> %s" + trace_act("age 0.0.0.0 --> %s" " via %s\n", naddr_ntoa(drp->dr_gate), drp->dr_ifp->int_name); @@ -309,10 +321,11 @@ rdisc_age(naddr bad_gate) } -/* zap all routes discovered via an interface that has gone bad +/* Zap all routes discovered via an interface that has gone bad + * This should only be called when !(ifp->int_state & IS_ALIAS) */ void -ifbad_rdisc(struct interface *ifp) +if_bad_rdisc(struct interface *ifp) { struct dr *drp; @@ -330,7 +343,7 @@ ifbad_rdisc(struct interface *ifp) /* mark an interface ok for router discovering. */ void -ifok_rdisc(struct interface *ifp) +if_ok_rdisc(struct interface *ifp) { set_rdisc_mg(ifp, 1); @@ -373,7 +386,8 @@ del_rdisc(struct dr *drp) */ if (i == 0 && ifp->int_rdisc_cnt >= MAX_SOLICITATIONS) { - trace_msg("re-solicit routers via %s\n", ifp->int_name); + 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(); @@ -390,10 +404,11 @@ rdisc_sort(void) struct dr *drp, *new_drp; struct rt_entry *rt; struct interface *ifp; - time_t sec; + u_int new_st; + n_long new_pref; - /* find the best discovered route + /* Find the best discovered route. */ new_drp = 0; for (drp = drs; drp < &drs[MAX_ADS]; drp++) { @@ -401,33 +416,14 @@ rdisc_sort(void) continue; ifp = drp->dr_ifp; - /* Get rid of expired discovered routes. - * Routes received over PPP links do not die until - * the link has been active long enough to be certain - * we should have heard from the router. + /* Get rid of expired discovered routers. */ if (drp->dr_ts + drp->dr_life <= now.tv_sec) { - if (drp->dr_recv_pref == 0 - || !ppp_noage - || !(ifp->int_if_flags & IFF_POINTOPOINT) - || !(ifp->int_state & IS_QUIET) - || (ifp->int_quiet_time - + (sec = MIN(MaxMaxAdvertiseInterval, - drp->dr_life)) <= now.tv_sec)) { - del_rdisc(drp); - continue; - } - - /* If the PPP link is quiet, keep checking - * in case the link becomes active. - * After the link is active, the timer on the - * discovered route might force its deletion. - */ - sec += now.tv_sec+1; - } else { - sec = drp->dr_ts+drp->dr_life+1; + del_rdisc(drp); + continue; } - LIM_SEC(rdisc_timer, sec); + + LIM_SEC(rdisc_timer, drp->dr_ts+drp->dr_life+1); /* Update preference with possibly changed interface * metric. @@ -437,14 +433,21 @@ rdisc_sort(void) /* 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_drp->dr_pref < drp->dr_pref - || (new_drp->dr_pref == drp->dr_pref - && (drp == cur_drp - || (new_drp != cur_drp - && new_drp->dr_life > drp->dr_life)))) - new_drp = drp; + || (!((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 @@ -455,12 +458,12 @@ rdisc_sort(void) /* Stop using discovered routes if they are all bad */ if (new_drp == 0) { - trace_msg("turn off Router Discovery\n"); + trace_act("turn off Router Discovery client\n"); rdisc_ok = 0; if (rt != 0 && (rt->rt_state & RS_RDISC)) { - rtchange(rt, rt->rt_state, + 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); @@ -472,16 +475,15 @@ rdisc_sort(void) } else { if (cur_drp == 0) { - trace_msg("turn on Router Discovery using" - " %s via %s\n", + 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; - rip_off(); } else { - trace_msg("switch Router Discovery from" + 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, @@ -499,6 +501,12 @@ rdisc_sort(void) 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; @@ -512,14 +520,13 @@ static void parse_ad(naddr from, naddr gate, n_long pref, - int life, + u_short life, struct interface *ifp) { static naddr bad_gate; struct dr *drp, *new_drp; - NTOHL(gate); if (gate == RIP_DEFAULT || !check_dst(gate)) { if (bad_gate != from) { @@ -534,55 +541,79 @@ parse_ad(naddr from, /* ignore pointers to ourself and routes via unreachable networks */ if (ifwithaddr(gate, 1, 0) != 0) { - if (TRACEPACKETS) - trace_msg("discard our own packet\n"); + trace_pkt("\tdiscard our own Router Discovery Ad\n"); return; } if (!on_net(gate, ifp->int_net, ifp->int_mask)) { - if (TRACEPACKETS) - trace_msg("discard packet from unreachable net\n"); + trace_pkt("\tdiscard Router Discovery Ad" + " from unreachable net\n"); return; } /* Convert preference to an unsigned value - * and bias it by the metric of the interface. + * 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 = drs, drp = drs; drp < &drs[MAX_ADS]; drp++) { - if (drp->dr_ts == 0) { - new_drp = drp; - continue; - } - + for (new_drp = 0, drp = drs; drp < &drs[MAX_ADS]; drp++) { + /* accept new info for a familiar entry + */ if (drp->dr_gate == gate) { - /* Zap an entry we are being told is kaput */ - if (pref == 0 || life == 0) { - drp->dr_recv_pref = 0; - drp->dr_life = 0; - return; - } new_drp = drp; break; } - /* look for least valueable entry */ - if (new_drp->dr_pref > drp->dr_pref) - new_drp = drp; + 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; + } } - /* ignore zap of an entry we do not know about. */ - if (pref == 0 || life == 0) + /* 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 = ntohl(life); + 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); - ifp->int_rdisc_cnt = MAX_SOLICITATIONS; + /* after hearing a good advertisement, stop asking + */ + if (!(ifp->int_state & IS_SICK)) + ifp->int_rdisc_cnt = MAX_SOLICITATIONS; } @@ -638,13 +669,19 @@ send_rdisc(union ad_u *p, msg = "Send pt-to-pt"; sin.sin_addr.s_addr = ifp->int_dstaddr; } else { - msg = "Broadcast"; + msg = "Send broadcast"; sin.sin_addr.s_addr = ifp->int_brdaddr; } break; case 2: /* multicast */ - msg = "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 @@ -660,11 +697,12 @@ send_rdisc(union ad_u *p, } else #endif tgt_mcast = ifp->int_addr; - if (setsockopt(rdisc_sock, - IPPROTO_IP, IP_MULTICAST_IF, - &tgt_mcast, sizeof(tgt_mcast))) { - DBGERR(1,"setsockopt(rdisc_sock," + 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; @@ -673,19 +711,19 @@ send_rdisc(union ad_u *p, break; } - if (TRACEPACKETS) - trace_rdisc(msg, ifp->int_addr, sin.sin_addr.s_addr, ifp, - p, p_size); + 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))) { - msglog("sendto(%s%s%s): %s", - ifp != 0 ? ifp->int_name : "", - ifp != 0 ? ", " : "", - inet_ntoa(sin.sin_addr), - strerror(errno)); + 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) - ifbad(ifp, 0); + if_sick(ifp); } } @@ -707,9 +745,7 @@ send_adv(struct interface *ifp, 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 : htonl(ifp->int_rdisc_int*3); - - u.ad.icmp_ad_life = stopint ? 0 : htonl(ifp->int_rdisc_int*3); + 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); @@ -741,8 +777,8 @@ rdisc_adv(void) if (!timercmp(&ifp->int_rdisc_timer, &now, >) || stopint) { - send_adv(ifp, INADDR_ALLHOSTS_GROUP, - (ifp->int_if_flags&IS_BCAST_RDISC) ? 1 : 2); + 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, @@ -788,9 +824,8 @@ rdisc_sol(void) u.so.icmp_cksum = in_cksum((u_short*)&u.so, sizeof(u.so)); send_rdisc(&u, sizeof(u.so), ifp, - INADDR_ALLROUTERS_GROUP, - ((ifp->int_if_flags & IS_BCAST_RDISC) - ? 1 : 2)); + htonl(INADDR_ALLROUTERS_GROUP), + ((ifp->int_state&IS_BCAST_RDISC) ? 1 : 2)); if (++ifp->int_rdisc_cnt >= MAX_SOLICITATIONS) continue; @@ -833,21 +868,19 @@ ck_icmp(char *act, } if (p->icmp.icmp_code != 0) { - if (TRACEPACKETS) - msglog("unrecognized ICMP Router" - " %s code=%d from %s to %s\n", - type, p->icmp.icmp_code, - naddr_ntoa(from), naddr_ntoa(to)); + 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; } - if (TRACEPACKETS) - trace_rdisc(act, from, to, ifp, p, len); + trace_rdisc(act, from, to, ifp, p, len); - if (ifp == 0 && TRACEPACKETS) - msglog("unknown interface for router-discovery %s" - " from %s to %s", - type, naddr_ntoa(from), naddr_ntoa(to)); + if (ifp == 0) + trace_pkt("unknown interface for router-discovery %s" + " from %s to %s", + type, naddr_ntoa(from), naddr_ntoa(to)); return ifp; } @@ -897,7 +930,7 @@ read_d(void) if (ifp == 0) continue; if (ifwithaddr(from.sin_addr.s_addr, 0, 0)) { - trace_msg("\tdiscard our own packet\n"); + trace_pkt("\tdiscard our own Router Discovery msg\n"); continue; } @@ -914,8 +947,7 @@ read_d(void) continue; } if (p->ad.icmp_ad_num == 0) { - if (TRACEPACKETS) - trace_msg("\tempty?\n"); + trace_pkt("\tempty?\n"); continue; } if (cc != (sizeof(p->ad) - sizeof(p->ad.icmp_ad_info) @@ -938,7 +970,7 @@ read_d(void) for (n = 0; n < p->ad.icmp_ad_num; n++) { parse_ad(from.sin_addr.s_addr, wp[0], wp[1], - p->ad.icmp_ad_life, + ntohs(p->ad.icmp_ad_life), ifp); wp += p->ad.icmp_ad_asize; } diff --git a/usr.sbin/routed/routed.8 b/usr.sbin/routed/routed.8 index eac9d5e9657..828bdd89fb1 100644 --- a/usr.sbin/routed/routed.8 +++ b/usr.sbin/routed/routed.8 @@ -31,12 +31,12 @@ .\" .\" @(#)routed.8 8.2 (Berkeley) 12/11/93 .\" -.Dd March 1, 1996 +.Dd June 1, 1996 .Dt ROUTED 8 .Os BSD 4.4 .Sh NAME .Nm routed -.Nd network routing daemon +.Nd network RIP and router discovery routing daemon .Sh SYNOPSIS .Nm .Op Fl sqdghmpAt @@ -54,8 +54,7 @@ It uses Routing Information Protocol, RIPv1 (RFC\ 1058), RIPv2 (RFC\ 1723), and Internet Router Discovery Protocol (RFC 1256) to maintain the kernel routing table. -The version of the RIPv1 protocol implemented -is based on the RIPv1 protocol implemented in the reference 4.3BSD daemon. +The RIPv1 protocol is based on the reference 4.3BSD daemon. .Pp It listens on the .Xr udp 4 @@ -65,7 +64,7 @@ service (see .Xr services 5 ) for Routing Information Protocol packets. It also sends and receives multicast Router Discovery ICMP messages. -If the host is an router, +If the host is a router, .Nm periodically supplies copies of its routing tables to any directly connected hosts and networks. @@ -116,8 +115,8 @@ Advertised metrics reflect the metric associated with interface 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 resquesting -network to implement +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 @@ -157,7 +156,7 @@ This is a form of .Em poison reverse . .Pp Routes in the kernel table that are added or changed as a result -of ICMP Redirect messages are deleted after a while to minimze +of ICMP Redirect messages are deleted after a while to minimize .Em black-holes . When a TCP connection suffers a timeout, the kernel tells @@ -170,7 +169,7 @@ age of any relevant Router Discovery Protocol default routes. Hosts acting as internetwork routers gratuitously supply their routing tables every 30 seconds to all directly connected hosts and networks. -The response is sent to the broadcast address on nets that support +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. @@ -198,6 +197,37 @@ 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 : @@ -219,9 +249,25 @@ 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. @@ -242,20 +288,16 @@ The option overrides the .Fl q option to the limited extent of advertising the host route. -.It Fl p -causes routes received over point-to-point links to not be timed -out while the link is idle. -This is handy for "demand dialed" PPP links that filter routing packets. .It Fl A do not ignore RIPv2 authentication if we do not care about RIPv2 authentication. -This option is required for conformance wiht RFC 1723, -but it makes little sense and breaks using RIP as a discovery protocol +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 file. +causes debugging information to be appended to the trace file. .It Fl t increases the debugging level, which causes more information to be logged on the tracefile specified with @@ -266,9 +308,11 @@ with the .Em SIGUSR1 or .Em SIGUSR2 -signals. +signals or with the +.Cm rtquery +command. .It Fl F Ar net[/mask][,metric] -minimize routes in transmissions to network +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 . @@ -279,6 +323,13 @@ 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 @@ -327,8 +378,8 @@ Distant active gateways are treated like network interfaces. RIP responses are sent to the distant .Em active -gateway and if no responses are received -in turn for a period of the time, the associated route deleted from +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. @@ -419,49 +470,68 @@ One of the keywords or .Cm external must be present to indicate whether the gateway should be treated as -.Em passive +.Cm passive or -.Em active +.Cm active (as described above), or whether the gateway is -.Em external +.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: +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] -causes other routes to be aggregated as if a compatible route to -Ar nname/mask -had been received. +.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. -.Ar " No_rip " +.Cm No_rip is equivalent to -.Ar " no_ripv1_in no_ripv2_in no_ripv1_out no_ripv2_out ." +.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 -disables the RIPv2 responses that are otherwise multicast containing -information that cannot be sent in RIPv2 packets. +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 @@ -488,9 +558,24 @@ are transmitted to N seconds and their lifetime to 3*N. .It Cm fake_default Ns \&= Ns Ar metric has an identical effect to .Fl F Ar net[/mask][,metric] -with the network and mask coming from the affected interface. +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 diff --git a/usr.sbin/routed/rtquery/Makefile b/usr.sbin/routed/rtquery/Makefile index dd2419b9941..a8d2110a8d3 100644 --- a/usr.sbin/routed/rtquery/Makefile +++ b/usr.sbin/routed/rtquery/Makefile @@ -3,4 +3,5 @@ PROG= rtquery MAN8= rtquery.8 +.include "../../Makefile.inc" .include diff --git a/usr.sbin/routed/rtquery/rtquery.8 b/usr.sbin/routed/rtquery/rtquery.8 index 3c59efd6644..1afb8b9478b 100644 --- a/usr.sbin/routed/rtquery/rtquery.8 +++ b/usr.sbin/routed/rtquery/rtquery.8 @@ -1,4 +1,4 @@ -.Dd April 9, 1996 +.Dd June 1, 1996 .Dt RTQUERY 8 .Os BSD 4.4 .Sh NAME @@ -7,7 +7,7 @@ .Sh SYNOPSIS .Nm .Op Fl np1 -.Op Fl 1 Ar timeout +.Op Fl w Ar timeout .Op Fl r Ar addr .Ar host ... .Sh DESCRIPTION @@ -29,22 +29,26 @@ by default uses the .Em request command. When the -.B \-p +.Ar -p option is specified, .Nm rtquery uses the .Em poll -command, which is an -undocumented extension to the RIP specification supported by -.IR gated (1M). -When querying -.IR gated (1M), -the -.I 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 +.I Request command because the response is not subject to Split Horizon and/or -Poisioned Reverse. +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 : @@ -57,10 +61,10 @@ The option displays only the numeric network and host numbers. .It Fl p Uses the -.Em poll +.Em Poll command to request full routing information from .Xr gated 8 , -This is an undocumented extension supported only by +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. @@ -69,10 +73,28 @@ 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 parms +.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. +.El +.Bl -tag -width Ds -offset indent-two +.It Em on=filename +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 /tmp/routed.log . +.It Em more +increases the debugging level. +.It Em off +turns off tracing. +.El .Sh SEE ALSO -.Xr routed 8, -.Xr gated 8, +.Xr routed 8 , +.Xr gated 8 . .br RFC\ 1058 - Routing Information Protocol, RIPv1 .br diff --git a/usr.sbin/routed/rtquery/rtquery.c b/usr.sbin/routed/rtquery/rtquery.c index 97899c97098..a75d8f478f2 100644 --- a/usr.sbin/routed/rtquery/rtquery.c +++ b/usr.sbin/routed/rtquery/rtquery.c @@ -31,13 +31,11 @@ * SUCH DAMAGE. */ -#ifndef lint -static char copyright[] = +char copyright[] = "@(#) Copyright (c) 1982, 1986, 1993\n\ The Regents of the University of California. All rights reserved.\n"; -#endif /* not lint */ -#ifndef lint +#if !defined(lint) && !defined(sgi) static char sccsid[] = "@(#)query.c 8.1 (Berkeley) 6/5/93"; #endif /* not lint */ @@ -71,24 +69,33 @@ int s; char *pgmname; -union pkt_buf { - char packet[MAXPACKETSIZE+4096]; +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; -} msg_buf; -#define MSG msg_buf.rip -#define MSG_LIM ((struct rip*)(&msg_buf.packet[MAXPACKETSIZE \ - - sizeof(struct netinfo)])) + 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 start; /* when query sent */ +struct timeval sent; /* when query sent */ static void rip_input(struct sockaddr_in*, int); -static int query(char *, struct netinfo *); +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); @@ -97,67 +104,120 @@ int main(int argc, char *argv[]) { - char *p; - struct seen { - struct seen *next; - struct in_addr addr; - } *seen, *sp; - int answered = 0; - int ch, cc, bsize; - fd_set bits; - struct timeval now, delay; - struct sockaddr_in from; - int fromlen; - struct netinfo rt; + int ch, bsize; + char *p, *options, *value; - - bzero(&rt, sizeof(rt)); + 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:")) != EOF) + 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, &rt); + 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 = AF_INET; + OMSG.rip_nets[0].n_mask = -1; + rflag = 1; + } break; - case '?': + + 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", + 0 + }; + switch (getsubopt(&options,traceopts,&value)) { + case TRACE_ON: + OMSG.rip_cmd = RIPCMD_TRACEON; + if (!value + || strlen(value) > MAXPATHLEN) + goto usage; + strcpy(OMSG.rip_tracefile, value); + omsg_len += (strlen(value) + - sizeof(OMSG.ripun)); + break; + case TRACE_MORE: + if (value) + goto usage; + OMSG.rip_cmd = RIPCMD_TRACEON; + OMSG.rip_tracefile[0] = '\0'; + break; + case TRACE_OFF: + if (value) + goto usage; + OMSG.rip_cmd = RIPCMD_TRACEOFF; + OMSG.rip_tracefile[0] = '\0'; + break; + default: + goto usage; + } + } + break; + default: goto usage; } argv += optind; argc -= optind; - if (argc == 0) { -usage: printf("usage: query [-np1v] [-w wtime] host1 [host2 ...]\n"); + 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); } - if (!rflag) { - rt.n_dst = RIP_DEFAULT; - rt.n_family = RIP_AF_UNSPEC; - rt.n_metric = htonl(HOPCNT_INFINITY); - } - 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) @@ -168,10 +228,89 @@ usage: printf("usage: query [-np1v] [-w wtime] host1 [host2 ...]\n"); } } - /* ask the first host */ + 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 > query(*argv++, &rt) && *argv != 0) + while (0 > out(*argv++)) { + if (*argv == 0) + exit(-1); answered++; + } FD_ZERO(&bits); for (;;) { @@ -181,8 +320,8 @@ usage: printf("usage: query [-np1v] [-w wtime] host1 [host2 ...]\n"); cc = select(s+1, &bits, 0,0, &delay); if (cc > 0) { fromlen = sizeof(from); - cc = recvfrom(s, msg_buf.packet, - sizeof(msg_buf.packet), 0, + cc = recvfrom(s, imsg_buf.packet, + sizeof(imsg_buf.packet), 0, (struct sockaddr *)&from, &fromlen); if (cc < 0) { perror("recvfrom"); @@ -220,7 +359,7 @@ usage: printf("usage: query [-np1v] [-w wtime] host1 [host2 ...]\n"); /* After a pause in responses, probe another host. * This reduces the intermingling of answers. */ - while (*argv != 0 && 0 > query(*argv++, &rt)) + while (*argv != 0 && 0 > out(*argv++)) answered++; /* continue until no more packets arrive @@ -235,28 +374,25 @@ usage: printf("usage: query [-np1v] [-w wtime] host1 [host2 ...]\n"); perror("gettimeofday(now)"); exit(1); } - if (start.tv_sec + wtime <= now.tv_sec) + if (sent.tv_sec + wtime <= now.tv_sec) break; } /* fail if there was no answer */ exit (answered >= argc ? 0 : 1); - /* NOTREACHED */ } -/* - * Poll one host. +/* sent do one host */ static int -query(char *host, - struct netinfo *rt) +out(char *host) { struct sockaddr_in router; struct hostent *hp; - if (gettimeofday(&start, 0) < 0) { - perror("gettimeofday(start)"); + if (gettimeofday(&sent, 0) < 0) { + perror("gettimeofday(sent)"); return -1; } @@ -265,29 +401,17 @@ query(char *host, #ifdef _HAVE_SIN_LEN router.sin_len = sizeof(router); #endif - router.sin_addr.s_addr = inet_addr(host); - if (router.sin_addr.s_addr == -1) { + if (!inet_aton(host, &router.sin_addr)) { hp = gethostbyname(host); if (hp == 0) { - fprintf(stderr,"%s: %s:", pgmname, host); - herror(0); + herror(host); return -1; } - bcopy(hp->h_addr, &router.sin_addr, hp->h_length); + bcopy(hp->h_addr, &router.sin_addr, sizeof(router.sin_addr)); } - router.sin_port = htons(RIP_PORT); - MSG.rip_cmd = (pflag)? RIPCMD_POLL : RIPCMD_REQUEST; - MSG.rip_nets[0] = *rt; - if (ripv2) { - MSG.rip_vers = RIPv2; - } else { - MSG.rip_vers = RIPv1; - MSG.rip_nets[0].n_mask = 0; - } - - if (sendto(s, msg_buf.packet, sizeof(struct rip), 0, + if (sendto(s, &omsg_buf, omsg_len, 0, (struct sockaddr *)&router, sizeof(router)) < 0) { perror(host); return -1; @@ -329,18 +453,18 @@ rip_input(struct sockaddr_in *from, inet_ntoa(from->sin_addr)); } } - if (MSG.rip_cmd != RIPCMD_RESPONSE) { - printf("\n unexpected response type %d\n", MSG.rip_cmd); + if (IMSG.rip_cmd != RIPCMD_RESPONSE) { + printf("\n unexpected response type %d\n", IMSG.rip_cmd); return; } - printf(" RIPv%d%s %d bytes\n", MSG.rip_vers, - (MSG.rip_vers != RIPv1 && MSG.rip_vers != RIPv2) ? " ?" : "", + 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(msg_buf) - sizeof(*n)) { + if (size > sizeof(imsg_buf) - sizeof(*n)) { printf(" at least %d bytes too long\n", size-MAXPACKETSIZE); - size = sizeof(msg_buf) - sizeof(*n); + size = sizeof(imsg_buf) - sizeof(*n); } else { printf(" %d bytes too long\n", size-MAXPACKETSIZE); @@ -349,7 +473,7 @@ rip_input(struct sockaddr_in *from, printf(" response of bad length=%d\n", size); } - n = MSG.rip_nets; + n = IMSG.rip_nets; lim = (struct netinfo *)((char*)n + size) - 1; for (; n <= lim; n++) { name = ""; @@ -361,7 +485,7 @@ rip_input(struct sockaddr_in *from, dmask = mask & -mask; if (mask != 0) { sp = &net_buf[strlen(net_buf)]; - if (MSG.rip_vers == RIPv1) { + if (IMSG.rip_vers == RIPv1) { (void)sprintf(sp," mask=%#x ? ",mask); mask = 0; } else if (mask + dmask == 0) { @@ -391,14 +515,16 @@ rip_input(struct sockaddr_in *from, * good guess. */ if ((in.s_addr & ~mask) == 0) { - np = getnetbyaddr(in.s_addr, AF_INET); + np = getnetbyaddr((long)in.s_addr, + AF_INET); if (np != 0) name = np->n_name; else if (in.s_addr == 0) name = "default"; } if (name[0] == '\0' - && (in.s_addr & ~mask) != 0) { + && ((in.s_addr & ~mask) != 0 + || mask == 0xffffffff)) { hp = gethostbyaddr((char*)&in, sizeof(in), AF_INET); @@ -425,7 +551,7 @@ rip_input(struct sockaddr_in *from, (char)n->n_dst); } - (void)printf(" %-18s metric %2d %8s", + (void)printf(" %-18s metric %2d %-10s", net_buf, ntohl(n->n_metric), name); if (n->n_nhop != 0) { @@ -435,13 +561,13 @@ rip_input(struct sockaddr_in *from, else hp = gethostbyaddr((char*)&in, sizeof(in), AF_INET); - (void)printf(" nhop=%-15s%s", + (void)printf(" nhop=%-15s%s", (hp != 0) ? hp->h_name : inet_ntoa(in), - (MSG.rip_vers == RIPv1) ? " ?" : ""); + (IMSG.rip_vers == RIPv1) ? " ?" : ""); } if (n->n_tag != 0) (void)printf(" tag=%#x%s", n->n_tag, - (MSG.rip_vers == RIPv1) ? " ?" : ""); + (IMSG.rip_vers == RIPv1) ? " ?" : ""); putc('\n', stdout); } } diff --git a/usr.sbin/routed/table.c b/usr.sbin/routed/table.c index 110656378b8..9b354e79dbf 100644 --- a/usr.sbin/routed/table.c +++ b/usr.sbin/routed/table.c @@ -31,14 +31,15 @@ * SUCH DAMAGE. */ -#ifndef lint +#if !defined(lint) && !defined(sgi) static char sccsid[] = "@(#)tables.c 8.1 (Berkeley) 6/5/93"; #endif /* not lint */ -#ident "$Revision: 1.2 $" +#ident "$Revision: 1.16 $" #include "defs.h" +static struct rt_spare *rts_better(struct rt_entry *); struct radix_node_head *rhead; /* root of the radix tree */ @@ -111,10 +112,11 @@ ag_out(struct ag_info *ag, naddr bit; - /* If we have both the even and odd twins, then the immediate parent, - * if it is present is redundant, unless it manages to aggregate - * something. On successive calls, this code detects the - * even and odd twins, and marks the parent. + /* 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. @@ -219,14 +221,14 @@ ag_flush(naddr lim_dst_h, /* flush routes to here */ * the target. */ if (ag->ag_gate != ag_cors->ag_gate - && !(ag->ag_state & AGS_DEAD) - && !(ag_cors->ag_state & AGS_RDISC)) { + && !(ag->ag_state & AGS_FINE_GATE) + && !(ag_cors->ag_state & AGS_CORS_GATE)) { ag_out(ag, out); break; } - /* If it has a good enough metric, it replaces - * the target. + /* 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) @@ -238,6 +240,10 @@ ag_flush(naddr lim_dst_h, /* flush routes to here */ 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; } } @@ -258,6 +264,7 @@ void ag_check(naddr dst, naddr mask, naddr gate, + naddr nhop, char metric, char pref, u_int seqno, @@ -282,6 +289,7 @@ ag_check(naddr dst, 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; @@ -298,7 +306,10 @@ ag_check(naddr dst, while (ag != 0) { if (ag->ag_mask >= mask) break; - /* Suppress routes as we look. + + /* 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. @@ -311,8 +322,8 @@ ag_check(naddr dst, && 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_DEAD) - || (ag_cors->ag_state & AGS_RDISC))) { + || (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) @@ -322,6 +333,10 @@ ag_check(naddr dst, 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 { @@ -343,40 +358,50 @@ ag_check(naddr dst, && ag->ag_mask == mask && ((ag->ag_dst_h ^ dst) & (mask<<1)) == 0) { - /* When a promoted route encounters the same but explicit - * route, assume the new one has been promoted, and - * so its gateway, metric and tag are right. - * - * Routes are encountered in lexical order, so an even/odd - * pair 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 that was made redundant by - * the pair. - * - * The sequence number only controls flash updating, and - * so should be the smaller of the two. + /* 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) { - ag->ag_metric = metric; - ag->ag_pref = pref; - ag->ag_gate = gate; - ag->ag_tag = tag; + /* 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 only if both routes have them */ - ag->ag_state &= ~(~state & (AGS_PROMOTE | AGS_RIPV2)); - /* others are set if they are set on either route */ - ag->ag_state |= (state & (AGS_REDUN0 | AGS_REDUN1 - | AGS_GATEWAY - | AGS_SUPPRESS)); + /* 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 suppressed - * and the other can at least be suppressed, they - * can be combined. + /* 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. */ @@ -389,15 +414,12 @@ ag_check(naddr dst, /* 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. - * Except that the kernel does not care about the - * 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 - && ag->ag_tag == tag)) { + && (state & ag->ag_state & AGS_PROMOTE) != 0)) { /* We have both the even and odd pairs. * Since the routes are encountered in order, @@ -413,7 +435,11 @@ ag_check(naddr dst, state |= AGS_REDUN0; else state &= ~AGS_REDUN0; - state |= (ag->ag_state & AGS_RIPV2); + 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. @@ -434,6 +460,10 @@ ag_check(naddr dst, 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; @@ -525,6 +555,7 @@ ag_check(naddr dst, 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; @@ -595,7 +626,7 @@ masktrim(struct sockaddr_in_new *ap) return; } cp = (char *)(&ap->sin_addr.s_addr+1); - while (*--cp != 0) + while (*--cp == 0) continue; ap->sin_len = cp - (char*)ap + 1; } @@ -661,14 +692,20 @@ again: if (cc == w.w_rtm.rtm_msglen) return; if (cc < 0) { - if (errno == ESRCH && action == RTM_CHANGE) { - trace_msg("route to %s disappeared before CHANGE", - addrname(dst, mask, 0)); - action = RTM_ADD; - goto again; + 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", - rtm_type_name(action), addrname(dst, mask, 0), + 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", @@ -696,9 +733,9 @@ static struct khash { #define KS_GATEWAY 0x040 #define KS_DYNAMIC 0x080 #define KS_DELETED 0x100 /* already deleted */ - time_t k_hold; - time_t k_time; -#define K_HOLD_LIM 30 + time_t k_keep; +#define K_KEEP_LIM 30 + time_t k_redirect_time; } *khash_bins[KHASH_SIZE]; @@ -732,121 +769,146 @@ kern_add(naddr dst, naddr mask) k->k_dst = dst; k->k_mask = mask; k->k_state = KS_NEW; - k->k_time = now.tv_sec; - k->k_hold = now.tv_sec; + k->k_redirect_time = now.tv_sec; + k->k_keep = now.tv_sec; *pk = k; return k; } -/* add a route the kernel told us - * rt_xaddrs() must have already been called. +/* If it has a non-zero metric, check that it is still in the table, not + * having been deleted by interfaces coming and going. */ static void -rtm_add(struct rt_msghdr *rtm) +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; - struct rt_entry *rt; naddr mask; if (rtm->rtm_flags & RTF_HOST) { mask = HOST_MASK; - } else if (RTINFO_NETMASK != 0) { - mask = ntohl(S_ADDR(RTINFO_NETMASK)); + } 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 (RTINFO_GATE == 0 - || RTINFO_GATE->sa_family != AF_INET) { + 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(RTINFO_DST), mask); - k->k_gate = S_ADDR(RTINFO_GATE); + 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_NEW | KS_DELETED | KS_GATEWAY | KS_STATIC); + 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 (rtm->rtm_flags & RTF_DYNAMIC) + if (rtm->rtm_flags & RTF_DYNAMIC) { k->k_state |= KS_DYNAMIC; - k->k_time = now.tv_sec; - k->k_hold = now.tv_sec; + k->k_redirect_time = now.tv_sec; + /* Routers are not supposed to listen to redirects, + * so delete it. + */ + if (supplier) { + k->k_keep = now.tv_sec; + 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)); + } + } + + /* If it is not a static route, quite until it is time to delete it. + */ + 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 */ - if (!(k->k_state & KS_STATIC)) - return; - - if (RTINFO_IFP != 0 - && RTINFO_IFP->sdl_nlen != 0) { - RTINFO_IFP->sdl_data[RTINFO_IFP->sdl_nlen] = '\0'; - ifp = ifwithname(RTINFO_IFP->sdl_data, k->k_gate); - } else { - ifp = iflookup(k->k_gate); - } + ifp = iflookup(k->k_gate); if (ifp == 0) { - msglog("static route %s --> %s impossibly lacks ifp", - addrname(S_ADDR(RTINFO_DST), mask, 0), - naddr_ntoa(k->k_gate)); - return; + /* if there is no interface, maybe it is new + */ + 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)); } - if (k->k_metric == 0) - return; - rt = rtget(k->k_dst, k->k_mask); - if (rt != 0) { - if (rt->rt_ifp != ifp - || 0 != (rt->rt_state & RS_NET_S)) { - rtdelete(rt); - rt = 0; - } else if (!(rt->rt_state & (RS_IF - | RS_LOCAL - | RS_MHOME - | RS_GW))) { - rtchange(rt, RS_STATIC, - k->k_gate, ifp->int_addr, - k->k_metric, 0, ifp, - now.tv_sec, 0); - } - } - if (rt == 0) - rtadd(k->k_dst, k->k_mask, k->k_gate, - ifp->int_addr, k->k_metric, - 0, RS_STATIC, ifp); + kern_check_static(k, ifp); } /* deal with packet loss */ static void -rtm_lose(struct rt_msghdr *rtm) +rtm_lose(struct rt_msghdr *rtm, + struct rt_addrinfo *info) { - if (RTINFO_GATE == 0 - || RTINFO_GATE->sa_family != AF_INET) { + 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(RTINFO_GATE)); + rdisc_age(S_ADDR(INFO_GATE(info))); - age(S_ADDR(RTINFO_GATE)); + age(S_ADDR(INFO_GATE(info))); } @@ -862,6 +924,7 @@ flush_kern(void) struct rt_msghdr *rtm; struct interface *ifp; static struct sockaddr_in gate_sa; + struct rt_addrinfo info; mib[0] = CTL_NET; @@ -881,39 +944,55 @@ flush_kern(void) for (next = buf; next < lim; next += rtm->rtm_msglen) { rtm = (struct rt_msghdr *)next; - rt_xaddrs((struct sockaddr *)(rtm+1), + rt_xaddrs(&info, + (struct sockaddr *)(rtm+1), (struct sockaddr *)(next + rtm->rtm_msglen), rtm->rtm_addrs); - if (RTINFO_DST == 0 - || RTINFO_DST->sa_family != AF_INET) + if (INFO_DST(&info) == 0 + || INFO_DST(&info)->sa_family != AF_INET) continue; - if (RTINFO_GATE == 0) + /* ignore ARP table entries on systems with a merged route + * and ARP table. + */ + if (rtm->rtm_flags & RTF_LLINFO) continue; - if (RTINFO_GATE->sa_family != AF_INET) { - if (RTINFO_GATE->sa_family != AF_LINK) + + 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 *) - RTINFO_GATE)->sdl_index); + INFO_GATE(&info))->sdl_index); if (ifp == 0) continue; - gate_sa.sin_addr.s_addr = ifp->int_addr; + 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; - RTINFO_GATE = (struct sockaddr *)&gate_sa; + INFO_GATE(&info) = (struct sockaddr *)&gate_sa; } /* ignore multicast addresses */ - if (IN_MULTICAST(ntohl(S_ADDR(RTINFO_DST)))) + if (IN_MULTICAST(ntohl(S_ADDR(INFO_DST(&info))))) continue; - /* Note static routes and interface routes. + /* 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); + rtm_add(rtm,&info,MIN_WAITTIME); } free(buf); } @@ -934,7 +1013,8 @@ read_rt(void) } r; struct if_msghdr ifm; } m; - char pid_str[10+19+1]; + char str[100], *strp; + struct rt_addrinfo info; for (;;) { @@ -963,16 +1043,18 @@ read_rt(void) continue; } - if (m.r.rtm.rtm_type == RTM_IFINFO) { + 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_msg("note %s with flags %#x" + 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_msg("note %s with flags %#x for %s\n", + 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); @@ -991,90 +1073,78 @@ read_rt(void) continue; } + strcpy(str, rtm_type_name(m.r.rtm.rtm_type)); + strp = &str[strlen(str)]; if (m.r.rtm.rtm_type <= RTM_CHANGE) - (void)sprintf(pid_str," from pid %d",m.r.rtm.rtm_pid); - else - pid_str[0] = '\0'; + strp += sprintf(strp," from pid %d",m.r.rtm.rtm_pid); - rt_xaddrs(m.r.addrs, &m.r.addrs[RTAX_MAX], + rt_xaddrs(&info, m.r.addrs, &m.r.addrs[RTAX_MAX], m.r.rtm.rtm_addrs); - if (RTINFO_DST == 0) { - trace_msg("ignore %s%s without dst\n", - rtm_type_name(m.r.rtm.rtm_type), pid_str); + if (INFO_DST(&info) == 0) { + trace_act("ignore %s without dst\n", str); continue; } - if (RTINFO_DST->sa_family != AF_INET) { - trace_msg("ignore %s%s for AF %d\n", - rtm_type_name(m.r.rtm.rtm_type), pid_str, - RTINFO_DST->sa_family); + if (INFO_DST(&info)->sa_family != AF_INET) { + trace_act("ignore %s for AF %d\n", str, + INFO_DST(&info)->sa_family); continue; } - mask = ((RTINFO_NETMASK != 0) - ? ntohl(S_ADDR(RTINFO_NETMASK)) + mask = ((INFO_MASK(&info) != 0) + ? ntohl(S_ADDR(INFO_MASK(&info))) : (m.r.rtm.rtm_flags & RTF_HOST) ? HOST_MASK - : std_mask(S_ADDR(RTINFO_DST))); + : std_mask(S_ADDR(INFO_DST(&info)))); - if (RTINFO_GATE == 0 - || RTINFO_GATE->sa_family != AF_INET) { - trace_msg("%s for %s%s\n", - rtm_type_name(m.r.rtm.rtm_type), - addrname(S_ADDR(RTINFO_DST), mask, 0), - pid_str); - } else { - trace_msg("%s %s --> %s%s\n", - rtm_type_name(m.r.rtm.rtm_type), - addrname(S_ADDR(RTINFO_DST), mask, 0), - saddr_ntoa(RTINFO_GATE), - pid_str); + strp += sprintf(strp, " %s", + addrname(S_ADDR(INFO_DST(&info)), mask, 0)); + + if (IN_MULTICAST(ntohl(S_ADDR(INFO_DST(&info))))) { + trace_act("ignore %s for 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: - if (m.r.rtm.rtm_errno != 0) { - trace_msg("ignore %s%s with \"%s\" error\n", - rtm_type_name(m.r.rtm.rtm_type), - pid_str, - strerror(m.r.rtm.rtm_errno)); - } else { - rtm_add(&m.r.rtm); - } - break; - case RTM_REDIRECT: if (m.r.rtm.rtm_errno != 0) { - trace_msg("ignore %s with \"%s\" from %s" - " for %s-->%s\n", - rtm_type_name(m.r.rtm.rtm_type), - strerror(m.r.rtm.rtm_errno), - saddr_ntoa(RTINFO_AUTHOR), - saddr_ntoa(RTINFO_GATE), - addrname(S_ADDR(RTINFO_DST), - mask, 0)); + trace_act("ignore %s with \"%s\" error\n", + str, strerror(m.r.rtm.rtm_errno)); } else { - rtm_add(&m.r.rtm); + trace_act("%s\n", str); + rtm_add(&m.r.rtm,&info,0); } break; case RTM_DELETE: if (m.r.rtm.rtm_errno != 0) { - trace_msg("ignore %s%s with \"%s\" error\n", - rtm_type_name(m.r.rtm.rtm_type), - pid_str, - strerror(m.r.rtm.rtm_errno)); + trace_act("ignore %s with \"%s\" error\n", + str, strerror(m.r.rtm.rtm_errno)); } else { - del_static(S_ADDR(RTINFO_DST), mask, 1); + trace_act("%s\n", str); + del_static(S_ADDR(INFO_DST(&info)), mask, 1); } break; case RTM_LOSING: - rtm_lose(&m.r.rtm); + trace_act("%s\n", str); + rtm_lose(&m.r.rtm,&info); break; + default: + trace_act("ignore %s\n", str); break; } } @@ -1090,7 +1160,7 @@ kern_out(struct ag_info *ag) /* Do not install bad routes if they are not already present. - * This includes routes that had RS_NET_S for interfaces that + * This includes routes that had RS_NET_SYN for interfaces that * recently died. */ if (ag->ag_metric == HOPCNT_INFINITY @@ -1099,8 +1169,8 @@ kern_out(struct ag_info *ag) k = kern_add(htonl(ag->ag_dst_h), ag->ag_mask); - /* will need to add new entry */ 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; @@ -1109,8 +1179,11 @@ kern_out(struct ag_info *ag) return; } + if (k->k_state & KS_STATIC) + return; + /* modify existing kernel entry if necessary */ - k->k_state &= ~(KS_DELETE | KS_DYNAMIC); + k->k_state &= ~KS_DELETE; if (k->k_gate != ag->ag_gate || k->k_metric != ag->ag_metric) { k->k_gate = ag->ag_gate; @@ -1118,6 +1191,11 @@ kern_out(struct ag_info *ag) 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; @@ -1137,45 +1215,51 @@ walk_kern(struct radix_node *rn, struct walkarg *w) { #define RT ((struct rt_entry *)rn) - char pref; + char metric, pref; u_int ags = 0; + /* Do not install synthetic routes */ - if (0 != (RT->rt_state & RS_NET_S)) + if (RT->rt_state & RS_NET_SYN) return 0; - /* Do not install routes for "external" remote interfaces. - */ - if ((RT->rt_state & RS_IF) - && RT->rt_ifp != 0 - && (RT->rt_ifp->int_state & IS_EXTERNAL)) - return 0; - - /* 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_state & RS_IF) - || RT->rt_ifp == 0 - || ((RT->rt_ifp->int_state & IS_REMOTE) - && RT->rt_ifp->int_metric == 0)) + if (!(RT->rt_state & RS_IF)) { ags |= (AGS_GATEWAY | AGS_SUPPRESS | AGS_PROMOTE); - if (RT->rt_metric == HOPCNT_INFINITY) { - pref = HOPCNT_INFINITY; - ags |= (AGS_DEAD | AGS_SUPPRESS); } else { - pref = 1; + /* 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_RDISC; + ags |= AGS_CORS_GATE; - ag_check(RT->rt_dst, RT->rt_mask, RT->rt_gate, - RT->rt_metric, pref, - 0, 0, ags, kern_out); + /* 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 } @@ -1183,7 +1267,7 @@ walk_kern(struct radix_node *rn, /* Update the kernel table to match the daemon table. */ -void +static void fix_kern(void) { int i, flags; @@ -1201,13 +1285,15 @@ fix_kern(void) 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_hold > now.tv_sec) { - LIM_SEC(need_kern, k->k_hold); + if (k->k_keep > now.tv_sec) { + LIM_SEC(need_kern, k->k_keep); + k->k_state |= KS_DELETE; pk = &k->k_next; continue; } @@ -1238,17 +1324,13 @@ fix_kern(void) } k->k_state &= ~(KS_ADD | KS_CHANGE | KS_DEL_ADD); - /* Unless it seems something else is handling the - * routes in the kernel, mark this route to be - * deleted in the next cycle. + /* 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 - * disappeard from the daemon table. + * disappeared from the daemon table. */ - if (now.tv_sec >= EPOCH+MIN_WAITTIME-1 - && (rip_interfaces != 0 || !supplier)) - k->k_state |= KS_DELETE; + k->k_state |= KS_DELETE; pk = &k->k_next; } } @@ -1268,7 +1350,7 @@ del_static(naddr dst, /* 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 - * hold timer so that it will not be deleted again for a while. + * 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. */ @@ -1278,7 +1360,7 @@ del_static(naddr dst, k->k_state |= KS_DELETE; if (gone) { k->k_state |= KS_DELETED; - k->k_hold = now.tv_sec + K_HOLD_LIM; + k->k_keep = now.tv_sec + K_KEEP_LIM; } } @@ -1289,7 +1371,7 @@ del_static(naddr dst, /* Delete all routes generated from ICMP Redirects that use a given - * gateway. + * gateway, as well as all old redirected routes. */ void del_redirects(naddr bad_gate, @@ -1302,20 +1384,19 @@ del_redirects(naddr bad_gate, for (i = 0; i < KHASH_SIZE; i++) { for (k = khash_bins[i]; k != 0; k = k->k_next) { if (!(k->k_state & KS_DYNAMIC) - || 0 != (k->k_state & (KS_STATIC | KS_DELETE))) + || (k->k_state & KS_STATIC)) continue; if (k->k_gate != bad_gate - && k->k_time > old) + && k->k_redirect_time > old + && !supplier) continue; k->k_state |= KS_DELETE; need_kern.tv_sec = now.tv_sec; - if (TRACEACTIONS) - trace_msg("mark redirected %s --> %s" - " for deletion\n", - addrname(k->k_dst, k->k_mask, 0), - naddr_ntoa(k->k_gate)); + trace_act("mark redirected %s --> %s for deletion\n", + addrname(k->k_dst, k->k_mask, 0), + naddr_ntoa(k->k_gate)); } } } @@ -1405,7 +1486,7 @@ rtadd(naddr dst, naddr router, /* on the authority of this router */ int metric, u_short tag, - u_int state, /* RS_ for our table */ + u_int state, /* rs_state for the entry */ struct interface *ifp) { struct rt_entry *rt; @@ -1440,18 +1521,11 @@ rtadd(naddr dst, rt->rt_gate = gate; rt->rt_router = router; rt->rt_time = now.tv_sec; - if (metric == HOPCNT_INFINITY) { - rt->rt_time -= POISON_SECS; - rt->rt_hold_down = now.tv_sec+HOLD_TIME; - } rt->rt_metric = metric; - if ((rt->rt_state & RS_NET_S) == 0) - rt->rt_hold_metric = metric; - else - rt->rt_hold_metric = HOPCNT_INFINITY; + rt->rt_poison_metric = HOPCNT_INFINITY; rt->rt_tag = tag; rt->rt_ifp = ifp; - rt->rt_seqno = update_seqno+1; + rt->rt_seqno = update_seqno; if (TRACEACTIONS) trace_add_del("Add", rt); @@ -1481,31 +1555,19 @@ rtchange(struct rt_entry *rt, char *label) { if (rt->rt_metric != metric) { - /* Hold down the route if it is bad, but only long enough - * for neighors that do not implement poison-reverse or - * split horizon to hear the bad news. + /* 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) { - if (new_time > now.tv_sec - POISON_SECS) - new_time = now.tv_sec - POISON_SECS; - if (!(rt->rt_state & RS_RDISC) - && rt->rt_hold_down < now.tv_sec+HOLD_TIME) - rt->rt_hold_down = now.tv_sec+HOLD_TIME; - if (now.tv_sec < rt->rt_hold_down) - LIM_SEC(age_timer, rt->rt_hold_down+1); - } else { - rt->rt_hold_down = 0; - if ((rt->rt_state & RS_NET_S) == 0) - rt->rt_hold_metric = metric; - } - - rt->rt_seqno = update_seqno+1; + if (metric == HOPCNT_INFINITY) + need_kern.tv_sec = now.tv_sec; + 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+1; + rt->rt_seqno = update_seqno; set_need_flash(); } @@ -1526,58 +1588,58 @@ rtchange(struct rt_entry *rt, } +/* 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 *rts1, swap; + struct rt_spare swap; char label[10]; - int i; /* Do not change permanent routes */ - if (0 != (rt->rt_state & (RS_GW | RS_MHOME | RS_STATIC | RS_IF))) + if (0 != (rt->rt_state & RS_PERMANENT)) return; /* Do not discard synthetic routes until they go bad */ - if (0 != (rt->rt_state & RS_NET_S) + if ((rt->rt_state & RS_NET_SYN) && rt->rt_metric < HOPCNT_INFINITY) return; - if (rts == 0) { - /* 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(rts1,rts)) - rts = rts1; - } - } + /* 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(rts, rt->rt_spares)) + if (!BETTER_LINK(rt, rts, rt->rt_spares)) return; - /* Do not change the route if it is being held down. - * Honor the hold-down to counter systems that do not support - * split horizon or for other causes of counting to infinity, - * and so only for routes worse than our last good route. - */ - if (now.tv_sec < rt->rt_hold_down - && rts->rts_metric > rt->rt_hold_metric) { - LIM_SEC(age_timer, rt->rt_hold_down+1); - return; - } - swap = rt->rt_spares[0]; - (void)sprintf(label, "Use #%d", rts - rt->rt_spares); - rtchange(rt, rt->rt_state & ~(RS_NET_S | RS_RDISC), + 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; } @@ -1623,7 +1685,8 @@ rtbad(struct rt_entry *rt) } -/* Junk a RS_NET_S route, but save if if it is needed by another interface. +/* Junk a RS_NET_SYN or RS_LOCAL route, + * unless it is needed by another interface. */ void rtbad_sub(struct rt_entry *rt) @@ -1638,14 +1701,11 @@ rtbad_sub(struct rt_entry *rt) 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, a - * point-to-point interface with the same local address. + * 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) { - if (ifp->int_metric == HOPCNT_INFINITY) - continue; - - /* Save it if another interface needs it + /* Retain it if another interface needs it. */ if (ifp->int_addr == rt->rt_ifp->int_addr) { state |= RS_LOCAL; @@ -1656,48 +1716,38 @@ rtbad_sub(struct rt_entry *rt) } - if (!(state & RS_LOCAL) - && (rt->rt_state & RS_NET_S)) { - for (ifp = ifnet; ifp != 0; ifp = ifp->int_next) { - if (ifp->int_metric == HOPCNT_INFINITY) - continue; - - /* Retain RIPv1 logical network route if - * there is another interface that justifies - * it. - */ - if ((ifp->int_state & IS_NEED_NET_SUB) - && rt->rt_mask == ifp->int_std_mask - && rt->rt_dst == ifp->int_std_addr) { - state |= RS_NET_SUB; - ifp1 = ifp; - - } else if ((ifp->int_if_flags & IFF_POINTOPOINT) - && rt->rt_mask == ifp->int_host_mask - && rt->rt_dst == ifp->int_host_addr - && ridhosts) { - state |= RS_NET_HOST; - ifp1 = ifp; - } - } - - if (ifp1 == 0) { - 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_SUB; + 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) { - rtchange(rt, (rt->rt_state & ~(RS_NET_S | RS_LOCAL)) | state, - rt->rt_gate, rt->rt_router, NET_S_METRIC, + if (ifp1 != 0 || (state & RS_NET_SYN)) { + rtchange(rt, ((rt->rt_state & ~(RS_NET_SYN | RS_LOCAL)) + | state), + rt->rt_gate, rt->rt_router, 1, rt->rt_tag, ifp1, rt->rt_time, 0); } else { rtbad(rt); @@ -1730,11 +1780,10 @@ walk_bad(struct radix_node *rn, new_time = rts->rts_time; if (new_time >= now_garbage) new_time = now_garbage-1; - if (TRACEACTIONS) - trace_upslot(RT, rts, rts->rts_gate, - rts->rts_router, 0, - HOPCNT_INFINITY, rts->rts_tag, - new_time); + 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; @@ -1750,7 +1799,7 @@ walk_bad(struct radix_node *rn, /* Bad routes for other than interfaces are easy. */ - if (!(RT->rt_state & RS_IF)) { + if (0 == (RT->rt_state & (RS_IF | RS_NET_SYN | RS_LOCAL))) { rtbad(RT); return 0; } @@ -1774,7 +1823,9 @@ walk_age(struct radix_node *rn, int i; - /* age the spare routes */ + /* 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++) { @@ -1789,37 +1840,26 @@ walk_age(struct radix_node *rn, /* forget RIP routes after RIP has been turned off. */ - if (rip_sock < 0 && !(RT->rt_state & RS_RDISC)) { + if (rip_sock < 0) { rtdelete(RT); return 0; } } + /* age failing routes + */ if (age_bad_gate == rts->rts_gate && rts->rts_time >= now_stale) { - /* age failing routes - */ rts->rts_time -= SUPPLY_INTERVAL; - - } else if (ppp_noage - && ifp != 0 - && (ifp->int_if_flags & IFF_POINTOPOINT) - && (ifp->int_state & IS_QUIET)) { - /* optionally do not age routes through quiet - * point-to-point interfaces - */ - rts->rts_time = now.tv_sec; - continue; } /* trash the spare routes when they go bad */ if (rts->rts_metric < HOPCNT_INFINITY && now_garbage > rts->rts_time) { - if (TRACEACTIONS) - trace_upslot(RT, rts, rts->rts_gate, - rts->rts_router, rts->rts_ifp, - HOPCNT_INFINITY, rts->rts_tag, - 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; } } @@ -1830,12 +1870,7 @@ walk_age(struct radix_node *rn, return 0; /* try to switch to an alternative */ - if (now.tv_sec < RT->rt_hold_down) { - LIM_SEC(age_timer, RT->rt_hold_down+1); - return 0; - } else { - rtswitch(RT, 0); - } + rtswitch(RT, 0); /* Delete a dead route after it has been publically mourned. */ if (now_garbage > RT->rt_time) { @@ -1871,11 +1906,16 @@ age(naddr bad_gate) 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) - ifbad(ifp, - "remote interface %s to %s timed out"); + + 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); + } } } diff --git a/usr.sbin/routed/trace.c b/usr.sbin/routed/trace.c index 5f4fc7d1a94..01220c7dbaa 100644 --- a/usr.sbin/routed/trace.c +++ b/usr.sbin/routed/trace.c @@ -31,11 +31,11 @@ * SUCH DAMAGE. */ -#ifndef lint +#if !defined(lint) && !defined(sgi) static char sccsid[] = "@(#)trace.c 8.1 (Berkeley) 6/5/93"; #endif /* not lint */ -#ident "$Revision: 1.1 $" +#ident "$Revision: 1.8 $" #define RIPCMDS #include "defs.h" @@ -54,11 +54,13 @@ static char sccsid[] = "@(#)trace.c 8.1 (Berkeley) 6/5/93"; u_int tracelevel, new_tracelevel; FILE *ftrace = stdout; /* output trace file */ -char *tracelevel_msg = ""; +static char *tracelevel_pat = "%s\n"; char savetracename[MAXPATHLEN+1]; +/* convert IP address to a string, but not into a single buffer + */ char * naddr_ntoa(naddr a) { @@ -110,42 +112,43 @@ lastlog(void) if (last.tv_sec != now.tv_sec || last.tv_usec != now.tv_usec) { - (void)fprintf(ftrace, "--- %s ---\n", ts(now.tv_sec)); + (void)fprintf(ftrace, "-- %s --\n", ts(now.tv_sec)); last = now; } } static void -tmsg(char *msg1, char* msg2) +tmsg(char *p, ...) { + va_list args; + if (ftrace != 0) { lastlog(); - (void)fprintf(ftrace, "%s%s\n", msg1,msg2); + va_start(args, p); + vfprintf(ftrace, p, args); + fflush(ftrace); } } static void -trace_close(char *msg1, char *msg2) +trace_close(void) { int fd; + fflush(stdout); fflush(stderr); - if (ftrace != 0) { - tmsg(msg1,msg2); - fflush(ftrace); - - if (savetracename[0] != '\0') { - fd = open(_PATH_DEVNULL, O_RDWR); - (void)dup2(fd, STDOUT_FILENO); - (void)dup2(fd, STDERR_FILENO); - (void)close(fd); - fclose(ftrace); - ftrace = 0; - } + 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; } } @@ -162,9 +165,18 @@ trace_flush(void) void -trace_off(char *msg1, char *msg2) +trace_off(char *p, ...) { - trace_close(msg1, msg2); + va_list args; + + + if (ftrace != 0) { + lastlog(); + va_start(args, p); + vfprintf(ftrace, p, args); + fflush(ftrace); + } + trace_close(); new_tracelevel = tracelevel = 0; } @@ -178,26 +190,48 @@ trace_on(char *filename, FILE *n_ftrace; - if (stat(filename, &stbuf) >= 0 && - (stbuf.st_mode & S_IFMT) != S_IFREG) { - msglog("wrong type (%#x) of trace file \"%s\"", - stbuf.st_mode, filename); - return; - } - if (!trusted - && strcmp(filename, savetracename) - && strncmp(filename, _PATH_TRACE, sizeof(_PATH_TRACE)-1)) { - msglog("wrong directory for trace file %s", filename); - return; + /* 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 (stat(filename, &stbuf) >= 0) { + if (!trusted) { + msglog("trace file \"%s\" already exists"); + return; + } + if ((stbuf.st_mode & S_IFMT) != S_IFREG) { + msglog("wrong type (%#x) of trace file \"%s\"", + stbuf.st_mode, filename); + return; + } + + if (!trusted + && strcmp(filename, savetracename) + && strncmp(filename, _PATH_TRACE, sizeof(_PATH_TRACE)-1)) { + msglog("wrong directory for trace file: \"%s\"", + filename); + return; + } } + n_ftrace = fopen(filename, "a"); if (n_ftrace == 0) { - msglog("failed to open trace file \"%s\": %s", + msglog("failed to open trace file \"%s\" %s", filename, strerror(errno)); return; } - trace_close("switch to trace file ", filename); + tmsg("switch to trace file %s\n", filename); + trace_close(); if (filename != savetracename) strncpy(savetracename, filename, sizeof(savetracename)-1); ftrace = n_ftrace; @@ -207,12 +241,9 @@ trace_on(char *filename, dup2(fileno(ftrace), STDOUT_FILENO); dup2(fileno(ftrace), STDERR_FILENO); - if (new_tracelevel == 0) { - tracelevel_msg = "trace command: "; + if (new_tracelevel == 0) new_tracelevel = 1; - } else { - tmsg("trace command",""); - } + set_tracelevel(); } @@ -221,7 +252,7 @@ void sigtrace_on(int s) { new_tracelevel++; - tracelevel_msg = "SIGUSR1: "; + tracelevel_pat = "SIGUSR1: %s\n"; } @@ -230,7 +261,7 @@ void sigtrace_off(int s) { new_tracelevel--; - tracelevel_msg = "SIGUSR2: "; + tracelevel_pat = "SIGUSR2: %s\n"; } @@ -255,14 +286,19 @@ set_tracelevel(void) }; - if (new_tracelevel > MAX_TRACELEVEL) + 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_msg, off_msgs[0]); + trace_off(tracelevel_pat, off_msgs[0]); else - tmsg(tracelevel_msg, off_msgs[tracelevel]); + tmsg(tracelevel_pat, off_msgs[tracelevel]); } else { if (ftrace == 0) { if (savetracename[0] != '\0') @@ -270,9 +306,10 @@ set_tracelevel(void) else ftrace = stdout; } - tmsg(tracelevel_msg, on_msgs[tracelevel++]); + tmsg(tracelevel_pat, on_msgs[tracelevel++]); } } + tracelevel_pat = "%s\n"; } @@ -312,106 +349,132 @@ addrname(naddr addr, /* in network byte order */ */ struct bits { int bits_mask; + int bits_clear; char *bits_name; }; static struct bits if_bits[] = { - { IFF_UP, "" }, - { IFF_BROADCAST, "" }, - { IFF_LOOPBACK, "LOOPBACK" }, - { IFF_POINTOPOINT, "PT-TO-PT" }, - { IFF_RUNNING, "" }, - { IFF_MULTICAST, "" }, - { -1, ""}, - { 0 } + { IFF_LOOPBACK, 0, "LOOPBACK" }, + { IFF_POINTOPOINT, 0, "PT-TO-PT" }, + { 0, 0, 0} }; static struct bits is_bits[] = { - { IS_SUBNET, "" }, - { IS_REMOTE, "REMOTE" }, - { IS_PASSIVE, "PASSIVE" }, - { IS_EXTERNAL, "EXTERNAL" }, - { IS_CHECKED, "" }, - { IS_ALL_HOSTS, "" }, - { IS_ALL_ROUTERS, "" }, - { IS_RIP_QUERIED, "" }, - { IS_BROKE, "BROKE" }, - { IS_ACTIVE, "ACTIVE" }, - { IS_QUIET, "QUIET" }, - { IS_NEED_NET_SUB, "" }, - { IS_NO_AG, "NO_AG" }, - { IS_NO_SUPER_AG, "NO_SUPER_AG" }, + { IS_SUBNET, 0, "" }, + { IS_REMOTE, 0, "REMOTE" }, + { IS_PASSIVE, (IS_NO_RDISC + | IS_BCAST_RDISC + | IS_NO_RIP + | IS_NO_SUPER_AG + | IS_PM_RDISC + | IS_NO_AG), "PASSIVE" }, + { IS_EXTERNAL, 0, "EXTERNAL" }, + { IS_CHECKED, 0, "" }, + { IS_ALL_HOSTS, 0, "" }, + { IS_ALL_ROUTERS, 0, "" }, + { IS_RIP_QUERIED, 0, "" }, + { IS_BROKE, IS_SICK, "BROKEN" }, + { IS_SICK, 0, "SICK" }, + { IS_ACTIVE, 0, "ACTIVE" }, + { IS_NEED_NET_SYN, 0, "" }, + { IS_NO_AG, IS_NO_SUPER_AG, "NO_AG" }, + { IS_NO_SUPER_AG, 0, "NO_SUPER_AG" }, { (IS_NO_RIPV1_IN | IS_NO_RIPV2_IN | IS_NO_RIPV1_OUT - | IS_NO_RIPV2_OUT), "NO_RIP" }, - { IS_NO_RIPV1_IN, "NO_RIPV1_IN" }, - { IS_NO_RIPV2_IN, "NO_RIPV2_IN" }, - { IS_NO_RIPV1_OUT, "NO_RIPV1_OUT" }, - { IS_NO_RIPV2_OUT, "NO_RIPV2_OUT" }, + | IS_NO_RIPV2_OUT), 0, "NO_RIP" }, + { (IS_NO_RIPV1_IN + | IS_NO_RIPV1_OUT), 0, "RIPV2" }, + { IS_NO_RIPV1_IN, 0, "NO_RIPV1_IN" }, + { IS_NO_RIPV2_IN, 0, "NO_RIPV2_IN" }, + { IS_NO_RIPV1_OUT, 0, "NO_RIPV1_OUT" }, + { IS_NO_RIPV2_OUT, 0, "NO_RIPV2_OUT" }, { (IS_NO_ADV_IN | IS_NO_SOL_OUT - | IS_NO_ADV_OUT), "NO_RDISC" }, - { IS_NO_SOL_OUT, "NO_SOLICIT" }, - { IS_SOL_OUT, "SEND_SOLICIT" }, - { IS_NO_ADV_OUT, "NO_RDISC_ADV" }, - { IS_ADV_OUT, "RDISC_ADV" }, - { IS_BCAST_RDISC, "BCAST_RDISC" }, - { 0 } + | IS_NO_ADV_OUT), IS_BCAST_RDISC, "NO_RDISC" }, + { IS_NO_SOL_OUT, 0, "NO_SOLICIT" }, + { IS_SOL_OUT, 0, "SEND_SOLICIT" }, + { IS_NO_ADV_OUT, IS_BCAST_RDISC, "NO_RDISC_ADV" }, + { IS_ADV_OUT, 0, "RDISC_ADV" }, + { IS_BCAST_RDISC, 0, "BCAST_RDISC" }, + { IS_PM_RDISC, 0, "PM_RDISC" }, + { 0, 0, "%#x"} }; static struct bits rs_bits[] = { - { RS_IF, "IF" }, - { RS_NET_SUB, "NET_SUB" }, - { RS_NET_HOST, "NET_HOST" }, - { RS_NET_INT, "NET_INT" }, - { RS_SUBNET, "" }, - { RS_LOCAL, "LOCAL" }, - { RS_MHOME, "MHOME" }, - { RS_GW, "GW" }, - { RS_STATIC, "STATIC" }, - { RS_RDISC, "RDISC" }, - { 0 } + { RS_IF, 0, "IF" }, + { RS_NET_INT, RS_NET_SYN, "NET_INT" }, + { RS_NET_SYN, 0, "NET_SYN" }, + { RS_SUBNET, 0, "" }, + { RS_LOCAL, 0, "LOCAL" }, + { RS_MHOME, 0, "MHOME" }, + { RS_STATIC, 0, "STATIC" }, + { RS_RDISC, 0, "RDISC" }, + { 0, 0, "%#x"} }; static void trace_bits(struct bits *tbl, - u_int field) + u_int field, + int force) { - int first = 1; int b; + char c; + if (force) { + (void)putc('<', ftrace); + c = 0; + } else { + c = '<'; + } - while (field != 0) { - b = tbl->bits_mask; - if (b == 0) - break; - if ((b & field) == b - && tbl->bits_name[0] != '\0') { - (void)fprintf(ftrace, first ? "<%s" : "|%s", - tbl->bits_name); - first = 0; + while (field != 0 + && (b = tbl->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; } - field &= ~b; tbl++; } - if (field != 0) { - (void)fputc(first ? '<' : '|', ftrace); - (void)fprintf(ftrace, "%#x", field); - first = 0; + if (field != 0 && tbl->bits_name != 0) { + if (c) + (void)putc(c, ftrace); + (void)fprintf(ftrace, tbl->bits_name, field); + c = '|'; } - if (!first) + 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 (ftrace == 0) + if (!TRACEACTIONS || ftrace == 0) return; lastlog(); @@ -422,8 +485,8 @@ trace_if(char *act, ? naddr_ntoa(ifp->int_dstaddr) : addrname(htonl(ifp->int_net), ifp->int_mask, 0))); (void)fprintf(ftrace, "metric=%d ", ifp->int_metric); - trace_bits(if_bits, ifp->int_if_flags); - trace_bits(is_bits, ifp->int_state); + trace_bits(if_bits, ifp->int_if_flags, 0); + trace_bits(is_bits, ifp->int_state, 0); (void)fputc('\n',ftrace); } @@ -438,7 +501,7 @@ trace_upslot(struct rt_entry *rt, u_short tag, time_t new_time) { - if (ftrace == 0) + if (!TRACEACTIONS || ftrace == 0) return; if (rts->rts_gate == gate && rts->rts_router == router @@ -448,11 +511,10 @@ trace_upslot(struct rt_entry *rt, lastlog(); if (rts->rts_gate != RIP_DEFAULT) { - (void)fprintf(ftrace, "Chg #%d %-16s--> ", + (void)fprintf(ftrace, "Chg #%d %-35s ", rts - rt->rt_spares, - addrname(rt->rt_dst, rt->rt_mask, 0)); - (void)fprintf(ftrace, "%-15s ", - naddr_ntoa(rts->rts_gate)); + 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)); @@ -464,9 +526,8 @@ trace_upslot(struct rt_entry *rt, rts->rts_ifp->int_name); (void)fprintf(ftrace, "%s\n", ts(rts->rts_time)); - (void)fprintf(ftrace, " %-16s--> ", - addrname(rt->rt_dst, rt->rt_mask, 0)); - (void)fprintf(ftrace, "%-15s ", + (void)fprintf(ftrace, " %19s%-16s ", + "", gate != rts->rts_gate ? naddr_ntoa(gate) : ""); if (gate != router) (void)fprintf(ftrace,"router=%s ",naddr_ntoa(router)); @@ -480,10 +541,10 @@ trace_upslot(struct rt_entry *rt, new_time != rts->rts_time ? ts(new_time) : ""); } else { - (void)fprintf(ftrace, "Add #%d %-16s--> ", + (void)fprintf(ftrace, "Add #%d %-35s ", rts - rt->rt_spares, - addrname(rt->rt_dst, rt->rt_mask, 0)); - (void)fprintf(ftrace, "%-15s ", naddr_ntoa(gate)); + 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) @@ -496,8 +557,10 @@ trace_upslot(struct rt_entry *rt, } +/* display a message if tracing actions + */ void -trace_msg(char *p, ...) +trace_act(char *p, ...) { va_list args; @@ -510,6 +573,22 @@ trace_msg(char *p, ...) } +/* 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, @@ -532,25 +611,25 @@ trace_change(struct rt_entry *rt, return; lastlog(); - (void)fprintf(ftrace, "%s %-16s--> %-15s metric=%-2d ", + (void)fprintf(ftrace, "%s %-35s metric=%-2d ", label, - addrname(rt->rt_dst, rt->rt_mask, 0), - naddr_ntoa(rt->rt_gate), rt->rt_metric); + 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 ", rt->rt_tag); - trace_bits(rs_bits, rt->rt_state); + trace_bits(rs_bits, rt->rt_state, rt->rt_state != state); (void)fprintf(ftrace, "%s ", - rt->rt_ifp == 0 ? "-" : rt->rt_ifp->int_name); + rt->rt_ifp == 0 ? "?" : rt->rt_ifp->int_name); (void)fprintf(ftrace, "%s\n", AGE_RT(rt, rt->rt_ifp) ? ts(rt->rt_time) : ""); - (void)fprintf(ftrace, "%*s %-16s--> %-15s ", - strlen(label), "", - addrname(rt->rt_dst, rt->rt_mask, 0), - (rt->rt_gate != gate) ? naddr_ntoa(gate) : ""); + (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) @@ -558,13 +637,10 @@ trace_change(struct rt_entry *rt, if (rt->rt_tag != tag) (void)fprintf(ftrace, "tag=%#x ", tag); if (rt->rt_state != state) - trace_bits(rs_bits, state); + trace_bits(rs_bits, state, 1); if (rt->rt_ifp != ifp) (void)fprintf(ftrace, "%s ", - ifp != 0 ? ifp->int_name : "-"); - if (rt->rt_hold_down > now.tv_sec) - (void)fprintf(ftrace, "hold-down=%d ", - rt->rt_hold_down - now.tv_sec); + ifp != 0 ? ifp->int_name : "?"); (void)fprintf(ftrace, "%s\n", ((rt->rt_time == new_time || !AGE_RT(rt, ifp)) ? "" : ts(new_time))); @@ -580,16 +656,17 @@ trace_add_del(char * action, struct rt_entry *rt) return; lastlog(); - (void)fprintf(ftrace, "%s %-16s--> %-15s metric=%-2d ", + (void)fprintf(ftrace, "%s %-35s metric=%-2d ", action, - addrname(rt->rt_dst, rt->rt_mask, 0), - naddr_ntoa(rt->rt_gate), rt->rt_metric); + 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 ", rt->rt_tag); - trace_bits(rs_bits, state); + trace_bits(rs_bits, state, 0); if (rt->rt_ifp != 0) (void)fprintf(ftrace, "%s ", rt->rt_ifp->int_name); (void)fprintf(ftrace, "%s\n", ts(rt->rt_time)); @@ -607,7 +684,7 @@ trace_rip(char *dir1, char *dir2, struct netauth *a; int i; - if (ftrace == 0) + if (!TRACEPACKETS || ftrace == 0) return; lastlog(); From 38cfd0b437d4376607181b9329b6e39940d8a553 Mon Sep 17 00:00:00 2001 From: Garrett Wollman Date: Tue, 27 Aug 1996 16:13:00 +0000 Subject: [PATCH 4/5] Latest version of routed from Vern Schryver @ SGI. This version fixes the bug where ICMP redirects would cause routes to be deleted even in cases where it wasn't warranted. Submitted by: Vernon J. Schryver --- usr.sbin/routed/Makefile | 16 ++- usr.sbin/routed/defs.h | 36 ++++--- usr.sbin/routed/if.c | 72 ++++++++----- usr.sbin/routed/input.c | 99 ++++++++++------- usr.sbin/routed/main.c | 31 +++--- usr.sbin/routed/output.c | 27 +++-- usr.sbin/routed/parms.c | 17 +-- usr.sbin/routed/pathnames.h | 2 + usr.sbin/routed/radix.c | 171 +++++++++++++++--------------- usr.sbin/routed/radix.h | 161 ++++++++++++++++++++++++++++ usr.sbin/routed/rdisc.c | 83 ++++++++++----- usr.sbin/routed/routed.h | 37 ++++--- usr.sbin/routed/rtquery/Makefile | 3 +- usr.sbin/routed/rtquery/rtquery.c | 25 +++-- usr.sbin/routed/table.c | 138 ++++++++++++++---------- usr.sbin/routed/trace.c | 78 ++++++++------ 16 files changed, 661 insertions(+), 335 deletions(-) create mode 100644 usr.sbin/routed/radix.h diff --git a/usr.sbin/routed/Makefile b/usr.sbin/routed/Makefile index 594a05059d7..c567e68e87a 100644 --- a/usr.sbin/routed/Makefile +++ b/usr.sbin/routed/Makefile @@ -2,7 +2,21 @@ PROG= routed SRCS= if.c input.c main.c output.c parms.c radix.c rdisc.c table.c trace.c -MAN8= routed.8 +MAN8= routed.0 SUBDIR= rtquery +DPADD= ${LIBCOMPAT} +LDADD= -lcompat +#COPTS= -g -DDEBUG -Wall .include + +.if (${MACHINE} == "vax") +# The following can be deleted where not appropriate to use the kernel's +# inline code expansions. +INLINE= /sys/vax/inline/obj/inline +C2= /usr/libexec/c2 +.c.o: + ${CC} -S ${CFLAGS} ${.CURDIR}/${.PREFIX}.c + @${C2} ${.PREFIX}.s | ${INLINE} | ${AS} -o ${.PREFIX}.o + @rm -f ${.PREFIX}.s +.endif diff --git a/usr.sbin/routed/defs.h b/usr.sbin/routed/defs.h index 85eb3dad517..f6499122219 100644 --- a/usr.sbin/routed/defs.h +++ b/usr.sbin/routed/defs.h @@ -31,9 +31,13 @@ * SUCH DAMAGE. * * @(#)defs.h 8.1 (Berkeley) 6/5/93 + * + * $NetBSD$ */ -#ident "$Revision: 1.11 $" +#ifndef __NetBSD__ +#ident "$Revision: 1.13 $" +#endif /* Definitions for RIPv2 routing process. * @@ -78,18 +82,20 @@ #include #include #include +#ifdef sgi +#include +#else +#include "radix.h" +#endif #include #include -#include -#ifndef sgi -struct walkarg; -#endif #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. @@ -99,7 +105,11 @@ struct walkarg; #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 @@ -148,6 +158,12 @@ union pkt_buf { }; +/* 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 { @@ -449,15 +465,10 @@ extern void logbad(int, char *, ...); #else #define DBGERR(dump,msg) LOGERR(msg) #endif -#ifdef MCAST_PPP_BUG -extern void mcasterr(struct interface *, int, char *); -#define MCASTERR(ifp,dump,msg) mcasterr(ifp, dump, "setsockopt(IP_"msg")") -#else -#define MCASTERR(ifp, dump,msg) DBGERR(dump,"setsockopt(IP_" msg ")") -#endif extern char *naddr_ntoa(naddr); extern char *saddr_ntoa(struct sockaddr *); +extern void *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 *); @@ -539,9 +550,6 @@ 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); -#ifdef sgi -extern int sysctl(int *, u_int, void *, size_t *, void *, size_t); -#endif extern void addrouteforif(register struct interface *); extern void ifinit(void); extern int walk_bad(struct radix_node *, struct walkarg *); diff --git a/usr.sbin/routed/if.c b/usr.sbin/routed/if.c index 38d344ca049..7143b319c54 100644 --- a/usr.sbin/routed/if.c +++ b/usr.sbin/routed/if.c @@ -31,11 +31,12 @@ * SUCH DAMAGE. */ -#if !defined(lint) && !defined(sgi) +#if !defined(lint) && !defined(sgi) && !defined(__NetBSD__) static char sccsid[] = "@(#)if.c 8.1 (Berkeley) 6/5/93"; -#endif /* not lint */ - -#ident "$Revision: 1.15 $" +#elif defined(__NetBSD__) +static char rcsid[] = "$NetBSD$"; +#endif +#ident "$Revision: 1.16 $" #include "defs.h" #include "pathnames.h" @@ -62,10 +63,7 @@ ifwithaddr(naddr addr, struct interface *ifp, *possible = 0; for (ifp = ifnet; ifp; ifp = ifp->int_next) { - if ((ifp->int_addr == addr - && !(ifp->int_if_flags & IFF_POINTOPOINT)) - || (ifp->int_dstaddr == addr - && (ifp->int_if_flags & IFF_POINTOPOINT)) + if (ifp->int_addr == addr || ((ifp->int_if_flags & IFF_BROADCAST) && ifp->int_brdaddr == addr && bcast)) { @@ -171,7 +169,7 @@ std_mask(naddr addr) /* in network order */ } -/* Find The netmask that would be inferred by RIPv1 listeners +/* 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. */ @@ -453,14 +451,15 @@ ifinit(void) static size_t sysctl_buf_size = 0; uint complaints = 0; static u_int prev_complaints = 0; -# define COMP_NOT_INET 0x01 -# define COMP_WIERD 0x02 -# define COMP_NOADDR 0x04 -# define COMP_NODST 0x08 -# define COMP_NOBADR 0x10 -# define COMP_NOMASK 0x20 -# define COMP_DUP 0x40 -# define COMP_BAD_METRIC 0x80 +# define COMP_NOT_INET 0x001 +# define COMP_WIERD 0x002 +# define COMP_NOADDR 0x004 +# define COMP_NODST 0x008 +# define COMP_NOBADR 0x010 +# define COMP_NOMASK 0x020 +# define COMP_DUP 0x040 +# define COMP_BAD_METRIC 0x080 +# define COMP_NETMASK 0x100 struct interface ifs, ifs0, *ifp, *ifp1; struct rt_entry *rt; @@ -498,7 +497,6 @@ ifinit(void) 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); @@ -506,8 +504,7 @@ ifinit(void) } if (sysctl(mib, 6, 0, &needed, 0, 0) < 0) BADERR(1,"ifinit: route-sysctl-estimate"); - if ((sysctl_buf = malloc(sysctl_buf_size = needed)) == 0) - BADERR(1,"ifinit: malloc"); + sysctl_buf = rtmalloc(sysctl_buf_size = needed, "ifinit"); } ifam_lim = (struct ifa_msghdr *)(sysctl_buf + needed); @@ -540,7 +537,7 @@ ifinit(void) continue; } if (ifam->ifam_type != RTM_NEWADDR) { - DBGERR(1,"ifinit: out of sync"); + logbad(1,"ifinit: out of sync"); continue; } @@ -705,6 +702,9 @@ ifinit(void) } if (ifp != 0) { + /* The primary representative of an alias worries + * about how things are working. + */ if (ifp->int_state & IS_ALIAS) continue; @@ -850,9 +850,7 @@ ifinit(void) /* Add it to the list of interfaces */ - ifp = (struct interface *)malloc(sizeof(*ifp)); - if (ifp == 0) - BADERR(1,"ifinit: out of memory"); + ifp = (struct interface *)rtmalloc(sizeof(*ifp), "ifinit"); bcopy(&ifs, ifp, sizeof(*ifp)); if (ifnet != 0) { ifp->int_next = ifnet; @@ -861,6 +859,32 @@ ifinit(void) 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)) { diff --git a/usr.sbin/routed/input.c b/usr.sbin/routed/input.c index aab1b0adc84..6bbc572af57 100644 --- a/usr.sbin/routed/input.c +++ b/usr.sbin/routed/input.c @@ -31,11 +31,12 @@ * SUCH DAMAGE. */ -#if !defined(lint) && !defined(sgi) +#if !defined(lint) && !defined(sgi) && !defined(__NetBSD__) static char sccsid[] = "@(#)input.c 8.1 (Berkeley) 6/5/93"; -#endif /* not lint */ - -#ident "$Revision: 1.10 $" +#elif defined(__NetBSD__) +static char rcsid[] = "$NetBSD$"; +#endif +#ident "$Revision: 1.13 $" #include "defs.h" @@ -201,10 +202,14 @@ input(struct sockaddr_in *from, /* received from this IP address */ && n == rip->rip_nets && n+1 == lim) { if (from->sin_port != htons(RIP_PORT)) { - /* query */ + /* query from `rtquery` or similar + */ supply(from, ifp, OUT_QUERY, 0, rip->rip_vers); } else if (supplier) { + /* a router trying to prime its + * tables. + */ supply(from, ifp, OUT_UNICAST, 0, rip->rip_vers); } @@ -234,34 +239,35 @@ input(struct sockaddr_in *from, /* received from this IP address */ } if (rip->rip_vers == RIPv1 - || 0 == (mask = ntohl(n->n_mask))) + || 0 == (mask = ntohl(n->n_mask)) + || 0 != (ntohl(dst) & ~mask)) mask = ripv1_mask_host(dst,ifp); rt = rtget(dst, mask); - if (!rt) + if (!rt && dst != RIP_DEFAULT) rt = rtfind(n->n_dst); n->n_tag = 0; n->n_nhop = 0; - if (!rt) { + 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; - if (ifp != 0) - n->n_metric += ifp->int_metric; + n->n_metric += (ifp!=0) ? ifp->int_metric : 1; if (n->n_metric > HOPCNT_INFINITY) n->n_metric = HOPCNT_INFINITY; - if (rip->rip_vers == RIPv1) { - n->n_mask = 0; - } else { + if (rip->rip_vers != RIPv1) { n->n_tag = rt->rt_tag; - if (!ifp - || !on_net(rt->rt_gate, + if (ifp != 0 + && on_net(rt->rt_gate, ifp->int_net, ifp->int_mask) - || rt->rt_gate != ifp->int_addr) - n->n_nhop = 0; - else + && rt->rt_gate != ifp->int_addr) n->n_nhop = rt->rt_gate; } } @@ -299,7 +305,7 @@ input(struct sockaddr_in *from, /* received from this IP address */ } if (rip->rip_cmd == RIPCMD_TRACEON) { rip->rip_tracefile[size-4] = '\0'; - trace_on(rip->rip_tracefile, 0); + trace_on((char*)rip->rip_tracefile, 0); } else { trace_off("tracing turned off by %s\n", naddr_ntoa(FROM_NADDR)); @@ -385,20 +391,27 @@ input(struct sockaddr_in *from, /* received from this IP address */ return; } - /* Authenticate the packet. + /* Authenticate the packet if we have a secret. */ - if (ifp->int_passwd[0] != '\0' - && (n >= lim - || n->n_family != RIP_AF_AUTH - || ((struct netauth*)n)->a_type != RIP_AUTH_PW - || 0 != bcmp(((struct netauth*)n)->au.au_pw, - ifp->int_passwd, - sizeof(ifp->int_passwd)))) { - if (from->sin_addr.s_addr != use_auth) - msglog("missing authentication from %s", - naddr_ntoa(FROM_NADDR)); - use_auth = from->sin_addr.s_addr; - return; + if (ifp->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, + ifp->int_passwd, + sizeof(ifp->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++) { @@ -525,15 +538,16 @@ input(struct sockaddr_in *from, /* received from this IP address */ || !(rt->rt_state & RS_NET_SYN)))) { ddst_h = v1_mask & -v1_mask; i = (v1_mask & ~mask)/ddst_h; - if (i >= 1024) { + if (i >= 511) { /* Punt if we would have to generate * an unreasonable number of routes. */ #ifdef DEBUG - msglog("accept %s from %s as-is" - " instead of as %d routes", + msglog("accept %s from %s as 1" + " instead of %d routes", addrname(dst,mask,0), - naddr_ntoa(FROM_NADDR), i); + naddr_ntoa(FROM_NADDR), + i+1); #endif i = 0; } else { @@ -596,7 +610,9 @@ input_route(struct interface *ifp, if (n->n_metric == HOPCNT_INFINITY) return; - rtadd(dst, mask, gate, from, n->n_metric, n->n_tag, 0, ifp); + if (total_routes < MAX_ROUTES) + rtadd(dst, mask, gate, from, n->n_metric, + n->n_tag, 0, ifp); return; } @@ -637,11 +653,12 @@ input_route(struct interface *ifp, */ int old_metric = rts->rts_metric; - /* Keep poisoned routes around only long - * enough to pass the poison on. + /* Keep poisoned routes around only long enough to pass + * the poison on. Get a new timestamp for good routes. */ - if (old_metric < HOPCNT_INFINITY) - new_time = now.tv_sec; + 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. diff --git a/usr.sbin/routed/main.c b/usr.sbin/routed/main.c index 96afecc25e0..700762ed5dc 100644 --- a/usr.sbin/routed/main.c +++ b/usr.sbin/routed/main.c @@ -34,11 +34,12 @@ char copyright[] = "@(#) Copyright (c) 1983, 1988, 1993\n\ The Regents of the University of California. All rights reserved.\n"; -#if !defined(lint) && !defined(sgi) +#if !defined(lint) && !defined(sgi) && !defined(__NetBSD__) static char sccsid[] = "@(#)main.c 8.1 (Berkeley) 6/5/93"; -#endif /* not lint */ - -#ident "$Revision: 1.13 $" +#elif defined(__NetBSD__) +static char rcsid[] = "$NetBSD$"; +#endif +#ident "$Revision: 1.14 $" #include "defs.h" #include "pathnames.h" @@ -274,14 +275,7 @@ usage: if (setsockopt(rt_sock, SOL_SOCKET,SO_USELOOPBACK, &off,sizeof(off)) < 0) LOGERR("setsockopt(SO_USELOOPBACK,0)"); - - /* prepare Router Discovery socket. - */ - rdisc_sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); - if (rdisc_sock < 0) - BADERR(1,"rdisc_sock = socket()"); - fix_sock(rdisc_sock,"rdisc_sock"); - + fix_select(); @@ -735,6 +729,19 @@ rip_on(struct interface *ifp) } +/* 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 diff --git a/usr.sbin/routed/output.c b/usr.sbin/routed/output.c index 8750c412dfd..be30609743a 100644 --- a/usr.sbin/routed/output.c +++ b/usr.sbin/routed/output.c @@ -31,11 +31,12 @@ * SUCH DAMAGE. */ -#if !defined(lint) && !defined(sgi) +#if !defined(lint) && !defined(sgi) && !defined(__NetBSD__) static char sccsid[] = "@(#)output.c 8.1 (Berkeley) 6/5/93"; -#endif /* not lint */ - -#ident "$Revision: 1.14 $" +#elif defined(__NetBSD__) +static char rcsid[] = "$NetBSD$"; +#endif +#ident "$Revision: 1.16 $" #include "defs.h" @@ -61,6 +62,7 @@ struct { } 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 */ @@ -170,6 +172,10 @@ output(enum output_type type, } 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); @@ -297,20 +303,22 @@ supply_out(struct ag_info *ag) ddst_h = v1_mask & -v1_mask; i = (v1_mask & ~mask)/ddst_h; - if (i >= 1024) { + 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-is instead" - " of as %d routes", - addrname(htonl(dst_h),mask,0), - naddr_ntoa(ws.to.sin_addr.s_addr), i); + 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; } } } @@ -539,6 +547,7 @@ supply(struct sockaddr_in *dst, ws.state = 0; + ws.gen_limit = 1024; ws.to = *dst; ws.to_std_mask = std_mask(ws.to.sin_addr.s_addr); diff --git a/usr.sbin/routed/parms.c b/usr.sbin/routed/parms.c index e5c9ac6f3f1..fcd37a6b6c3 100644 --- a/usr.sbin/routed/parms.c +++ b/usr.sbin/routed/parms.c @@ -31,11 +31,12 @@ * SUCH DAMAGE. */ -#if !defined(lint) && !defined(sgi) +#if !defined(lint) && !defined(sgi) && !defined(__NetBSD__) static char sccsid[] = "@(#)if.c 8.1 (Berkeley) 6/5/93"; -#endif /* not lint */ - -#ident "$Revision: 1.7 $" +#elif defined(__NetBSD__) +static char rcsid[] = "$NetBSD$"; +#endif +#ident "$Revision: 1.8 $" #include "defs.h" #include "pathnames.h" @@ -110,7 +111,8 @@ get_parms(struct interface *ifp) 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)) + if ((ifp->int_state & (IS_NO_RIP | IS_NO_RDISC)) + == (IS_NO_RIP|IS_NO_RDISC)) ifp->int_state |= IS_PASSIVE; } @@ -270,7 +272,8 @@ gwkludge(void) 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)) + if ((state & (IS_NO_RIP | IS_NO_RDISC)) + == (IS_NO_RIP|IS_NO_RDISC)) state |= IS_PASSIVE; parmp = (struct parm*)malloc(sizeof(*parmp)); @@ -353,7 +356,7 @@ parse_parms(char *line) if (!strncasecmp("subnet=",line,7)) { intnetp = (struct intnet*)malloc(sizeof(*intnetp)); intnetp->intnet_metric = 1; - if (p = strrchr(line,',')) { + if ((p = strrchr(line,','))) { *p++ = '\0'; intnetp->intnet_metric = (int)strtol(p,&p,0); if (*p != '\0' diff --git a/usr.sbin/routed/pathnames.h b/usr.sbin/routed/pathnames.h index d539eb51dab..e9e6c48c639 100644 --- a/usr.sbin/routed/pathnames.h +++ b/usr.sbin/routed/pathnames.h @@ -31,6 +31,8 @@ * SUCH DAMAGE. * * @(#)pathnames.h 8.1 (Berkeley) 6/5/93 + * + * $NetBSD$ */ #include diff --git a/usr.sbin/routed/radix.c b/usr.sbin/routed/radix.c index 7552e0832a5..7f7e1e47b79 100644 --- a/usr.sbin/routed/radix.c +++ b/usr.sbin/routed/radix.c @@ -36,16 +36,18 @@ /* * Routines to build and maintain radix trees for routing lookups. */ -#include -#include -#include -#include -#include -#include -#define min(a,b) (((a)<(b))?(a):(b)) +#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; @@ -75,18 +77,18 @@ static int rn_satsifies_leaf(char *, struct radix_node *, int); * 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. + * 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 @@ -95,9 +97,8 @@ static int rn_satsifies_leaf(char *, struct radix_node *, int); */ struct radix_node * -rn_search(v_arg, head) - void *v_arg; - struct radix_node *head; +rn_search(void *v_arg, + struct radix_node *head) { register struct radix_node *x; register caddr_t v; @@ -112,9 +113,9 @@ rn_search(v_arg, head) } struct radix_node * -rn_search_m(v_arg, head, m_arg) - struct radix_node *head; - void *v_arg, *m_arg; +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; @@ -130,8 +131,7 @@ rn_search_m(v_arg, head, m_arg) } int -rn_refines(m_arg, n_arg) - void *m_arg, *n_arg; +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; @@ -198,9 +198,8 @@ rn_satsifies_leaf(char *trial, } struct radix_node * -rn_match(v_arg, head) - void *v_arg; - struct radix_node_head *head; +rn_match(void *v_arg, + struct radix_node_head *head) { caddr_t v = v_arg; register struct radix_node *t = head->rnh_treetop, *x; @@ -240,14 +239,30 @@ rn_match(v_arg, head) /* * 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) && t->rn_dupedkey) + if (!(t->rn_flags & RNF_ROOT)) + return t; /* not a root */ + if (t->rn_dupedkey) { t = t->rn_dupedkey; - return t; + 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; @@ -272,7 +287,7 @@ on1: do { register struct radix_mask *m; t = t->rn_p; - if (m = t->rn_mklist) { + if ((m = t->rn_mklist)) { /* * If non-contiguous masks ever become important * we can restore the masking and open coding of @@ -291,12 +306,12 @@ on1: if (x && rn_satsifies_leaf(v, x, off)) return x; } - } while (m = m->rm_mklist); + } while ((m = m->rm_mklist)); } } while (t != top); return 0; } - + #ifdef RN_DEBUG int rn_nodenum; struct radix_node *rn_clist; @@ -305,10 +320,7 @@ int rn_debug = 1; #endif struct radix_node * -rn_newpair(v, b, nodes) - void *v; - int b; - struct radix_node nodes[2]; +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); @@ -323,11 +335,10 @@ rn_newpair(v, b, nodes) } struct radix_node * -rn_insert(v_arg, head, dupentry, nodes) - void *v_arg; - struct radix_node_head *head; - int *dupentry; - struct radix_node nodes[2]; +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; @@ -336,7 +347,8 @@ rn_insert(v_arg, head, dupentry, nodes) 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 */ { @@ -347,8 +359,11 @@ rn_insert(v_arg, head, dupentry, nodes) while (cp < cplim) if (*cp2++ != *cp++) goto on1; - *dupentry = 1; - return t; + /* 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; @@ -360,7 +375,7 @@ on1: cp = v; do { p = x; - if (cp[x->rn_off] & x->rn_bmask) + 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 */ @@ -388,9 +403,7 @@ on1: } struct radix_node * -rn_addmask(n_arg, search, skip) - int search, skip; - void *n_arg; +rn_addmask(void *n_arg, int search, int skip) { caddr_t netmask = (caddr_t)n_arg; register struct radix_node *x; @@ -448,7 +461,7 @@ rn_addmask(n_arg, search, skip) for (cp = netmask + skip; (cp < cplim) && *(u_char *)cp == 0xff;) cp++; if (cp != cplim) { - for (j = 0x80; (j & *cp) != 0; j >>= 1) + for (j = 0x80; (j & *cp) != 0; j >>= 1) b++; if (*cp != normal_chars[b] || cp != (cplim - 1)) isnormal = 0; @@ -467,7 +480,7 @@ rn_lexobetter(void *m_arg, void *n_arg) if (*mp > *np) return 1; /* not really, but need to check longer one first */ - if (*mp == *np) + if (*mp == *np) for (lim = mp + *mp; mp < lim;) if (*mp++ > *np++) return 1; @@ -498,15 +511,15 @@ rn_new_radix_mask(register struct radix_node *tt, } struct radix_node * -rn_addroute(v_arg, n_arg, head, treenodes) - void *v_arg, *n_arg; - struct radix_node_head *head; - struct radix_node treenodes[2]; +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, *tt; + register struct radix_node *t, *x = 0, *tt; struct radix_node *saved_tt, *top = head->rnh_treetop; - short b = 0, b_leaf; + short b = 0, b_leaf = 0; int keyduplicated; caddr_t mmask; struct radix_mask *m, **mp; @@ -584,17 +597,17 @@ rn_addroute(v_arg, n_arg, head, treenodes) 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) { + 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)) + 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) + for (mp = &x->rn_mklist; (m = *mp); mp = &m->rm_mklist) if (m->rm_b >= b_leaf) break; t->rn_mklist = m; *mp = 0; @@ -614,7 +627,7 @@ on2: * Need same criteria as when sorting dupedkeys to avoid * double loop on deletion. */ - for (mp = &x->rn_mklist; m = *mp; mp = &m->rm_mklist) { + for (mp = &x->rn_mklist; (m = *mp); mp = &m->rm_mklist) { if (m->rm_b < b_leaf) continue; if (m->rm_b > b_leaf) @@ -641,9 +654,9 @@ on2: } struct radix_node * -rn_delete(v_arg, netmask_arg, head) - void *v_arg, *netmask_arg; - struct radix_node_head *head; +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; @@ -680,7 +693,7 @@ rn_delete(v_arg, netmask_arg, head) log(LOG_ERR, "rn_delete: inconsistent annotation\n"); return 0; /* dangling ref could cause disaster */ } - } else { + } else { if (m->rm_mask != tt->rn_mask) { log(LOG_ERR, "rn_delete: inconsistent annotation\n"); goto on1; @@ -696,7 +709,7 @@ rn_delete(v_arg, netmask_arg, head) x = t; t = t->rn_p; } while (b <= t->rn_b && x != top); - for (mp = &x->rn_mklist; m = *mp; mp = &m->rm_mklist) + for (mp = &x->rn_mklist; (m = *mp); mp = &m->rm_mklist) if (m == saved_m) { *mp = m->rm_mklist; MKFree(m); @@ -719,7 +732,7 @@ on1: if (t) t->rn_ybro = tt->rn_ybro; #endif t = tt->rn_p; - if (dupedkey = saved_tt->rn_dupedkey) { + 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; @@ -750,7 +763,7 @@ on1: */ if (t->rn_mklist) { if (x->rn_b >= 0) { - for (mp = &x->rn_mklist; m = *mp;) + for (mp = &x->rn_mklist; (m = *mp);) mp = &m->rm_mklist; *mp = t->rn_mklist; } else { @@ -766,13 +779,10 @@ on1: m = mm; } if (m) -#ifdef _KERNEL - printf("%s %x at %x\n", - "rn_delete: Orphaned Mask", m, x); -#else - syslog(LOG_ERR, "%s %x at %x\n", - "rn_delete: Orphaned Mask", m, x); -#endif + syslog(LOG_ERR, "%s %lx at %lx\n", + "rn_delete: Orphaned Mask", + (unsigned long)m, + (unsigned long)x); } } /* @@ -796,10 +806,9 @@ out: } int -rn_walktree(h, f, w) - struct radix_node_head *h; - register int (*f)(); - void *w; +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; @@ -822,7 +831,7 @@ rn_walktree(h, f, w) rn = rn->rn_l; next = rn; /* Process leaves */ - while (rn = base) { + while ((rn = base)) { base = rn->rn_dupedkey; if (!(rn->rn_flags & RNF_ROOT) && (error = (*f)(rn, w))) return (error); @@ -835,9 +844,7 @@ rn_walktree(h, f, w) } int -rn_inithead(head, off) - void **head; - int off; +rn_inithead(void **head, int off) { register struct radix_node_head *rnh; register struct radix_node *t, *tt, *ttt; @@ -867,16 +874,9 @@ rn_inithead(head, off) } void -rn_init() +rn_init(void) { char *cp, *cplim; -#ifdef KERNEL - struct domain *dom; - - for (dom = domains; dom; dom = dom->dom_next) - if (dom->dom_maxrtkey > max_keylen) - max_keylen = dom->dom_maxrtkey; -#endif if (max_keylen == 0) { printf("rn_init: radix functions require max_keylen be set\n"); return; @@ -892,3 +892,4 @@ rn_init() if (rn_inithead((void **)&mask_rnhead, 0) == 0) panic("rn_init 2"); } + diff --git a/usr.sbin/routed/radix.h b/usr.sbin/routed/radix.h new file mode 100644 index 00000000000..fddf02ead7a --- /dev/null +++ b/usr.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/usr.sbin/routed/rdisc.c b/usr.sbin/routed/rdisc.c index 5a4809475bc..da1784c793e 100644 --- a/usr.sbin/routed/rdisc.c +++ b/usr.sbin/routed/rdisc.c @@ -31,11 +31,12 @@ * SUCH DAMAGE. */ -#if !defined(lint) && !defined(sgi) +#if !defined(lint) && !defined(sgi) && !defined(__NetBSD__) static char sccsid[] = "@(#)rdisc.c 8.1 (Berkeley) x/y/95"; -#endif /* not lint */ - -#ident "$Revision: 1.14 $" +#elif defined(__NetBSD__) +static char rcsid[] = "$NetBSD$"; +#endif +#ident "$Revision: 1.16 $" #include "defs.h" #include @@ -44,12 +45,12 @@ static char sccsid[] = "@(#)rdisc.c 8.1 (Berkeley) x/y/95"; /* router advertisement ICMP packet */ struct icmp_ad { - u_char icmp_type; /* type of message */ - u_char icmp_code; /* type sub code */ - u_short icmp_cksum; /* ones complement cksum of struct */ - u_char icmp_ad_num; /* # of following router addresses */ - u_char icmp_ad_asize; /* 2--words in each advertisement */ - u_short icmp_ad_life; /* seconds of validity */ + 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; @@ -58,10 +59,10 @@ struct icmp_ad { /* router solicitation ICMP packet */ struct icmp_so { - u_char icmp_type; /* type of message */ - u_char icmp_code; /* type sub code */ - u_short icmp_cksum; /* ones complement cksum of struct */ - n_long icmp_so_rsvd; + 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 { @@ -127,7 +128,7 @@ trace_rdisc(char *act, 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]), ntohl(wp[1])); + naddr_ntoa(wp[0]), (int)ntohl(wp[1])); wp += p->ad.icmp_ad_asize; } (void)fputc('\n',ftrace); @@ -141,6 +142,20 @@ trace_rdisc(char *act, } } +/* 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 */ @@ -149,8 +164,17 @@ set_rdisc_mg(struct interface *ifp, int on) { /* 0=turn it off */ struct ip_mreq m; - if (rdisc_sock == -1 - || !(ifp->int_if_flags & IFF_MULTICAST) + 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; @@ -167,7 +191,8 @@ set_rdisc_mg(struct interface *ifp, if (supplier || (ifp->int_state & IS_NO_ADV_IN) || !on) { - /* stop listening to advertisements */ + /* 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, @@ -178,7 +203,8 @@ set_rdisc_mg(struct interface *ifp, } } else if (!(ifp->int_state & IS_ALL_HOSTS)) { - /* start listening to advertisements */ + /* 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) { @@ -191,7 +217,8 @@ set_rdisc_mg(struct interface *ifp, if (!supplier || (ifp->int_state & IS_NO_ADV_OUT) || !on) { - /* stop listening to solicitations */ + /* 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, @@ -202,7 +229,8 @@ set_rdisc_mg(struct interface *ifp, } } else if (!(ifp->int_state & IS_ALL_ROUTERS)) { - /* start hearing solicitations */ + /* 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) { @@ -541,12 +569,12 @@ parse_ad(naddr from, /* ignore pointers to ourself and routes via unreachable networks */ if (ifwithaddr(gate, 1, 0) != 0) { - trace_pkt("\tdiscard our own Router Discovery Ad\n"); + 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" - " from unreachable net\n"); + " toward unreachable net\n"); return; } @@ -554,7 +582,7 @@ parse_ad(naddr from, * and later bias it by the metric of the interface. */ pref = ntohl(pref) ^ MIN_PreferenceLevel; - + if (pref == 0 || life == 0) { pref = 0; life = 0; @@ -657,6 +685,10 @@ send_rdisc(union ad_u *p, 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) { @@ -711,6 +743,9 @@ send_rdisc(union ad_u *p, break; } + if (rdisc_sock < 0) + get_rdisc_sock(); + trace_rdisc(msg, ifp->int_addr, sin.sin_addr.s_addr, ifp, p, p_size); diff --git a/usr.sbin/routed/routed.h b/usr.sbin/routed/routed.h index 2ab50f64f12..8bfc0a19125 100644 --- a/usr.sbin/routed/routed.h +++ b/usr.sbin/routed/routed.h @@ -31,6 +31,8 @@ * SUCH DAMAGE. * * @(#)routed.h 8.1 (Berkeley) 6/2/93 + * + * $NetBSD$ */ #ifndef _ROUTED_H_ @@ -38,7 +40,7 @@ #ifdef __cplusplus extern "C" { #endif -#ident "$Revision: 1.8 $" +#ident "$Revision: 1.9 $" /* * Routing Information Protocol @@ -55,48 +57,49 @@ extern "C" { #endif #define RIP_PORT 520 - + #if RIPVERSION == 1 /* Note that this so called sockaddr has a 2-byte sa_family and no sa_len. * It is not a UNIX sockaddr, but the shape of an address as defined - * in RIPv1. + * in RIPv1. It is still defined to allow old versions of programs + * such as `gated` to use this file to define RIPv1. */ struct netinfo { struct sockaddr rip_dst; /* destination net/host */ - int rip_metric; /* cost of route */ + u_int32_t rip_metric; /* cost of route */ }; #else struct netinfo { - u_short n_family; + u_int16_t n_family; #define RIP_AF_INET htons(AF_INET) #define RIP_AF_UNSPEC 0 #define RIP_AF_AUTH 0xffff - u_short n_tag; /* optional in RIPv2 */ - u_int n_dst; /* destination net or host */ + u_int16_t n_tag; /* optional in RIPv2 */ + u_int32_t n_dst; /* destination net or host */ #define RIP_DEFAULT 0 - u_int n_mask; /* netmask in RIPv2 */ - u_int n_nhop; /* optional next hop in RIPv2 */ - u_int n_metric; /* cost of route */ + u_int32_t n_mask; /* netmask in RIPv2 */ + u_int32_t n_nhop; /* optional next hop in RIPv2 */ + u_int32_t n_metric; /* cost of route */ }; #endif /* RIPv2 authentication */ struct netauth { - u_short a_type; + u_int16_t a_type; #define RIP_AUTH_PW htons(2) /* password type */ union { #define RIP_AUTH_PW_LEN 16 - char au_pw[RIP_AUTH_PW_LEN]; + int8_t au_pw[RIP_AUTH_PW_LEN]; } au; }; struct rip { - u_char rip_cmd; /* request/response */ - u_char rip_vers; /* protocol version # */ - u_short rip_res1; /* pad to 32-bit boundary */ + u_int8_t rip_cmd; /* request/response */ + u_int8_t rip_vers; /* protocol version # */ + u_int16_t rip_res1; /* pad to 32-bit boundary */ union { /* variable length... */ struct netinfo ru_nets[1]; - char ru_tracefile[1]; + int8_t ru_tracefile[1]; struct netauth ru_auth[1]; } ripun; #define rip_nets ripun.ru_nets @@ -129,7 +132,7 @@ char *ripcmds[RIPCMD_MAX] = { #define NETS_LEN ((MAXPACKETSIZE-sizeof(struct rip)) \ / sizeof(struct netinfo) +1) -#define INADDR_RIP_GROUP (u_long)0xe0000009 /* 224.0.0.9 */ +#define INADDR_RIP_GROUP (u_int32_t)0xe0000009 /* 224.0.0.9 */ /* Timer values used in managing the routing table. diff --git a/usr.sbin/routed/rtquery/Makefile b/usr.sbin/routed/rtquery/Makefile index a8d2110a8d3..f9aee5c55d0 100644 --- a/usr.sbin/routed/rtquery/Makefile +++ b/usr.sbin/routed/rtquery/Makefile @@ -1,7 +1,8 @@ # @(#)Makefile 8.1 (Berkeley) 6/5/93 PROG= rtquery -MAN8= rtquery.8 +MAN8= rtquery.0 +#COPTS= -g -DDEBUG -Wall .include "../../Makefile.inc" .include diff --git a/usr.sbin/routed/rtquery/rtquery.c b/usr.sbin/routed/rtquery/rtquery.c index a75d8f478f2..cc1be10ab22 100644 --- a/usr.sbin/routed/rtquery/rtquery.c +++ b/usr.sbin/routed/rtquery/rtquery.c @@ -35,9 +35,12 @@ char copyright[] = "@(#) Copyright (c) 1982, 1986, 1993\n\ The Regents of the University of California. All rights reserved.\n"; -#if !defined(lint) && !defined(sgi) +#if !defined(lint) && !defined(sgi) && !defined(__NetBSD__) static char sccsid[] = "@(#)query.c 8.1 (Berkeley) 6/5/93"; -#endif /* not lint */ +#elif defined(__NetBSD__) +static char rcsid[] = "$NetBSD$"; +#endif +#ident "$Revision: 1.8 $" #include #include @@ -151,7 +154,7 @@ main(int argc, } bcopy(hp->h_addr, &OMSG.rip_nets[0].n_dst, sizeof(OMSG.rip_nets[0].n_dst)); - OMSG.rip_nets[0].n_family = AF_INET; + OMSG.rip_nets[0].n_family = RIP_AF_INET; OMSG.rip_nets[0].n_mask = -1; rflag = 1; } @@ -176,7 +179,7 @@ main(int argc, if (!value || strlen(value) > MAXPATHLEN) goto usage; - strcpy(OMSG.rip_tracefile, value); + strcpy((char*)OMSG.rip_tracefile,value); omsg_len += (strlen(value) - sizeof(OMSG.ripun)); break; @@ -544,7 +547,7 @@ rip_input(struct sockaddr_in *from, } else { (void)sprintf(net_buf, "(af %#x) %d.%d.%d.%d", - n->n_family, + ntohs(n->n_family), (char)(n->n_dst >> 24), (char)(n->n_dst >> 16), (char)(n->n_dst >> 8), @@ -576,11 +579,11 @@ rip_input(struct sockaddr_in *from, /* Return the classical netmask for an IP address. */ static u_int -std_mask(u_int addr) +std_mask(u_int addr) /* in network order */ { - NTOHL(addr); + NTOHL(addr); /* was a host, not a network */ - if (addr == 0) + if (addr == 0) /* default route has mask 0 */ return 0; if (IN_CLASSA(addr)) return IN_CLASSA_NET; @@ -628,6 +631,8 @@ getnet(char *name, 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) @@ -635,8 +640,8 @@ getnet(char *name, mask = 0xffffffff << (32-mask); } - rt->n_dst = in.s_addr; - rt->n_family = AF_INET; + rt->n_dst = htonl(in.s_addr); + rt->n_family = RIP_AF_INET; rt->n_mask = htonl(mask); return 1; } diff --git a/usr.sbin/routed/table.c b/usr.sbin/routed/table.c index 9b354e79dbf..b1566a3792a 100644 --- a/usr.sbin/routed/table.c +++ b/usr.sbin/routed/table.c @@ -31,11 +31,12 @@ * SUCH DAMAGE. */ -#if !defined(lint) && !defined(sgi) +#if !defined(lint) && !defined(sgi) && !defined(__NetBSD__) static char sccsid[] = "@(#)tables.c 8.1 (Berkeley) 6/5/93"; -#endif /* not lint */ - -#ident "$Revision: 1.16 $" +#elif defined(__NetBSD__) +static char rcsid[] = "$NetBSD$"; +#endif +#ident "$Revision: 1.23 $" #include "defs.h" @@ -54,6 +55,8 @@ struct timeval need_kern = { /* need to update kernel table */ int stopint; +int total_routes; + naddr age_bad_gate; @@ -726,12 +729,12 @@ static struct khash { u_short k_state; #define KS_NEW 0x001 #define KS_DELETE 0x002 -#define KS_ADD 0x004 -#define KS_CHANGE 0x008 -#define KS_DEL_ADD 0x010 -#define KS_STATIC 0x020 -#define KS_GATEWAY 0x040 -#define KS_DYNAMIC 0x080 +#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 @@ -769,7 +772,6 @@ kern_add(naddr dst, naddr mask) k->k_dst = dst; k->k_mask = mask; k->k_state = KS_NEW; - k->k_redirect_time = now.tv_sec; k->k_keep = now.tv_sec; *pk = k; @@ -777,8 +779,8 @@ kern_add(naddr dst, naddr mask) } -/* If it has a non-zero metric, check that it is still in the table, not - * having been deleted by interfaces coming and going. +/* 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, @@ -848,22 +850,25 @@ rtm_add(struct rt_msghdr *rtm, k->k_state |= KS_GATEWAY; if (rtm->rtm_flags & RTF_STATIC) k->k_state |= KS_STATIC; - if (rtm->rtm_flags & RTF_DYNAMIC) { - k->k_state |= KS_DYNAMIC; - k->k_redirect_time = now.tv_sec; - /* Routers are not supposed to listen to redirects, - * so delete it. - */ + if (0 != (rtm->rtm_flags & (RTF_DYNAMIC | RTF_MODIFIED))) { if (supplier) { - k->k_keep = now.tv_sec; + /* Routers are not supposed to listen to redirects, + * so delete it. + */ + k->k_state &= ~KS_DYNAMIC; + k->k_state |= KS_DELETE; trace_act("mark redirected %s --> %s for deletion" - "since this is a router\n", + " 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; } } - /* If it is not a static route, quite until it is time to delete it. + /* 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; @@ -878,7 +883,8 @@ rtm_add(struct rt_msghdr *rtm, */ ifp = iflookup(k->k_gate); if (ifp == 0) { - /* if there is no interface, maybe it is new + /* if there is no known interface, + * maybe there is a new interface */ ifinit(); ifp = iflookup(k->k_gate); @@ -1098,11 +1104,11 @@ read_rt(void) ? HOST_MASK : std_mask(S_ADDR(INFO_DST(&info)))); - strp += sprintf(strp, " %s", + strp += sprintf(strp, ": %s", addrname(S_ADDR(INFO_DST(&info)), mask, 0)); if (IN_MULTICAST(ntohl(S_ADDR(INFO_DST(&info))))) { - trace_act("ignore %s for multicast %s\n", str); + trace_act("ignore multicast %s\n", str); continue; } @@ -1163,11 +1169,13 @@ kern_out(struct ag_info *ag) * This includes routes that had RS_NET_SYN for interfaces that * recently died. */ - if (ag->ag_metric == HOPCNT_INFINITY - && 0 == kern_find(htonl(ag->ag_dst_h), ag->ag_mask, 0)) - return; - - k = kern_add(htonl(ag->ag_dst_h), ag->ag_mask); + 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 */ @@ -1183,7 +1191,6 @@ kern_out(struct ag_info *ag) return; /* modify existing kernel entry if necessary */ - k->k_state &= ~KS_DELETE; if (k->k_gate != ag->ag_gate || k->k_metric != ag->ag_metric) { k->k_gate = ag->ag_gate; @@ -1205,6 +1212,16 @@ kern_out(struct ag_info *ag) 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 } @@ -1298,31 +1315,40 @@ fix_kern(void) continue; } - if (k->k_state & KS_DELETE) { + 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); + k->k_dst, k->k_gate, k->k_mask, + 0, 0); *pk = k->k_next; free(k); continue; } - if (k->k_state & KS_DEL_ADD) - rtioctl(RTM_DELETE, - k->k_dst,k->k_gate,k->k_mask, 0, 0); + 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 = (k->k_state & KS_GATEWAY) ? RTF_GATEWAY : 0; - 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); + 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); } - 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 @@ -1356,7 +1382,7 @@ del_static(naddr dst, */ k = kern_find(dst, mask, 0); if (k != 0) { - k->k_state &= ~KS_STATIC; + k->k_state &= ~(KS_STATIC | KS_DYNAMIC); k->k_state |= KS_DELETE; if (gone) { k->k_state |= KS_DELETED; @@ -1370,8 +1396,8 @@ del_static(naddr dst, } -/* Delete all routes generated from ICMP Redirects that use a given - * gateway, as well as all old redirected routes. +/* Delete all routes generated from ICMP Redirects that use a given gateway, + * as well as old redirected routes. */ void del_redirects(naddr bad_gate, @@ -1393,6 +1419,7 @@ del_redirects(naddr bad_gate, 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), @@ -1494,11 +1521,7 @@ rtadd(naddr dst, int i; struct rt_spare *rts; - rt = (struct rt_entry *)malloc(sizeof (*rt)); - if (rt == 0) { - BADERR(1,"rtadd malloc"); - return; - } + 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; @@ -1527,6 +1550,8 @@ rtadd(naddr dst, 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); @@ -1667,6 +1692,7 @@ rtdelete(struct rt_entry *rt) msglog("rnh_deladdr() failed"); } else { free(rt); + total_routes--; } } @@ -1747,7 +1773,7 @@ rtbad_sub(struct rt_entry *rt) if (ifp1 != 0 || (state & RS_NET_SYN)) { rtchange(rt, ((rt->rt_state & ~(RS_NET_SYN | RS_LOCAL)) | state), - rt->rt_gate, rt->rt_router, 1, + rt->rt_gate, rt->rt_router, rt->rt_metric, rt->rt_tag, ifp1, rt->rt_time, 0); } else { rtbad(rt); diff --git a/usr.sbin/routed/trace.c b/usr.sbin/routed/trace.c index 01220c7dbaa..e9c1e03f05a 100644 --- a/usr.sbin/routed/trace.c +++ b/usr.sbin/routed/trace.c @@ -31,11 +31,12 @@ * SUCH DAMAGE. */ -#if !defined(lint) && !defined(sgi) +#if !defined(lint) && !defined(sgi) && !defined(__NetBSD__) static char sccsid[] = "@(#)trace.c 8.1 (Berkeley) 6/5/93"; -#endif /* not lint */ - -#ident "$Revision: 1.8 $" +#elif defined(__NetBSD__) +static char rcsid[] = "$NetBSD$"; +#endif +#ident "$Revision: 1.11 $" #define RIPCMDS #include "defs.h" @@ -65,17 +66,18 @@ char * naddr_ntoa(naddr a) { #define NUM_BUFS 4 - static int i; + static int bufno; static struct { char str[16]; /* xxx.xxx.xxx.xxx\0 */ } bufs[NUM_BUFS]; - struct in_addr addr; char *s; + struct in_addr addr; addr.s_addr = a; - s = strcpy(bufs[i].str, inet_ntoa(addr)); - i = (i+1) % NUM_BUFS; + s = strcpy(bufs[bufno].str, inet_ntoa(addr)); + bufno = (bufno+1) % NUM_BUFS; return s; +#undef NUM_BUFS } @@ -320,12 +322,17 @@ addrname(naddr addr, /* in network byte order */ naddr mask, int force) /* 0=show mask if nonstandard, */ { /* 1=always show mask, 2=never */ - static char s[15+20]; - char *sp; +#define NUM_BUFS 4 + static int bufno; + static struct { + char str[15+20]; + } bufs[NUM_BUFS]; + char *s, *sp; naddr dmask; int i; - (void)strcpy(s, naddr_ntoa(addr)); + 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)]; @@ -337,11 +344,12 @@ addrname(naddr addr, /* in network byte order */ (void)sprintf(sp, "/%d", 32-i); } else { - (void)sprintf(sp, " (mask %#x)", mask); + (void)sprintf(sp, " (mask %#x)", (u_int)mask); } } return s; +#undef NUM_BUFS } @@ -450,7 +458,7 @@ trace_bits(struct bits *tbl, c = '|'; } - if (c || force) + if (c != '<' || force) (void)fputs("> ", ftrace); } @@ -479,12 +487,14 @@ trace_if(char *act, lastlog(); (void)fprintf(ftrace, "%s interface %-4s ", act, ifp->int_name); - (void)fprintf(ftrace, "%-15s --> %s ", + (void)fprintf(ftrace, "%-15s-->%-15s ", naddr_ntoa(ifp->int_addr), - ((ifp->int_if_flags & IFF_POINTOPOINT) - ? naddr_ntoa(ifp->int_dstaddr) - : addrname(htonl(ifp->int_net), ifp->int_mask, 0))); - (void)fprintf(ftrace, "metric=%d ", ifp->int_metric); + 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); @@ -519,7 +529,7 @@ trace_upslot(struct rt_entry *rt, (void)fprintf(ftrace, "router=%s ", naddr_ntoa(rts->rts_gate)); if (rts->rts_tag != 0) - (void)fprintf(ftrace, "tag=%#x ", rts->rts_tag); + (void)fprintf(ftrace, "tag=%#x ", ntohs(rts->rts_tag)); (void)fprintf(ftrace, "metric=%-2d ", rts->rts_metric); if (rts->rts_ifp != 0) (void)fprintf(ftrace, "%s ", @@ -532,7 +542,7 @@ trace_upslot(struct rt_entry *rt, if (gate != router) (void)fprintf(ftrace,"router=%s ",naddr_ntoa(router)); if (tag != rts->rts_tag) - (void)fprintf(ftrace, "tag=%#x ", 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 ) @@ -548,7 +558,7 @@ trace_upslot(struct rt_entry *rt, if (gate != router) (void)fprintf(ftrace, "router=%s ", naddr_ntoa(gate)); if (tag != 0) - (void)fprintf(ftrace, "tag=%#x ", tag); + (void)fprintf(ftrace, "tag=%#x ", ntohs(tag)); (void)fprintf(ftrace, "metric=%-2d ", metric); if (ifp != 0) (void)fprintf(ftrace, "%s ", ifp->int_name); @@ -620,7 +630,7 @@ trace_change(struct rt_entry *rt, (void)fprintf(ftrace, "router=%s ", naddr_ntoa(rt->rt_router)); if (rt->rt_tag != 0) - (void)fprintf(ftrace, "tag=%#x ", rt->rt_tag); + (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); @@ -635,7 +645,7 @@ trace_change(struct rt_entry *rt, if (router != gate) (void)fprintf(ftrace, "router=%s ", naddr_ntoa(router)); if (rt->rt_tag != tag) - (void)fprintf(ftrace, "tag=%#x ", tag); + (void)fprintf(ftrace, "tag=%#x ", ntohs(tag)); if (rt->rt_state != state) trace_bits(rs_bits, state, 1); if (rt->rt_ifp != ifp) @@ -665,10 +675,10 @@ trace_add_del(char * action, struct rt_entry *rt) (void)fprintf(ftrace, "router=%s ", naddr_ntoa(rt->rt_router)); if (rt->rt_tag != 0) - (void)fprintf(ftrace, "tag=%#x ", rt->rt_tag); + (void)fprintf(ftrace, "tag=%#x ", ntohs(rt->rt_tag)); trace_bits(rs_bits, state, 0); - if (rt->rt_ifp != 0) - (void)fprintf(ftrace, "%s ", rt->rt_ifp->int_name); + (void)fprintf(ftrace, "%s ", + rt->rt_ifp != 0 ? rt->rt_ifp->int_name : "?"); (void)fprintf(ftrace, "%s\n", ts(rt->rt_time)); } @@ -690,12 +700,12 @@ trace_rip(char *dir1, char *dir2, lastlog(); if (msg->rip_cmd >= RIPCMD_MAX || msg->rip_vers == 0) { - (void)fprintf(ftrace, "%s bad RIPv%d cmd=%d %s %s.%d%s%s" - " size=%d msg=%#x\n", + (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, msg); + size); return; } @@ -723,13 +733,13 @@ trace_rip(char *dir1, char *dir2, naddr_ntoa(n->n_dst)); if (n->n_mask != 0) (void)fprintf(ftrace, "mask=%#x ", - ntohl(n->n_mask)); + (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", - n->n_tag); + ntohs(n->n_tag)); (void)fputc('\n',ftrace); continue; } @@ -753,7 +763,7 @@ trace_rip(char *dir1, char *dir2, "\t(af %d) %-18s mask=%#x", ntohs(n->n_family), naddr_ntoa(n->n_dst), - ntohl(n->n_mask)); + (u_int)ntohl(n->n_mask)); } else if (msg->rip_vers == RIPv1) { (void)fprintf(ftrace, "\t%-18s ", addrname(n->n_dst, @@ -766,13 +776,13 @@ trace_rip(char *dir1, char *dir2, n->n_mask==0 ? 2 : 0)); } (void)fprintf(ftrace, "metric=%-2d ", - ntohl(n->n_metric)); + (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", - n->n_tag); + ntohs(n->n_tag)); (void)fputc('\n',ftrace); } if (size != (char *)n - (char *)msg) From 7b6ab19dde850fe54facf1dbc5b00466a0a12b53 Mon Sep 17 00:00:00 2001 From: Garrett Wollman Date: Mon, 16 Sep 1996 16:51:32 +0000 Subject: [PATCH 5/5] Virgin import of new BSD/SGI routed. This update contains a number of important bug fixes. Obtained from: Vernon J. Schryver --- {usr.sbin => sbin}/routed/Makefile | 0 {usr.sbin => sbin}/routed/defs.h | 22 ++- {usr.sbin => sbin}/routed/if.c | 49 ++++-- {usr.sbin => sbin}/routed/input.c | 173 +++++++++++--------- {usr.sbin => sbin}/routed/main.c | 24 ++- {usr.sbin => sbin}/routed/output.c | 80 +++++---- {usr.sbin => sbin}/routed/parms.c | 24 ++- {usr.sbin => sbin}/routed/pathnames.h | 5 +- {usr.sbin => sbin}/routed/radix.c | 0 {usr.sbin => sbin}/routed/radix.h | 0 {usr.sbin => sbin}/routed/rdisc.c | 0 {usr.sbin => sbin}/routed/routed.8 | 3 + {usr.sbin => sbin}/routed/routed.h | 0 {usr.sbin => sbin}/routed/rtquery/Makefile | 0 {usr.sbin => sbin}/routed/rtquery/rtquery.8 | 8 +- {usr.sbin => sbin}/routed/rtquery/rtquery.c | 19 ++- {usr.sbin => sbin}/routed/table.c | 36 ++-- {usr.sbin => sbin}/routed/trace.c | 111 +++++++++++-- usr.sbin/routed/Makefile.inc | 1 - usr.sbin/routed/rttrace/Makefile | 6 - usr.sbin/routed/rttrace/rttrace.c | 146 ----------------- 21 files changed, 377 insertions(+), 330 deletions(-) rename {usr.sbin => sbin}/routed/Makefile (100%) rename {usr.sbin => sbin}/routed/defs.h (97%) rename {usr.sbin => sbin}/routed/if.c (96%) rename {usr.sbin => sbin}/routed/input.c (84%) rename {usr.sbin => sbin}/routed/main.c (98%) rename {usr.sbin => sbin}/routed/output.c (96%) rename {usr.sbin => sbin}/routed/parms.c (97%) rename {usr.sbin => sbin}/routed/pathnames.h (92%) rename {usr.sbin => sbin}/routed/radix.c (100%) rename {usr.sbin => sbin}/routed/radix.h (100%) rename {usr.sbin => sbin}/routed/rdisc.c (100%) rename {usr.sbin => sbin}/routed/routed.8 (99%) rename {usr.sbin => sbin}/routed/routed.h (100%) rename {usr.sbin => sbin}/routed/rtquery/Makefile (100%) rename {usr.sbin => sbin}/routed/rtquery/rtquery.8 (92%) rename {usr.sbin => sbin}/routed/rtquery/rtquery.c (97%) rename {usr.sbin => sbin}/routed/table.c (98%) rename {usr.sbin => sbin}/routed/trace.c (88%) delete mode 100644 usr.sbin/routed/Makefile.inc delete mode 100644 usr.sbin/routed/rttrace/Makefile delete mode 100644 usr.sbin/routed/rttrace/rttrace.c diff --git a/usr.sbin/routed/Makefile b/sbin/routed/Makefile similarity index 100% rename from usr.sbin/routed/Makefile rename to sbin/routed/Makefile diff --git a/usr.sbin/routed/defs.h b/sbin/routed/defs.h similarity index 97% rename from usr.sbin/routed/defs.h rename to sbin/routed/defs.h index f6499122219..452b71aa067 100644 --- a/usr.sbin/routed/defs.h +++ b/sbin/routed/defs.h @@ -36,7 +36,7 @@ */ #ifndef __NetBSD__ -#ident "$Revision: 1.13 $" +#ident "$Revision: 1.16 $" #endif /* Definitions for RIPv2 routing process. @@ -178,7 +178,6 @@ struct rt_entry { # define RS_MHOME 0x020 /* from -m */ # define RS_STATIC 0x040 /* from the kernel */ # define RS_RDISC 0x080 /* from router discovery */ -# define RS_PERMANENT (RS_MHOME | RS_STATIC | RS_NET_SYN | RS_RDISC) struct sockaddr_in rt_dst_sock; naddr rt_mask; struct rt_spare { @@ -212,11 +211,12 @@ struct rt_entry { * nor non-passive, remote interfaces that are not aliases * (i.e. remote & metric=0) */ -#define AGE_RT(rt,ifp) (0 == ((rt)->rt_state & RS_PERMANENT) \ - && (!((rt)->rt_state & RS_IF) \ - || (ifp) == 0 \ - || (((ifp)->int_state & IS_REMOTE) \ - && !((ifp)->int_state & IS_PASSIVE)))) +#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 @@ -256,7 +256,7 @@ struct interface { 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; /* copied from kernel */ + 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 */ @@ -279,6 +279,7 @@ struct interface { 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 */ @@ -407,6 +408,7 @@ 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 */ @@ -428,7 +430,8 @@ extern struct timeval need_kern; /* need to update kernel table */ extern int update_seqno; /* a route has changed */ extern u_int tracelevel, new_tracelevel; -#define MAX_TRACELEVEL 3 +#define 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) @@ -483,6 +486,7 @@ 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 *); diff --git a/usr.sbin/routed/if.c b/sbin/routed/if.c similarity index 96% rename from usr.sbin/routed/if.c rename to sbin/routed/if.c index 7143b319c54..539b07323e3 100644 --- a/usr.sbin/routed/if.c +++ b/sbin/routed/if.c @@ -36,7 +36,7 @@ static char sccsid[] = "@(#)if.c 8.1 (Berkeley) 6/5/93"; #elif defined(__NetBSD__) static char rcsid[] = "$NetBSD$"; #endif -#ident "$Revision: 1.16 $" +#ident "$Revision: 1.17 $" #include "defs.h" #include "pathnames.h" @@ -454,12 +454,13 @@ ifinit(void) # define COMP_NOT_INET 0x001 # define COMP_WIERD 0x002 # define COMP_NOADDR 0x004 -# define COMP_NODST 0x008 -# define COMP_NOBADR 0x010 -# define COMP_NOMASK 0x020 -# define COMP_DUP 0x040 -# define COMP_BAD_METRIC 0x080 -# define COMP_NETMASK 0x100 +# 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; @@ -548,7 +549,7 @@ ifinit(void) if (INFO_IFA(&info) == 0) { if (iff_alive(ifs.int_if_flags)) { if (!(prev_complaints & COMP_NOADDR)) - msglog("%s has a bad address", + msglog("%s has no address", sdl->sdl_data); complaints |= COMP_NOADDR; } @@ -569,6 +570,17 @@ ifinit(void) 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)) { @@ -612,6 +624,17 @@ ifinit(void) 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); @@ -949,12 +972,10 @@ ifinit(void) /* If we ever have a RIPv1 interface, assume we always will. * It might come back if it ever goes away. */ - if (!(ifp->int_if_flags & IFF_LOOPBACK)) { - if (!(ifp->int_state & IS_NO_RIPV1_OUT)) - have_ripv1_out = 1; - if (!(ifp->int_state & IS_NO_RIPV1_IN)) - have_ripv1_in = 1; - } + 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) { diff --git a/usr.sbin/routed/input.c b/sbin/routed/input.c similarity index 84% rename from usr.sbin/routed/input.c rename to sbin/routed/input.c index 6bbc572af57..a854c41914e 100644 --- a/usr.sbin/routed/input.c +++ b/sbin/routed/input.c @@ -36,7 +36,7 @@ static char sccsid[] = "@(#)input.c 8.1 (Berkeley) 6/5/93"; #elif defined(__NetBSD__) static char rcsid[] = "$NetBSD$"; #endif -#ident "$Revision: 1.13 $" +#ident "$Revision: 1.16 $" #include "defs.h" @@ -69,9 +69,7 @@ read_rip(int sock, logbad(1,"impossible recvfrom(rip) fromlen=%d", fromlen); - input(&from, - (ifp != 0) ? ifp : iflookup(from.sin_addr.s_addr), - &inbuf.rip, cc); + input(&from, ifp, &inbuf.rip, cc); } } @@ -80,7 +78,7 @@ read_rip(int sock, */ static void input(struct sockaddr_in *from, /* received from this IP address */ - struct interface *ifp, + struct interface *sifp, /* interface by which it arrived */ struct rip *rip, int size) { @@ -88,17 +86,21 @@ input(struct sockaddr_in *from, /* received from this IP address */ 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 (ifp != 0) - ifp->int_state |= IS_ACTIVE; + if (sifp != 0) + sifp->int_state |= IS_ACTIVE; - trace_rip("Recv", "from", from, ifp, rip, size); + trace_rip("Recv", "from", from, sifp, rip, size); if (rip->rip_vers == 0) { if (from->sin_addr.s_addr != bad_router) @@ -107,6 +109,8 @@ input(struct sockaddr_in *from, /* received from this IP address */ 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) @@ -131,7 +135,7 @@ input(struct sockaddr_in *from, /* received from this IP address */ * RIPv1 systems will listen? Crazy! */ if (!auth_ok - && rip->rip_vers >= RIPv2 + && 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" @@ -187,32 +191,42 @@ input(struct sockaddr_in *from, /* received from this IP address */ * We respond to routers only if we are acting * as a supplier, or to anyone other than a router * (i.e. a query). - * - * Answer a query from a stray program with all - * we know. Filter the answer to a query from a - * router in the about same way broadcasts are - * filtered. - * - * Only answer a router if we are a supplier - * to keep an unwary host that is just starting - * from picking us an a router. */ if (n->n_family == RIP_AF_UNSPEC && n->n_metric == HOPCNT_INFINITY && n == rip->rip_nets && n+1 == lim) { if (from->sin_port != htons(RIP_PORT)) { - /* query from `rtquery` or similar + /* Answer a query from a utility + * program with all we know. */ - supply(from, ifp, - OUT_QUERY, 0, rip->rip_vers); - } else if (supplier) { - /* a router trying to prime its - * tables. - */ - supply(from, ifp, - OUT_UNICAST, 0, rip->rip_vers); + 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; } @@ -241,7 +255,7 @@ input(struct sockaddr_in *from, /* received from this IP address */ if (rip->rip_vers == RIPv1 || 0 == (mask = ntohl(n->n_mask)) || 0 != (ntohl(dst) & ~mask)) - mask = ripv1_mask_host(dst,ifp); + mask = ripv1_mask_host(dst,sifp); rt = rtget(dst, mask); if (!rt && dst != RIP_DEFAULT) @@ -258,16 +272,16 @@ input(struct sockaddr_in *from, /* received from this IP address */ n->n_metric = HOPCNT_INFINITY; } else { n->n_metric = rt->rt_metric+1; - n->n_metric += (ifp!=0) ? ifp->int_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 (ifp != 0 + if (sifp != 0 && on_net(rt->rt_gate, - ifp->int_net, - ifp->int_mask) - && rt->rt_gate != ifp->int_addr) + sifp->int_net, + sifp->int_mask) + && rt->rt_gate != sifp->int_addr) n->n_nhop = rt->rt_gate; } } @@ -284,9 +298,9 @@ input(struct sockaddr_in *from, /* received from this IP address */ rip->rip_vers = RIPv2; if (from->sin_port != htons(RIP_PORT)) { /* query */ - (void)output(OUT_QUERY, from, ifp, rip, size); + (void)output(OUT_QUERY, from, sifp, rip, size); } else if (supplier) { - (void)output(OUT_UNICAST, from, ifp, rip, size); + (void)output(OUT_UNICAST, from, sifp, rip, size); } return; @@ -298,7 +312,7 @@ input(struct sockaddr_in *from, /* received from this IP address */ naddr_ntoa(FROM_NADDR)); return; } - if (ifp == 0) { + if (aifp == 0) { msglog("trace command from unknown router %s", naddr_ntoa(FROM_NADDR)); return; @@ -357,26 +371,27 @@ input(struct sockaddr_in *from, /* received from this IP address */ * broadcast or point-to-point networks, and from * those listed in /etc/gateways. */ - if (!ifp) { + if (!aifp) { if (from->sin_addr.s_addr != unk_router) - msglog("packet from unknown router %s" + msglog("discard packet from unknown router %s" " or via unidentified interface", naddr_ntoa(FROM_NADDR)); unk_router = from->sin_addr.s_addr; return; } - if (ifp->int_state & IS_PASSIVE) { - trace_act("packet from %s via passive interface %s\n", + if (aifp->int_state & IS_PASSIVE) { + trace_act("discard packet from %s" + " via passive interface %s\n", naddr_ntoa(FROM_NADDR), - ifp->int_name); + aifp->int_name); return; } /* Check required version */ - if (((ifp->int_state & IS_NO_RIPV1_IN) + if (((aifp->int_state & IS_NO_RIPV1_IN) && rip->rip_vers == RIPv1) - || ((ifp->int_state & IS_NO_RIPV2_IN) + || ((aifp->int_state & IS_NO_RIPV2_IN) && rip->rip_vers != RIPv1)) { trace_pkt("discard RIPv%d response\n", rip->rip_vers); @@ -385,15 +400,15 @@ input(struct sockaddr_in *from, /* received from this IP address */ /* Ignore routes via dead interface. */ - if (ifp->int_state & IS_BROKE) { + if (aifp->int_state & IS_BROKE) { trace_pkt("discard response via broken interface %s\n", - ifp->int_name); + aifp->int_name); return; } /* Authenticate the packet if we have a secret. */ - if (ifp->int_passwd[0] != '\0') { + if (aifp->int_passwd[0] != '\0') { if (n >= lim || n->n_family != RIP_AF_AUTH || ((struct netauth*)n)->a_type != RIP_AUTH_PW) { @@ -404,8 +419,8 @@ input(struct sockaddr_in *from, /* received from this IP address */ return; } else if (0 != bcmp(((struct netauth*)n)->au.au_pw, - ifp->int_passwd, - sizeof(ifp->int_passwd))) { + aifp->int_passwd, + sizeof(aifp->int_passwd))) { if (from->sin_addr.s_addr != use_auth) msglog("bad password from %s", naddr_ntoa(FROM_NADDR)); @@ -456,31 +471,31 @@ input(struct sockaddr_in *from, /* received from this IP address */ /* Notice the next-hop. */ gate = from->sin_addr.s_addr; - if (n->n_nhop != 0 - && rip->rip_vers == RIPv2) { - /* Ignore the route if it points to us */ - if (0 != ifwithaddr(n->n_nhop, 1, 0)) - continue; - - /* Use it only if it is valid. */ - if (on_net(n->n_nhop, - ifp->int_net, ifp->int_mask) - && check_dst(n->n_nhop)) { - gate = n->n_nhop; + 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)); + 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,ifp); + 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" @@ -497,7 +512,7 @@ input(struct sockaddr_in *from, /* received from this IP address */ /* Adjust metric according to incoming interface.. */ - n->n_metric += ifp->int_metric; + n->n_metric += aifp->int_metric; if (n->n_metric > HOPCNT_INFINITY) n->n_metric = HOPCNT_INFINITY; @@ -507,9 +522,9 @@ input(struct sockaddr_in *from, /* received from this IP address */ * Be a little more paranoid than that, and reject * default routes with the same metric we advertised. */ - if (ifp->int_d_metric != 0 + if (aifp->int_d_metric != 0 && dst == RIP_DEFAULT - && n->n_metric >= ifp->int_d_metric) + && n->n_metric >= aifp->int_d_metric) continue; /* We can receive aggregated RIPv2 routes that must @@ -558,7 +573,7 @@ input(struct sockaddr_in *from, /* received from this IP address */ } for (;;) { - input_route(ifp, FROM_NADDR, + input_route(aifp, FROM_NADDR, dst, mask, gate, n); if (i-- == 0) break; @@ -610,6 +625,14 @@ input_route(struct interface *ifp, 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); @@ -630,10 +653,8 @@ input_route(struct interface *ifp, * synthetic, RIPv1 network route of our own. * The worst is that both kinds of routes might be * received, and the bad one might have the smaller - * metric. Partly solve this problem by faking the - * RIPv1 route with a metric that reflects the most - * distant part of the subnet. Also never - * aggregate into such a route. Also keep it + * metric. Partly solve this problem by never + * aggregating into such a route. Also keep it * around as long as the interface exists. */ @@ -686,7 +707,13 @@ input_route(struct interface *ifp, } 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 diff --git a/usr.sbin/routed/main.c b/sbin/routed/main.c similarity index 98% rename from usr.sbin/routed/main.c rename to sbin/routed/main.c index 700762ed5dc..84a7fac3dbe 100644 --- a/usr.sbin/routed/main.c +++ b/sbin/routed/main.c @@ -39,7 +39,7 @@ static char sccsid[] = "@(#)main.c 8.1 (Berkeley) 6/5/93"; #elif defined(__NetBSD__) static char rcsid[] = "$NetBSD$"; #endif -#ident "$Revision: 1.14 $" +#ident "$Revision: 1.17 $" #include "defs.h" #include "pathnames.h" @@ -70,6 +70,7 @@ 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 */ @@ -111,6 +112,7 @@ main(int argc, 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; @@ -247,6 +249,16 @@ usage: } + 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 @@ -275,7 +287,7 @@ usage: if (setsockopt(rt_sock, SOL_SOCKET,SO_USELOOPBACK, &off,sizeof(off)) < 0) LOGERR("setsockopt(SO_USELOOPBACK,0)"); - + fix_select(); @@ -304,13 +316,6 @@ usage: rdisc_timer = next_bcast; ifinit_timer.tv_usec = next_bcast.tv_usec; - signal(SIGALRM, sigalrm); - signal(SIGHUP, sigterm); - signal(SIGTERM, sigterm); - signal(SIGINT, sigterm); - signal(SIGUSR1, sigtrace_on); - signal(SIGUSR2, sigtrace_off); - /* Collect an initial view of the world by checking the interface * configuration and the kludge file. */ @@ -342,6 +347,7 @@ usage: } 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 */ diff --git a/usr.sbin/routed/output.c b/sbin/routed/output.c similarity index 96% rename from usr.sbin/routed/output.c rename to sbin/routed/output.c index be30609743a..eafcf31e786 100644 --- a/usr.sbin/routed/output.c +++ b/sbin/routed/output.c @@ -36,7 +36,7 @@ static char sccsid[] = "@(#)output.c 8.1 (Berkeley) 6/5/93"; #elif defined(__NetBSD__) static char rcsid[] = "$NetBSD$"; #endif -#ident "$Revision: 1.16 $" +#ident "$Revision: 1.17 $" #include "defs.h" @@ -263,7 +263,7 @@ supply_out(struct ag_info *ag) && (ws.state & WS_ST_FLASH)) return; - /* Skip this route if required by split-horizon + /* Skip this route if required by split-horizon. */ if (ag->ag_state & AGS_SPLIT_HZ) return; @@ -363,7 +363,7 @@ walk_supply(struct radix_node *rn, struct walkarg *w) { #define RT ((struct rt_entry *)rn) - u_short ags = 0; + u_short ags; char metric, pref; naddr dst, nhop; @@ -371,7 +371,8 @@ walk_supply(struct radix_node *rn, /* Do not advertise the loopback interface * or external remote interfaces */ - if (RT->rt_ifp != 0 + 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)) @@ -429,32 +430,8 @@ walk_supply(struct radix_node *rn, nhop = 0; } - /* Adjust the outgoing metric by the cost of the link. - */ - pref = metric = RT->rt_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_garbage) { - RT->rt_poison_time = now.tv_sec; - RT->rt_poison_metric = RT->rt_metric; - } - - } 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) - return 0; - - metric = HOPCNT_INFINITY; - } + metric = RT->rt_metric; + ags = 0; if (RT->rt_state & RS_MHOME) { /* retain host route of multi-homed servers */ @@ -521,8 +498,47 @@ walk_supply(struct radix_node *rn, && (ws.state & WS_ST_TO_ON_NET) && (!(RT->rt_state & RS_IF) || ws.ifp->int_if_flags & IFF_POINTOPOINT)) { - ags |= AGS_SPLIT_HZ; - ags &= ~(AGS_PROMOTE | AGS_SUPPRESS); + /* 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, diff --git a/usr.sbin/routed/parms.c b/sbin/routed/parms.c similarity index 97% rename from usr.sbin/routed/parms.c rename to sbin/routed/parms.c index fcd37a6b6c3..0d178c3698b 100644 --- a/usr.sbin/routed/parms.c +++ b/sbin/routed/parms.c @@ -36,7 +36,7 @@ static char sccsid[] = "@(#)if.c 8.1 (Berkeley) 6/5/93"; #elif defined(__NetBSD__) static char rcsid[] = "$NetBSD$"; #endif -#ident "$Revision: 1.8 $" +#ident "$Revision: 1.9 $" #include "defs.h" #include "pathnames.h" @@ -474,13 +474,6 @@ parse_parms(char *line) if (tgt != 0) return tgt; - if (parm.parm_int_state & IS_NO_ADV_IN) - parm.parm_int_state |= IS_NO_SOL_OUT; - - if ((parm.parm_int_state & (IS_NO_RIP | IS_NO_RDISC)) - == (IS_NO_RIP | IS_NO_RDISC)) - parm.parm_int_state |= IS_PASSIVE; - return check_parms(&parm); #undef DELIMS #undef PARS @@ -495,6 +488,21 @@ 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; diff --git a/usr.sbin/routed/pathnames.h b/sbin/routed/pathnames.h similarity index 92% rename from usr.sbin/routed/pathnames.h rename to sbin/routed/pathnames.h index e9e6c48c639..14b721c2d47 100644 --- a/usr.sbin/routed/pathnames.h +++ b/sbin/routed/pathnames.h @@ -43,5 +43,8 @@ * 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 "/tmp/routed.log" +#define _PATH_TRACE "/etc/routed.trace" diff --git a/usr.sbin/routed/radix.c b/sbin/routed/radix.c similarity index 100% rename from usr.sbin/routed/radix.c rename to sbin/routed/radix.c diff --git a/usr.sbin/routed/radix.h b/sbin/routed/radix.h similarity index 100% rename from usr.sbin/routed/radix.h rename to sbin/routed/radix.h diff --git a/usr.sbin/routed/rdisc.c b/sbin/routed/rdisc.c similarity index 100% rename from usr.sbin/routed/rdisc.c rename to sbin/routed/rdisc.c diff --git a/usr.sbin/routed/routed.8 b/sbin/routed/routed.8 similarity index 99% rename from usr.sbin/routed/routed.8 rename to sbin/routed/routed.8 index 828bdd89fb1..de3abf34e84 100644 --- a/usr.sbin/routed/routed.8 +++ b/sbin/routed/routed.8 @@ -298,6 +298,9 @@ 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 diff --git a/usr.sbin/routed/routed.h b/sbin/routed/routed.h similarity index 100% rename from usr.sbin/routed/routed.h rename to sbin/routed/routed.h diff --git a/usr.sbin/routed/rtquery/Makefile b/sbin/routed/rtquery/Makefile similarity index 100% rename from usr.sbin/routed/rtquery/Makefile rename to sbin/routed/rtquery/Makefile diff --git a/usr.sbin/routed/rtquery/rtquery.8 b/sbin/routed/rtquery/rtquery.8 similarity index 92% rename from usr.sbin/routed/rtquery/rtquery.8 rename to sbin/routed/rtquery/rtquery.8 index 1afb8b9478b..d77ca30b9c8 100644 --- a/usr.sbin/routed/rtquery/rtquery.8 +++ b/sbin/routed/rtquery/rtquery.8 @@ -79,18 +79,20 @@ 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. +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=filename +.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 /tmp/routed.log . +.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 , diff --git a/usr.sbin/routed/rtquery/rtquery.c b/sbin/routed/rtquery/rtquery.c similarity index 97% rename from usr.sbin/routed/rtquery/rtquery.c rename to sbin/routed/rtquery/rtquery.c index cc1be10ab22..7d6913a048d 100644 --- a/usr.sbin/routed/rtquery/rtquery.c +++ b/sbin/routed/rtquery/rtquery.c @@ -40,7 +40,7 @@ static char sccsid[] = "@(#)query.c 8.1 (Berkeley) 6/5/93"; #elif defined(__NetBSD__) static char rcsid[] = "$NetBSD$"; #endif -#ident "$Revision: 1.8 $" +#ident "$Revision: 1.9 $" #include #include @@ -171,6 +171,8 @@ main(int argc, "more", # define TRACE_OFF 2 "off", +# define TRACE_DUMP 3 + "dump", 0 }; switch (getsubopt(&options,traceopts,&value)) { @@ -179,25 +181,30 @@ main(int argc, if (!value || strlen(value) > MAXPATHLEN) goto usage; - strcpy((char*)OMSG.rip_tracefile,value); - omsg_len += (strlen(value) - - sizeof(OMSG.ripun)); break; case TRACE_MORE: if (value) goto usage; OMSG.rip_cmd = RIPCMD_TRACEON; - OMSG.rip_tracefile[0] = '\0'; + value = ""; break; case TRACE_OFF: if (value) goto usage; OMSG.rip_cmd = RIPCMD_TRACEOFF; - OMSG.rip_tracefile[0] = '\0'; + 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; diff --git a/usr.sbin/routed/table.c b/sbin/routed/table.c similarity index 98% rename from usr.sbin/routed/table.c rename to sbin/routed/table.c index b1566a3792a..6ad97f0ada1 100644 --- a/usr.sbin/routed/table.c +++ b/sbin/routed/table.c @@ -36,7 +36,7 @@ static char sccsid[] = "@(#)tables.c 8.1 (Berkeley) 6/5/93"; #elif defined(__NetBSD__) static char rcsid[] = "$NetBSD$"; #endif -#ident "$Revision: 1.23 $" +#ident "$Revision: 1.25 $" #include "defs.h" @@ -690,6 +690,13 @@ again: 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) @@ -850,6 +857,7 @@ rtm_add(struct rt_msghdr *rtm, 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, @@ -857,6 +865,7 @@ rtm_add(struct rt_msghdr *rtm, */ 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), @@ -865,6 +874,7 @@ rtm_add(struct rt_msghdr *rtm, 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 @@ -1584,8 +1594,11 @@ rtchange(struct rt_entry *rt, * has gone bad, since there may be a working route that * aggregates this route. */ - if (metric == HOPCNT_INFINITY) + 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(); } @@ -1598,6 +1611,11 @@ rtchange(struct rt_entry *rt, 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, @@ -1643,12 +1661,8 @@ rtswitch(struct rt_entry *rt, /* Do not change permanent routes */ - if (0 != (rt->rt_state & RS_PERMANENT)) - return; - - /* Do not discard synthetic routes until they go bad */ - if ((rt->rt_state & RS_NET_SYN) - && rt->rt_metric < HOPCNT_INFINITY) + if (0 != (rt->rt_state & (RS_MHOME | RS_STATIC | RS_RDISC + | RS_NET_SYN | RS_IF))) return; /* find the best alternative among the spares */ @@ -1803,6 +1817,7 @@ walk_bad(struct radix_node *rn, 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; @@ -1857,9 +1872,10 @@ walk_age(struct radix_node *rn, ifp = rts->rts_ifp; if (i == NUM_SPARES) { - if (!AGE_RT(RT, ifp)) { + if (!AGE_RT(RT->rt_state, ifp)) { /* Keep various things from deciding ageless - * routes are stale */ + * routes are stale + */ rts->rts_time = now.tv_sec; continue; } diff --git a/usr.sbin/routed/trace.c b/sbin/routed/trace.c similarity index 88% rename from usr.sbin/routed/trace.c rename to sbin/routed/trace.c index e9c1e03f05a..8a0b59ccb02 100644 --- a/usr.sbin/routed/trace.c +++ b/sbin/routed/trace.c @@ -36,7 +36,7 @@ static char sccsid[] = "@(#)trace.c 8.1 (Berkeley) 6/5/93"; #elif defined(__NetBSD__) static char rcsid[] = "$NetBSD$"; #endif -#ident "$Revision: 1.11 $" +#ident "$Revision: 1.13 $" #define RIPCMDS #include "defs.h" @@ -59,6 +59,8 @@ 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 */ @@ -205,21 +207,26 @@ trace_on(char *filename, } filename = savetracename; - } else if (stat(filename, &stbuf) >= 0) { - if (!trusted) { - msglog("trace file \"%s\" already exists"); - return; - } - if ((stbuf.st_mode & S_IFMT) != S_IFREG) { + } 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 - && strcmp(filename, savetracename) - && strncmp(filename, _PATH_TRACE, sizeof(_PATH_TRACE)-1)) { - msglog("wrong directory for trace file: \"%s\"", +#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; } @@ -280,11 +287,13 @@ set_tracelevel(void) "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", }; @@ -567,6 +576,22 @@ trace_upslot(struct rt_entry *rt, } +/* 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 @@ -635,7 +660,7 @@ trace_change(struct rt_entry *rt, (void)fprintf(ftrace, "%s ", rt->rt_ifp == 0 ? "?" : rt->rt_ifp->int_name); (void)fprintf(ftrace, "%s\n", - AGE_RT(rt, rt->rt_ifp) ? ts(rt->rt_time) : ""); + AGE_RT(rt->rt_state, rt->rt_ifp) ? ts(rt->rt_time) : ""); (void)fprintf(ftrace, "%*s %19s%-16s ", strlen(label), "", "", @@ -652,7 +677,7 @@ trace_change(struct rt_entry *rt, (void)fprintf(ftrace, "%s ", ifp != 0 ? ifp->int_name : "?"); (void)fprintf(ftrace, "%s\n", - ((rt->rt_time == new_time || !AGE_RT(rt, ifp)) + ((rt->rt_time == new_time || !AGE_RT(rt->rt_state, ifp)) ? "" : ts(new_time))); } @@ -683,6 +708,68 @@ trace_add_del(char * action, struct rt_entry *rt) } +/* 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, diff --git a/usr.sbin/routed/Makefile.inc b/usr.sbin/routed/Makefile.inc deleted file mode 100644 index 10fa13f1ed8..00000000000 --- a/usr.sbin/routed/Makefile.inc +++ /dev/null @@ -1 +0,0 @@ -.include "../../Makefile.inc" diff --git a/usr.sbin/routed/rttrace/Makefile b/usr.sbin/routed/rttrace/Makefile deleted file mode 100644 index df19d5c61b4..00000000000 --- a/usr.sbin/routed/rttrace/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -# @(#)Makefile 8.1 (Berkeley) 6/5/93 - -PROG= rttrace -NOMAN= noman - -.include diff --git a/usr.sbin/routed/rttrace/rttrace.c b/usr.sbin/routed/rttrace/rttrace.c deleted file mode 100644 index 0d8ac6224ce..00000000000 --- a/usr.sbin/routed/rttrace/rttrace.c +++ /dev/null @@ -1,146 +0,0 @@ -/*- - * 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. - */ - -#ifndef lint -static char copyright[] = -"@(#) Copyright (c) 1983, 1988, 1993\n\ - The Regents of the University of California. All rights reserved.\n"; -#endif /* not lint */ - -#ifndef lint -static char sccsid[] = "@(#)trace.c 8.1 (Berkeley) 6/5/93"; -#endif /* not lint */ - -#include -#include -#include -#include -#include -#include -#ifdef sgi -#include -#endif -#include -#include -#include -#include -#include -#include - -#ifndef sgi -#define _HAVE_SIN_LEN -#endif - -struct sockaddr_in myaddr; -char packet[MAXPACKETSIZE]; - -int -main(int argc, - char **argv) -{ - int size, s; - struct sockaddr_in router; - char *tgt; - register struct rip *msg = (struct rip *)packet; - struct hostent *hp; - - if (argc < 2) { -usage: - printf("usage: on filename host1 host2 ...\n" - " or: off host1 host2 ...\n"); - exit(1); - } - s = socket(AF_INET, SOCK_DGRAM, 0); - if (s < 0) { - perror("socket"); - exit(2); - } - 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); - } - - msg->rip_vers = RIPVERSION; - size = sizeof(int); - - argv++, argc--; - if (!strcmp(*argv, "on")) { - msg->rip_cmd = RIPCMD_TRACEON; - if (--argc <= 1) - goto usage; - strcpy(msg->rip_tracefile, *++argv); - size += strlen(msg->rip_tracefile); - - } else if (!strcmp(*argv, "off")) { - msg->rip_cmd = RIPCMD_TRACEOFF; - - } else { - goto usage; - } - argv++, argc--; - - bzero(&router, sizeof(router)); - router.sin_family = AF_INET; -#ifdef _HAVE_SIN_LEN - router.sin_len = sizeof(router); -#endif - router.sin_port = htons(RIP_PORT); - - do { - tgt = argc > 0 ? *argv++ : "localhost"; - router.sin_family = AF_INET; - router.sin_addr.s_addr = inet_addr(tgt); - if (router.sin_addr.s_addr == -1) { - hp = gethostbyname(tgt); - if (hp == 0) { - herror(tgt); - continue; - } - bcopy(hp->h_addr, &router.sin_addr, hp->h_length); - } - if (sendto(s, packet, size, 0, - (struct sockaddr *)&router, sizeof(router)) < 0) - perror(*argv); - } while (--argc > 0); - - return 0; -}