From 7b6ab19dde850fe54facf1dbc5b00466a0a12b53 Mon Sep 17 00:00:00 2001 From: Garrett Wollman Date: Mon, 16 Sep 1996 16:51:32 +0000 Subject: Virgin import of new BSD/SGI routed. This update contains a number of important bug fixes. Obtained from: Vernon J. Schryver --- sbin/routed/Makefile | 22 + sbin/routed/defs.h | 566 +++++++++++ sbin/routed/if.c | 1153 ++++++++++++++++++++++ sbin/routed/input.c | 741 ++++++++++++++ sbin/routed/main.c | 830 ++++++++++++++++ sbin/routed/output.c | 874 ++++++++++++++++ sbin/routed/parms.c | 630 ++++++++++++ sbin/routed/pathnames.h | 50 + sbin/routed/radix.c | 895 +++++++++++++++++ sbin/routed/radix.h | 161 +++ sbin/routed/rdisc.c | 1032 +++++++++++++++++++ sbin/routed/routed.8 | 605 ++++++++++++ sbin/routed/routed.h | 162 +++ sbin/routed/rtquery/Makefile | 8 + sbin/routed/rtquery/rtquery.8 | 103 ++ sbin/routed/rtquery/rtquery.c | 654 ++++++++++++ sbin/routed/table.c | 1970 +++++++++++++++++++++++++++++++++++++ sbin/routed/trace.c | 887 +++++++++++++++++ usr.sbin/routed/Makefile | 22 - usr.sbin/routed/Makefile.inc | 1 - usr.sbin/routed/defs.h | 562 ----------- usr.sbin/routed/if.c | 1132 --------------------- usr.sbin/routed/input.c | 714 -------------- usr.sbin/routed/main.c | 824 ---------------- usr.sbin/routed/output.c | 858 ---------------- usr.sbin/routed/parms.c | 622 ------------ usr.sbin/routed/pathnames.h | 47 - usr.sbin/routed/radix.c | 895 ----------------- usr.sbin/routed/radix.h | 161 --- usr.sbin/routed/rdisc.c | 1032 ------------------- usr.sbin/routed/routed.8 | 602 ------------ usr.sbin/routed/routed.h | 162 --- usr.sbin/routed/rtquery/Makefile | 8 - usr.sbin/routed/rtquery/rtquery.8 | 101 -- usr.sbin/routed/rtquery/rtquery.c | 647 ------------ usr.sbin/routed/rttrace/Makefile | 6 - usr.sbin/routed/rttrace/rttrace.c | 146 --- usr.sbin/routed/table.c | 1954 ------------------------------------ usr.sbin/routed/trace.c | 800 --------------- 39 files changed, 11343 insertions(+), 11296 deletions(-) create mode 100644 sbin/routed/Makefile create mode 100644 sbin/routed/defs.h create mode 100644 sbin/routed/if.c create mode 100644 sbin/routed/input.c create mode 100644 sbin/routed/main.c create mode 100644 sbin/routed/output.c create mode 100644 sbin/routed/parms.c create mode 100644 sbin/routed/pathnames.h create mode 100644 sbin/routed/radix.c create mode 100644 sbin/routed/radix.h create mode 100644 sbin/routed/rdisc.c create mode 100644 sbin/routed/routed.8 create mode 100644 sbin/routed/routed.h create mode 100644 sbin/routed/rtquery/Makefile create mode 100644 sbin/routed/rtquery/rtquery.8 create mode 100644 sbin/routed/rtquery/rtquery.c create mode 100644 sbin/routed/table.c create mode 100644 sbin/routed/trace.c delete mode 100644 usr.sbin/routed/Makefile delete mode 100644 usr.sbin/routed/Makefile.inc delete mode 100644 usr.sbin/routed/defs.h delete mode 100644 usr.sbin/routed/if.c delete mode 100644 usr.sbin/routed/input.c delete mode 100644 usr.sbin/routed/main.c delete mode 100644 usr.sbin/routed/output.c delete mode 100644 usr.sbin/routed/parms.c delete mode 100644 usr.sbin/routed/pathnames.h delete mode 100644 usr.sbin/routed/radix.c delete mode 100644 usr.sbin/routed/radix.h delete mode 100644 usr.sbin/routed/rdisc.c delete mode 100644 usr.sbin/routed/routed.8 delete mode 100644 usr.sbin/routed/routed.h delete mode 100644 usr.sbin/routed/rtquery/Makefile delete mode 100644 usr.sbin/routed/rtquery/rtquery.8 delete mode 100644 usr.sbin/routed/rtquery/rtquery.c delete mode 100644 usr.sbin/routed/rttrace/Makefile delete mode 100644 usr.sbin/routed/rttrace/rttrace.c delete mode 100644 usr.sbin/routed/table.c delete mode 100644 usr.sbin/routed/trace.c diff --git a/sbin/routed/Makefile b/sbin/routed/Makefile new file mode 100644 index 000000000000..c567e68e87ad --- /dev/null +++ b/sbin/routed/Makefile @@ -0,0 +1,22 @@ +# @(#)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.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/sbin/routed/defs.h b/sbin/routed/defs.h new file mode 100644 index 000000000000..452b71aa067e --- /dev/null +++ b/sbin/routed/defs.h @@ -0,0 +1,566 @@ +/* + * Copyright (c) 1983, 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)defs.h 8.1 (Berkeley) 6/5/93 + * + * $NetBSD$ + */ + +#ifndef __NetBSD__ +#ident "$Revision: 1.16 $" +#endif + +/* Definitions for RIPv2 routing process. + * + * This code is based on the 4.4BSD `routed` daemon, with extensions to + * support: + * RIPv2, including variable length subnet masks. + * Router Discovery + * aggregate routes in the kernel tables. + * aggregate advertised routes. + * maintain spare routes for faster selection of another gateway + * when the current gateway dies. + * timers on routes with second granularity so that selection + * of a new route does not wait 30-60 seconds. + * tolerance of static routes. + * tell the kernel hop counts + * do not advertise if ipforwarding=0 + * + * The vestigual support for other protocols has been removed. There + * is no likelihood that IETF RIPv1 or RIPv2 will ever be used with + * other protocols. The result is far smaller, faster, cleaner, and + * perhaps understandable. + * + * The accumulation of special flags and kludges added over the many + * years have been simplified and integrated. + */ + +#include +#include +#include +#include +#include +#include +#ifdef sgi +#include +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef sgi +#include +#else +#include "radix.h" +#endif +#include +#include +#include +#include +#include +#define RIPVERSION RIPv2 +#include + + +/* Type of an IP address. + * Some systems do not like to pass structures, so do not use in_addr. + * Some systems think a long has 64 bits, which would be a gross waste. + * So define it here so it can be changed for the target system. + * It should be defined somewhere netinet/in.h, but it is not. + */ +#ifdef sgi +#define naddr __uint32_t +#else +#ifdef __NetBSD__ +#define naddr u_int32_t +#else +#define naddr u_long +#endif +#define _HAVE_SA_LEN +#define _HAVE_SIN_LEN +#endif + +/* Turn on if IP_DROP_MEMBERSHIP and IP_ADD_MEMBERSHIP do not look at + * the dstaddr of point-to-point interfaces. + */ +/* #define MCAST_PPP_BUG */ + +#define NEVER (24*60*60) /* a long time */ +#define EPOCH NEVER /* bias time by this to avoid <0 */ + +/* Scan the kernel regularly to see if any interfaces have appeared or been + * turned off. These must be less than STALE_TIME. + */ +#define CHECK_BAD_INTERVAL 5 /* when an interface is known bad */ +#define CHECK_ACT_INTERVAL 30 /* when advertising */ +#define CHECK_QUIET_INTERVAL 300 /* when not */ + +#define LIM_SEC(s,l) ((s).tv_sec = MIN((s).tv_sec, (l))) + + +/* Router Discovery parameters */ +#ifndef sgi +#define INADDR_ALLROUTERS_GROUP 0xe0000002 /* 224.0.0.2 */ +#endif +#define MaxMaxAdvertiseInterval 1800 +#define MinMaxAdvertiseInterval 4 +#define DefMaxAdvertiseInterval 600 +#define DEF_PreferenceLevel 0 +#define MIN_PreferenceLevel 0x80000000 + +#define MAX_INITIAL_ADVERT_INTERVAL 16 +#define MAX_INITIAL_ADVERTS 3 +#define MAX_RESPONSE_DELAY 2 + +#define MAX_SOLICITATION_DELAY 1 +#define SOLICITATION_INTERVAL 3 +#define MAX_SOLICITATIONS 3 + + +/* typical packet buffers */ +union pkt_buf { + char packet[MAXPACKETSIZE+1]; + struct rip rip; +}; + + +/* no more routes than this, to protect ourself in case something goes + * whacko and starts broadcast zillions of bogus routes. + */ +#define MAX_ROUTES (128*1024) +extern int total_routes; + +/* Main, daemon routing table structure + */ +struct rt_entry { + struct radix_node rt_nodes[2]; /* radix tree glue */ + u_int rt_state; +# define RS_IF 0x001 /* for network interface */ +# define RS_NET_INT 0x002 /* authority route */ +# define RS_NET_SYN 0x004 /* fake net route for subnet */ +# define RS_NO_NET_SYN (RS_LOCAL | RS_LOCAL | RS_IF) +# define RS_SUBNET 0x008 /* subnet route from any source */ +# define RS_LOCAL 0x010 /* loopback for pt-to-pt */ +# define RS_MHOME 0x020 /* from -m */ +# define RS_STATIC 0x040 /* from the kernel */ +# define RS_RDISC 0x080 /* from router discovery */ + struct sockaddr_in rt_dst_sock; + naddr rt_mask; + struct rt_spare { + struct interface *rts_ifp; + naddr rts_gate; /* forward packets here */ + naddr rts_router; /* on the authority of this router */ + char rts_metric; + u_short rts_tag; + time_t rts_time; /* timer to junk stale routes */ +#define NUM_SPARES 4 + } rt_spares[NUM_SPARES]; + u_int rt_seqno; /* when last changed */ + char rt_poison_metric; /* to notice maximum recently */ + time_t rt_poison_time; /* advertised metric */ +}; +#define rt_dst rt_dst_sock.sin_addr.s_addr +#define rt_ifp rt_spares[0].rts_ifp +#define rt_gate rt_spares[0].rts_gate +#define rt_router rt_spares[0].rts_router +#define rt_metric rt_spares[0].rts_metric +#define rt_tag rt_spares[0].rts_tag +#define rt_time rt_spares[0].rts_time + +#define HOST_MASK 0xffffffff +#define RT_ISHOST(rt) ((rt)->rt_mask == HOST_MASK) + +/* age all routes that + * are not from -g, -m, or static routes from the kernel + * not unbroken interface routes + * but not broken interfaces + * nor non-passive, remote interfaces that are not aliases + * (i.e. remote & metric=0) + */ +#define AGE_RT(rt_state,ifp) (0 == ((rt_state) & (RS_MHOME | RS_STATIC \ + | RS_NET_SYN | RS_RDISC)) \ + && (!((rt_state) & RS_IF) \ + || (ifp) == 0 \ + || (((ifp)->int_state & IS_REMOTE) \ + && !((ifp)->int_state & IS_PASSIVE)))) + +/* true if A is better than B + * Better if + * - A is not a poisoned route + * - and A is not stale + * - and A has a shorter path + * - or is the router speaking for itself + * - or the current route is equal but stale + * - or it is a host route advertised by a system for itself + */ +#define BETTER_LINK(rt,A,B) ((A)->rts_metric < HOPCNT_INFINITY \ + && now_stale <= (A)->rts_time \ + && ((A)->rts_metric < (B)->rts_metric \ + || ((A)->rts_gate == (A)->rts_router \ + && (B)->rts_gate != (B)->rts_router) \ + || ((A)->rts_metric == (B)->rts_metric \ + && now_stale > (B)->rts_time) \ + || (RT_ISHOST(rt) \ + && (rt)->rt_dst == (A)->rts_router \ + && (A)->rts_metric == (B)->rts_metric))) + + +/* An "interface" is similar to a kernel ifnet structure, except it also + * handles "logical" or "IS_REMOTE" interfaces (remote gateways). + */ +struct interface { + struct interface *int_next, *int_prev; + char int_name[IFNAMSIZ+15+1]; /* big enough for IS_REMOTE */ + u_short int_index; + naddr int_addr; /* address on this host (net order) */ + naddr int_brdaddr; /* broadcast address (n) */ + naddr int_dstaddr; /* other end of pt-to-pt link (n) */ + naddr int_net; /* working network # (host order)*/ + naddr int_mask; /* working net mask (host order) */ + naddr int_ripv1_mask; /* for inferring a mask (n) */ + naddr int_std_addr; /* class A/B/C address (n) */ + naddr int_std_net; /* class A/B/C network (h) */ + naddr int_std_mask; /* class A/B/C netmask (h) */ + int int_rip_sock; /* for queries */ + int int_if_flags; /* some bits copied from kernel */ + u_int int_state; + time_t int_act_time; /* last thought healthy */ + u_short int_transitions; /* times gone up-down */ + char int_metric; + char int_d_metric; /* for faked default route */ + struct int_data { + u_int ipackets; /* previous network stats */ + u_int ierrors; + u_int opackets; + u_int oerrors; +#ifdef sgi + u_int odrops; +#endif + time_t ts; /* timestamp on network stats */ + } int_data; + char int_passwd[RIP_AUTH_PW_LEN]; /* RIPv2 password */ + int int_rdisc_pref; /* advertised rdisc preference */ + int int_rdisc_int; /* MaxAdvertiseInterval */ + int int_rdisc_cnt; + struct timeval int_rdisc_timer; +}; + +/* bits in int_state */ +#define IS_ALIAS 0x0000001 /* interface alias */ +#define IS_SUBNET 0x0000002 /* interface on subnetted network */ +#define IS_REMOTE 0x0000004 /* interface is not on this machine */ +#define IS_PASSIVE 0x0000008 /* remote and does not do RIP */ +#define IS_EXTERNAL 0x0000010 /* handled by EGP or something */ +#define IS_CHECKED 0x0000020 /* still exists */ +#define IS_ALL_HOSTS 0x0000040 /* in INADDR_ALLHOSTS_GROUP */ +#define IS_ALL_ROUTERS 0x0000080 /* in INADDR_ALLROUTERS_GROUP */ +#define IS_RIP_QUERIED 0x0000100 /* query broadcast */ +#define IS_BROKE 0x0000200 /* seems to be broken */ +#define IS_SICK 0x0000400 /* seems to be broken */ +#define IS_DUP 0x0000800 /* has a duplicate address */ +#define IS_ACTIVE 0x0001000 /* heard from it at least once */ +#define IS_NEED_NET_SYN 0x0002000 /* need RS_NET_SYN route */ +#define IS_NO_AG 0x0004000 /* do not aggregate subnets */ +#define IS_NO_SUPER_AG 0x0008000 /* do not aggregate networks */ +#define IS_NO_RIPV1_IN 0x0010000 /* no RIPv1 input at all */ +#define IS_NO_RIPV2_IN 0x0020000 /* no RIPv2 input at all */ +#define IS_NO_RIP_IN (IS_NO_RIPV1_IN | IS_NO_RIPV2_IN) +#define IS_RIP_IN_OFF(s) (((s) & IS_NO_RIP_IN) == IS_NO_RIP_IN) +#define IS_NO_RIPV1_OUT 0x0040000 /* no RIPv1 output at all */ +#define IS_NO_RIPV2_OUT 0x0080000 /* no RIPv2 output at all */ +#define IS_NO_RIP_OUT (IS_NO_RIPV1_OUT | IS_NO_RIPV2_OUT) +#define IS_NO_RIP (IS_NO_RIP_OUT | IS_NO_RIP_IN) +#define IS_RIP_OUT_OFF(s) (((s) & IS_NO_RIP_OUT) == IS_NO_RIP_OUT) +#define IS_RIP_OFF(s) (((s) & IS_NO_RIP) == IS_NO_RIP) +#define IS_NO_ADV_IN 0x0100000 +#define IS_NO_SOL_OUT 0x0200000 /* no solicitations */ +#define IS_SOL_OUT 0x0400000 /* send solicitations */ +#define GROUP_IS_SOL (IS_NO_ADV_IN|IS_NO_SOL_OUT) +#define IS_NO_ADV_OUT 0x0800000 /* do not advertise rdisc */ +#define IS_ADV_OUT 0x1000000 /* advertise rdisc */ +#define GROUP_IS_ADV (IS_NO_ADV_OUT|IS_ADV_OUT) +#define IS_BCAST_RDISC 0x2000000 /* broadcast instead of multicast */ +#define IS_NO_RDISC (IS_NO_ADV_IN | IS_NO_SOL_OUT | IS_NO_ADV_OUT) +#define IS_PM_RDISC 0x4000000 /* poor-man's router discovery */ + +#ifdef sgi +#define IFF_UP_RUNNING (IFF_RUNNING|IFF_UP) +#else +#define IFF_UP_RUNNING IFF_UP +#endif +#define iff_alive(f) (((f) & IFF_UP_RUNNING) == IFF_UP_RUNNING) + + +/* Information for aggregating routes */ +#define NUM_AG_SLOTS 32 +struct ag_info { + struct ag_info *ag_fine; /* slot with finer netmask */ + struct ag_info *ag_cors; /* more coarse netmask */ + naddr ag_dst_h; /* destination in host byte order */ + naddr ag_mask; + naddr ag_gate; + naddr ag_nhop; + char ag_metric; /* metric to be advertised */ + char ag_pref; /* aggregate based on this */ + u_int ag_seqno; + u_short ag_tag; + u_short ag_state; +#define AGS_SUPPRESS 0x001 /* combine with coaser mask */ +#define AGS_PROMOTE 0x002 /* synthesize combined routes */ +#define AGS_REDUN0 0x004 /* redundant, finer routes output */ +#define AGS_REDUN1 0x008 +#define AG_IS_REDUN(state) (((state) & (AGS_REDUN0 | AGS_REDUN1)) \ + == (AGS_REDUN0 | AGS_REDUN1)) +#define AGS_GATEWAY 0x010 /* tell kernel RTF_GATEWAY */ +#define AGS_IF 0x020 /* for an interface */ +#define AGS_RIPV2 0x040 /* send only as RIPv2 */ +#define AGS_FINE_GATE 0x080 /* ignore differing ag_gate when this + * has the finer netmask */ +#define AGS_CORS_GATE 0x100 /* ignore differing gate when this + * has the coarser netmaks */ +#define AGS_SPLIT_HZ 0x200 /* suppress for split horizon */ + + /* some bits are set if they are set on either route */ +#define AGS_PROMOTE_EITHER (AGS_RIPV2 | AGS_GATEWAY | \ + AGS_SUPPRESS | AGS_CORS_GATE) +}; + + +/* parameters for interfaces */ +extern struct parm { + struct parm *parm_next; + char parm_name[IFNAMSIZ+1]; + naddr parm_addr_h; + naddr parm_mask; + + char parm_d_metric; + u_int parm_int_state; + int parm_rdisc_pref; + int parm_rdisc_int; + char parm_passwd[RIP_AUTH_PW_LEN+1]; +} *parms; + +/* authority for internal networks */ +extern struct intnet { + struct intnet *intnet_next; + naddr intnet_addr; + naddr intnet_mask; + char intnet_metric; +} *intnets; + + + +extern pid_t mypid; +extern naddr myaddr; /* main address of this system */ + +extern int stopint; /* !=0 to stop */ + +extern int sock_max; +extern int rip_sock; /* RIP socket */ +extern struct interface *rip_sock_mcast; /* current multicast interface */ +extern int rt_sock; /* routing socket */ +extern int rt_sock_seqno; +extern int rdisc_sock; /* router-discovery raw socket */ + +extern int seqno; /* sequence number for messages */ +extern int supplier; /* process should supply updates */ +extern int lookforinterfaces; /* 1=probe for new up interfaces */ +extern int supplier_set; /* -s or -q requested */ +extern int ridhosts; /* 1=reduce host routes */ +extern int mhome; /* 1=want multi-homed host route */ +extern int advertise_mhome; /* 1=must continue adverising it */ +extern int auth_ok; /* 1=ignore auth if we do not care */ + +extern struct timeval epoch; /* when started */ +extern struct timeval now; /* current idea of time */ +extern time_t now_stale; +extern time_t now_expire; +extern time_t now_garbage; + +extern struct timeval next_bcast; /* next general broadcast */ +extern struct timeval age_timer; /* next check of old routes */ +extern struct timeval no_flash; /* inhibit flash update until then */ +extern struct timeval rdisc_timer; /* next advert. or solicitation */ +extern int rdisc_ok; /* using solicited route */ + +extern struct timeval ifinit_timer; /* time to check interfaces */ + +extern naddr loopaddr; /* our address on loopback */ +extern int tot_interfaces; /* # of remote and local interfaces */ +extern int rip_interfaces; /* # of interfaces doing RIP */ +extern struct interface *ifnet; /* all interfaces */ +extern int have_ripv1_out; /* have a RIPv1 interface */ +extern int have_ripv1_in; +extern int need_flash; /* flash update needed */ +extern struct timeval need_kern; /* need to update kernel table */ +extern int update_seqno; /* a route has changed */ + +extern u_int tracelevel, new_tracelevel; +#define MAX_TRACELEVEL 4 +#define TRACEKERNEL (tracelevel >= 4) /* log kernel changes */ +#define TRACECONTENTS (tracelevel >= 3) /* display packet contents */ +#define TRACEPACKETS (tracelevel >= 2) /* note packets */ +#define TRACEACTIONS (tracelevel != 0) +extern FILE *ftrace; /* output trace file */ + +extern struct radix_node_head *rhead; + + +#ifdef sgi +/* Fix conflicts */ +#define dup2(x,y) BSDdup2(x,y) +#endif /* sgi */ + +extern void fix_sock(int, char *); +extern void fix_select(void); +extern void rip_off(void); +extern void rip_on(struct interface *); + +enum output_type {OUT_QUERY, OUT_UNICAST, OUT_BROADCAST, OUT_MULTICAST, + NO_OUT_MULTICAST, NO_OUT_RIPV2}; +extern int output(enum output_type, struct sockaddr_in *, + struct interface *, struct rip *, int); +extern void rip_query(void); +extern void rip_bcast(int); +extern void supply(struct sockaddr_in *, struct interface *, + enum output_type, int, int); + +extern void msglog(char *, ...); +#define LOGERR(msg) msglog(msg ": %s", strerror(errno)) +extern void logbad(int, char *, ...); +#define BADERR(dump,msg) logbad(dump,msg ": %s", strerror(errno)) +#ifdef DEBUG +#define DBGERR(dump,msg) BADERR(dump,msg) +#else +#define DBGERR(dump,msg) LOGERR(msg) +#endif +extern char *naddr_ntoa(naddr); +extern char *saddr_ntoa(struct sockaddr *); + +extern void *rtmalloc(size_t, char *); +extern void timevaladd(struct timeval *, struct timeval *); +extern void intvl_random(struct timeval *, u_long, u_long); +extern int getnet(char *, naddr *, naddr *); +extern int gethost(char *, naddr *); +extern void gwkludge(void); +extern char *parse_parms(char *); +extern char *check_parms(struct parm *); +extern void get_parms(struct interface *); + +extern void lastlog(void); +extern void trace_on(char *, int); +extern void trace_off(char*, ...); +extern void trace_flush(void); +extern void set_tracelevel(void); +extern void trace_kernel(char *, ...); +extern void trace_act(char *, ...); +extern void trace_pkt(char *, ...); +extern void trace_add_del(char *, struct rt_entry *); +extern void trace_change(struct rt_entry *, u_int, naddr, naddr, int, + u_short, struct interface *, time_t, char *); +extern void trace_if(char *, struct interface *); +extern void trace_upslot(struct rt_entry *, struct rt_spare *, + naddr, naddr, + struct interface *, int, u_short, time_t); +extern void trace_rip(char*, char*, struct sockaddr_in *, + struct interface *, struct rip *, int); +extern char *addrname(naddr, naddr, int); + +extern void rdisc_age(naddr); +extern void set_rdisc_mg(struct interface *, int); +extern void set_supplier(void); +extern void if_bad_rdisc(struct interface *); +extern void if_ok_rdisc(struct interface *); +extern void read_rip(int, struct interface *); +extern void read_rt(void); +extern void read_d(void); +extern void rdisc_adv(void); +extern void rdisc_sol(void); + +extern void sigalrm(int); +extern void sigterm(int); + +extern void sigtrace_on(int); +extern void sigtrace_off(int); + +extern void flush_kern(void); +extern void age(naddr); + +extern void ag_flush(naddr, naddr, void (*)(struct ag_info *)); +extern void ag_check(naddr, naddr, naddr, naddr, char, char, u_int, + u_short, u_short, void (*)(struct ag_info *)); +extern void del_static(naddr, naddr, int); +extern void del_redirects(naddr, time_t); +extern struct rt_entry *rtget(naddr, naddr); +extern struct rt_entry *rtfind(naddr); +extern void rtinit(void); +extern void rtadd(naddr, naddr, naddr, naddr, + int, u_short, u_int, struct interface *); +extern void rtchange(struct rt_entry *, u_int, naddr,naddr, int, u_short, + struct interface *ifp, time_t, char *); +extern void rtdelete(struct rt_entry *); +extern void rtbad_sub(struct rt_entry *); +extern void rtswitch(struct rt_entry *, struct rt_spare *); +extern void rtbad(struct rt_entry *); + + +#define S_ADDR(x) (((struct sockaddr_in *)(x))->sin_addr.s_addr) +#define INFO_DST(I) ((I)->rti_info[RTAX_DST]) +#define INFO_GATE(I) ((I)->rti_info[RTAX_GATEWAY]) +#define INFO_MASK(I) ((I)->rti_info[RTAX_NETMASK]) +#define INFO_IFA(I) ((I)->rti_info[RTAX_IFA]) +#define INFO_IFP(I) ((I)->rti_info[RTAX_IFP]) +#define INFO_AUTHOR(I) ((I)->rti_info[RTAX_AUTHOR]) +#define INFO_BRD(I) ((I)->rti_info[RTAX_BRD]) +void rt_xaddrs(struct rt_addrinfo *, struct sockaddr *, struct sockaddr *, + int); + +extern naddr std_mask(naddr); +extern naddr ripv1_mask_net(naddr, struct interface *); +extern naddr ripv1_mask_host(naddr,struct interface *); +#define on_net(a,net,mask) (((ntohl(a) ^ (net)) & (mask)) == 0) +extern int check_dst(naddr); +extern void addrouteforif(register struct interface *); +extern void ifinit(void); +extern int walk_bad(struct radix_node *, struct walkarg *); +extern int if_ok(struct interface *, char *); +extern void if_sick(struct interface *); +extern void if_bad(struct interface *); +extern struct interface *ifwithaddr(naddr, int, int); +extern struct interface *ifwithname(char *, naddr); +extern struct interface *ifwithindex(u_short); +extern struct interface *iflookup(naddr); diff --git a/sbin/routed/if.c b/sbin/routed/if.c new file mode 100644 index 000000000000..539b07323e3a --- /dev/null +++ b/sbin/routed/if.c @@ -0,0 +1,1153 @@ +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if !defined(lint) && !defined(sgi) && !defined(__NetBSD__) +static char sccsid[] = "@(#)if.c 8.1 (Berkeley) 6/5/93"; +#elif defined(__NetBSD__) +static char rcsid[] = "$NetBSD$"; +#endif +#ident "$Revision: 1.17 $" + +#include "defs.h" +#include "pathnames.h" + +struct interface *ifnet; /* all interfaces */ +int tot_interfaces; /* # of remote and local interfaces */ +int rip_interfaces; /* # of interfaces doing RIP */ +int foundloopback; /* valid flag for loopaddr */ +naddr loopaddr; /* our address on loopback */ + +struct timeval ifinit_timer; + +int have_ripv1_out; /* have a RIPv1 interface */ +int have_ripv1_in; + + +/* Find the interface with an address + */ +struct interface * +ifwithaddr(naddr addr, + int bcast, /* notice IFF_BROADCAST address */ + int remote) /* include IS_REMOTE interfaces */ +{ + struct interface *ifp, *possible = 0; + + for (ifp = ifnet; ifp; ifp = ifp->int_next) { + if (ifp->int_addr == addr + || ((ifp->int_if_flags & IFF_BROADCAST) + && ifp->int_brdaddr == addr + && bcast)) { + if ((ifp->int_state & IS_REMOTE) && !remote) + continue; + + if (!(ifp->int_state & IS_BROKE) + && !(ifp->int_state & IS_PASSIVE)) + return ifp; + + possible = ifp; + } + } + + return possible; +} + + +/* find the interface with a name + */ +struct interface * +ifwithname(char *name, /* "ec0" or whatever */ + naddr addr) /* 0 or network address */ +{ + struct interface *ifp; + + + for (ifp = ifnet; 0 != ifp; ifp = ifp->int_next) { + if (!strcmp(ifp->int_name, name) + && (ifp->int_addr == addr + || (addr == 0 && !(ifp->int_state & IS_ALIAS)))) + return ifp; + } + return 0; +} + + +struct interface * +ifwithindex(u_short index) +{ + struct interface *ifp; + + + for (ifp = ifnet; 0 != ifp; ifp = ifp->int_next) { + if (ifp->int_index == index) + return ifp; + } + return 0; +} + + +/* Find an interface from which the specified address + * should have come from. Used for figuring out which + * interface a packet came in on -- for tracing. + */ +struct interface * +iflookup(naddr addr) +{ + struct interface *ifp, *maybe; + + maybe = 0; + for (ifp = ifnet; ifp; ifp = ifp->int_next) { + if (ifp->int_if_flags & IFF_POINTOPOINT) { + if (ifp->int_dstaddr == addr) + /* finished with a match */ + return ifp; + + } else { + /* finished with an exact match */ + if (ifp->int_addr == addr) + return ifp; + if ((ifp->int_if_flags & IFF_BROADCAST) + && ifp->int_brdaddr == addr) + return ifp; + + /* Look for the longest approximate match. + */ + if (on_net(addr, ifp->int_net, ifp->int_mask) + && (maybe == 0 + || ifp->int_mask > maybe->int_mask)) + maybe = ifp; + } + } + + return maybe; +} + + +/* Return the classical netmask for an IP address. + */ +naddr +std_mask(naddr addr) /* in network order */ +{ + NTOHL(addr); /* was a host, not a network */ + + if (addr == 0) /* default route has mask 0 */ + return 0; + if (IN_CLASSA(addr)) + return IN_CLASSA_NET; + if (IN_CLASSB(addr)) + return IN_CLASSB_NET; + return IN_CLASSC_NET; +} + + +/* Find the netmask that would be inferred by RIPv1 listeners + * on the given interface for a given network. + * If no interface is specified, look for the best fitting interface. + */ +naddr +ripv1_mask_net(naddr addr, /* in network byte order */ + struct interface *ifp) /* as seen on this interface */ +{ + naddr mask = 0; + + if (addr == 0) /* default always has 0 mask */ + return mask; + + if (ifp != 0) { + /* If the target network is that of the associated interface + * on which it arrived, then use the netmask of the interface. + */ + if (on_net(addr, ifp->int_net, ifp->int_std_mask)) + mask = ifp->int_ripv1_mask; + + } else { + /* Examine all interfaces, and if it the target seems + * to have the same network number of an interface, use the + * netmask of that interface. If there is more than one + * such interface, prefer the interface with the longest + * match. + */ + for (ifp = ifnet; ifp != 0; ifp = ifp->int_next) { + if (on_net(addr, ifp->int_std_net, ifp->int_std_mask) + && ifp->int_ripv1_mask > mask) + mask = ifp->int_ripv1_mask; + } + } + + /* Otherwise, make the classic A/B/C guess. + */ + if (mask == 0) + mask = std_mask(addr); + + return mask; +} + + +naddr +ripv1_mask_host(naddr addr, /* in network byte order */ + struct interface *ifp) /* as seen on this interface */ +{ + naddr mask = ripv1_mask_net(addr, ifp); + + + /* If the computed netmask does not mask the address, + * then assume it is a host address + */ + if ((ntohl(addr) & ~mask) != 0) + mask = HOST_MASK; + return mask; +} + + +/* See if a IP address looks reasonable as a destination + */ +int /* 0=bad */ +check_dst(naddr addr) +{ + NTOHL(addr); + + if (IN_CLASSA(addr)) { + if (addr == 0) + return 1; /* default */ + + addr >>= IN_CLASSA_NSHIFT; + return (addr != 0 && addr != IN_LOOPBACKNET); + } + + return (IN_CLASSB(addr) || IN_CLASSC(addr)); +} + + +/* Delete an interface. + */ +static void +ifdel(struct interface *ifp) +{ + struct ip_mreq m; + struct interface *ifp1; + + + trace_if("Del", ifp); + + ifp->int_state |= IS_BROKE; + + /* unlink the interface + */ + if (rip_sock_mcast == ifp) + rip_sock_mcast = 0; + if (ifp->int_next != 0) + ifp->int_next->int_prev = ifp->int_prev; + if (ifp->int_prev != 0) + ifp->int_prev->int_next = ifp->int_next; + else + ifnet = ifp->int_next; + + if (!(ifp->int_state & IS_ALIAS)) { + /* delete aliases + */ + for (ifp1 = ifnet; 0 != ifp1; ifp1 = ifp1->int_next) { + if (ifp1 != ifp + && !strcmp(ifp->int_name, ifp1->int_name)) + ifdel(ifp1); + } + + if ((ifp->int_if_flags & IFF_MULTICAST) +#ifdef MCAST_PPP_BUG + && !(ifp->int_if_flags & IFF_POINTOPOINT) +#endif + && rip_sock >= 0) { + m.imr_multiaddr.s_addr = htonl(INADDR_RIP_GROUP); + m.imr_interface.s_addr = ((ifp->int_if_flags + & IFF_POINTOPOINT) + ? ifp->int_dstaddr + : ifp->int_addr); + if (setsockopt(rip_sock,IPPROTO_IP,IP_DROP_MEMBERSHIP, + &m, sizeof(m)) < 0 + && errno != EADDRNOTAVAIL + && !TRACEACTIONS) + LOGERR("setsockopt(IP_DROP_MEMBERSHIP RIP)"); + } + if (ifp->int_rip_sock >= 0) { + (void)close(ifp->int_rip_sock); + ifp->int_rip_sock = -1; + fix_select(); + } + + tot_interfaces--; + if (!IS_RIP_OFF(ifp->int_state)) + rip_interfaces--; + + /* Zap all routes associated with this interface. + * Assume routes just using gateways beyond this interface will + * timeout naturally, and have probably already died. + */ + (void)rn_walktree(rhead, walk_bad, 0); + + set_rdisc_mg(ifp, 0); + if_bad_rdisc(ifp); + } + + free(ifp); +} + + +/* Mark an interface ill. + */ +void +if_sick(struct interface *ifp) +{ + if (0 == (ifp->int_state & (IS_SICK | IS_BROKE))) { + ifp->int_state |= IS_SICK; + trace_if("Chg", ifp); + + LIM_SEC(ifinit_timer, now.tv_sec+CHECK_BAD_INTERVAL); + } +} + + +/* Mark an interface dead. + */ +void +if_bad(struct interface *ifp) +{ + struct interface *ifp1; + + + if (ifp->int_state & IS_BROKE) + return; + + LIM_SEC(ifinit_timer, now.tv_sec+CHECK_BAD_INTERVAL); + + ifp->int_state |= (IS_BROKE | IS_SICK); + ifp->int_state &= ~(IS_RIP_QUERIED | IS_ACTIVE); + ifp->int_data.ts = 0; + + trace_if("Chg", ifp); + + if (!(ifp->int_state & IS_ALIAS)) { + for (ifp1 = ifnet; 0 != ifp1; ifp1 = ifp1->int_next) { + if (ifp1 != ifp + && !strcmp(ifp->int_name, ifp1->int_name)) + if_bad(ifp1); + } + (void)rn_walktree(rhead, walk_bad, 0); + if_bad_rdisc(ifp); + } +} + + +/* Mark an interface alive + */ +int /* 1=it was dead */ +if_ok(struct interface *ifp, + char *type) +{ + struct interface *ifp1; + + + if (!(ifp->int_state & IS_BROKE)) { + if (ifp->int_state & IS_SICK) { + trace_act("%sinterface %s to %s working better\n", + type, + ifp->int_name, naddr_ntoa(ifp->int_addr)); + ifp->int_state &= ~IS_SICK; + } + return 0; + } + + msglog("%sinterface %s to %s restored", + type, ifp->int_name, naddr_ntoa(ifp->int_addr)); + ifp->int_state &= ~(IS_BROKE | IS_SICK); + ifp->int_data.ts = 0; + + if (!(ifp->int_state & IS_ALIAS)) { + for (ifp1 = ifnet; 0 != ifp1; ifp1 = ifp1->int_next) { + if (ifp1 != ifp + && !strcmp(ifp->int_name, ifp1->int_name)) + if_ok(ifp1, type); + } + if_ok_rdisc(ifp); + } + return 1; +} + + +/* disassemble routing message + */ +void +rt_xaddrs(struct rt_addrinfo *info, + struct sockaddr *sa, + struct sockaddr *lim, + int addrs) +{ + int i; +#ifdef _HAVE_SA_LEN + static struct sockaddr sa_zero; +#endif +#ifdef sgi +#define ROUNDUP(a) ((a) > 0 ? (1 + (((a) - 1) | (sizeof(__uint64_t) - 1))) \ + : sizeof(__uint64_t)) +#else +#define ROUNDUP(a) ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) \ + : sizeof(long)) +#endif + + + bzero(info, sizeof(*info)); + info->rti_addrs = addrs; + for (i = 0; i < RTAX_MAX && sa < lim; i++) { + if ((addrs & (1 << i)) == 0) + continue; +#ifdef _HAVE_SA_LEN + info->rti_info[i] = (sa->sa_len != 0) ? sa : &sa_zero; + sa = (struct sockaddr *)((char*)(sa) + + ROUNDUP(sa->sa_len)); +#else + info->rti_info[i] = sa; + sa = (struct sockaddr *)((char*)(sa) + + ROUNDUP(_FAKE_SA_LEN_DST(sa))); +#endif + } +} + + +/* Find the network interfaces which have configured themselves. + * This must be done regularly, if only for extra addresses + * that come and go on interfaces. + */ +void +ifinit(void) +{ + static char *sysctl_buf; + static size_t sysctl_buf_size = 0; + uint complaints = 0; + static u_int prev_complaints = 0; +# define COMP_NOT_INET 0x001 +# define COMP_WIERD 0x002 +# define COMP_NOADDR 0x004 +# define COMP_BADADDR 0x008 +# define COMP_NODST 0x010 +# define COMP_NOBADR 0x020 +# define COMP_NOMASK 0x040 +# define COMP_DUP 0x080 +# define COMP_BAD_METRIC 0x100 +# define COMP_NETMASK 0x200 + + struct interface ifs, ifs0, *ifp, *ifp1; + struct rt_entry *rt; + size_t needed; + int mib[6]; + struct if_msghdr *ifm; + struct ifa_msghdr *ifam, *ifam_lim, *ifam2; + struct sockaddr_dl *sdl; + int in, ierr, out, oerr; + struct intnet *intnetp; + struct rt_addrinfo info; +#ifdef SIOCGIFMETRIC + struct ifreq ifr; +#endif + + + ifinit_timer.tv_sec = now.tv_sec + (supplier + ? CHECK_ACT_INTERVAL + : CHECK_QUIET_INTERVAL); + + /* mark all interfaces so we can get rid of thost that disappear */ + for (ifp = ifnet; 0 != ifp; ifp = ifp->int_next) + ifp->int_state &= ~(IS_CHECKED | IS_DUP); + + /* Fetch the interface list, without too many system calls + * since we do it repeatedly. + */ + mib[0] = CTL_NET; + mib[1] = PF_ROUTE; + mib[2] = 0; + mib[3] = AF_INET; + mib[4] = NET_RT_IFLIST; + mib[5] = 0; + for (;;) { + if ((needed = sysctl_buf_size) != 0) { + if (sysctl(mib, 6, sysctl_buf,&needed, 0, 0) >= 0) + break; + if (errno != ENOMEM && errno != EFAULT) + BADERR(1, "ifinit: get interface table"); + free(sysctl_buf); + needed = 0; + } + if (sysctl(mib, 6, 0, &needed, 0, 0) < 0) + BADERR(1,"ifinit: route-sysctl-estimate"); + sysctl_buf = rtmalloc(sysctl_buf_size = needed, "ifinit"); + } + + ifam_lim = (struct ifa_msghdr *)(sysctl_buf + needed); + for (ifam = (struct ifa_msghdr *)sysctl_buf; + ifam < ifam_lim; + ifam = ifam2) { + + ifam2 = (struct ifa_msghdr*)((char*)ifam + ifam->ifam_msglen); + + if (ifam->ifam_type == RTM_IFINFO) { + ifm = (struct if_msghdr *)ifam; + /* make prototype structure for the IP aliases + */ + bzero(&ifs0, sizeof(ifs0)); + ifs0.int_rip_sock = -1; + ifs0.int_index = ifm->ifm_index; + ifs0.int_if_flags = ifm->ifm_flags; + ifs0.int_state = IS_CHECKED; + ifs0.int_act_time = now.tv_sec; + ifs0.int_data.ts = now.tv_sec; + ifs0.int_data.ipackets = ifm->ifm_data.ifi_ipackets; + ifs0.int_data.ierrors = ifm->ifm_data.ifi_ierrors; + ifs0.int_data.opackets = ifm->ifm_data.ifi_opackets; + ifs0.int_data.oerrors = ifm->ifm_data.ifi_oerrors; +#ifdef sgi + ifs0.int_data.odrops = ifm->ifm_data.ifi_odrops; +#endif + sdl = (struct sockaddr_dl *)(ifm + 1); + sdl->sdl_data[sdl->sdl_nlen] = 0; + continue; + } + if (ifam->ifam_type != RTM_NEWADDR) { + logbad(1,"ifinit: out of sync"); + continue; + } + + rt_xaddrs(&info, (struct sockaddr *)(ifam+1), + (struct sockaddr *)ifam2, + ifam->ifam_addrs); + + if (INFO_IFA(&info) == 0) { + if (iff_alive(ifs.int_if_flags)) { + if (!(prev_complaints & COMP_NOADDR)) + msglog("%s has no address", + sdl->sdl_data); + complaints |= COMP_NOADDR; + } + continue; + } + if (INFO_IFA(&info)->sa_family != AF_INET) { + if (iff_alive(ifs.int_if_flags)) { + if (!(prev_complaints & COMP_NOT_INET)) + trace_act("%s: not AF_INET\n", + sdl->sdl_data); + complaints |= COMP_NOT_INET; + } + continue; + } + + bcopy(&ifs0, &ifs, sizeof(ifs0)); + ifs0.int_state |= IS_ALIAS; /* next will be an alias */ + + ifs.int_addr = S_ADDR(INFO_IFA(&info)); + + if (ntohl(ifs.int_addr)>>24 == 0 + || ntohl(ifs.int_addr)>>24 == 0xff) { + if (iff_alive(ifs.int_if_flags)) { + if (!(prev_complaints & COMP_BADADDR)) + msglog("%s has a bad address", + sdl->sdl_data); + complaints |= COMP_BADADDR; + } + continue; + } + + if (ifs.int_if_flags & IFF_BROADCAST) { + if (INFO_MASK(&info) == 0) { + if (iff_alive(ifs.int_if_flags)) { + if (!(prev_complaints & COMP_NOMASK)) + msglog("%s has no netmask", + sdl->sdl_data); + complaints |= COMP_NOMASK; + } + continue; + } + ifs.int_dstaddr = ifs.int_addr; + ifs.int_mask = ntohl(S_ADDR(INFO_MASK(&info))); + ifs.int_ripv1_mask = ifs.int_mask; + ifs.int_net = ntohl(ifs.int_addr) & ifs.int_mask; + ifs.int_std_mask = std_mask(ifs.int_addr); + if (ifs.int_mask != ifs.int_std_mask) + ifs.int_state |= IS_SUBNET; + + if (INFO_BRD(&info) == 0) { + if (iff_alive(ifs.int_if_flags)) { + if (!(prev_complaints & COMP_NOBADR)) + msglog("%s has no" + " broadcast address", + sdl->sdl_data); + complaints |= COMP_NOBADR; + } + continue; + } + ifs.int_brdaddr = S_ADDR(INFO_BRD(&info)); + + } else if (ifs.int_if_flags & IFF_POINTOPOINT) { + if (INFO_BRD(&info) == 0 + || INFO_BRD(&info)->sa_family != AF_INET) { + if (iff_alive(ifs.int_if_flags)) { + if (!(prev_complaints & COMP_NODST)) + msglog("%s has a bad" + " destination address", + sdl->sdl_data); + complaints |= COMP_NODST; + } + continue; + } + ifs.int_dstaddr = S_ADDR(INFO_BRD(&info)); + if (ntohl(ifs.int_dstaddr)>>24 == 0 + || ntohl(ifs.int_dstaddr)>>24 == 0xff) { + if (iff_alive(ifs.int_if_flags)) { + if (!(prev_complaints & COMP_NODST)) + msglog("%s has a bad" + " destination address", + sdl->sdl_data); + complaints |= COMP_NODST; + } + continue; + } + ifs.int_mask = HOST_MASK; + ifs.int_ripv1_mask = ntohl(S_ADDR(INFO_MASK(&info))); + ifs.int_net = ntohl(ifs.int_dstaddr); + ifs.int_std_mask = std_mask(ifs.int_dstaddr); + + } else if (ifs.int_if_flags & IFF_LOOPBACK) { + ifs.int_state |= IS_PASSIVE | IS_NO_RIP; + ifs.int_dstaddr = ifs.int_addr; + ifs.int_mask = HOST_MASK; + ifs.int_ripv1_mask = HOST_MASK; + ifs.int_net = ntohl(ifs.int_dstaddr); + ifs.int_std_mask = std_mask(ifs.int_dstaddr); + if (!foundloopback) { + foundloopback = 1; + loopaddr = ifs.int_addr; + } + + } else { + if (!(prev_complaints & COMP_WIERD)) + trace_act("%s is neither broadcast" + " nor point-to-point nor loopback", + sdl->sdl_data); + complaints |= COMP_WIERD; + continue; + } + ifs.int_std_net = ifs.int_net & ifs.int_std_mask; + ifs.int_std_addr = htonl(ifs.int_std_net); + + /* Use a minimum metric of one. Treat the interface metric + * (default 0) as an increment to the hop count of one. + * + * The metric obtained from the routing socket dump of + * interface addresses is wrong. It is not set by the + * SIOCSIFMETRIC ioctl. + */ +#ifdef SIOCGIFMETRIC + strncpy(ifr.ifr_name, sdl->sdl_data, sizeof(ifr.ifr_name)); + if (ioctl(rt_sock, SIOCGIFMETRIC, &ifr) < 0) { + DBGERR(1, "ioctl(SIOCGIFMETRIC)"); + ifs.int_metric = 0; + } else { + ifs.int_metric = ifr.ifr_metric; + } +#else + ifs.int_metric = ifam->ifam_metric; +#endif + if (ifs.int_metric > HOPCNT_INFINITY) { + ifs.int_metric = 0; + if (!(prev_complaints & COMP_BAD_METRIC) + && iff_alive(ifs.int_if_flags)) { + complaints |= COMP_BAD_METRIC; + msglog("%s has a metric of %d", + sdl->sdl_data, ifs.int_metric); + } + } + + /* See if this is a familiar interface. + * If so, stop worrying about it if it is the same. + * Start it over if it now is to somewhere else, as happens + * frequently with PPP and SLIP. + */ + ifp = ifwithname(sdl->sdl_data, ((ifs.int_state & IS_ALIAS) + ? ifs.int_addr + : 0)); + if (ifp != 0) { + ifp->int_state |= IS_CHECKED; + + if (0 != ((ifp->int_if_flags ^ ifs.int_if_flags) + & (IFF_BROADCAST + | IFF_LOOPBACK + | IFF_POINTOPOINT + | IFF_MULTICAST)) + || 0 != ((ifp->int_state ^ ifs.int_state) + & IS_ALIAS) + || ifp->int_addr != ifs.int_addr + || ifp->int_brdaddr != ifs.int_brdaddr + || ifp->int_dstaddr != ifs.int_dstaddr + || ifp->int_mask != ifs.int_mask + || ifp->int_metric != ifs.int_metric) { + /* Forget old information about + * a changed interface. + */ + trace_act("interface %s has changed\n", + ifp->int_name); + ifdel(ifp); + ifp = 0; + } + } + + if (ifp != 0) { + /* The primary representative of an alias worries + * about how things are working. + */ + if (ifp->int_state & IS_ALIAS) + continue; + + /* note interfaces that have been turned off + */ + if (!iff_alive(ifs.int_if_flags)) { + if (iff_alive(ifp->int_if_flags)) { + msglog("interface %s to %s turned off", + ifp->int_name, + naddr_ntoa(ifp->int_addr)); + if_bad(ifp); + ifp->int_if_flags &= ~IFF_UP_RUNNING; + } + continue; + } + /* or that were off and are now ok */ + if (!iff_alive(ifp->int_if_flags)) { + ifp->int_if_flags |= IFF_UP_RUNNING; + (void)if_ok(ifp, ""); + } + + /* If it has been long enough, + * see if the interface is broken. + */ + if (now.tv_sec < ifp->int_data.ts+CHECK_BAD_INTERVAL) + continue; + + in = ifs.int_data.ipackets - ifp->int_data.ipackets; + ierr = ifs.int_data.ierrors - ifp->int_data.ierrors; + out = ifs.int_data.opackets - ifp->int_data.opackets; + oerr = ifs.int_data.oerrors - ifp->int_data.oerrors; +#ifdef sgi + /* Through at least IRIX 6.2, PPP and SLIP + * count packets dropped by the filters. + * But FDDI rings stuck non-operational count + * dropped packets as they wait for improvement. + */ + if (!(ifp->int_if_flags & IFF_POINTOPOINT)) + oerr += (ifs.int_data.odrops + - ifp->int_data.odrops); +#endif + /* If the interface just awoke, restart the counters. + */ + if (ifp->int_data.ts == 0) { + ifp->int_data = ifs.int_data; + continue; + } + ifp->int_data = ifs.int_data; + + /* Withhold judgement when the short error + * counters wrap or the interface is reset. + */ + if (ierr < 0 || in < 0 || oerr < 0 || out < 0) { + LIM_SEC(ifinit_timer, + now.tv_sec+CHECK_BAD_INTERVAL); + continue; + } + + /* Withhold judgement when there is no traffic + */ + if (in == 0 && out == 0 && ierr == 0 && oerr == 0) + continue; + + /* It is bad if input or output is not working. + * Require presistent problems before marking it dead. + */ + if ((in <= ierr && ierr > 0) + || (out <= oerr && oerr > 0)) { + if (!(ifp->int_state & IS_SICK)) { + trace_act("interface %s to %s" + " sick: in=%d ierr=%d" + " out=%d oerr=%d\n", + ifp->int_name, + naddr_ntoa(ifp->int_addr), + in, ierr, out, oerr); + if_sick(ifp); + continue; + } + if (!(ifp->int_state & IS_BROKE)) { + msglog("interface %s to %s bad:" + " in=%d ierr=%d out=%d oerr=%d", + ifp->int_name, + naddr_ntoa(ifp->int_addr), + in, ierr, out, oerr); + if_bad(ifp); + } + continue; + } + + /* otherwise, it is active and healthy + */ + ifp->int_act_time = now.tv_sec; + (void)if_ok(ifp, ""); + continue; + } + + /* This is a new interface. + * If it is dead, forget it. + */ + if (!iff_alive(ifs.int_if_flags)) + continue; + + /* See if it duplicates an existing interface. + */ + for (ifp = ifnet; 0 != ifp; ifp = ifp->int_next) { + if (ifp->int_mask != ifs.int_mask) + continue; + if (((ifp->int_addr != ifs.int_addr + && ifs.int_mask != HOST_MASK) + || (ifp->int_dstaddr != ifs.int_dstaddr + && ifs.int_mask == HOST_MASK))) + continue; + if (!iff_alive(ifp->int_if_flags)) + continue; + /* Let one of our real interfaces be marked + * passive. + */ + if ((ifp->int_state & IS_PASSIVE) + && !(ifp->int_state & IS_EXTERNAL)) + continue; + + /* It does duplicate an existing interface, + * so complain about it, mark the other one + * duplicated, and for get this one. + */ + if (!(prev_complaints & COMP_DUP)) { + complaints |= COMP_DUP; + msglog("%s is duplicated by %s at %s", + sdl->sdl_data, ifp->int_name, + naddr_ntoa(ifp->int_addr)); + } + ifp->int_state |= IS_DUP; + break; + } + if (ifp != 0) + continue; + + /* It is new and ok. So make it real + */ + strncpy(ifs.int_name, sdl->sdl_data, + MIN(sizeof(ifs.int_name)-1, sdl->sdl_nlen)); + get_parms(&ifs); + + /* Add it to the list of interfaces + */ + ifp = (struct interface *)rtmalloc(sizeof(*ifp), "ifinit"); + bcopy(&ifs, ifp, sizeof(*ifp)); + if (ifnet != 0) { + ifp->int_next = ifnet; + ifnet->int_prev = ifp; + } + ifnet = ifp; + trace_if("Add", ifp); + + /* Notice likely bad netmask. + */ + if (!(prev_complaints & COMP_NETMASK) + && !(ifp->int_if_flags & IFF_POINTOPOINT)) { + for (ifp1 = ifnet; 0 != ifp1; ifp1 = ifp1->int_next) { + if (ifp1->int_mask == ifp->int_mask) + continue; + if (ifp1->int_if_flags & IFF_POINTOPOINT) + continue; + if (on_net(ifp->int_addr, + ifp1->int_net, ifp1->int_mask) + || on_net(ifp1->int_addr, + ifp->int_net, ifp->int_mask)) { + msglog("possible netmask problem" + " betwen %s:%s and %s:%s", + ifp->int_name, + addrname(htonl(ifp->int_net), + ifp->int_mask, 1), + ifp1->int_name, + addrname(htonl(ifp1->int_net), + ifp1->int_mask, 1)); + complaints |= COMP_NETMASK; + } + } + } + + /* Count the # of directly connected networks. + */ + if (!(ifp->int_state & IS_ALIAS)) { + if (!(ifp->int_if_flags & IFF_LOOPBACK)) + tot_interfaces++; + if (!IS_RIP_OFF(ifp->int_state)) + rip_interfaces++; + } + + if_ok_rdisc(ifp); + rip_on(ifp); + } + + /* If we are multi-homed and have at least one interface + * listening to RIP, then output by default. + */ + if (!supplier_set && rip_interfaces > 1) + set_supplier(); + + /* If we are multi-homed, optionally advertise a route to + * our main address. + */ + if (advertise_mhome + || (tot_interfaces > 1 + && mhome + && (ifp = ifwithaddr(myaddr, 0, 0)) != 0 + && foundloopback)) { + advertise_mhome = 1; + rt = rtget(myaddr, HOST_MASK); + if (rt != 0) { + if (rt->rt_ifp != ifp + || rt->rt_router != loopaddr) { + rtdelete(rt); + rt = 0; + } else { + rtchange(rt, rt->rt_state | RS_MHOME, + loopaddr, loopaddr, + 0, 0, ifp, rt->rt_time, 0); + } + } + if (rt == 0) + rtadd(myaddr, HOST_MASK, loopaddr, loopaddr, + 0, 0, RS_MHOME, ifp); + } + + for (ifp = ifnet; ifp != 0; ifp = ifp1) { + ifp1 = ifp->int_next; /* because we may delete it */ + + /* Forget any interfaces that have disappeared. + */ + if (!(ifp->int_state & (IS_CHECKED | IS_REMOTE))) { + trace_act("interface %s has disappeared\n", + ifp->int_name); + ifdel(ifp); + continue; + } + + if ((ifp->int_state & IS_BROKE) + && !(ifp->int_state & IS_PASSIVE)) + LIM_SEC(ifinit_timer, now.tv_sec+CHECK_BAD_INTERVAL); + + /* If we ever have a RIPv1 interface, assume we always will. + * It might come back if it ever goes away. + */ + if (!(ifp->int_state & IS_NO_RIPV1_OUT) && supplier) + have_ripv1_out = 1; + if (!(ifp->int_state & IS_NO_RIPV1_IN)) + have_ripv1_in = 1; + } + + for (ifp = ifnet; ifp != 0; ifp = ifp->int_next) { + /* Ensure there is always a network route for interfaces, + * after any dead interfaces have been deleted, which + * might affect routes for point-to-point links. + */ + addrouteforif(ifp); + + /* Add routes to the local end of point-to-point interfaces + * using loopback. + */ + if ((ifp->int_if_flags & IFF_POINTOPOINT) + && !(ifp->int_state & IS_REMOTE) + && foundloopback) { + /* Delete any routes to the network address through + * foreign routers. Remove even static routes. + */ + del_static(ifp->int_addr, HOST_MASK, 0); + rt = rtget(ifp->int_addr, HOST_MASK); + if (rt != 0 && rt->rt_router != loopaddr) { + rtdelete(rt); + rt = 0; + } + if (rt != 0) { + if (!(rt->rt_state & RS_LOCAL) + || rt->rt_metric > ifp->int_metric) { + ifp1 = ifp; + } else { + ifp1 = rt->rt_ifp; + } + rtchange(rt,((rt->rt_state & ~RS_NET_SYN) + | (RS_IF|RS_LOCAL)), + loopaddr, loopaddr, + 0, 0, ifp1, rt->rt_time, 0); + } else { + rtadd(ifp->int_addr, HOST_MASK, + loopaddr, loopaddr, + 0, 0, (RS_IF | RS_LOCAL), ifp); + } + } + } + + /* add the authority routes */ + for (intnetp = intnets; intnetp!=0; intnetp = intnetp->intnet_next) { + rt = rtget(intnetp->intnet_addr, intnetp->intnet_mask); + if (rt != 0 + && !(rt->rt_state & RS_NO_NET_SYN) + && !(rt->rt_state & RS_NET_INT)) { + rtdelete(rt); + rt = 0; + } + if (rt == 0) + rtadd(intnetp->intnet_addr, intnetp->intnet_mask, + loopaddr, loopaddr, intnetp->intnet_metric-1, + 0, RS_NET_SYN | RS_NET_INT, 0); + } + + prev_complaints = complaints; +} + + +static void +check_net_syn(struct interface *ifp) +{ + struct rt_entry *rt; + + + /* Turn on the need to automatically synthesize a network route + * for this interface only if we are running RIPv1 on some other + * interface that is on a different class-A,B,or C network. + */ + if (have_ripv1_out || have_ripv1_in) { + ifp->int_state |= IS_NEED_NET_SYN; + rt = rtget(ifp->int_std_addr, ifp->int_std_mask); + if (rt != 0 + && 0 == (rt->rt_state & RS_NO_NET_SYN) + && (!(rt->rt_state & RS_NET_SYN) + || rt->rt_metric > ifp->int_metric)) { + rtdelete(rt); + rt = 0; + } + if (rt == 0) + rtadd(ifp->int_std_addr, ifp->int_std_mask, + ifp->int_addr, ifp->int_addr, + ifp->int_metric, 0, RS_NET_SYN, ifp); + + } else { + ifp->int_state &= ~IS_NEED_NET_SYN; + + rt = rtget(ifp->int_std_addr, + ifp->int_std_mask); + if (rt != 0 + && (rt->rt_state & RS_NET_SYN) + && rt->rt_ifp == ifp) + rtbad_sub(rt); + } +} + + +/* Add route for interface if not currently installed. + * Create route to other end if a point-to-point link, + * otherwise a route to this (sub)network. + */ +void +addrouteforif(struct interface *ifp) +{ + struct rt_entry *rt; + naddr dst, gate; + + + /* skip sick interfaces + */ + if (ifp->int_state & IS_BROKE) + return; + + /* If the interface on a subnet, then install a RIPv1 route to + * the network as well (unless it is sick). + */ + if (ifp->int_state & IS_SUBNET) + check_net_syn(ifp); + + if (ifp->int_state & IS_REMOTE) { + dst = ifp->int_addr; + gate = ifp->int_dstaddr; + /* If we are going to send packets to the gateway, + * it must be reachable using our physical interfaces + */ + if (!(ifp->int_state && IS_EXTERNAL) + && !rtfind(ifp->int_dstaddr) + && ifp->int_transitions == 0) { + msglog("unreachable gateway %s in " + _PATH_GATEWAYS" entry %s", + naddr_ntoa(gate), ifp->int_name); + return; + } + + } else { + dst = (0 != (ifp->int_if_flags & (IFF_POINTOPOINT + | IFF_LOOPBACK)) + ? ifp->int_dstaddr + : htonl(ifp->int_net)); + gate = ifp->int_addr; + } + + /* We are finished if the correct main interface route exists. + * The right route must be for the right interface, not synthesized + * from a subnet, be a "gateway" or not as appropriate, and so forth. + */ + del_static(dst, ifp->int_mask, 0); + rt = rtget(dst, ifp->int_mask); + if (rt != 0) { + if ((rt->rt_ifp != ifp + || rt->rt_router != ifp->int_addr) + && (!(ifp->int_state & IS_DUP) + || rt->rt_ifp == 0 + || (rt->rt_ifp->int_state & IS_BROKE))) { + rtdelete(rt); + rt = 0; + } else { + rtchange(rt, ((rt->rt_state | RS_IF) + & ~(RS_NET_SYN | RS_LOCAL)), + ifp->int_addr, ifp->int_addr, + ifp->int_metric, 0, ifp, now.tv_sec, 0); + } + } + if (rt == 0) { + if (ifp->int_transitions++ > 0) + trace_act("re-install interface %s\n", + ifp->int_name); + + rtadd(dst, ifp->int_mask, gate, gate, + ifp->int_metric, 0, RS_IF, ifp); + } +} diff --git a/sbin/routed/input.c b/sbin/routed/input.c new file mode 100644 index 000000000000..a854c41914e4 --- /dev/null +++ b/sbin/routed/input.c @@ -0,0 +1,741 @@ +/* + * Copyright (c) 1983, 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if !defined(lint) && !defined(sgi) && !defined(__NetBSD__) +static char sccsid[] = "@(#)input.c 8.1 (Berkeley) 6/5/93"; +#elif defined(__NetBSD__) +static char rcsid[] = "$NetBSD$"; +#endif +#ident "$Revision: 1.16 $" + +#include "defs.h" + +static void input(struct sockaddr_in *, struct interface*, struct rip *, int); +static void input_route(struct interface *, naddr, + naddr, naddr, naddr, struct netinfo *); + + +/* process RIP input + */ +void +read_rip(int sock, + struct interface *ifp) +{ + struct sockaddr_in from; + int fromlen, cc; + union pkt_buf inbuf; + + + for (;;) { + fromlen = sizeof(from); + cc = recvfrom(sock, &inbuf, sizeof(inbuf), 0, + (struct sockaddr*)&from, &fromlen); + if (cc <= 0) { + if (cc < 0 && errno != EWOULDBLOCK) + LOGERR("recvfrom(rip)"); + break; + } + if (fromlen != sizeof(struct sockaddr_in)) + logbad(1,"impossible recvfrom(rip) fromlen=%d", + fromlen); + + input(&from, ifp, &inbuf.rip, cc); + } +} + + +/* Process a RIP packet + */ +static void +input(struct sockaddr_in *from, /* received from this IP address */ + struct interface *sifp, /* interface by which it arrived */ + struct rip *rip, + int size) +{ +# define FROM_NADDR from->sin_addr.s_addr + static naddr use_auth, bad_len, bad_mask; + static naddr unk_router, bad_router, bad_nhop; + + struct interface *aifp; /* interface if via 1 hop */ + struct rt_entry *rt; + struct netinfo *n, *lim; + struct interface *ifp1; + naddr gate, mask, v1_mask, dst, ddst_h; + int i; + + aifp = iflookup(from->sin_addr.s_addr); + if (sifp == 0) + sifp = aifp; + + if (sifp != 0) + sifp->int_state |= IS_ACTIVE; + + trace_rip("Recv", "from", from, sifp, rip, size); + + if (rip->rip_vers == 0) { + if (from->sin_addr.s_addr != bad_router) + msglog("RIP version 0, cmd %d, packet received" + " from %s", + rip->rip_cmd, naddr_ntoa(FROM_NADDR)); + bad_router = from->sin_addr.s_addr; + return; + } else if (rip->rip_vers > RIPv2) { + rip->rip_vers = RIPv2; + } + if (size > MAXPACKETSIZE) { + if (from->sin_addr.s_addr != bad_router) + msglog("packet at least %d bytes too long received" + " from %s", + size-MAXPACKETSIZE, naddr_ntoa(FROM_NADDR)); + bad_router = from->sin_addr.s_addr; + return; + } + + n = rip->rip_nets; + lim = (struct netinfo *)((char*)rip + size); + + /* Notice authentication. + * As required by section 4.2 in RFC 1723, discard authenticated + * RIPv2 messages, but only if configured for that silliness. + * + * RIPv2 authentication is lame, since snooping on the wire makes + * its simple passwords evident. Also, why authenticate queries? + * Why should a RIPv2 implementation with authentication disabled + * not be able to listen to RIPv2 packets with authenication, while + * RIPv1 systems will listen? Crazy! + */ + if (!auth_ok + && rip->rip_vers == RIPv2 + && n < lim && n->n_family == RIP_AF_AUTH) { + if (from->sin_addr.s_addr != use_auth) + msglog("RIPv2 message with authentication" + " from %s discarded", + naddr_ntoa(FROM_NADDR)); + use_auth = from->sin_addr.s_addr; + trace_pkt("discard authenticated RIPv2 message\n"); + return; + } + + switch (rip->rip_cmd) { + case RIPCMD_REQUEST: + /* did the request come from a router? + */ + if (from->sin_port == htons(RIP_PORT)) { + /* yes, ignore it if RIP is off so that it does not + * depend on us. + */ + if (rip_sock < 0) { + trace_pkt("ignore request while RIP off\n"); + return; + } + + /* Ignore the request if we talking to ourself + * (and not a remote gateway). + */ + if (ifwithaddr(FROM_NADDR, 0, 0) != 0) { + trace_pkt("discard our own RIP request\n"); + return; + } + } + + /* According to RFC 1723, we should ignore unathenticated + * queries. That is too silly to bother with. Sheesh! + * Are forwarding tables supposed to be secret? When + * a bad guy can infer them with test traffic? + * Maybe on firewalls you'd care, but not enough to + * give up the diagnostic facilities of remote probing. + */ + + if (n >= lim + || size%sizeof(*n) != sizeof(struct rip)%sizeof(*n)) { + if (from->sin_addr.s_addr != bad_len) + msglog("request of bad length (%d) from %s", + size, naddr_ntoa(FROM_NADDR)); + bad_len = from->sin_addr.s_addr; + } + for (; n < lim; n++) { + n->n_metric = ntohl(n->n_metric); + + /* A single entry with family RIP_AF_UNSPEC and + * metric HOPCNT_INFINITY means "all routes". + * We respond to routers only if we are acting + * as a supplier, or to anyone other than a router + * (i.e. a query). + */ + if (n->n_family == RIP_AF_UNSPEC + && n->n_metric == HOPCNT_INFINITY + && n == rip->rip_nets + && n+1 == lim) { + if (from->sin_port != htons(RIP_PORT)) { + /* Answer a query from a utility + * program with all we know. + */ + supply(from, sifp, OUT_QUERY, 0, + rip->rip_vers); + return; + } + /* A router trying to prime its tables. + * Filter the answer in the about same way + * broadcasts are filtered. + * + * Only answer a router if we are a supplier + * to keep an unwary host that is just starting + * from picking us as a router. Respond with + * RIPv1 instead of RIPv2 if that is what we + * are broadcasting on the interface to keep + * the remote router from getting the wrong + * initial idea of the routes we send. + */ + if (!supplier + || aifp == 0 + || (aifp->int_state & IS_PASSIVE) + || (aifp->int_state & IS_ALIAS) + || ((aifp->int_state & IS_NO_RIPV1_OUT) + && (aifp->int_state&IS_NO_RIPV2_OUT))) + return; + + supply(from, aifp, OUT_UNICAST, 0, + (aifp->int_state&IS_NO_RIPV1_OUT) + ? RIPv2 : RIPv1); + return; + } + + if (n->n_family != RIP_AF_INET) { + if (from->sin_addr.s_addr != bad_router) + msglog("request from %s" + " for unsupported (af %d) %s", + naddr_ntoa(FROM_NADDR), + ntohs(n->n_family), + naddr_ntoa(n->n_dst)); + bad_router = from->sin_addr.s_addr; + return; + } + + dst = n->n_dst; + if (!check_dst(dst)) { + if (from->sin_addr.s_addr != bad_router) + msglog("bad queried destination" + " %s from %s", + naddr_ntoa(dst), + naddr_ntoa(FROM_NADDR)); + bad_router = from->sin_addr.s_addr; + return; + } + + if (rip->rip_vers == RIPv1 + || 0 == (mask = ntohl(n->n_mask)) + || 0 != (ntohl(dst) & ~mask)) + mask = ripv1_mask_host(dst,sifp); + + rt = rtget(dst, mask); + if (!rt && dst != RIP_DEFAULT) + rt = rtfind(n->n_dst); + + n->n_tag = 0; + n->n_nhop = 0; + if (rip->rip_vers == RIPv1) { + n->n_mask = 0; + } else { + n->n_mask = mask; + } + if (rt == 0) { + n->n_metric = HOPCNT_INFINITY; + } else { + n->n_metric = rt->rt_metric+1; + n->n_metric += (sifp!=0)?sifp->int_metric : 1; + if (n->n_metric > HOPCNT_INFINITY) + n->n_metric = HOPCNT_INFINITY; + if (rip->rip_vers != RIPv1) { + n->n_tag = rt->rt_tag; + if (sifp != 0 + && on_net(rt->rt_gate, + sifp->int_net, + sifp->int_mask) + && rt->rt_gate != sifp->int_addr) + n->n_nhop = rt->rt_gate; + } + } + HTONL(n->n_metric); + } + /* Answer about specific routes. + * Only answer a router if we are a supplier + * to keep an unwary host that is just starting + * from picking us an a router. + */ + rip->rip_cmd = RIPCMD_RESPONSE; + rip->rip_res1 = 0; + if (rip->rip_vers != RIPv1) + rip->rip_vers = RIPv2; + if (from->sin_port != htons(RIP_PORT)) { + /* query */ + (void)output(OUT_QUERY, from, sifp, rip, size); + } else if (supplier) { + (void)output(OUT_UNICAST, from, sifp, rip, size); + } + return; + + case RIPCMD_TRACEON: + case RIPCMD_TRACEOFF: + /* verify message came from a privileged port */ + if (ntohs(from->sin_port) > IPPORT_RESERVED) { + msglog("trace command from untrusted port on %s", + naddr_ntoa(FROM_NADDR)); + return; + } + if (aifp == 0) { + msglog("trace command from unknown router %s", + naddr_ntoa(FROM_NADDR)); + return; + } + if (rip->rip_cmd == RIPCMD_TRACEON) { + rip->rip_tracefile[size-4] = '\0'; + trace_on((char*)rip->rip_tracefile, 0); + } else { + trace_off("tracing turned off by %s\n", + naddr_ntoa(FROM_NADDR)); + } + return; + + case RIPCMD_RESPONSE: + if (size%sizeof(*n) != sizeof(struct rip)%sizeof(*n)) { + if (from->sin_addr.s_addr != bad_len) + msglog("response of bad length (%d) from %s", + size, naddr_ntoa(FROM_NADDR)); + bad_len = from->sin_addr.s_addr; + } + + /* verify message came from a router */ + if (from->sin_port != ntohs(RIP_PORT)) { + trace_pkt("discard RIP response from unknown port\n"); + return; + } + + if (rip_sock < 0) { + trace_pkt("discard response while RIP off\n"); + return; + } + + /* Are we talking to ourself or a remote gateway? + */ + ifp1 = ifwithaddr(FROM_NADDR, 0, 1); + if (ifp1) { + if (ifp1->int_state & IS_REMOTE) { + if (ifp1->int_state & IS_PASSIVE) { + msglog("bogus input from %s on" + " supposedly passive %s", + naddr_ntoa(FROM_NADDR), + ifp1->int_name); + } else { + ifp1->int_act_time = now.tv_sec; + if (if_ok(ifp1, "remote ")) + addrouteforif(ifp1); + } + } else { + trace_pkt("discard our own RIP response\n"); + } + return; + } + + /* Check the router from which message originated. We accept + * routing packets from routers directly connected via + * broadcast or point-to-point networks, and from + * those listed in /etc/gateways. + */ + if (!aifp) { + if (from->sin_addr.s_addr != unk_router) + msglog("discard packet from unknown router %s" + " or via unidentified interface", + naddr_ntoa(FROM_NADDR)); + unk_router = from->sin_addr.s_addr; + return; + } + if (aifp->int_state & IS_PASSIVE) { + trace_act("discard packet from %s" + " via passive interface %s\n", + naddr_ntoa(FROM_NADDR), + aifp->int_name); + return; + } + + /* Check required version + */ + if (((aifp->int_state & IS_NO_RIPV1_IN) + && rip->rip_vers == RIPv1) + || ((aifp->int_state & IS_NO_RIPV2_IN) + && rip->rip_vers != RIPv1)) { + trace_pkt("discard RIPv%d response\n", + rip->rip_vers); + return; + } + + /* Ignore routes via dead interface. + */ + if (aifp->int_state & IS_BROKE) { + trace_pkt("discard response via broken interface %s\n", + aifp->int_name); + return; + } + + /* Authenticate the packet if we have a secret. + */ + if (aifp->int_passwd[0] != '\0') { + if (n >= lim + || n->n_family != RIP_AF_AUTH + || ((struct netauth*)n)->a_type != RIP_AUTH_PW) { + if (from->sin_addr.s_addr != use_auth) + msglog("missing password from %s", + naddr_ntoa(FROM_NADDR)); + use_auth = from->sin_addr.s_addr; + return; + + } else if (0 != bcmp(((struct netauth*)n)->au.au_pw, + aifp->int_passwd, + sizeof(aifp->int_passwd))) { + if (from->sin_addr.s_addr != use_auth) + msglog("bad password from %s", + naddr_ntoa(FROM_NADDR)); + use_auth = from->sin_addr.s_addr; + return; + } + } + + for (; n < lim; n++) { + if (n->n_family == RIP_AF_AUTH) + continue; + + NTOHL(n->n_metric); + dst = n->n_dst; + if (n->n_family != RIP_AF_INET + && (n->n_family != RIP_AF_UNSPEC + || dst != RIP_DEFAULT)) { + if (from->sin_addr.s_addr != bad_router) + msglog("route from %s to unsupported" + " address family %d," + " destination %s", + naddr_ntoa(FROM_NADDR), + n->n_family, + naddr_ntoa(dst)); + bad_router = from->sin_addr.s_addr; + continue; + } + if (!check_dst(dst)) { + if (from->sin_addr.s_addr != bad_router) + msglog("bad destination %s from %s", + naddr_ntoa(dst), + naddr_ntoa(FROM_NADDR)); + bad_router = from->sin_addr.s_addr; + return; + } + if (n->n_metric == 0 + || n->n_metric > HOPCNT_INFINITY) { + if (from->sin_addr.s_addr != bad_router) + msglog("bad metric %d from %s" + " for destination %s", + n->n_metric, + naddr_ntoa(FROM_NADDR), + naddr_ntoa(dst)); + bad_router = from->sin_addr.s_addr; + return; + } + + /* Notice the next-hop. + */ + gate = from->sin_addr.s_addr; + if (n->n_nhop != 0) { + if (rip->rip_vers == RIPv2) { + n->n_nhop = 0; + } else { + /* Use it only if it is valid. */ + if (on_net(n->n_nhop, + aifp->int_net, aifp->int_mask) + && check_dst(n->n_nhop)) { + gate = n->n_nhop; + } else { + if (bad_nhop != from->sin_addr.s_addr) + msglog("router %s to %s has" + " bad next hop %s", + naddr_ntoa(FROM_NADDR), + naddr_ntoa(dst), + naddr_ntoa(n->n_nhop)); + bad_nhop = from->sin_addr.s_addr; + n->n_nhop = 0; + } + } + } + + if (rip->rip_vers == RIPv1 + || 0 == (mask = ntohl(n->n_mask))) { + mask = ripv1_mask_host(dst,aifp); + } else if ((ntohl(dst) & ~mask) != 0) { + if (bad_mask != from->sin_addr.s_addr) { + msglog("router %s sent bad netmask" + " %#x with %s", + naddr_ntoa(FROM_NADDR), + mask, + naddr_ntoa(dst)); + bad_mask = from->sin_addr.s_addr; + } + continue; + } + if (rip->rip_vers == RIPv1) + n->n_tag = 0; + + /* Adjust metric according to incoming interface.. + */ + n->n_metric += aifp->int_metric; + if (n->n_metric > HOPCNT_INFINITY) + n->n_metric = HOPCNT_INFINITY; + + /* Recognize and ignore a default route we faked + * which is being sent back to us by a machine with + * broken split-horizon. + * Be a little more paranoid than that, and reject + * default routes with the same metric we advertised. + */ + if (aifp->int_d_metric != 0 + && dst == RIP_DEFAULT + && n->n_metric >= aifp->int_d_metric) + continue; + + /* We can receive aggregated RIPv2 routes that must + * be broken down before they are transmitted by + * RIPv1 via an interface on a subnet. + * We might also receive the same routes aggregated + * via other RIPv2 interfaces. + * This could cause duplicate routes to be sent on + * the RIPv1 interfaces. "Longest matching variable + * length netmasks" lets RIPv2 listeners understand, + * but breaking down the aggregated routes for RIPv1 + * listeners can produce duplicate routes. + * + * Breaking down aggregated routes here bloats + * the daemon table, but does not hurt the kernel + * table, since routes are always aggregated for + * the kernel. + * + * Notice that this does not break down network + * routes corresponding to subnets. This is part + * of the defense against RS_NET_SYN. + */ + if (have_ripv1_out + && (v1_mask = ripv1_mask_net(dst,0)) > mask + && (((rt = rtget(dst,mask)) == 0 + || !(rt->rt_state & RS_NET_SYN)))) { + ddst_h = v1_mask & -v1_mask; + i = (v1_mask & ~mask)/ddst_h; + if (i >= 511) { + /* Punt if we would have to generate + * an unreasonable number of routes. + */ +#ifdef DEBUG + msglog("accept %s from %s as 1" + " instead of %d routes", + addrname(dst,mask,0), + naddr_ntoa(FROM_NADDR), + i+1); +#endif + i = 0; + } else { + mask = v1_mask; + } + } else { + i = 0; + } + + for (;;) { + input_route(aifp, FROM_NADDR, + dst, mask, gate, n); + if (i-- == 0) + break; + dst = htonl(ntohl(dst) + ddst_h); + } + } + break; + } +} + + +/* Process a single input route. + */ +static void +input_route(struct interface *ifp, + naddr from, + naddr dst, + naddr mask, + naddr gate, + struct netinfo *n) +{ + int i; + struct rt_entry *rt; + struct rt_spare *rts, *rts0; + struct interface *ifp1; + time_t new_time; + + + /* See if the other guy is telling us to send our packets to him. + * Sometimes network routes arrive over a point-to-point link for + * the network containing the address(es) of the link. + * + * If our interface is broken, switch to using the other guy. + */ + ifp1 = ifwithaddr(dst, 1, 1); + if (ifp1 != 0 + && !(ifp1->int_state & IS_BROKE)) + return; + + /* Look for the route in our table. + */ + rt = rtget(dst, mask); + + /* Consider adding the route if we do not already have it. + */ + if (rt == 0) { + /* Ignore unknown routes being poisoned. + */ + if (n->n_metric == HOPCNT_INFINITY) + return; + + /* Ignore the route if it points to us */ + if (n->n_nhop != 0 + && 0 != ifwithaddr(n->n_nhop, 1, 0)) + return; + + /* If something has not gone crazy and tried to fill + * our memory, accept the new route. + */ + if (total_routes < MAX_ROUTES) + rtadd(dst, mask, gate, from, n->n_metric, + n->n_tag, 0, ifp); + return; + } + + /* We already know about the route. Consider this update. + * + * If (rt->rt_state & RS_NET_SYN), then this route + * is the same as a network route we have inferred + * for subnets we know, in order to tell RIPv1 routers + * about the subnets. + * + * It is impossible to tell if the route is coming + * from a distant RIPv2 router with the standard + * netmask because that router knows about the entire + * network, or if it is a round-about echo of a + * synthetic, RIPv1 network route of our own. + * The worst is that both kinds of routes might be + * received, and the bad one might have the smaller + * metric. Partly solve this problem by never + * aggregating into such a route. Also keep it + * around as long as the interface exists. + */ + + rts0 = rt->rt_spares; + for (rts = rts0, i = NUM_SPARES; i != 0; i--, rts++) { + if (rts->rts_router == from) + break; + /* Note the worst slot to reuse, + * other than the current slot. + */ + if (rts0 == rt->rt_spares + || BETTER_LINK(rt, rts0, rts)) + rts0 = rts; + } + if (i != 0) { + /* Found the router + */ + int old_metric = rts->rts_metric; + + /* Keep poisoned routes around only long enough to pass + * the poison on. Get a new timestamp for good routes. + */ + new_time =((old_metric == HOPCNT_INFINITY) + ? rts->rts_time + : now.tv_sec); + + /* If this is an update for the router we currently prefer, + * then note it. + */ + if (i == NUM_SPARES) { + rtchange(rt,rt->rt_state, gate,rt->rt_router, + n->n_metric, n->n_tag, ifp, new_time, 0); + /* If the route got worse, check for something better. + */ + if (n->n_metric > old_metric) + rtswitch(rt, 0); + return; + } + + /* This is an update for a spare route. + * Finished if the route is unchanged. + */ + if (rts->rts_gate == gate + && old_metric == n->n_metric + && rts->rts_tag == n->n_tag) { + rts->rts_time = new_time; + return; + } + + } else { + /* The update is for a route we know about, + * but not from a familiar router. + * + * Ignore the route if it points to us. + */ + if (n->n_nhop != 0 + && 0 != ifwithaddr(n->n_nhop, 1, 0)) + return; + + rts = rts0; + + /* Save the route as a spare only if it has + * a better metric than our worst spare. + * This also ignores poisoned routes (those + * received with metric HOPCNT_INFINITY). + */ + if (n->n_metric >= rts->rts_metric) + return; + + new_time = now.tv_sec; + } + + trace_upslot(rt, rts, gate, from, ifp, n->n_metric,n->n_tag, new_time); + + rts->rts_gate = gate; + rts->rts_router = from; + rts->rts_metric = n->n_metric; + rts->rts_tag = n->n_tag; + rts->rts_time = new_time; + rts->rts_ifp = ifp; + + /* try to switch to a better route */ + rtswitch(rt, rts); +} diff --git a/sbin/routed/main.c b/sbin/routed/main.c new file mode 100644 index 000000000000..84a7fac3dbe7 --- /dev/null +++ b/sbin/routed/main.c @@ -0,0 +1,830 @@ +/* + * Copyright (c) 1983, 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +char copyright[] = +"@(#) Copyright (c) 1983, 1988, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#if !defined(lint) && !defined(sgi) && !defined(__NetBSD__) +static char sccsid[] = "@(#)main.c 8.1 (Berkeley) 6/5/93"; +#elif defined(__NetBSD__) +static char rcsid[] = "$NetBSD$"; +#endif +#ident "$Revision: 1.17 $" + +#include "defs.h" +#include "pathnames.h" +#ifdef sgi +#include "math.h" +#endif +#include +#include +#include + +pid_t mypid; + +naddr myaddr; /* system address */ +char myname[MAXHOSTNAMELEN+1]; + +int supplier; /* supply or broadcast updates */ +int supplier_set; +int ipforwarding = 1; /* kernel forwarding on */ + +int default_gateway; /* 1=advertise default */ +int background = 1; +int ridhosts; /* 1=reduce host routes */ +int mhome; /* 1=want multi-homed host route */ +int advertise_mhome; /* 1=must continue adverising it */ +int auth_ok = 1; /* 1=ignore auth if we do not care */ + +struct timeval epoch; /* when started */ +struct timeval clk, prev_clk; +struct timeval now; /* current idea of time */ +time_t now_stale; +time_t now_expire; +time_t now_garbage; + +struct timeval next_bcast; /* next general broadcast */ +struct timeval no_flash = {EPOCH+SUPPLY_INTERVAL}; /* inhibit flash update */ + +fd_set fdbits; +int sock_max; +int rip_sock = -1; /* RIP socket */ +struct interface *rip_sock_mcast; /* current multicast interface */ +int rt_sock; /* routing socket */ +int rt_sock_seqno; + + +static int get_rip_sock(naddr, int); +static void timevalsub(struct timeval *, struct timeval *, struct timeval *); + +int +main(int argc, + char *argv[]) +{ + int n, mib[4], off; + size_t len; + char *p, *q; + struct timeval wtime, t2; + time_t dt; + fd_set ibits; + naddr p_addr, p_mask; + struct interface *ifp; + struct parm parm; + char *tracename = 0; + + + openlog("routed", LOG_PID | LOG_ODELAY, LOG_DAEMON); + ftrace = stdout; + + gettimeofday(&clk, 0); + prev_clk = clk; + epoch = clk; + epoch.tv_sec -= EPOCH; + now.tv_sec = EPOCH; + now_stale = EPOCH - STALE_TIME; + now_expire = EPOCH - EXPIRE_TIME; + now_garbage = EPOCH - GARBAGE_TIME; + wtime.tv_sec = 0; + + (void)gethostname(myname, sizeof(myname)-1); + (void)gethost(myname, &myaddr); + + while ((n = getopt(argc, argv, "sqdghmpAtT:F:P:")) != EOF) { + switch (n) { + case 's': + supplier = 1; + supplier_set = 1; + break; + + case 'q': + supplier = 0; + supplier_set = 1; + break; + + case 'd': + background = 0; + break; + + case 'g': + bzero(&parm, sizeof(parm)); + parm.parm_d_metric = 1; + p = check_parms(&parm); + if (p != 0) + msglog("bad -g: %s", p); + else + default_gateway = 1; + break; + + case 'h': /* suppress extra host routes */ + ridhosts = 1; + break; + + case 'm': /* advertise host route */ + mhome = 1; /* on multi-homed hosts */ + break; + + case 'A': + /* Ignore authentication if we do not care. + * Crazy as it is, that is what RFC 1723 requires. + */ + auth_ok = 0; + break; + + case 't': + new_tracelevel++; + break; + + case 'T': + tracename = optarg; + break; + + case 'F': /* minimal routes for SLIP */ + n = HOPCNT_INFINITY-2; + p = strchr(optarg,','); + if (p && *p != '\0') { + n = (int)strtoul(p+1, &q, 0); + if (*q == '\0' + && n <= HOPCNT_INFINITY-1 + && n >= 1) + *p = '\0'; + } + if (!getnet(optarg, &p_addr, &p_mask)) { + msglog("bad network; \"-F %s\"", + optarg); + break; + } + bzero(&parm, sizeof(parm)); + parm.parm_addr_h = ntohl(p_addr); + parm.parm_mask = p_mask; + parm.parm_d_metric = n; + p = check_parms(&parm); + if (p != 0) + msglog("bad -F: %s", p); + break; + + case 'P': + /* handle arbirary, (usually) per-interface + * parameters. + */ + p = parse_parms(optarg); + if (p != 0) + msglog("bad \"%s\" in \"%s\"", + p, optarg); + break; + + default: + goto usage; + } + } + argc -= optind; + argv += optind; + + if (tracename == 0 && argc >= 1) { + tracename = *argv++; + argc--; + } + if (argc != 0) { +usage: + logbad(0, "usage: routed [-sqdghmpAt] [-T /tracefile]" + " [-F net[,metric]] [-P parms]"); + } + if (geteuid() != 0) + logbad(0, "requires UID 0"); + + mib[0] = CTL_NET; + mib[1] = PF_INET; + mib[2] = IPPROTO_IP; + mib[3] = IPCTL_FORWARDING; + len = sizeof(ipforwarding); + if (sysctl(mib, 4, &ipforwarding, &len, 0, 0) < 0) + LOGERR("sysctl(IPCTL_FORWARDING)"); + + if (!ipforwarding) { + if (supplier) + msglog("-s incompatible with ipforwarding=0"); + if (default_gateway) { + msglog("-g incompatible with ipforwarding=0"); + default_gateway = 0; + } + supplier = 0; + supplier_set = 1; + } + if (default_gateway) { + if (supplier_set && !supplier) { + msglog("-g and -q incompatible"); + } else { + supplier = 1; + supplier_set = 1; + } + } + + + signal(SIGALRM, sigalrm); + if (!background) + signal(SIGHUP, sigterm); /* SIGHUP fatal during debugging */ + else + signal(SIGHUP, SIG_IGN); + signal(SIGTERM, sigterm); + signal(SIGINT, sigterm); + signal(SIGUSR1, sigtrace_on); + signal(SIGUSR2, sigtrace_off); + + /* get into the background */ + if (background) { +#ifdef sgi + if (0 > _daemonize(_DF_NOCHDIR, + new_tracelevel == 0 ? -1 : STDOUT_FILENO, + new_tracelevel == 0 ? -1 : STDERR_FILENO, + -1)) + BADERR(0, "_daemonize()"); +#else + if (daemon(1, 1) < 0) + BADERR(0,"daemon()"); +#endif + } + + mypid = getpid(); + srandom((int)(clk.tv_sec ^ clk.tv_usec ^ mypid)); + + /* prepare socket connected to the kernel. + */ + rt_sock = socket(AF_ROUTE, SOCK_RAW, 0); + if (rt_sock < 0) + BADERR(1,"rt_sock = socket()"); + if (fcntl(rt_sock, F_SETFL, O_NONBLOCK) == -1) + logbad(1, "fcntl(rt_sock) O_NONBLOCK: %s", strerror(errno)); + off = 0; + if (setsockopt(rt_sock, SOL_SOCKET,SO_USELOOPBACK, + &off,sizeof(off)) < 0) + LOGERR("setsockopt(SO_USELOOPBACK,0)"); + + fix_select(); + + + if (background && new_tracelevel == 0) + ftrace = 0; + if (tracename != 0) { + trace_on(tracename, 1); + if (new_tracelevel == 0) /* use stdout if file is bad */ + new_tracelevel = 1; + } + set_tracelevel(); + + /* initialize radix tree */ + rtinit(); + + /* Pick a random part of the second for our output to minimize + * collisions. + * + * Start broadcasting after hearing from other routers, and + * at a random time so a bunch of systems do not get synchronized + * after a power failure. + */ + intvl_random(&next_bcast, EPOCH+MIN_WAITTIME, EPOCH+SUPPLY_INTERVAL); + age_timer.tv_usec = next_bcast.tv_usec; + age_timer.tv_sec = EPOCH+MIN_WAITTIME; + rdisc_timer = next_bcast; + ifinit_timer.tv_usec = next_bcast.tv_usec; + + /* Collect an initial view of the world by checking the interface + * configuration and the kludge file. + */ + gwkludge(); + ifinit(); + flush_kern(); + + /* Ask for routes */ + rip_query(); + if (!supplier) + rdisc_sol(); + + /* Loop forever, listening and broadcasting. + */ + for (;;) { + prev_clk = clk; + gettimeofday(&clk, 0); + timevalsub(&t2, &clk, &prev_clk); + if (t2.tv_sec < 0 + || t2.tv_sec > wtime.tv_sec + 5) { + /* Deal with time changes before other housekeeping to + * keep everything straight. + */ + dt = t2.tv_sec; + if (dt > 0) + dt -= wtime.tv_sec; + trace_act("time changed by %d sec\n", dt); + epoch.tv_sec += dt; + } + timevalsub(&now, &clk, &epoch); + now_stale = now.tv_sec - STALE_TIME; + now_expire = now.tv_sec - EXPIRE_TIME; + now_garbage = now.tv_sec - GARBAGE_TIME; + + /* deal with interrupts that should affect tracing */ + set_tracelevel(); + + if (stopint != 0) { + if (supplier) { + rip_bcast(0); + rdisc_adv(); + } + trace_off("exiting with signal %d\n", stopint); + exit(stopint | 128); + } + + /* look for new or dead interfaces */ + timevalsub(&wtime, &ifinit_timer, &now); + if (wtime.tv_sec <= 0) { + wtime.tv_sec = 0; + ifinit(); + rip_query(); + continue; + } + + /* If it is time, then broadcast our routes. + */ + if (supplier || advertise_mhome) { + timevalsub(&t2, &next_bcast, &now); + if (t2.tv_sec <= 0) { + /* Synchronize the aging and broadcast + * timers to minimize awakenings + */ + age(0); + + rip_bcast(0); + + /* It is desirable to send routing updates + * regularly. So schedule the next update + * 30 seconds after the previous one was + * secheduled, instead of 30 seconds after + * the previous update was finished. + * Even if we just started after discovering + * a 2nd interface or were otherwise delayed, + * pick a 30-second aniversary of the + * original broadcast time. + */ + n = 1 + (0-t2.tv_sec)/SUPPLY_INTERVAL; + next_bcast.tv_sec += n*SUPPLY_INTERVAL; + + continue; + } + + if (timercmp(&t2, &wtime, <)) + wtime = t2; + } + + /* If we need a flash update, either do it now or + * set the delay to end when it is time. + * + * If we are within MIN_WAITTIME seconds of a full update, + * do not bother. + */ + if (need_flash + && supplier + && no_flash.tv_sec+MIN_WAITTIME < next_bcast.tv_sec) { + /* accurate to the millisecond */ + if (!timercmp(&no_flash, &now, >)) + rip_bcast(1); + timevalsub(&t2, &no_flash, &now); + if (timercmp(&t2, &wtime, <)) + wtime = t2; + } + + /* trigger the main aging timer. + */ + timevalsub(&t2, &age_timer, &now); + if (t2.tv_sec <= 0) { + age(0); + continue; + } + if (timercmp(&t2, &wtime, <)) + wtime = t2; + + /* update the kernel routing table + */ + timevalsub(&t2, &need_kern, &now); + if (t2.tv_sec <= 0) { + age(0); + continue; + } + if (timercmp(&t2, &wtime, <)) + wtime = t2; + + /* take care of router discovery, + * but do it to the millisecond + */ + if (!timercmp(&rdisc_timer, &now, >)) { + rdisc_age(0); + continue; + } + timevalsub(&t2, &rdisc_timer, &now); + if (timercmp(&t2, &wtime, <)) + wtime = t2; + + + /* wait for input or a timer to expire. + */ + trace_flush(); + ibits = fdbits; + n = select(sock_max, &ibits, 0, 0, &wtime); + if (n <= 0) { + if (n < 0 && errno != EINTR && errno != EAGAIN) + BADERR(1,"select"); + continue; + } + + if (FD_ISSET(rt_sock, &ibits)) { + read_rt(); + n--; + } + if (rdisc_sock >= 0 && FD_ISSET(rdisc_sock, &ibits)) { + read_d(); + n--; + } + if (rip_sock >= 0 && FD_ISSET(rip_sock, &ibits)) { + read_rip(rip_sock, 0); + n--; + } + + for (ifp = ifnet; n > 0 && 0 != ifp; ifp = ifp->int_next) { + if (ifp->int_rip_sock >= 0 + && FD_ISSET(ifp->int_rip_sock, &ibits)) { + read_rip(ifp->int_rip_sock, ifp); + n--; + } + } + } +} + + +/* ARGSUSED */ +void +sigalrm(int sig) +{ + /* Historically, SIGALRM would cause the daemon to check for + * new and broken interfaces. + */ + ifinit_timer.tv_sec = now.tv_sec; + trace_act("SIGALRM\n"); +} + + +/* watch for fatal signals */ +void +sigterm(int sig) +{ + stopint = sig; + (void)signal(sig, SIG_DFL); /* catch it only once */ +} + + +void +fix_select(void) +{ + struct interface *ifp; + + + FD_ZERO(&fdbits); + sock_max = 0; + + FD_SET(rt_sock, &fdbits); + if (sock_max <= rt_sock) + sock_max = rt_sock+1; + if (rip_sock >= 0) { + FD_SET(rip_sock, &fdbits); + if (sock_max <= rip_sock) + sock_max = rip_sock+1; + } + for (ifp = ifnet; 0 != ifp; ifp = ifp->int_next) { + if (ifp->int_rip_sock >= 0) { + FD_SET(ifp->int_rip_sock, &fdbits); + if (sock_max <= ifp->int_rip_sock) + sock_max = ifp->int_rip_sock+1; + } + } + if (rdisc_sock >= 0) { + FD_SET(rdisc_sock, &fdbits); + if (sock_max <= rdisc_sock) + sock_max = rdisc_sock+1; + } +} + + +void +fix_sock(int sock, + char *name) +{ + int on; +#define MIN_SOCKBUF (4*1024) + static int rbuf; + + if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1) + logbad(1, "fcntl(%s) O_NONBLOCK: %s", + name, strerror(errno)); + on = 1; + if (setsockopt(sock, SOL_SOCKET,SO_BROADCAST, + &on,sizeof(on)) < 0) + msglog("setsockopt(%s,SO_BROADCAST): %s", + name, strerror(errno)); + if (rbuf >= MIN_SOCKBUF) { + if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, + &rbuf, sizeof(rbuf)) < 0) + msglog("setsockopt(%s,SO_RCVBUF=%d): %s", + name, rbuf, strerror(errno)); + } else { + for (rbuf = 60*1024; ; rbuf -= 4096) { + if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, + &rbuf, sizeof(rbuf)) == 0) { + trace_act("RCVBUF=%d\n", rbuf); + break; + } + if (rbuf < MIN_SOCKBUF) { + msglog("setsockopt(%s,SO_RCVBUF = %d): %s", + name, rbuf, strerror(errno)); + break; + } + } + } +} + + +/* get a rip socket + */ +static int /* <0 or file descriptor */ +get_rip_sock(naddr addr, + int serious) /* 1=failure to bind is serious */ +{ + struct sockaddr_in sin; + unsigned char ttl; + int s; + + + if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) + BADERR(1,"rip_sock = socket()"); + + bzero(&sin,sizeof(sin)); +#ifdef _HAVE_SIN_LEN + sin.sin_len = sizeof(sin); +#endif + sin.sin_family = AF_INET; + sin.sin_port = htons(RIP_PORT); + sin.sin_addr.s_addr = addr; + if (bind(s, (struct sockaddr *)&sin,sizeof(sin)) < 0) { + if (serious) + BADERR(errno != EADDRINUSE, "bind(rip_sock)"); + return -1; + } + fix_sock(s,"rip_sock"); + + ttl = 1; + if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_TTL, + &ttl, sizeof(ttl)) < 0) + DBGERR(1,"rip_sock setsockopt(IP_MULTICAST_TTL)"); + + return s; +} + + +/* turn off main RIP socket */ +void +rip_off(void) +{ + struct interface *ifp; + register naddr addr; + + + if (rip_sock >= 0 && !mhome) { + trace_act("turn off RIP\n"); + + (void)close(rip_sock); + rip_sock = -1; + + /* get non-broadcast sockets to listen to queries. + */ + for (ifp = ifnet; ifp != 0; ifp = ifp->int_next) { + if (ifp->int_rip_sock < 0 + && !(ifp->int_state & IS_ALIAS)) { + addr = ((ifp->int_if_flags & IFF_POINTOPOINT) + ? ifp->int_dstaddr + : ifp->int_addr); + ifp->int_rip_sock = get_rip_sock(addr, 0); + } + } + + fix_select(); + + age(0); + } +} + + +/* turn on RIP multicast input via an interface + */ +static void +rip_mcast_on(struct interface *ifp) +{ + struct ip_mreq m; + + if (!IS_RIP_IN_OFF(ifp->int_state) + && (ifp->int_if_flags & IFF_MULTICAST) +#ifdef MCAST_PPP_BUG + && !(ifp->int_if_flags & IFF_POINTOPOINT) +#endif + && !(ifp->int_state & IS_ALIAS)) { + m.imr_multiaddr.s_addr = htonl(INADDR_RIP_GROUP); + m.imr_interface.s_addr = ((ifp->int_if_flags & IFF_POINTOPOINT) + ? ifp->int_dstaddr + : ifp->int_addr); + if (setsockopt(rip_sock,IPPROTO_IP, IP_ADD_MEMBERSHIP, + &m, sizeof(m)) < 0) + LOGERR("setsockopt(IP_ADD_MEMBERSHIP RIP)"); + } +} + + +/* Prepare socket used for RIP. + */ +void +rip_on(struct interface *ifp) +{ + /* If the main RIP socket is already alive, only start receiving + * multicasts for this interface. + */ + if (rip_sock >= 0) { + if (ifp != 0) + rip_mcast_on(ifp); + return; + } + + /* If the main RIP socket is off, and it makes sense to turn it on, + * turn it on for all of the interfaces. + */ + if (rip_interfaces > 0 && !rdisc_ok) { + trace_act("turn on RIP\n"); + + /* Close all of the query sockets so that we can open + * the main socket. SO_REUSEPORT is not a solution, + * since that would let two daemons bind to the broadcast + * socket. + */ + for (ifp = ifnet; ifp != 0; ifp = ifp->int_next) { + if (ifp->int_rip_sock >= 0) { + (void)close(ifp->int_rip_sock); + ifp->int_rip_sock = -1; + } + } + + rip_sock = get_rip_sock(INADDR_ANY, 1); + rip_sock_mcast = 0; + + /* Do not advertise anything until we have heard something + */ + if (next_bcast.tv_sec < now.tv_sec+MIN_WAITTIME) + next_bcast.tv_sec = now.tv_sec+MIN_WAITTIME; + + for (ifp = ifnet; ifp != 0; ifp = ifp->int_next) { + if (!IS_RIP_IN_OFF(ifp->int_state)) + ifp->int_state &= ~IS_RIP_QUERIED; + rip_mcast_on(ifp); + } + + ifinit_timer.tv_sec = now.tv_sec; + + fix_select(); + + } else if (ifp != 0 + && ifp->int_rip_sock < 0 + && !(ifp->int_state & IS_ALIAS)) { + /* RIP is off, so ensure there are sockets on which + * to listen for queries. + */ + ifp->int_rip_sock = get_rip_sock(ifp->int_addr, 0); + + fix_select(); + } +} + + +/* die if malloc(3) fails + */ +void * +rtmalloc(size_t size, + char *msg) +{ + void *p = malloc(size); + if (p == 0) + logbad(1,"malloc() failed in %s", msg); + return p; +} + + +/* get a random instant in an interval + */ +void +intvl_random(struct timeval *tp, /* put value here */ + u_long lo, /* value is after this second */ + u_long hi) /* and before this */ +{ + tp->tv_sec = (time_t)(hi == lo + ? lo + : (lo + random() % ((hi - lo)))); + tp->tv_usec = random() % 1000000; +} + + +void +timevaladd(struct timeval *t1, + struct timeval *t2) +{ + + t1->tv_sec += t2->tv_sec; + if ((t1->tv_usec += t2->tv_usec) > 1000000) { + t1->tv_sec++; + t1->tv_usec -= 1000000; + } +} + + +/* t1 = t2 - t3 + */ +static void +timevalsub(struct timeval *t1, + struct timeval *t2, + struct timeval *t3) +{ + t1->tv_sec = t2->tv_sec - t3->tv_sec; + if ((t1->tv_usec = t2->tv_usec - t3->tv_usec) < 0) { + t1->tv_sec--; + t1->tv_usec += 1000000; + } +} + + +void +msglog(char *p, ...) +{ + va_list args; + + trace_flush(); + + va_start(args, p); + vsyslog(LOG_ERR, p, args); + + if (ftrace != 0) { + if (ftrace == stdout) + (void)fputs("routed: ", ftrace); + (void)vfprintf(ftrace, p, args); + (void)fputc('\n', ftrace); + } +} + + +void +logbad(int dump, char *p, ...) +{ + va_list args; + + trace_flush(); + + va_start(args, p); + vsyslog(LOG_ERR, p, args); + + (void)fputs("routed: ", stderr); + (void)vfprintf(stderr, p, args); + (void)fputs("; giving up\n",stderr); + (void)fflush(stderr); + + if (dump) + abort(); + exit(1); +} diff --git a/sbin/routed/output.c b/sbin/routed/output.c new file mode 100644 index 000000000000..eafcf31e7864 --- /dev/null +++ b/sbin/routed/output.c @@ -0,0 +1,874 @@ +/* + * Copyright (c) 1983, 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if !defined(lint) && !defined(sgi) && !defined(__NetBSD__) +static char sccsid[] = "@(#)output.c 8.1 (Berkeley) 6/5/93"; +#elif defined(__NetBSD__) +static char rcsid[] = "$NetBSD$"; +#endif +#ident "$Revision: 1.17 $" + +#include "defs.h" + + +int update_seqno; + + +/* walk the tree of routes with this for output + */ +struct { + struct sockaddr_in to; + naddr to_mask; + naddr to_net; + naddr to_std_mask; + naddr to_std_net; + struct interface *ifp; /* usually output interface */ + struct ws_buf { /* info for each buffer */ + struct rip *buf; + struct netinfo *n; + struct netinfo *base; + struct netinfo *lim; + enum output_type type; + } v12, v2; + char metric; /* adjust metrics by interface */ + int npackets; + int gen_limit; + u_int state; +#define WS_ST_FLASH 0x001 /* send only changed routes */ +#define WS_ST_RIP2_SAFE 0x002 /* send RIPv2 safe for RIPv1 */ +#define WS_ST_RIP2_ALL 0x004 /* send full featured RIPv2 */ +#define WS_ST_AG 0x008 /* ok to aggregate subnets */ +#define WS_ST_SUPER_AG 0x010 /* ok to aggregate networks */ +#define WS_ST_SUB_AG 0x020 /* aggregate subnets in odd case */ +#define WS_ST_QUERY 0x040 /* responding to a query */ +#define WS_ST_TO_ON_NET 0x080 /* sending onto one of our nets */ +#define WS_ST_DEFAULT 0x100 /* faking a default */ +#define WS_ST_PM_RDISC 0x200 /* poor-man's router discovery */ +} ws; + +/* A buffer for what can be heard by both RIPv1 and RIPv2 listeners */ +union pkt_buf ripv12_buf; + +/* Another for only RIPv2 listeners */ +union pkt_buf rip_v2_buf; + + + +/* Send the contents of the global buffer via the non-multicast socket + */ +int /* <0 on failure */ +output(enum output_type type, + struct sockaddr_in *dst, /* send to here */ + struct interface *ifp, + struct rip *buf, + int size) /* this many bytes */ +{ + struct sockaddr_in sin; + int flags; + char *msg; + int res; + naddr tgt_mcast; + int soc; + int serrno; + + sin = *dst; + if (sin.sin_port == 0) + sin.sin_port = htons(RIP_PORT); +#ifdef _HAVE_SIN_LEN + if (sin.sin_len == 0) + sin.sin_len = sizeof(sin); +#endif + + soc = rip_sock; + flags = 0; + + switch (type) { + case OUT_QUERY: + msg = "Answer Query"; + if (soc < 0) + soc = ifp->int_rip_sock; + break; + case OUT_UNICAST: + msg = "Send"; + if (soc < 0) + soc = ifp->int_rip_sock; + flags = MSG_DONTROUTE; + break; + case OUT_BROADCAST: + if (ifp->int_if_flags & IFF_POINTOPOINT) { + msg = "Send"; + } else { + msg = "Send bcast"; + } + flags = MSG_DONTROUTE; + break; + case OUT_MULTICAST: + if (ifp->int_if_flags & IFF_POINTOPOINT) { + msg = "Send pt-to-pt"; + } else if (ifp->int_state & IS_DUP) { + trace_act("abort multicast output via %s" + " with duplicate address\n", + ifp->int_name); + return 0; + } else { + msg = "Send mcast"; + if (rip_sock_mcast != ifp) { +#ifdef MCAST_PPP_BUG + /* Do not specifiy the primary interface + * explicitly if we have the multicast + * point-to-point kernel bug, since the + * kernel will do the wrong thing if the + * local address of a point-to-point link + * is the same as the address of an ordinary + * interface. + */ + if (ifp->int_addr == myaddr) { + tgt_mcast = 0; + } else +#endif + tgt_mcast = ifp->int_addr; + if (0 > setsockopt(rip_sock, + IPPROTO_IP, IP_MULTICAST_IF, + &tgt_mcast, + sizeof(tgt_mcast))) { + serrno = errno; + LOGERR("setsockopt(rip_sock," + "IP_MULTICAST_IF)"); + errno = serrno; + ifp = 0; + return -1; + } + rip_sock_mcast = ifp; + } + sin.sin_addr.s_addr = htonl(INADDR_RIP_GROUP); + } + + case NO_OUT_MULTICAST: + case NO_OUT_RIPV2: + break; + } + + trace_rip(msg, "to", &sin, ifp, buf, size); + + res = sendto(soc, buf, size, flags, + (struct sockaddr *)&sin, sizeof(sin)); + if (res < 0 + && (ifp == 0 || !(ifp->int_state & IS_BROKE))) { + serrno = errno; + msglog("%s sendto(%s%s%s.%d): %s", msg, + ifp != 0 ? ifp->int_name : "", + ifp != 0 ? ", " : "", + inet_ntoa(sin.sin_addr), + ntohs(sin.sin_port), + strerror(errno)); + errno = serrno; + } + + return res; +} + + +/* install authentication if appropriate + */ +static void +set_auth(struct ws_buf *w) +{ + if (ws.ifp != 0 + && ws.ifp->int_passwd[0] != '\0' + && (ws.state & WS_ST_RIP2_SAFE)) { + w->n->n_family = RIP_AF_AUTH; + ((struct netauth*)w->n)->a_type = RIP_AUTH_PW; + bcopy(ws.ifp->int_passwd, ((struct netauth*)w->n)->au.au_pw, + sizeof(((struct netauth*)w->n)->au.au_pw)); + w->n++; + } +} + + +/* Send the buffer + */ +static void +supply_write(struct ws_buf *wb) +{ + /* Output multicast only if legal. + * If we would multcast and it would be illegal, then discard the + * packet. + */ + switch (wb->type) { + case NO_OUT_MULTICAST: + trace_pkt("skip multicast to %s because impossible\n", + naddr_ntoa(ws.to.sin_addr.s_addr)); + break; + case NO_OUT_RIPV2: + break; + default: + if (output(wb->type, &ws.to, ws.ifp, wb->buf, + ((char *)wb->n - (char*)wb->buf)) < 0 + && ws.ifp != 0) + if_sick(ws.ifp); + ws.npackets++; + break; + } + + bzero(wb->n = wb->base, sizeof(*wb->n)*NETS_LEN); + if (wb->buf->rip_vers == RIPv2) + set_auth(wb); +} + + +/* put an entry into the packet + */ +static void +supply_out(struct ag_info *ag) +{ + int i; + naddr mask, v1_mask, s_mask, dst_h, ddst_h; + struct ws_buf *wb; + + + /* Skip this route if doing a flash update and it and the routes + * it aggregates have not changed recently. + */ + if (ag->ag_seqno < update_seqno + && (ws.state & WS_ST_FLASH)) + return; + + /* Skip this route if required by split-horizon. + */ + if (ag->ag_state & AGS_SPLIT_HZ) + return; + + dst_h = ag->ag_dst_h; + mask = ag->ag_mask; + v1_mask = ripv1_mask_host(htonl(dst_h), + (ws.state & WS_ST_TO_ON_NET) ? ws.ifp : 0); + s_mask = std_mask(htonl(dst_h)); + i = 0; + + /* If we are sending RIPv2 packets that cannot (or must not) be + * heard by RIPv1 listeners, do not worry about sub- or supernets. + * Subnets (from other networks) can only be sent via multicast. + * A pair of subnet routes might have been promoted so that they + * are legal to send by RIPv1. + * If RIPv1 is off, use the multicast buffer, unless this is the + * fake default route and it is acting as a poor-man's router- + * discovery mechanism. + */ + if (((ws.state & WS_ST_RIP2_ALL) + && (dst_h != RIP_DEFAULT || !(ws.state & WS_ST_PM_RDISC))) + || ((ag->ag_state & AGS_RIPV2) && v1_mask != mask)) { + /* use the RIPv2-only buffer */ + wb = &ws.v2; + + } else { + /* use the RIPv1-or-RIPv2 buffer */ + wb = &ws.v12; + + /* Convert supernet route into corresponding set of network + * routes for RIPv1, but leave non-contiguous netmasks + * to ag_check(). + */ + if (v1_mask > mask + && mask + (mask & -mask) == 0) { + ddst_h = v1_mask & -v1_mask; + i = (v1_mask & ~mask)/ddst_h; + + if (i > ws.gen_limit) { + /* Punt if we would have to generate an + * unreasonable number of routes. + */ +#ifdef DEBUG + msglog("sending %s to %s as 1 instead" + " of %d routes", + addrname(htonl(dst_h),mask,1), + naddr_ntoa(ws.to.sin_addr.s_addr), + i+1); +#endif + i = 0; + + } else { + mask = v1_mask; + ws.gen_limit -= i; + } + } + } + + do { + wb->n->n_family = RIP_AF_INET; + wb->n->n_dst = htonl(dst_h); + /* If the route is from router-discovery or we are + * shutting down, admit only a bad metric. + */ + wb->n->n_metric = ((stopint || ag->ag_metric < 1) + ? HOPCNT_INFINITY + : ag->ag_metric); + HTONL(wb->n->n_metric); + if (wb->buf->rip_vers == RIPv2) { + if (ag->ag_nhop != 0 + && (ws.state & WS_ST_RIP2_SAFE) + && ((ws.state & WS_ST_QUERY) + || (ag->ag_nhop != ws.ifp->int_addr + && on_net(ag->ag_nhop, + ws.ifp->int_net, + ws.ifp->int_mask)))) + wb->n->n_nhop = ag->ag_nhop; + if ((ws.state & WS_ST_RIP2_ALL) + || mask != s_mask) + wb->n->n_mask = htonl(mask); + wb->n->n_tag = ag->ag_tag; + } + dst_h += ddst_h; + + if (++wb->n >= wb->lim) + supply_write(wb); + } while (i-- != 0); +} + + +/* supply one route from the table + */ +/* ARGSUSED */ +static int +walk_supply(struct radix_node *rn, + struct walkarg *w) +{ +#define RT ((struct rt_entry *)rn) + u_short ags; + char metric, pref; + naddr dst, nhop; + + + /* Do not advertise the loopback interface + * or external remote interfaces + */ + if ((RT->rt_state & RS_IF) + && RT->rt_ifp != 0 + && ((RT->rt_ifp->int_if_flags & IFF_LOOPBACK) + || (RT->rt_ifp->int_state & IS_EXTERNAL)) + && !(RT->rt_state & RS_MHOME)) + return 0; + + /* If being quiet about our ability to forward, then + * do not say anything unless responding to a query. + */ + if (!supplier && !(ws.state & WS_ST_QUERY)) + return 0; + + dst = RT->rt_dst; + + /* do not collide with the fake default route */ + if (dst == RIP_DEFAULT + && (ws.state & WS_ST_DEFAULT)) + return 0; + + if (RT->rt_state & RS_NET_SYN) { + if (RT->rt_state & RS_NET_INT) { + /* Do not send manual synthetic network routes + * into the subnet. + */ + if (on_net(ws.to.sin_addr.s_addr, + ntohl(dst), RT->rt_mask)) + return 0; + + } else { + /* Do not send automatic synthetic network routes + * if they are not needed becaus no RIPv1 listeners + * can hear them. + */ + if (ws.state & WS_ST_RIP2_ALL) + return 0; + + /* Do not send automatic synthetic network routes to + * the real subnet. + */ + if (on_net(ws.to.sin_addr.s_addr, + ntohl(dst), RT->rt_mask)) + return 0; + } + nhop = 0; + + } else { + /* Advertise the next hop if this is not a route for one + * of our interfaces and the next hop is on the same + * network as the target. + */ + if (!(RT->rt_state & RS_IF) + && RT->rt_gate != myaddr + && RT->rt_gate != loopaddr) + nhop = RT->rt_gate; + else + nhop = 0; + } + + metric = RT->rt_metric; + ags = 0; + + if (RT->rt_state & RS_MHOME) { + /* retain host route of multi-homed servers */ + ; + + } else if (RT_ISHOST(RT)) { + /* We should always aggregate the host routes + * for the local end of our point-to-point links. + * If we are suppressing host routes in general, then do so. + * Avoid advertising host routes onto their own network, + * where they should be handled by proxy-ARP. + */ + if ((RT->rt_state & RS_LOCAL) + || ridhosts + || (ws.state & WS_ST_SUPER_AG) + || on_net(dst, ws.to_net, ws.to_mask)) + ags |= AGS_SUPPRESS; + + if (ws.state & WS_ST_SUPER_AG) + ags |= AGS_PROMOTE; + + } else if (ws.state & WS_ST_AG) { + /* Aggregate network routes, if we are allowed. + */ + ags |= AGS_SUPPRESS; + + /* Generate supernets if allowed. + * If we can be heard by RIPv1 systems, we will + * later convert back to ordinary nets. + * This unifies dealing with received supernets. + */ + if ((RT->rt_state & RS_SUBNET) + || (ws.state & WS_ST_SUPER_AG)) + ags |= AGS_PROMOTE; + + } + + /* Do not send RIPv1 advertisements of subnets to other + * networks. If possible, multicast them by RIPv2. + */ + if ((RT->rt_state & RS_SUBNET) + && !(ws.state & WS_ST_RIP2_ALL) + && !on_net(dst, ws.to_std_net, ws.to_std_mask)) { + ags |= AGS_RIPV2 | AGS_PROMOTE; + if (ws.state & WS_ST_SUB_AG) + ags |= AGS_SUPPRESS; + } + + /* Do not send a route back to where it came from, except in + * response to a query. This is "split-horizon". That means not + * advertising back to the same network and so via the same interface. + * + * We want to suppress routes that might have been fragmented + * from this route by a RIPv1 router and sent back to us, and so we + * cannot forget this route here. Let the split-horizon route + * aggregate (suppress) the fragmented routes and then itself be + * forgotten. + * + * Include the routes for both ends of point-to-point interfaces + * since the other side presumably knows them as well as we do. + */ + if (RT->rt_ifp == ws.ifp && ws.ifp != 0 + && !(ws.state & WS_ST_QUERY) + && (ws.state & WS_ST_TO_ON_NET) + && (!(RT->rt_state & RS_IF) + || ws.ifp->int_if_flags & IFF_POINTOPOINT)) { + /* Poison-reverse the route instead of only not advertising it + * it is recently changed from some other route. + * In almost all cases, if there is no spare for the route + * then it is either old or a brand new route, and if it + * is brand new, there is no need for poison-reverse. + */ + metric = HOPCNT_INFINITY; + if (RT->rt_poison_time < now_expire + || RT->rt_spares[1].rts_gate ==0) { + ags |= AGS_SPLIT_HZ; + ags &= ~(AGS_PROMOTE | AGS_SUPPRESS); + } + } + + /* Adjust the outgoing metric by the cost of the link. + */ + pref = metric + ws.metric; + if (pref < HOPCNT_INFINITY) { + /* Keep track of the best metric with which the + * route has been advertised recently. + */ + if (RT->rt_poison_metric >= metric + || RT->rt_poison_time < now_expire) { + RT->rt_poison_time = now.tv_sec; + RT->rt_poison_metric = metric; + } + metric = pref; + + } else { + /* Do not advertise stable routes that will be ignored, + * unless they are being held down and poisoned. If the + * route recently was advertised with a metric that would + * have been less than infinity through this interface, we + * need to continue to advertise it in order to poison it. + */ + pref = RT->rt_poison_metric + ws.metric; + if (pref >= HOPCNT_INFINITY + || RT->rt_poison_time < now_garbage ) + return 0; + + metric = HOPCNT_INFINITY; + } + + ag_check(dst, RT->rt_mask, 0, nhop, metric, pref, + RT->rt_seqno, RT->rt_tag, ags, supply_out); + return 0; +#undef RT +} + + +/* Supply dst with the contents of the routing tables. + * If this won't fit in one packet, chop it up into several. + */ +void +supply(struct sockaddr_in *dst, + struct interface *ifp, /* output interface */ + enum output_type type, + int flash, /* 1=flash update */ + int vers) /* RIP version */ +{ + static int init = 1; + struct rt_entry *rt; + + + ws.state = 0; + ws.gen_limit = 1024; + + ws.to = *dst; + ws.to_std_mask = std_mask(ws.to.sin_addr.s_addr); + ws.to_std_net = ntohl(ws.to.sin_addr.s_addr) & ws.to_std_mask; + + if (ifp != 0) { + ws.to_mask = ifp->int_mask; + ws.to_net = ifp->int_net; + if (on_net(ws.to.sin_addr.s_addr, ws.to_net, ws.to_mask)) + ws.state |= WS_ST_TO_ON_NET; + + } else { + ws.to_mask = ripv1_mask_net(ws.to.sin_addr.s_addr, 0); + ws.to_net = ntohl(ws.to.sin_addr.s_addr) & ws.to_mask; + rt = rtfind(dst->sin_addr.s_addr); + if (rt) + ifp = rt->rt_ifp; + } + + ws.npackets = 0; + if (flash) + ws.state |= WS_ST_FLASH; + if (type == OUT_QUERY) + ws.state |= WS_ST_QUERY; + + if ((ws.ifp = ifp) == 0) { + ws.metric = 1; + } else { + /* Adjust the advertised metric by the outgoing interface + * metric. + */ + ws.metric = ifp->int_metric+1; + } + + if (init) { + init = 0; + + bzero(&ripv12_buf, sizeof(ripv12_buf)); + ripv12_buf.rip.rip_cmd = RIPCMD_RESPONSE; + ws.v12.buf = &ripv12_buf.rip; + ws.v12.base = &ws.v12.buf->rip_nets[0]; + ws.v12.lim = ws.v12.base + NETS_LEN; + + bzero(&rip_v2_buf, sizeof(rip_v2_buf)); + rip_v2_buf.rip.rip_cmd = RIPCMD_RESPONSE; + rip_v2_buf.rip.rip_vers = RIPv2; + ws.v2.buf = &rip_v2_buf.rip; + ws.v2.base = &ws.v2.buf->rip_nets[0]; + ws.v2.lim = ws.v2.base + NETS_LEN; + } + ripv12_buf.rip.rip_vers = vers; + + ws.v12.n = ws.v12.base; + set_auth(&ws.v12); + ws.v2.n = ws.v2.base; + set_auth(&ws.v2); + + switch (type) { + case OUT_BROADCAST: + ws.v2.type = ((ws.ifp != 0 + && (ws.ifp->int_if_flags & IFF_MULTICAST)) + ? OUT_MULTICAST + : NO_OUT_MULTICAST); + ws.v12.type = OUT_BROADCAST; + break; + case OUT_MULTICAST: + ws.v2.type = ((ws.ifp != 0 + && (ws.ifp->int_if_flags & IFF_MULTICAST)) + ? OUT_MULTICAST + : NO_OUT_MULTICAST); + ws.v12.type = OUT_BROADCAST; + break; + case OUT_UNICAST: + case OUT_QUERY: + ws.v2.type = (vers == RIPv2) ? type : NO_OUT_RIPV2; + ws.v12.type = type; + break; + default: + ws.v2.type = type; + ws.v12.type = type; + break; + } + + if (vers == RIPv2) { + /* if asked to send RIPv2, send at least that which can + * be safely heard by RIPv1 listeners. + */ + ws.state |= WS_ST_RIP2_SAFE; + + /* full RIPv2 only if cannot be heard by RIPv1 listeners */ + if (type != OUT_BROADCAST) + ws.state |= WS_ST_RIP2_ALL; + if (!(ws.state & WS_ST_TO_ON_NET)) { + ws.state |= (WS_ST_AG | WS_ST_SUPER_AG); + } else if (ws.ifp == 0 || !(ws.ifp->int_state & IS_NO_AG)) { + ws.state |= WS_ST_AG; + if (type != OUT_BROADCAST + && (ws.ifp == 0 + || !(ws.ifp->int_state & IS_NO_SUPER_AG))) + ws.state |= WS_ST_SUPER_AG; + } + + } else if (ws.ifp == 0 || !(ws.ifp->int_state & IS_NO_AG)) { + ws.state |= WS_ST_SUB_AG; + } + + if (supplier) { + /* Fake a default route if asked, and if there is not + * a better, real default route. + */ + if (ifp->int_d_metric != 0 + && (0 == (rt = rtget(RIP_DEFAULT, 0)) + || rt->rt_metric+ws.metric >= ifp->int_d_metric)) { + ws.state |= WS_ST_DEFAULT; + ag_check(0, 0, 0, 0, + ifp->int_d_metric,ifp->int_d_metric, + 0, 0, 0, supply_out); + } + if ((ws.state & WS_ST_RIP2_ALL) + && (ifp->int_state & IS_PM_RDISC)) { + ws.state |= WS_ST_PM_RDISC; + ripv12_buf.rip.rip_vers = RIPv1; + } + } + + (void)rn_walktree(rhead, walk_supply, 0); + ag_flush(0,0,supply_out); + + /* Flush the packet buffers, provided they are not empty and + * do not contain only the password. + */ + if (ws.v12.n != ws.v12.base + && (ws.v12.n > ws.v12.base+1 + || ws.v12.n->n_family != RIP_AF_AUTH)) + supply_write(&ws.v12); + if (ws.v2.n != ws.v2.base + && (ws.v2.n > ws.v2.base+1 + || ws.v2.n->n_family != RIP_AF_AUTH)) + supply_write(&ws.v2); + + /* If we sent nothing and this is an answer to a query, send + * an empty buffer. + */ + if (ws.npackets == 0 + && (ws.state & WS_ST_QUERY)) + supply_write(&ws.v12); +} + + +/* send all of the routing table or just do a flash update + */ +void +rip_bcast(int flash) +{ +#ifdef _HAVE_SIN_LEN + static struct sockaddr_in dst = {sizeof(dst), AF_INET}; +#else + static struct sockaddr_in dst = {AF_INET}; +#endif + struct interface *ifp; + enum output_type type; + int vers; + struct timeval rtime; + + + need_flash = 0; + intvl_random(&rtime, MIN_WAITTIME, MAX_WAITTIME); + no_flash = rtime; + timevaladd(&no_flash, &now); + + if (rip_sock < 0) + return; + + trace_act("send %s and inhibit dynamic updates for %.3f sec\n", + flash ? "dynamic update" : "all routes", + rtime.tv_sec + ((float)rtime.tv_usec)/1000000.0); + + for (ifp = ifnet; ifp != 0; ifp = ifp->int_next) { + /* skip interfaces not doing RIP, those already queried, + * and aliases. Do try broken interfaces to see + * if they have healed. + */ + if (0 != (ifp->int_state & (IS_PASSIVE | IS_ALIAS))) + continue; + + /* skip turned off interfaces */ + if (!iff_alive(ifp->int_if_flags)) + continue; + + /* default to RIPv1 output */ + if (ifp->int_state & IS_NO_RIPV1_OUT) { + /* Say nothing if this interface is turned off */ + if (ifp->int_state & IS_NO_RIPV2_OUT) + continue; + vers = RIPv2; + } else { + vers = RIPv1; + } + + if (ifp->int_if_flags & IFF_BROADCAST) { + /* ordinary, hardware interface */ + dst.sin_addr.s_addr = ifp->int_brdaddr; + /* if RIPv1 is not turned off, then broadcast so + * that RIPv1 listeners can hear. + */ + if (vers == RIPv2 + && (ifp->int_state & IS_NO_RIPV1_OUT)) { + type = OUT_MULTICAST; + } else { + type = OUT_BROADCAST; + } + + } else if (ifp->int_if_flags & IFF_POINTOPOINT) { + /* point-to-point hardware interface */ + dst.sin_addr.s_addr = ifp->int_dstaddr; + type = OUT_UNICAST; + + } else { + /* remote interface */ + dst.sin_addr.s_addr = ifp->int_addr; + type = OUT_UNICAST; + } + + supply(&dst, ifp, type, flash, vers); + } + + update_seqno++; /* all routes are up to date */ +} + + +/* Ask for routes + * Do it only once to an interface, and not even after the interface + * was broken and recovered. + */ +void +rip_query(void) +{ +#ifdef _HAVE_SIN_LEN + static struct sockaddr_in dst = {sizeof(dst), AF_INET}; +#else + static struct sockaddr_in dst = {AF_INET}; +#endif + struct interface *ifp; + struct rip buf; + enum output_type type; + + + if (rip_sock < 0) + return; + + bzero(&buf, sizeof(buf)); + + for (ifp = ifnet; ifp; ifp = ifp->int_next) { + /* skip interfaces not doing RIP, those already queried, + * and aliases. Do try broken interfaces to see + * if they have healed. + */ + if (0 != (ifp->int_state & (IS_RIP_QUERIED + | IS_PASSIVE | IS_ALIAS))) + continue; + + /* skip turned off interfaces */ + if (!iff_alive(ifp->int_if_flags)) + continue; + + /* default to RIPv1 output */ + if (ifp->int_state & IS_NO_RIPV2_OUT) { + /* Say nothing if this interface is turned off */ + if (ifp->int_state & IS_NO_RIPV1_OUT) + continue; + buf.rip_vers = RIPv1; + } else { + buf.rip_vers = RIPv2; + } + + buf.rip_cmd = RIPCMD_REQUEST; + buf.rip_nets[0].n_family = RIP_AF_UNSPEC; + buf.rip_nets[0].n_metric = htonl(HOPCNT_INFINITY); + + if (ifp->int_if_flags & IFF_BROADCAST) { + /* ordinary, hardware interface */ + dst.sin_addr.s_addr = ifp->int_brdaddr; + /* if RIPv1 is not turned off, then broadcast so + * that RIPv1 listeners can hear. + */ + if (buf.rip_vers == RIPv2 + && (ifp->int_state & IS_NO_RIPV1_OUT)) { + type = OUT_MULTICAST; + } else { + type = OUT_BROADCAST; + } + + } else if (ifp->int_if_flags & IFF_POINTOPOINT) { + /* point-to-point hardware interface */ + dst.sin_addr.s_addr = ifp->int_dstaddr; + type = OUT_UNICAST; + + } else { + /* remote interface */ + dst.sin_addr.s_addr = ifp->int_addr; + type = OUT_UNICAST; + } + + ifp->int_state |= IS_RIP_QUERIED; + if (output(type, &dst, ifp, &buf, sizeof(buf)) < 0) + if_sick(ifp); + } +} diff --git a/sbin/routed/parms.c b/sbin/routed/parms.c new file mode 100644 index 000000000000..0d178c3698bc --- /dev/null +++ b/sbin/routed/parms.c @@ -0,0 +1,630 @@ +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if !defined(lint) && !defined(sgi) && !defined(__NetBSD__) +static char sccsid[] = "@(#)if.c 8.1 (Berkeley) 6/5/93"; +#elif defined(__NetBSD__) +static char rcsid[] = "$NetBSD$"; +#endif +#ident "$Revision: 1.9 $" + +#include "defs.h" +#include "pathnames.h" + + +struct parm *parms; +struct intnet *intnets; + + +/* use configured parameters + */ +void +get_parms(struct interface *ifp) +{ + struct parm *parmp; + + /* get all relevant parameters + */ + for (parmp = parms; parmp != 0; parmp = parmp->parm_next) { + if ((parmp->parm_name[0] == '\0' + && on_net(ifp->int_addr, + parmp->parm_addr_h, parmp->parm_mask)) + || (parmp->parm_name[0] != '\0' + && !strcmp(ifp->int_name, parmp->parm_name))) { + /* this group of parameters is relevant, + * so get its settings + */ + ifp->int_state |= parmp->parm_int_state; + if (parmp->parm_passwd[0] != '\0') + bcopy(parmp->parm_passwd, ifp->int_passwd, + sizeof(ifp->int_passwd)); + if (parmp->parm_rdisc_pref != 0) + ifp->int_rdisc_pref = parmp->parm_rdisc_pref; + if (parmp->parm_rdisc_int != 0) + ifp->int_rdisc_int = parmp->parm_rdisc_int; + if (parmp->parm_d_metric != 0) + ifp->int_d_metric = parmp->parm_d_metric; + } + } + /* default poor-man's router discovery to a metric that will + * be heard by old versions of routed. + */ + if ((ifp->int_state & IS_PM_RDISC) + && ifp->int_d_metric == 0) + ifp->int_d_metric = HOPCNT_INFINITY-2; + + if (IS_RIP_IN_OFF(ifp->int_state)) + ifp->int_state |= IS_NO_RIP_OUT; + + if (ifp->int_rdisc_int == 0) + ifp->int_rdisc_int = DefMaxAdvertiseInterval; + + if (!(ifp->int_if_flags & IFF_MULTICAST) + && !(ifp->int_if_flags & IFF_POINTOPOINT)) + ifp->int_state |= IS_NO_RIPV2_OUT; + + if (!(ifp->int_if_flags & IFF_MULTICAST)) + ifp->int_state |= IS_BCAST_RDISC; + + if (ifp->int_if_flags & IFF_POINTOPOINT) { + ifp->int_state |= IS_BCAST_RDISC; + /* By default, point-to-point links should be passive + * about router-discovery for the sake of demand-dialing. + */ + if (0 == (ifp->int_state & GROUP_IS_SOL)) + ifp->int_state |= IS_NO_SOL_OUT; + if (0 == (ifp->int_state & GROUP_IS_ADV)) + ifp->int_state |= IS_NO_ADV_OUT; + } + + if (0 != (ifp->int_state & (IS_PASSIVE | IS_REMOTE))) + ifp->int_state |= IS_NO_RDISC; + if (ifp->int_state & IS_PASSIVE) + ifp->int_state |= (IS_NO_RIP | IS_NO_RDISC); + if ((ifp->int_state & (IS_NO_RIP | IS_NO_RDISC)) + == (IS_NO_RIP|IS_NO_RDISC)) + ifp->int_state |= IS_PASSIVE; +} + + +/* Read a list of gateways from /etc/gateways and add them to our tables. + * + * This file contains a list of "remote" gateways. That is usually + * a gateway which we cannot immediately determine if it is present or + * not as we can do for those provided by directly connected hardware. + * + * If a gateway is marked "passive" in the file, then we assume it + * does not understand RIP and assume it is always present. Those + * not marked passive are treated as if they were directly connected + * and assumed to be broken if they do not send us advertisements. + * All remote interfaces are added to our list, and those not marked + * passive are sent routing updates. + * + * A passive interface can also be local, hardware interface exempt + * from RIP. + */ +void +gwkludge(void) +{ + FILE *fp; + char *p, *lptr; + char lbuf[200], net_host[5], dname[64+1+64+1], gname[64+1], qual[9]; + struct interface *ifp; + naddr dst, netmask, gate; + int metric, n; + u_int state; + char *type; + struct parm *parmp; + + + fp = fopen(_PATH_GATEWAYS, "r"); + if (fp == 0) + return; + + for (;;) { + if (0 == fgets(lbuf, sizeof(lbuf)-1, fp)) + break; + lptr = lbuf; + while (*lptr == ' ') + lptr++; + if (*lptr == '\n' /* ignore null and comment lines */ + || *lptr == '#') + continue; + p = lptr+strlen(lptr)-1; + while (*p == '\n' + || *p == ' ') + *p-- = '\0'; + + /* notice newfangled parameter lines + */ + if (strncasecmp("net", lptr, 3) + && strncasecmp("host", lptr, 4)) { + p = parse_parms(lptr); + if (p != 0) { + if (strcmp(p,lptr)) + msglog("bad \"%s\" in "_PATH_GATEWAYS + " entry \"%s\"", lptr, p); + else + msglog("bad \"%s\" in "_PATH_GATEWAYS, + lptr); + } + continue; + } + +/* {net | host} XX[/M] XX gateway XX metric DD [passive | external]\n */ + n = sscanf(lptr, "%4s %129[^ \t] gateway" + " %64[^ / \t] metric %d %8s\n", + net_host, dname, gname, &metric, qual); + if (n != 5) { + msglog("bad "_PATH_GATEWAYS" entry \"%s\"", lptr); + continue; + } + if (metric < 0 || metric >= HOPCNT_INFINITY) { + msglog("bad metric in "_PATH_GATEWAYS" entry \"%s\"", + lptr); + continue; + } + if (!strcmp(net_host, "host")) { + if (!gethost(dname, &dst)) { + msglog("bad host \"%s\" in "_PATH_GATEWAYS + " entry \"%s\"", dname, lptr); + continue; + } + netmask = HOST_MASK; + } else if (!strcmp(net_host, "net")) { + if (!getnet(dname, &dst, &netmask)) { + msglog("bad net \"%s\" in "_PATH_GATEWAYS + " entry \"%s\"", dname, lptr); + continue; + } + } else { + msglog("bad \"%s\" in "_PATH_GATEWAYS + " entry \"%s\"", lptr); + continue; + } + + if (!gethost(gname, &gate)) { + msglog("bad gateway \"%s\" in "_PATH_GATEWAYS + " entry \"%s\"", gname, lptr); + continue; + } + + if (strcmp(qual, type = "passive") == 0) { + /* Passive entries are not placed in our tables, + * only the kernel's, so we don't copy all of the + * external routing information within a net. + * Internal machines should use the default + * route to a suitable gateway (like us). + */ + state = IS_REMOTE | IS_PASSIVE; + if (metric == 0) + metric = 1; + + } else if (strcmp(qual, type = "external") == 0) { + /* External entries are handled by other means + * such as EGP, and are placed only in the daemon + * tables to prevent overriding them with something + * else. + */ + state = IS_REMOTE | IS_PASSIVE | IS_EXTERNAL; + if (metric == 0) + metric = 1; + + } else if (qual[0] == '\0') { + if (metric != 0) { + /* Entries that are neither "passive" nor + * "external" are "remote" and must behave + * like physical interfaces. If they are not + * heard from regularly, they are deleted. + */ + state = IS_REMOTE; + type = "remote"; + } else { + /* "remote" entries with a metric of 0 + * are aliases for our own interfaces + */ + state = IS_REMOTE | IS_PASSIVE; + type = "alias"; + } + + } else { + msglog("bad "_PATH_GATEWAYS" entry \"%s\"", lptr); + continue; + } + + /* Remember to advertise the corresponding logical network. + */ + if (!(state & IS_EXTERNAL) + && netmask != std_mask(dst)) + state |= IS_SUBNET; + + if (0 != (state & (IS_PASSIVE | IS_REMOTE))) + state |= IS_NO_RDISC; + if (state & IS_PASSIVE) + state |= (IS_NO_RIP | IS_NO_RDISC); + if ((state & (IS_NO_RIP | IS_NO_RDISC)) + == (IS_NO_RIP|IS_NO_RDISC)) + state |= IS_PASSIVE; + + parmp = (struct parm*)malloc(sizeof(*parmp)); + bzero(parmp, sizeof(*parmp)); + parmp->parm_next = parms; + parms = parmp; + parmp->parm_addr_h = ntohl(dst); + parmp->parm_mask = -1; + parmp->parm_d_metric = 0; + parmp->parm_int_state = state; + + /* See if this new interface duplicates an existing + * interface. + */ + for (ifp = ifnet; 0 != ifp; ifp = ifp->int_next) { + if (ifp->int_mask == netmask + && ((ifp->int_addr == dst + && netmask != HOST_MASK) + || (ifp->int_dstaddr == dst + && netmask == HOST_MASK))) + break; + } + if (ifp != 0) { + /* Let one of our real interfaces be marked passive. + */ + if ((state & IS_PASSIVE) && !(state & IS_EXTERNAL)) { + ifp->int_state |= state; + } else { + msglog("%s is duplicated in "_PATH_GATEWAYS + " by %s", + ifp->int_name, lptr); + } + continue; + } + + tot_interfaces++; + + ifp = (struct interface *)malloc(sizeof(*ifp)); + bzero(ifp, sizeof(*ifp)); + if (ifnet != 0) { + ifp->int_next = ifnet; + ifnet->int_prev = ifp; + } + ifnet = ifp; + + ifp->int_state = state; + ifp->int_net = ntohl(dst) & netmask; + ifp->int_mask = netmask; + if (netmask == HOST_MASK) + ifp->int_if_flags |= IFF_POINTOPOINT; + ifp->int_dstaddr = dst; + ifp->int_addr = gate; + ifp->int_metric = metric; + (void)sprintf(ifp->int_name, "%s-%s", type, naddr_ntoa(dst)); + ifp->int_index = -1; + + get_parms(ifp); + + trace_if("Add", ifp); + } +} + + +/* parse a set of parameters for an interface + */ +char * /* 0 or error message */ +parse_parms(char *line) +{ +#define PARS(str) (0 == (tgt = str, strcasecmp(tok, tgt))) +#define PARSE(str) (0 == (tgt = str, strncasecmp(tok, str "=", sizeof(str)))) +#define CKF(g,b) {if (0 != (parm.parm_int_state & ((g) & ~(b)))) break; \ + parm.parm_int_state |= (b);} +#define DELIMS " ,\t\n" + struct parm parm; + struct intnet *intnetp; + char *tok, *tgt, *p; + + + /* "subnet=x.y.z.u/mask" must be alone on the line */ + if (!strncasecmp("subnet=",line,7)) { + intnetp = (struct intnet*)malloc(sizeof(*intnetp)); + intnetp->intnet_metric = 1; + if ((p = strrchr(line,','))) { + *p++ = '\0'; + intnetp->intnet_metric = (int)strtol(p,&p,0); + if (*p != '\0' + || intnetp->intnet_metric <= 0 + || intnetp->intnet_metric >= HOPCNT_INFINITY) + return line; + } + if (!getnet(&line[7], &intnetp->intnet_addr, + &intnetp->intnet_mask) + || intnetp->intnet_mask == HOST_MASK + || intnetp->intnet_addr == RIP_DEFAULT) { + free(intnetp); + return line; + } + intnetp->intnet_next = intnets; + intnets = intnetp; + return 0; + } + + bzero(&parm, sizeof(parm)); + + tgt = "null"; + for (tok = strtok(line, DELIMS); + tok != 0 && tok[0] != '\0'; + tgt = 0, tok = strtok(0,DELIMS)) { + if (PARSE("if")) { + if (parm.parm_name[0] != '\0' + || tok[3] == '\0' + || strlen(tok) > IFNAMSIZ+3) + break; + strcpy(parm.parm_name, tok+3); + + } else if (PARSE("passwd")) { + if (tok[7] == '\0' + || strlen(tok) > RIP_AUTH_PW_LEN+7) + break; + strcpy(parm.parm_passwd, tok+7); + + } else if (PARS("no_ag")) { + parm.parm_int_state |= (IS_NO_AG | IS_NO_SUPER_AG); + + } else if (PARS("no_super_ag")) { + parm.parm_int_state |= IS_NO_SUPER_AG; + + } else if (PARS("no_ripv1_in")) { + parm.parm_int_state |= IS_NO_RIPV1_IN; + + } else if (PARS("no_ripv2_in")) { + parm.parm_int_state |= IS_NO_RIPV2_IN; + + } else if (PARS("ripv2_out")) { + if (parm.parm_int_state & IS_NO_RIPV2_OUT) + break; + parm.parm_int_state |= IS_NO_RIPV1_OUT; + + } else if (PARS("no_rip")) { + parm.parm_int_state |= IS_NO_RIP; + + } else if (PARS("no_rdisc")) { + CKF((GROUP_IS_SOL|GROUP_IS_ADV), IS_NO_RDISC); + + } else if (PARS("no_solicit")) { + CKF(GROUP_IS_SOL, IS_NO_SOL_OUT); + + } else if (PARS("send_solicit")) { + CKF(GROUP_IS_SOL, IS_SOL_OUT); + + } else if (PARS("no_rdisc_adv")) { + CKF(GROUP_IS_ADV, IS_NO_ADV_OUT); + + } else if (PARS("rdisc_adv")) { + CKF(GROUP_IS_ADV, IS_ADV_OUT); + + } else if (PARS("bcast_rdisc")) { + parm.parm_int_state |= IS_BCAST_RDISC; + + } else if (PARS("passive")) { + CKF((GROUP_IS_SOL|GROUP_IS_ADV), IS_NO_RDISC); + parm.parm_int_state |= IS_NO_RIP; + + } else if (PARSE("rdisc_pref")) { + if (parm.parm_rdisc_pref != 0 + || tok[11] == '\0' + || (parm.parm_rdisc_pref = (int)strtol(&tok[11], + &p,0), + *p != '\0')) + break; + + } else if (PARS("pm_rdisc")) { + parm.parm_int_state |= IS_PM_RDISC; + + } else if (PARSE("rdisc_interval")) { + if (parm.parm_rdisc_int != 0 + || tok[15] == '\0' + || (parm.parm_rdisc_int = (int)strtol(&tok[15], + &p,0), + *p != '\0') + || parm.parm_rdisc_int < MinMaxAdvertiseInterval + || parm.parm_rdisc_int > MaxMaxAdvertiseInterval) + break; + + } else if (PARSE("fake_default")) { + if (parm.parm_d_metric != 0 + || tok[13] == '\0' + || (parm.parm_d_metric=(int)strtol(&tok[13],&p,0), + *p != '\0') + || parm.parm_d_metric > HOPCNT_INFINITY-1) + break; + + } else { + tgt = tok; + break; + } + } + if (tgt != 0) + return tgt; + + return check_parms(&parm); +#undef DELIMS +#undef PARS +#undef PARSE +} + + +/* check for duplicate parameter specifications */ +char * /* 0 or error message */ +check_parms(struct parm *new) +{ + struct parm *parmp; + + + /* set implicit values + */ + if (!supplier && supplier_set) + new->parm_int_state |= (IS_NO_RIPV1_OUT + | IS_NO_RIPV2_OUT + | IS_NO_ADV_OUT); + if (new->parm_int_state & IS_NO_ADV_IN) + new->parm_int_state |= IS_NO_SOL_OUT; + + if ((new->parm_int_state & (IS_NO_RIP | IS_NO_RDISC)) + == (IS_NO_RIP | IS_NO_RDISC)) + new->parm_int_state |= IS_PASSIVE; + + /* compare with existing sets of parameters + */ + for (parmp = parms; parmp != 0; parmp = parmp->parm_next) { + if (strcmp(new->parm_name, parmp->parm_name)) + continue; + if (!on_net(htonl(parmp->parm_addr_h), + new->parm_addr_h, new->parm_mask) + && !on_net(htonl(new->parm_addr_h), + parmp->parm_addr_h, parmp->parm_mask)) + continue; + + if (strcmp(parmp->parm_passwd, new->parm_passwd) + || (0 != (new->parm_int_state & GROUP_IS_SOL) + && 0 != (parmp->parm_int_state & GROUP_IS_SOL) + && 0 != ((new->parm_int_state ^ parmp->parm_int_state) + && GROUP_IS_SOL)) + || (0 != (new->parm_int_state & GROUP_IS_ADV) + && 0 != (parmp->parm_int_state & GROUP_IS_ADV) + && 0 != ((new->parm_int_state ^ parmp->parm_int_state) + && GROUP_IS_ADV)) + || (new->parm_rdisc_pref != 0 + && parmp->parm_rdisc_pref != 0 + && new->parm_rdisc_pref != parmp->parm_rdisc_pref) + || (new->parm_rdisc_int != 0 + && parmp->parm_rdisc_int != 0 + && new->parm_rdisc_int != parmp->parm_rdisc_int) + || (new->parm_d_metric != 0 + && parmp->parm_d_metric != 0 + && new->parm_d_metric != parmp->parm_d_metric)) + return "duplicate"; + } + + parmp = (struct parm*)malloc(sizeof(*parmp)); + bcopy(new, parmp, sizeof(*parmp)); + parmp->parm_next = parms; + parms = parmp; + + return 0; +} + + +/* get a network number as a name or a number, with an optional "/xx" + * netmask. + */ +int /* 0=bad */ +getnet(char *name, + naddr *addrp, /* host byte order */ + naddr *maskp) +{ + int i; + struct netent *np; + naddr mask; + struct in_addr in; + char hname[MAXHOSTNAMELEN+1]; + char *mname, *p; + + + /* Detect and separate "1.2.3.4/24" + */ + if (0 != (mname = rindex(name,'/'))) { + i = (int)(mname - name); + if (i > sizeof(hname)-1) /* name too long */ + return 0; + bcopy(name, hname, i); + hname[i] = '\0'; + mname++; + name = hname; + } + + np = getnetbyname(name); + if (np != 0) { + in.s_addr = (naddr)np->n_net; + } else if (inet_aton(name, &in) == 1) { + HTONL(in.s_addr); + } else { + return 0; + } + + if (mname == 0) { + /* we cannot use the interfaces here because we have not + * looked at them yet. + */ + mask = std_mask(in.s_addr); + if ((~mask & ntohl(in.s_addr)) != 0) + mask = HOST_MASK; + } else { + mask = (naddr)strtoul(mname, &p, 0); + if (*p != '\0' || mask > 32) + return 0; + mask = HOST_MASK << (32-mask); + } + if (mask != 0 && in.s_addr == RIP_DEFAULT) + return 0; + if ((~mask & ntohl(in.s_addr)) != 0) + return 0; + + *addrp = in.s_addr; + *maskp = mask; + return 1; +} + + +int /* 0=bad */ +gethost(char *name, + naddr *addrp) +{ + struct hostent *hp; + struct in_addr in; + + + /* Try for a number first, even in IRIX where gethostbyname() + * is smart. This avoids hitting the name server which + * might be sick because routing is. + */ + if (inet_aton(name, &in) == 1) { + *addrp = in.s_addr; + return 1; + } + + hp = gethostbyname(name); + if (hp) { + bcopy(hp->h_addr, addrp, sizeof(*addrp)); + return 1; + } + + return 0; +} diff --git a/sbin/routed/pathnames.h b/sbin/routed/pathnames.h new file mode 100644 index 000000000000..14b721c2d473 --- /dev/null +++ b/sbin/routed/pathnames.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)pathnames.h 8.1 (Berkeley) 6/5/93 + * + * $NetBSD$ + */ + +#include + +#define _PATH_GATEWAYS "/etc/gateways" + +/* All remotely requested trace files must either start with this prefix + * or be the same as the tracefile specified when the daemon was started. + * If this is a directory, routed will create log files in it. That + * might be a security problem. + * + * Leave this undefined, and only the trace file originally specified + * when routed was started, if any, will be appended to. + */ +#define _PATH_TRACE "/etc/routed.trace" diff --git a/sbin/routed/radix.c b/sbin/routed/radix.c new file mode 100644 index 000000000000..7f7e1e47b79e --- /dev/null +++ b/sbin/routed/radix.c @@ -0,0 +1,895 @@ +/* + * Copyright (c) 1988, 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)radix.c 8.4 (Berkeley) 11/2/94 + */ + +/* + * Routines to build and maintain radix trees for routing lookups. + */ +#if !defined(lint) && !defined(sgi) && !defined(__NetBSD__) +static char sccsid[] = "@(#)rdisc.c 8.1 (Berkeley) x/y/95"; +#elif defined(__NetBSD__) +static char rcsid[] = "$NetBSD$"; +#endif +#ident "$Revision: 1.10 $" + +#include "defs.h" + +#define log(x, msg) syslog(x, msg) +#define panic(s) {log(LOG_ERR,s); exit(1);} +#define min(a,b) (((a)<(b))?(a):(b)) + +int max_keylen; +struct radix_mask *rn_mkfreelist; +struct radix_node_head *mask_rnhead; +static char *addmask_key; +static char normal_chars[] = {0, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, -1}; +static char *rn_zeros, *rn_ones; + +#define rn_masktop (mask_rnhead->rnh_treetop) +#undef Bcmp +#define Bcmp(a, b, l) (l == 0 ? 0 : bcmp((caddr_t)(a), (caddr_t)(b), (u_long)l)) + +static int rn_satsifies_leaf(char *, struct radix_node *, int); + +/* + * The data structure for the keys is a radix tree with one way + * branching removed. The index rn_b at an internal node n represents a bit + * position to be tested. The tree is arranged so that all descendants + * of a node n have keys whose bits all agree up to position rn_b - 1. + * (We say the index of n is rn_b.) + * + * There is at least one descendant which has a one bit at position rn_b, + * and at least one with a zero there. + * + * A route is determined by a pair of key and mask. We require that the + * bit-wise logical and of the key and mask to be the key. + * We define the index of a route to associated with the mask to be + * the first bit number in the mask where 0 occurs (with bit number 0 + * representing the highest order bit). + * + * We say a mask is normal if every bit is 0, past the index of the mask. + * If a node n has a descendant (k, m) with index(m) == index(n) == rn_b, + * and m is a normal mask, then the route applies to every descendant of n. + * If the index(m) < rn_b, this implies the trailing last few bits of k + * before bit b are all 0, (and hence consequently true of every descendant + * of n), so the route applies to all descendants of the node as well. + * + * Similar logic shows that a non-normal mask m such that + * index(m) <= index(n) could potentially apply to many children of n. + * Thus, for each non-host route, we attach its mask to a list at an internal + * node as high in the tree as we can go. + * + * The present version of the code makes use of normal routes in short- + * circuiting an explict mask and compare operation when testing whether + * a key satisfies a normal route, and also in remembering the unique leaf + * that governs a subtree. + */ + +struct radix_node * +rn_search(void *v_arg, + struct radix_node *head) +{ + register struct radix_node *x; + register caddr_t v; + + for (x = head, v = v_arg; x->rn_b >= 0;) { + if (x->rn_bmask & v[x->rn_off]) + x = x->rn_r; + else + x = x->rn_l; + } + return (x); +} + +struct radix_node * +rn_search_m(void *v_arg, + struct radix_node *head, + void *m_arg) +{ + register struct radix_node *x; + register caddr_t v = v_arg, m = m_arg; + + for (x = head; x->rn_b >= 0;) { + if ((x->rn_bmask & m[x->rn_off]) && + (x->rn_bmask & v[x->rn_off])) + x = x->rn_r; + else + x = x->rn_l; + } + return x; +} + +int +rn_refines(void* m_arg, void *n_arg) +{ + register caddr_t m = m_arg, n = n_arg; + register caddr_t lim, lim2 = lim = n + *(u_char *)n; + int longer = (*(u_char *)n++) - (int)(*(u_char *)m++); + int masks_are_equal = 1; + + if (longer > 0) + lim -= longer; + while (n < lim) { + if (*n & ~(*m)) + return 0; + if (*n++ != *m++) + masks_are_equal = 0; + } + while (n < lim2) + if (*n++) + return 0; + if (masks_are_equal && (longer < 0)) + for (lim2 = m - longer; m < lim2; ) + if (*m++) + return 1; + return (!masks_are_equal); +} + +struct radix_node * +rn_lookup(v_arg, m_arg, head) + void *v_arg, *m_arg; + struct radix_node_head *head; +{ + register struct radix_node *x; + caddr_t netmask = 0; + + if (m_arg) { + if ((x = rn_addmask(m_arg, 1, head->rnh_treetop->rn_off)) == 0) + return (0); + netmask = x->rn_key; + } + x = rn_match(v_arg, head); + if (x && netmask) { + while (x && x->rn_mask != netmask) + x = x->rn_dupedkey; + } + return x; +} + +static int +rn_satsifies_leaf(char *trial, + register struct radix_node *leaf, + int skip) +{ + register char *cp = trial, *cp2 = leaf->rn_key, *cp3 = leaf->rn_mask; + char *cplim; + int length = min(*(u_char *)cp, *(u_char *)cp2); + + if (cp3 == 0) + cp3 = rn_ones; + else + length = min(length, *(u_char *)cp3); + cplim = cp + length; cp3 += skip; cp2 += skip; + for (cp += skip; cp < cplim; cp++, cp2++, cp3++) + if ((*cp ^ *cp2) & *cp3) + return 0; + return 1; +} + +struct radix_node * +rn_match(void *v_arg, + struct radix_node_head *head) +{ + caddr_t v = v_arg; + register struct radix_node *t = head->rnh_treetop, *x; + register caddr_t cp = v, cp2; + caddr_t cplim; + struct radix_node *saved_t, *top = t; + int off = t->rn_off, vlen = *(u_char *)cp, matched_off; + register int test, b, rn_b; + + /* + * Open code rn_search(v, top) to avoid overhead of extra + * subroutine call. + */ + for (; t->rn_b >= 0; ) { + if (t->rn_bmask & cp[t->rn_off]) + t = t->rn_r; + else + t = t->rn_l; + } + /* + * See if we match exactly as a host destination + * or at least learn how many bits match, for normal mask finesse. + * + * It doesn't hurt us to limit how many bytes to check + * to the length of the mask, since if it matches we had a genuine + * match and the leaf we have is the most specific one anyway; + * if it didn't match with a shorter length it would fail + * with a long one. This wins big for class B&C netmasks which + * are probably the most common case... + */ + if (t->rn_mask) + vlen = *(u_char *)t->rn_mask; + cp += off; cp2 = t->rn_key + off; cplim = v + vlen; + for (; cp < cplim; cp++, cp2++) + if (*cp != *cp2) + goto on1; + /* + * This extra grot is in case we are explicitly asked + * to look up the default. Ugh! + * Or 255.255.255.255 + * + * In this case, we have a complete match of the key. Unless + * the node is one of the roots, we are finished. + * If it is the zeros root, then take what we have, prefering + * any real data. + * If it is the ones root, then pretend the target key was followed + * by a byte of zeros. + */ + if (!(t->rn_flags & RNF_ROOT)) + return t; /* not a root */ + if (t->rn_dupedkey) { + t = t->rn_dupedkey; + return t; /* have some real data */ + } + if (*(cp-1) == 0) + return t; /* not the ones root */ + b = 0; /* fake a zero after 255.255.255.255 */ + goto on2; +on1: + test = (*cp ^ *cp2) & 0xff; /* find first bit that differs */ + for (b = 7; (test >>= 1) > 0;) + b--; +on2: + matched_off = cp - v; + b += matched_off << 3; + rn_b = -1 - b; + /* + * If there is a host route in a duped-key chain, it will be first. + */ + if ((saved_t = t)->rn_mask == 0) + t = t->rn_dupedkey; + for (; t; t = t->rn_dupedkey) + /* + * Even if we don't match exactly as a host, + * we may match if the leaf we wound up at is + * a route to a net. + */ + if (t->rn_flags & RNF_NORMAL) { + if (rn_b <= t->rn_b) + return t; + } else if (rn_satsifies_leaf(v, t, matched_off)) + return t; + t = saved_t; + /* start searching up the tree */ + do { + register struct radix_mask *m; + t = t->rn_p; + if ((m = t->rn_mklist)) { + /* + * If non-contiguous masks ever become important + * we can restore the masking and open coding of + * the search and satisfaction test and put the + * calculation of "off" back before the "do". + */ + do { + if (m->rm_flags & RNF_NORMAL) { + if (rn_b <= m->rm_b) + return (m->rm_leaf); + } else { + off = min(t->rn_off, matched_off); + x = rn_search_m(v, t, m->rm_mask); + while (x && x->rn_mask != m->rm_mask) + x = x->rn_dupedkey; + if (x && rn_satsifies_leaf(v, x, off)) + return x; + } + } while ((m = m->rm_mklist)); + } + } while (t != top); + return 0; +} + +#ifdef RN_DEBUG +int rn_nodenum; +struct radix_node *rn_clist; +int rn_saveinfo; +int rn_debug = 1; +#endif + +struct radix_node * +rn_newpair(void *v, int b, struct radix_node nodes[2]) +{ + register struct radix_node *tt = nodes, *t = tt + 1; + t->rn_b = b; t->rn_bmask = 0x80 >> (b & 7); + t->rn_l = tt; t->rn_off = b >> 3; + tt->rn_b = -1; tt->rn_key = (caddr_t)v; tt->rn_p = t; + tt->rn_flags = t->rn_flags = RNF_ACTIVE; +#ifdef RN_DEBUG + tt->rn_info = rn_nodenum++; t->rn_info = rn_nodenum++; + tt->rn_twin = t; tt->rn_ybro = rn_clist; rn_clist = tt; +#endif + return t; +} + +struct radix_node * +rn_insert(void* v_arg, + struct radix_node_head *head, + int *dupentry, + struct radix_node nodes[2]) +{ + caddr_t v = v_arg; + struct radix_node *top = head->rnh_treetop; + int head_off = top->rn_off, vlen = (int)*((u_char *)v); + register struct radix_node *t = rn_search(v_arg, top); + register caddr_t cp = v + head_off; + register int b; + struct radix_node *tt; + + /* + * Find first bit at which v and t->rn_key differ + */ + { + register caddr_t cp2 = t->rn_key + head_off; + register int cmp_res; + caddr_t cplim = v + vlen; + + while (cp < cplim) + if (*cp2++ != *cp++) + goto on1; + /* handle adding 255.255.255.255 */ + if (!(t->rn_flags & RNF_ROOT) || *(cp2-1) == 0) { + *dupentry = 1; + return t; + } +on1: + *dupentry = 0; + cmp_res = (cp[-1] ^ cp2[-1]) & 0xff; + for (b = (cp - v) << 3; cmp_res; b--) + cmp_res >>= 1; + } + { + register struct radix_node *p, *x = top; + cp = v; + do { + p = x; + if (cp[x->rn_off] & x->rn_bmask) + x = x->rn_r; + else x = x->rn_l; + } while (b > (unsigned) x->rn_b); /* x->rn_b < b && x->rn_b >= 0 */ +#ifdef RN_DEBUG + if (rn_debug) + log(LOG_DEBUG, "rn_insert: Going In:\n"), traverse(p); +#endif + t = rn_newpair(v_arg, b, nodes); tt = t->rn_l; + if ((cp[p->rn_off] & p->rn_bmask) == 0) + p->rn_l = t; + else + p->rn_r = t; + x->rn_p = t; t->rn_p = p; /* frees x, p as temp vars below */ + if ((cp[t->rn_off] & t->rn_bmask) == 0) { + t->rn_r = x; + } else { + t->rn_r = tt; t->rn_l = x; + } +#ifdef RN_DEBUG + if (rn_debug) + log(LOG_DEBUG, "rn_insert: Coming Out:\n"), traverse(p); +#endif + } + return (tt); +} + +struct radix_node * +rn_addmask(void *n_arg, int search, int skip) +{ + caddr_t netmask = (caddr_t)n_arg; + register struct radix_node *x; + register caddr_t cp, cplim; + register int b = 0, mlen, j; + int maskduplicated, m0, isnormal; + struct radix_node *saved_x; + static int last_zeroed = 0; + + if ((mlen = *(u_char *)netmask) > max_keylen) + mlen = max_keylen; + if (skip == 0) + skip = 1; + if (mlen <= skip) + return (mask_rnhead->rnh_nodes); + if (skip > 1) + Bcopy(rn_ones + 1, addmask_key + 1, skip - 1); + if ((m0 = mlen) > skip) + Bcopy(netmask + skip, addmask_key + skip, mlen - skip); + /* + * Trim trailing zeroes. + */ + for (cp = addmask_key + mlen; (cp > addmask_key) && cp[-1] == 0;) + cp--; + mlen = cp - addmask_key; + if (mlen <= skip) { + if (m0 >= last_zeroed) + last_zeroed = mlen; + return (mask_rnhead->rnh_nodes); + } + if (m0 < last_zeroed) + Bzero(addmask_key + m0, last_zeroed - m0); + *addmask_key = last_zeroed = mlen; + x = rn_search(addmask_key, rn_masktop); + if (Bcmp(addmask_key, x->rn_key, mlen) != 0) + x = 0; + if (x || search) + return (x); + R_Malloc(x, struct radix_node *, max_keylen + 2 * sizeof (*x)); + if ((saved_x = x) == 0) + return (0); + Bzero(x, max_keylen + 2 * sizeof (*x)); + netmask = cp = (caddr_t)(x + 2); + Bcopy(addmask_key, cp, mlen); + x = rn_insert(cp, mask_rnhead, &maskduplicated, x); + if (maskduplicated) { + log(LOG_ERR, "rn_addmask: mask impossibly already in tree"); + Free(saved_x); + return (x); + } + /* + * Calculate index of mask, and check for normalcy. + */ + cplim = netmask + mlen; isnormal = 1; + for (cp = netmask + skip; (cp < cplim) && *(u_char *)cp == 0xff;) + cp++; + if (cp != cplim) { + for (j = 0x80; (j & *cp) != 0; j >>= 1) + b++; + if (*cp != normal_chars[b] || cp != (cplim - 1)) + isnormal = 0; + } + b += (cp - netmask) << 3; + x->rn_b = -1 - b; + if (isnormal) + x->rn_flags |= RNF_NORMAL; + return (x); +} + +static int /* XXX: arbitrary ordering for non-contiguous masks */ +rn_lexobetter(void *m_arg, void *n_arg) +{ + register u_char *mp = m_arg, *np = n_arg, *lim; + + if (*mp > *np) + return 1; /* not really, but need to check longer one first */ + if (*mp == *np) + for (lim = mp + *mp; mp < lim;) + if (*mp++ > *np++) + return 1; + return 0; +} + +static struct radix_mask * +rn_new_radix_mask(register struct radix_node *tt, + register struct radix_mask *next) +{ + register struct radix_mask *m; + + MKGet(m); + if (m == 0) { + log(LOG_ERR, "Mask for route not entered\n"); + return (0); + } + Bzero(m, sizeof *m); + m->rm_b = tt->rn_b; + m->rm_flags = tt->rn_flags; + if (tt->rn_flags & RNF_NORMAL) + m->rm_leaf = tt; + else + m->rm_mask = tt->rn_mask; + m->rm_mklist = next; + tt->rn_mklist = m; + return m; +} + +struct radix_node * +rn_addroute(void *v_arg, + void *n_arg, + struct radix_node_head *head, + struct radix_node treenodes[2]) +{ + caddr_t v = (caddr_t)v_arg, netmask = (caddr_t)n_arg; + register struct radix_node *t, *x = 0, *tt; + struct radix_node *saved_tt, *top = head->rnh_treetop; + short b = 0, b_leaf = 0; + int keyduplicated; + caddr_t mmask; + struct radix_mask *m, **mp; + + /* + * In dealing with non-contiguous masks, there may be + * many different routes which have the same mask. + * We will find it useful to have a unique pointer to + * the mask to speed avoiding duplicate references at + * nodes and possibly save time in calculating indices. + */ + if (netmask) { + if ((x = rn_addmask(netmask, 0, top->rn_off)) == 0) + return (0); + b_leaf = x->rn_b; + b = -1 - x->rn_b; + netmask = x->rn_key; + } + /* + * Deal with duplicated keys: attach node to previous instance + */ + saved_tt = tt = rn_insert(v, head, &keyduplicated, treenodes); + if (keyduplicated) { + for (t = tt; tt; t = tt, tt = tt->rn_dupedkey) { + if (tt->rn_mask == netmask) + return (0); + if (netmask == 0 || + (tt->rn_mask && + ((b_leaf < tt->rn_b) || /* index(netmask) > node */ + rn_refines(netmask, tt->rn_mask) || + rn_lexobetter(netmask, tt->rn_mask)))) + break; + } + /* + * If the mask is not duplicated, we wouldn't + * find it among possible duplicate key entries + * anyway, so the above test doesn't hurt. + * + * We sort the masks for a duplicated key the same way as + * in a masklist -- most specific to least specific. + * This may require the unfortunate nuisance of relocating + * the head of the list. + */ + if (tt == saved_tt) { + struct radix_node *xx = x; + /* link in at head of list */ + (tt = treenodes)->rn_dupedkey = t; + tt->rn_flags = t->rn_flags; + tt->rn_p = x = t->rn_p; + if (x->rn_l == t) x->rn_l = tt; else x->rn_r = tt; + saved_tt = tt; x = xx; + } else { + (tt = treenodes)->rn_dupedkey = t->rn_dupedkey; + t->rn_dupedkey = tt; + } +#ifdef RN_DEBUG + t=tt+1; tt->rn_info = rn_nodenum++; t->rn_info = rn_nodenum++; + tt->rn_twin = t; tt->rn_ybro = rn_clist; rn_clist = tt; +#endif + tt->rn_key = (caddr_t) v; + tt->rn_b = -1; + tt->rn_flags = RNF_ACTIVE; + } + /* + * Put mask in tree. + */ + if (netmask) { + tt->rn_mask = netmask; + tt->rn_b = x->rn_b; + tt->rn_flags |= x->rn_flags & RNF_NORMAL; + } + t = saved_tt->rn_p; + if (keyduplicated) + goto on2; + b_leaf = -1 - t->rn_b; + if (t->rn_r == saved_tt) x = t->rn_l; else x = t->rn_r; + /* Promote general routes from below */ + if (x->rn_b < 0) { + for (mp = &t->rn_mklist; x; x = x->rn_dupedkey) + if (x->rn_mask && (x->rn_b >= b_leaf) && x->rn_mklist == 0) { + if ((*mp = m = rn_new_radix_mask(x, 0))) + mp = &m->rm_mklist; + } + } else if (x->rn_mklist) { + /* + * Skip over masks whose index is > that of new node + */ + for (mp = &x->rn_mklist; (m = *mp); mp = &m->rm_mklist) + if (m->rm_b >= b_leaf) + break; + t->rn_mklist = m; *mp = 0; + } +on2: + /* Add new route to highest possible ancestor's list */ + if ((netmask == 0) || (b > t->rn_b )) + return tt; /* can't lift at all */ + b_leaf = tt->rn_b; + do { + x = t; + t = t->rn_p; + } while (b <= t->rn_b && x != top); + /* + * Search through routes associated with node to + * insert new route according to index. + * Need same criteria as when sorting dupedkeys to avoid + * double loop on deletion. + */ + for (mp = &x->rn_mklist; (m = *mp); mp = &m->rm_mklist) { + if (m->rm_b < b_leaf) + continue; + if (m->rm_b > b_leaf) + break; + if (m->rm_flags & RNF_NORMAL) { + mmask = m->rm_leaf->rn_mask; + if (tt->rn_flags & RNF_NORMAL) { + log(LOG_ERR, + "Non-unique normal route, mask not entered"); + return tt; + } + } else + mmask = m->rm_mask; + if (mmask == netmask) { + m->rm_refs++; + tt->rn_mklist = m; + return tt; + } + if (rn_refines(netmask, mmask) || rn_lexobetter(netmask, mmask)) + break; + } + *mp = rn_new_radix_mask(tt, *mp); + return tt; +} + +struct radix_node * +rn_delete(void *v_arg, + void *netmask_arg, + struct radix_node_head *head) +{ + register struct radix_node *t, *p, *x, *tt; + struct radix_mask *m, *saved_m, **mp; + struct radix_node *dupedkey, *saved_tt, *top; + caddr_t v, netmask; + int b, head_off, vlen; + + v = v_arg; + netmask = netmask_arg; + x = head->rnh_treetop; + tt = rn_search(v, x); + head_off = x->rn_off; + vlen = *(u_char *)v; + saved_tt = tt; + top = x; + if (tt == 0 || + Bcmp(v + head_off, tt->rn_key + head_off, vlen - head_off)) + return (0); + /* + * Delete our route from mask lists. + */ + if (netmask) { + if ((x = rn_addmask(netmask, 1, head_off)) == 0) + return (0); + netmask = x->rn_key; + while (tt->rn_mask != netmask) + if ((tt = tt->rn_dupedkey) == 0) + return (0); + } + if (tt->rn_mask == 0 || (saved_m = m = tt->rn_mklist) == 0) + goto on1; + if (tt->rn_flags & RNF_NORMAL) { + if (m->rm_leaf != tt || m->rm_refs > 0) { + log(LOG_ERR, "rn_delete: inconsistent annotation\n"); + return 0; /* dangling ref could cause disaster */ + } + } else { + if (m->rm_mask != tt->rn_mask) { + log(LOG_ERR, "rn_delete: inconsistent annotation\n"); + goto on1; + } + if (--m->rm_refs >= 0) + goto on1; + } + b = -1 - tt->rn_b; + t = saved_tt->rn_p; + if (b > t->rn_b) + goto on1; /* Wasn't lifted at all */ + do { + x = t; + t = t->rn_p; + } while (b <= t->rn_b && x != top); + for (mp = &x->rn_mklist; (m = *mp); mp = &m->rm_mklist) + if (m == saved_m) { + *mp = m->rm_mklist; + MKFree(m); + break; + } + if (m == 0) { + log(LOG_ERR, "rn_delete: couldn't find our annotation\n"); + if (tt->rn_flags & RNF_NORMAL) + return (0); /* Dangling ref to us */ + } +on1: + /* + * Eliminate us from tree + */ + if (tt->rn_flags & RNF_ROOT) + return (0); +#ifdef RN_DEBUG + /* Get us out of the creation list */ + for (t = rn_clist; t && t->rn_ybro != tt; t = t->rn_ybro) {} + if (t) t->rn_ybro = tt->rn_ybro; +#endif + t = tt->rn_p; + if ((dupedkey = saved_tt->rn_dupedkey)) { + if (tt == saved_tt) { + x = dupedkey; x->rn_p = t; + if (t->rn_l == tt) t->rn_l = x; else t->rn_r = x; + } else { + for (x = p = saved_tt; p && p->rn_dupedkey != tt;) + p = p->rn_dupedkey; + if (p) p->rn_dupedkey = tt->rn_dupedkey; + else log(LOG_ERR, "rn_delete: couldn't find us\n"); + } + t = tt + 1; + if (t->rn_flags & RNF_ACTIVE) { +#ifndef RN_DEBUG + *++x = *t; p = t->rn_p; +#else + b = t->rn_info; *++x = *t; t->rn_info = b; p = t->rn_p; +#endif + if (p->rn_l == t) p->rn_l = x; else p->rn_r = x; + x->rn_l->rn_p = x; x->rn_r->rn_p = x; + } + goto out; + } + if (t->rn_l == tt) x = t->rn_r; else x = t->rn_l; + p = t->rn_p; + if (p->rn_r == t) p->rn_r = x; else p->rn_l = x; + x->rn_p = p; + /* + * Demote routes attached to us. + */ + if (t->rn_mklist) { + if (x->rn_b >= 0) { + for (mp = &x->rn_mklist; (m = *mp);) + mp = &m->rm_mklist; + *mp = t->rn_mklist; + } else { + /* If there are any key,mask pairs in a sibling + duped-key chain, some subset will appear sorted + in the same order attached to our mklist */ + for (m = t->rn_mklist; m && x; x = x->rn_dupedkey) + if (m == x->rn_mklist) { + struct radix_mask *mm = m->rm_mklist; + x->rn_mklist = 0; + if (--(m->rm_refs) < 0) + MKFree(m); + m = mm; + } + if (m) + syslog(LOG_ERR, "%s %lx at %lx\n", + "rn_delete: Orphaned Mask", + (unsigned long)m, + (unsigned long)x); + } + } + /* + * We may be holding an active internal node in the tree. + */ + x = tt + 1; + if (t != x) { +#ifndef RN_DEBUG + *t = *x; +#else + b = t->rn_info; *t = *x; t->rn_info = b; +#endif + t->rn_l->rn_p = t; t->rn_r->rn_p = t; + p = x->rn_p; + if (p->rn_l == x) p->rn_l = t; else p->rn_r = t; + } +out: + tt->rn_flags &= ~RNF_ACTIVE; + tt[1].rn_flags &= ~RNF_ACTIVE; + return (tt); +} + +int +rn_walktree(struct radix_node_head *h, + register int (*f)(struct radix_node *, struct walkarg*), + struct walkarg *w) +{ + int error; + struct radix_node *base, *next; + register struct radix_node *rn = h->rnh_treetop; + /* + * This gets complicated because we may delete the node + * while applying the function f to it, so we need to calculate + * the successor node in advance. + */ + /* First time through node, go left */ + while (rn->rn_b >= 0) + rn = rn->rn_l; + for (;;) { + base = rn; + /* If at right child go back up, otherwise, go right */ + while (rn->rn_p->rn_r == rn && (rn->rn_flags & RNF_ROOT) == 0) + rn = rn->rn_p; + /* Find the next *leaf* since next node might vanish, too */ + for (rn = rn->rn_p->rn_r; rn->rn_b >= 0;) + rn = rn->rn_l; + next = rn; + /* Process leaves */ + while ((rn = base)) { + base = rn->rn_dupedkey; + if (!(rn->rn_flags & RNF_ROOT) && (error = (*f)(rn, w))) + return (error); + } + rn = next; + if (rn->rn_flags & RNF_ROOT) + return (0); + } + /* NOTREACHED */ +} + +int +rn_inithead(void **head, int off) +{ + register struct radix_node_head *rnh; + register struct radix_node *t, *tt, *ttt; + if (*head) + return (1); + R_Malloc(rnh, struct radix_node_head *, sizeof (*rnh)); + if (rnh == 0) + return (0); + Bzero(rnh, sizeof (*rnh)); + *head = rnh; + t = rn_newpair(rn_zeros, off, rnh->rnh_nodes); + ttt = rnh->rnh_nodes + 2; + t->rn_r = ttt; + t->rn_p = t; + tt = t->rn_l; + tt->rn_flags = t->rn_flags = RNF_ROOT | RNF_ACTIVE; + tt->rn_b = -1 - off; + *ttt = *tt; + ttt->rn_key = rn_ones; + rnh->rnh_addaddr = rn_addroute; + rnh->rnh_deladdr = rn_delete; + rnh->rnh_matchaddr = rn_match; + rnh->rnh_lookup = rn_lookup; + rnh->rnh_walktree = rn_walktree; + rnh->rnh_treetop = t; + return (1); +} + +void +rn_init(void) +{ + char *cp, *cplim; + if (max_keylen == 0) { + printf("rn_init: radix functions require max_keylen be set\n"); + return; + } + R_Malloc(rn_zeros, char *, 3 * max_keylen); + if (rn_zeros == NULL) + panic("rn_init"); + Bzero(rn_zeros, 3 * max_keylen); + rn_ones = cp = rn_zeros + max_keylen; + addmask_key = cplim = rn_ones + max_keylen; + while (cp < cplim) + *cp++ = -1; + if (rn_inithead((void **)&mask_rnhead, 0) == 0) + panic("rn_init 2"); +} + diff --git a/sbin/routed/radix.h b/sbin/routed/radix.h new file mode 100644 index 000000000000..fddf02ead7a6 --- /dev/null +++ b/sbin/routed/radix.h @@ -0,0 +1,161 @@ +/* + * Copyright (c) 1988, 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)radix.h 8.2 (Berkeley) 10/31/94 + */ + +#ifndef __RADIX_H_ +#define __RADIX_H_ + +#include +struct walkarg; + +/* + * Radix search tree node layout. + */ + +struct radix_node { + struct radix_mask *rn_mklist; /* list of masks contained in subtree */ + struct radix_node *rn_p; /* parent */ + short rn_b; /* bit offset; -1-index(netmask) */ + char rn_bmask; /* node: mask for bit test*/ + u_char rn_flags; /* enumerated next */ +#define RNF_NORMAL 1 /* leaf contains normal route */ +#define RNF_ROOT 2 /* leaf is root leaf for tree */ +#define RNF_ACTIVE 4 /* This node is alive (for rtfree) */ + union { + struct { /* leaf only data: */ + caddr_t rn_Key; /* object of search */ + caddr_t rn_Mask; /* netmask, if present */ + struct radix_node *rn_Dupedkey; + } rn_leaf; + struct { /* node only data: */ + int rn_Off; /* where to start compare */ + struct radix_node *rn_L;/* progeny */ + struct radix_node *rn_R;/* progeny */ + }rn_node; + } rn_u; +#ifdef RN_DEBUG + int rn_info; + struct radix_node *rn_twin; + struct radix_node *rn_ybro; +#endif +}; + +#define rn_dupedkey rn_u.rn_leaf.rn_Dupedkey +#define rn_key rn_u.rn_leaf.rn_Key +#define rn_mask rn_u.rn_leaf.rn_Mask +#define rn_off rn_u.rn_node.rn_Off +#define rn_l rn_u.rn_node.rn_L +#define rn_r rn_u.rn_node.rn_R + +/* + * Annotations to tree concerning potential routes applying to subtrees. + */ + +extern struct radix_mask { + short rm_b; /* bit offset; -1-index(netmask) */ + char rm_unused; /* cf. rn_bmask */ + u_char rm_flags; /* cf. rn_flags */ + struct radix_mask *rm_mklist; /* more masks to try */ + union { + caddr_t rmu_mask; /* the mask */ + struct radix_node *rmu_leaf; /* for normal routes */ + } rm_rmu; + int rm_refs; /* # of references to this struct */ +} *rn_mkfreelist; + +#define rm_mask rm_rmu.rmu_mask +#define rm_leaf rm_rmu.rmu_leaf /* extra field would make 32 bytes */ + +#define MKGet(m) {\ + if (rn_mkfreelist) {\ + m = rn_mkfreelist; \ + rn_mkfreelist = (m)->rm_mklist; \ + } else \ + R_Malloc(m, struct radix_mask *, sizeof (*(m))); }\ + +#define MKFree(m) { (m)->rm_mklist = rn_mkfreelist; rn_mkfreelist = (m);} + +struct radix_node_head { + struct radix_node *rnh_treetop; + int rnh_addrsize; /* permit, but not require fixed keys */ + int rnh_pktsize; /* permit, but not require fixed keys */ + struct radix_node *(*rnh_addaddr) /* add based on sockaddr */ + __P((void *v, void *mask, + struct radix_node_head *head, struct radix_node nodes[])); + struct radix_node *(*rnh_addpkt) /* add based on packet hdr */ + __P((void *v, void *mask, + struct radix_node_head *head, struct radix_node nodes[])); + struct radix_node *(*rnh_deladdr) /* remove based on sockaddr */ + __P((void *v, void *mask, struct radix_node_head *head)); + struct radix_node *(*rnh_delpkt) /* remove based on packet hdr */ + __P((void *v, void *mask, struct radix_node_head *head)); + struct radix_node *(*rnh_matchaddr) /* locate based on sockaddr */ + __P((void *v, struct radix_node_head *head)); + struct radix_node *(*rnh_lookup) /* locate based on sockaddr */ + __P((void *v, void *mask, struct radix_node_head *head)); + struct radix_node *(*rnh_matchpkt) /* locate based on packet hdr */ + __P((void *v, struct radix_node_head *head)); + int (*rnh_walktree) /* traverse tree */ + (struct radix_node_head *head, + int (*f)(struct radix_node *, struct walkarg *), + struct walkarg *w); + struct radix_node rnh_nodes[3]; /* empty tree for common case */ +}; + + +#define Bcmp(a, b, n) bcmp(((char *)(a)), ((char *)(b)), (n)) +#define Bcopy(a, b, n) bcopy(((char *)(a)), ((char *)(b)), (unsigned)(n)) +#define Bzero(p, n) bzero((char *)(p), (int)(n)); +#define R_Malloc(p, t, n) (p = (t) malloc((unsigned int)(n))) +#define Free(p) free((char *)p); + +void rn_init __P((void)); +int rn_inithead __P((void **, int)); +int rn_refines __P((void *, void *)); +int rn_walktree __P((struct radix_node_head *, + int (*)__P((struct radix_node *, struct walkarg*)), + struct walkarg*)); +struct radix_node + *rn_addmask __P((void *, int, int)), + *rn_addroute __P((void *, void *, struct radix_node_head *, + struct radix_node [2])), + *rn_delete __P((void *, void *, struct radix_node_head *)), + *rn_insert __P((void *, struct radix_node_head *, int *, + struct radix_node [2])), + *rn_match __P((void *, struct radix_node_head *)), + *rn_newpair __P((void *, int, struct radix_node[2])), + *rn_search __P((void *, struct radix_node *)), + *rn_search_m __P((void *, struct radix_node *, void *)); + +#endif /* __RADIX_H_ */ diff --git a/sbin/routed/rdisc.c b/sbin/routed/rdisc.c new file mode 100644 index 000000000000..da1784c793e4 --- /dev/null +++ b/sbin/routed/rdisc.c @@ -0,0 +1,1032 @@ +/* + * Copyright (c) 1995 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if !defined(lint) && !defined(sgi) && !defined(__NetBSD__) +static char sccsid[] = "@(#)rdisc.c 8.1 (Berkeley) x/y/95"; +#elif defined(__NetBSD__) +static char rcsid[] = "$NetBSD$"; +#endif +#ident "$Revision: 1.16 $" + +#include "defs.h" +#include +#include +#include + +/* router advertisement ICMP packet */ +struct icmp_ad { + u_int8_t icmp_type; /* type of message */ + u_int8_t icmp_code; /* type sub code */ + u_int16_t icmp_cksum; /* ones complement cksum of struct */ + u_int8_t icmp_ad_num; /* # of following router addresses */ + u_int8_t icmp_ad_asize; /* 2--words in each advertisement */ + u_int16_t icmp_ad_life; /* seconds of validity */ + struct icmp_ad_info { + n_long icmp_ad_addr; + n_long icmp_ad_pref; + } icmp_ad_info[1]; +}; + +/* router solicitation ICMP packet */ +struct icmp_so { + u_int8_t icmp_type; /* type of message */ + u_int8_t icmp_code; /* type sub code */ + u_int16_t icmp_cksum; /* ones complement cksum of struct */ + n_long icmp_so_rsvd; +}; + +union ad_u { + struct icmp icmp; + struct icmp_ad ad; + struct icmp_so so; +}; + + +int rdisc_sock = -1; /* router-discovery raw socket */ +struct interface *rdisc_sock_mcast; /* current multicast interface */ + +struct timeval rdisc_timer; +int rdisc_ok; /* using solicited route */ + + +#define MAX_ADS 5 +struct dr { /* accumulated advertisements */ + struct interface *dr_ifp; + naddr dr_gate; /* gateway */ + time_t dr_ts; /* when received */ + time_t dr_life; /* lifetime */ + n_long dr_recv_pref; /* received but biased preference */ + n_long dr_pref; /* preference adjusted by metric */ +} *cur_drp, drs[MAX_ADS]; + +/* adjust preference by interface metric without driving it to infinity */ +#define PREF(p, ifp) ((p) <= (ifp)->int_metric ? ((p) != 0 ? 1 : 0) \ + : (p) - ((ifp)->int_metric)) + +static void rdisc_sort(void); + + +/* dump an ICMP Router Discovery Advertisement Message + */ +static void +trace_rdisc(char *act, + naddr from, + naddr to, + struct interface *ifp, + union ad_u *p, + u_int len) +{ + int i; + n_long *wp, *lim; + + + if (!TRACEPACKETS || ftrace == 0) + return; + + lastlog(); + + if (p->icmp.icmp_type == ICMP_ROUTERADVERT) { + (void)fprintf(ftrace, "%s Router Ad" + " from %s to %s via %s life=%d\n", + act, naddr_ntoa(from), naddr_ntoa(to), + ifp ? ifp->int_name : "?", + ntohs(p->ad.icmp_ad_life)); + if (!TRACECONTENTS) + return; + + wp = &p->ad.icmp_ad_info[0].icmp_ad_addr; + lim = &wp[(len - sizeof(p->ad)) / sizeof(*wp)]; + for (i = 0; i < p->ad.icmp_ad_num && wp <= lim; i++) { + (void)fprintf(ftrace, "\t%s preference=%#x", + naddr_ntoa(wp[0]), (int)ntohl(wp[1])); + wp += p->ad.icmp_ad_asize; + } + (void)fputc('\n',ftrace); + + } else { + trace_act("%s Router Solic. from %s to %s via %s" + " value=%#x\n", + act, naddr_ntoa(from), naddr_ntoa(to), + ifp ? ifp->int_name : "?", + ntohl(p->so.icmp_so_rsvd)); + } +} + +/* prepare Router Discovery socket. + */ +static void +get_rdisc_sock(void) +{ + if (rdisc_sock < 0) { + rdisc_sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); + if (rdisc_sock < 0) + BADERR(1,"rdisc_sock = socket()"); + fix_sock(rdisc_sock,"rdisc_sock"); + fix_select(); + } +} + + +/* Pick multicast group for router-discovery socket + */ +void +set_rdisc_mg(struct interface *ifp, + int on) { /* 0=turn it off */ + struct ip_mreq m; + + if (rdisc_sock < 0) { + /* Create the raw socket so that we can hear at least + * broadcast router discovery packets. + */ + if ((ifp->int_state & IS_NO_RDISC) == IS_NO_RDISC + || !on) + return; + get_rdisc_sock(); + } + + if (!(ifp->int_if_flags & IFF_MULTICAST) + || (ifp->int_state & IS_ALIAS)) { + ifp->int_state &= ~(IS_ALL_HOSTS | IS_ALL_ROUTERS); + return; + } + +#ifdef MCAST_PPP_BUG + if (ifp->int_if_flags & IFF_POINTOPOINT) + return; +#endif + bzero(&m, sizeof(m)); + m.imr_interface.s_addr = ((ifp->int_if_flags & IFF_POINTOPOINT) + ? ifp->int_dstaddr + : ifp->int_addr); + if (supplier + || (ifp->int_state & IS_NO_ADV_IN) + || !on) { + /* stop listening to advertisements + */ + if (ifp->int_state & IS_ALL_HOSTS) { + m.imr_multiaddr.s_addr = htonl(INADDR_ALLHOSTS_GROUP); + if (setsockopt(rdisc_sock, IPPROTO_IP, + IP_DROP_MEMBERSHIP, + &m, sizeof(m)) < 0) + LOGERR("IP_DROP_MEMBERSHIP ALLHOSTS"); + ifp->int_state &= ~IS_ALL_HOSTS; + } + + } else if (!(ifp->int_state & IS_ALL_HOSTS)) { + /* start listening to advertisements + */ + m.imr_multiaddr.s_addr = htonl(INADDR_ALLHOSTS_GROUP); + if (setsockopt(rdisc_sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, + &m, sizeof(m)) < 0) { + LOGERR("IP_ADD_MEMBERSHIP ALLHOSTS"); + } else { + ifp->int_state |= IS_ALL_HOSTS; + } + } + + if (!supplier + || (ifp->int_state & IS_NO_ADV_OUT) + || !on) { + /* stop listening to solicitations + */ + if (ifp->int_state & IS_ALL_ROUTERS) { + m.imr_multiaddr.s_addr=htonl(INADDR_ALLROUTERS_GROUP); + if (setsockopt(rdisc_sock, IPPROTO_IP, + IP_DROP_MEMBERSHIP, + &m, sizeof(m)) < 0) + LOGERR("IP_DROP_MEMBERSHIP ALLROUTERS"); + ifp->int_state &= ~IS_ALL_ROUTERS; + } + + } else if (!(ifp->int_state & IS_ALL_ROUTERS)) { + /* start hearing solicitations + */ + m.imr_multiaddr.s_addr=htonl(INADDR_ALLROUTERS_GROUP); + if (setsockopt(rdisc_sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, + &m, sizeof(m)) < 0) { + LOGERR("IP_ADD_MEMBERSHIP ALLROUTERS"); + } else { + ifp->int_state |= IS_ALL_ROUTERS; + } + } +} + + +/* start supplying routes + */ +void +set_supplier(void) +{ + struct interface *ifp; + struct dr *drp; + + if (supplier_set) + return; + + trace_act("start suppying routes\n"); + + /* Forget discovered routes. + */ + for (drp = drs; drp < &drs[MAX_ADS]; drp++) { + drp->dr_recv_pref = 0; + drp->dr_life = 0; + } + rdisc_age(0); + + supplier_set = 1; + supplier = 1; + + /* Do not start advertising until we have heard some RIP routes */ + LIM_SEC(rdisc_timer, now.tv_sec+MIN_WAITTIME); + + /* Switch router discovery multicast groups from soliciting + * to advertising. + */ + for (ifp = ifnet; ifp; ifp = ifp->int_next) { + if (ifp->int_state & IS_BROKE) + continue; + ifp->int_rdisc_cnt = 0; + ifp->int_rdisc_timer.tv_usec = rdisc_timer.tv_usec; + ifp->int_rdisc_timer.tv_sec = now.tv_sec+MIN_WAITTIME; + set_rdisc_mg(ifp, 1); + } + + /* get rid of any redirects */ + del_redirects(0,0); +} + + +/* age discovered routes and find the best one + */ +void +rdisc_age(naddr bad_gate) +{ + time_t sec; + struct dr *drp; + + + /* If only adverising, then do only that. */ + if (supplier) { + /* if switching from client to server, get rid of old + * default routes. + */ + if (cur_drp != 0) + rdisc_sort(); + rdisc_adv(); + return; + } + + /* If we are being told about a bad router, + * then age the discovered default route, and if there is + * no alternative, solicite a replacement. + */ + if (bad_gate != 0) { + /* Look for the bad discovered default route. + * Age it and note its interface. + */ + for (drp = drs; drp < &drs[MAX_ADS]; drp++) { + if (drp->dr_ts == 0) + continue; + + /* When we find the bad router, then age the route + * to at most SUPPLY_INTERVAL. + * This is contrary to RFC 1256, but defends against + * black holes. + */ + if (drp->dr_gate == bad_gate) { + sec = (now.tv_sec - drp->dr_life + + SUPPLY_INTERVAL); + if (drp->dr_ts > sec) { + trace_act("age 0.0.0.0 --> %s" + " via %s\n", + naddr_ntoa(drp->dr_gate), + drp->dr_ifp->int_name); + drp->dr_ts = sec; + } + break; + } + } + } + + /* delete old redirected routes to keep the kernel table small + */ + sec = (cur_drp == 0) ? MaxMaxAdvertiseInterval : cur_drp->dr_life; + del_redirects(bad_gate, now.tv_sec-sec); + + rdisc_sol(); + + rdisc_sort(); +} + + +/* Zap all routes discovered via an interface that has gone bad + * This should only be called when !(ifp->int_state & IS_ALIAS) + */ +void +if_bad_rdisc(struct interface *ifp) +{ + struct dr *drp; + + for (drp = drs; drp < &drs[MAX_ADS]; drp++) { + if (drp->dr_ifp != ifp) + continue; + drp->dr_recv_pref = 0; + drp->dr_life = 0; + } + + rdisc_sort(); +} + + +/* mark an interface ok for router discovering. + */ +void +if_ok_rdisc(struct interface *ifp) +{ + set_rdisc_mg(ifp, 1); + + ifp->int_rdisc_cnt = 0; + ifp->int_rdisc_timer.tv_sec = now.tv_sec + (supplier + ? MIN_WAITTIME + : MAX_SOLICITATION_DELAY); + if (timercmp(&rdisc_timer, &ifp->int_rdisc_timer, >)) + rdisc_timer = ifp->int_rdisc_timer; +} + + +/* get rid of a dead discovered router + */ +static void +del_rdisc(struct dr *drp) +{ + struct interface *ifp; + int i; + + + del_redirects(drp->dr_gate, 0); + drp->dr_ts = 0; + drp->dr_life = 0; + + + /* Count the other discovered routes on the interface. + */ + i = 0; + ifp = drp->dr_ifp; + for (drp = drs; drp < &drs[MAX_ADS]; drp++) { + if (drp->dr_ts != 0 + && drp->dr_ifp == ifp) + i++; + } + + /* If that was the last good discovered router on the interface, + * then solicit a new one. + * This is contrary to RFC 1256, but defends against black holes. + */ + if (i == 0 + && ifp->int_rdisc_cnt >= MAX_SOLICITATIONS) { + trace_act("discovered route is bad" + "--re-solicit routers via %s\n", ifp->int_name); + ifp->int_rdisc_cnt = 0; + ifp->int_rdisc_timer.tv_sec = 0; + rdisc_sol(); + } +} + + +/* Find the best discovered route, + * and discard stale routers. + */ +static void +rdisc_sort(void) +{ + struct dr *drp, *new_drp; + struct rt_entry *rt; + struct interface *ifp; + u_int new_st; + n_long new_pref; + + + /* Find the best discovered route. + */ + new_drp = 0; + for (drp = drs; drp < &drs[MAX_ADS]; drp++) { + if (drp->dr_ts == 0) + continue; + ifp = drp->dr_ifp; + + /* Get rid of expired discovered routers. + */ + if (drp->dr_ts + drp->dr_life <= now.tv_sec) { + del_rdisc(drp); + continue; + } + + LIM_SEC(rdisc_timer, drp->dr_ts+drp->dr_life+1); + + /* Update preference with possibly changed interface + * metric. + */ + drp->dr_pref = PREF(drp->dr_recv_pref, ifp); + + /* Prefer the current route to prevent thrashing. + * Prefer shorter lifetimes to speed the detection of + * bad routers. + * Avoid sick interfaces. + */ + if (new_drp == 0 + || (!((new_st ^ drp->dr_ifp->int_state) & IS_SICK) + && (new_pref < drp->dr_pref + || (new_pref == drp->dr_pref + && (drp == cur_drp + || (new_drp != cur_drp + && new_drp->dr_life > drp->dr_life))))) + || ((new_st & IS_SICK) + && !(drp->dr_ifp->int_state & IS_SICK))) { + new_drp = drp; + new_st = drp->dr_ifp->int_state; + new_pref = drp->dr_pref; + } + } + + /* switch to a better default route + */ + if (new_drp != cur_drp) { + rt = rtget(RIP_DEFAULT, 0); + + /* Stop using discovered routes if they are all bad + */ + if (new_drp == 0) { + trace_act("turn off Router Discovery client\n"); + rdisc_ok = 0; + + if (rt != 0 + && (rt->rt_state & RS_RDISC)) { + rtchange(rt, rt->rt_state & ~RS_RDISC, + rt->rt_gate, rt->rt_router, + HOPCNT_INFINITY, 0, rt->rt_ifp, + now.tv_sec - GARBAGE_TIME, 0); + rtswitch(rt, 0); + } + + /* turn on RIP if permitted */ + rip_on(0); + + } else { + if (cur_drp == 0) { + trace_act("turn on Router Discovery client" + " using %s via %s\n", + naddr_ntoa(new_drp->dr_gate), + new_drp->dr_ifp->int_name); + + rdisc_ok = 1; + + } else { + trace_act("switch Router Discovery from" + " %s via %s to %s via %s\n", + naddr_ntoa(cur_drp->dr_gate), + cur_drp->dr_ifp->int_name, + naddr_ntoa(new_drp->dr_gate), + new_drp->dr_ifp->int_name); + } + + if (rt != 0) { + rtchange(rt, rt->rt_state | RS_RDISC, + new_drp->dr_gate, new_drp->dr_gate, + 0,0, new_drp->dr_ifp, + now.tv_sec, 0); + } else { + rtadd(RIP_DEFAULT, 0, + new_drp->dr_gate, new_drp->dr_gate, + 0, 0, RS_RDISC, new_drp->dr_ifp); + } + + /* Now turn off RIP and delete RIP routes, + * which might otherwise include the default + * we just modified. + */ + rip_off(); + } + + cur_drp = new_drp; + } +} + + +/* handle a single address in an advertisement + */ +static void +parse_ad(naddr from, + naddr gate, + n_long pref, + u_short life, + struct interface *ifp) +{ + static naddr bad_gate; + struct dr *drp, *new_drp; + + + if (gate == RIP_DEFAULT + || !check_dst(gate)) { + if (bad_gate != from) { + msglog("router %s advertising bad gateway %s", + naddr_ntoa(from), + naddr_ntoa(gate)); + bad_gate = from; + } + return; + } + + /* ignore pointers to ourself and routes via unreachable networks + */ + if (ifwithaddr(gate, 1, 0) != 0) { + trace_pkt("\tdiscard Router Discovery Ad pointing at us\n"); + return; + } + if (!on_net(gate, ifp->int_net, ifp->int_mask)) { + trace_pkt("\tdiscard Router Discovery Ad" + " toward unreachable net\n"); + return; + } + + /* Convert preference to an unsigned value + * and later bias it by the metric of the interface. + */ + pref = ntohl(pref) ^ MIN_PreferenceLevel; + + if (pref == 0 || life == 0) { + pref = 0; + life = 0; + } + + for (new_drp = 0, drp = drs; drp < &drs[MAX_ADS]; drp++) { + /* accept new info for a familiar entry + */ + if (drp->dr_gate == gate) { + new_drp = drp; + break; + } + + if (life == 0) + continue; /* do not worry about dead ads */ + + if (drp->dr_ts == 0) { + new_drp = drp; /* use unused entry */ + + } else if (new_drp == 0) { + /* look for an entry worse than the new one to + * reuse. + */ + if ((!(ifp->int_state & IS_SICK) + && (drp->dr_ifp->int_state & IS_SICK)) + || (pref > drp->dr_pref + && !((ifp->int_state ^ drp->dr_ifp->int_state) + & IS_SICK))) + new_drp = drp; + + } else if (new_drp->dr_ts != 0) { + /* look for the least valueable entry to reuse + */ + if ((!(new_drp->dr_ifp->int_state & IS_SICK) + && (drp->dr_ifp->int_state & IS_SICK)) + || (new_drp->dr_pref > drp->dr_pref + && !((new_drp->dr_ifp->int_state + ^ drp->dr_ifp->int_state) + & IS_SICK))) + new_drp = drp; + } + } + + /* forget it if all of the current entries are better */ + if (new_drp == 0) + return; + + new_drp->dr_ifp = ifp; + new_drp->dr_gate = gate; + new_drp->dr_ts = now.tv_sec; + new_drp->dr_life = ntohs(life); + new_drp->dr_recv_pref = pref; + /* bias functional preference by metric of the interface */ + new_drp->dr_pref = PREF(pref,ifp); + + /* after hearing a good advertisement, stop asking + */ + if (!(ifp->int_state & IS_SICK)) + ifp->int_rdisc_cnt = MAX_SOLICITATIONS; +} + + +/* Compute the IP checksum + * This assumes the packet is less than 32K long. + */ +static u_short +in_cksum(u_short *p, + u_int len) +{ + u_int sum = 0; + int nwords = len >> 1; + + while (nwords-- != 0) + sum += *p++; + + if (len & 1) + sum += *(u_char *)p; + + /* end-around-carry */ + sum = (sum >> 16) + (sum & 0xffff); + sum += (sum >> 16); + return (~sum); +} + + +/* Send a router discovery advertisement or solicitation ICMP packet. + */ +static void +send_rdisc(union ad_u *p, + int p_size, + struct interface *ifp, + naddr dst, /* 0 or unicast destination */ + int type) /* 0=unicast, 1=bcast, 2=mcast */ +{ + struct sockaddr_in sin; + int flags; + char *msg; + naddr tgt_mcast; + + + bzero(&sin, sizeof(sin)); + sin.sin_addr.s_addr = dst; + sin.sin_family = AF_INET; +#ifdef _HAVE_SIN_LEN + sin.sin_len = sizeof(sin); +#endif + flags = MSG_DONTROUTE; + + switch (type) { + case 0: /* unicast */ + msg = "Send"; + break; + + case 1: /* broadcast */ + if (ifp->int_if_flags & IFF_POINTOPOINT) { + msg = "Send pt-to-pt"; + sin.sin_addr.s_addr = ifp->int_dstaddr; + } else { + msg = "Send broadcast"; + sin.sin_addr.s_addr = ifp->int_brdaddr; + } + break; + + case 2: /* multicast */ + msg = "Send multicast"; + if (ifp->int_state & IS_DUP) { + trace_act("abort multicast output via %s" + " with duplicate address\n", + ifp->int_name); + return; + } + if (rdisc_sock_mcast != ifp) { + /* select the right interface. */ +#ifdef MCAST_PPP_BUG + /* Do not specifiy the primary interface explicitly + * if we have the multicast point-to-point kernel + * bug, since the kernel will do the wrong thing + * if the local address of a point-to-point link + * is the same as the address of an ordinary + * interface. + */ + if (ifp->int_addr == myaddr) { + tgt_mcast = 0; + } else +#endif + tgt_mcast = ifp->int_addr; + if (0 > setsockopt(rdisc_sock, + IPPROTO_IP, IP_MULTICAST_IF, + &tgt_mcast, sizeof(tgt_mcast))) { + LOGERR("setsockopt(rdisc_sock," + "IP_MULTICAST_IF)"); + rdisc_sock_mcast = 0; + return; + } + rdisc_sock_mcast = ifp; + } + flags = 0; + break; + } + + if (rdisc_sock < 0) + get_rdisc_sock(); + + trace_rdisc(msg, ifp->int_addr, sin.sin_addr.s_addr, ifp, + p, p_size); + + if (0 > sendto(rdisc_sock, p, p_size, flags, + (struct sockaddr *)&sin, sizeof(sin))) { + if (ifp == 0 || !(ifp->int_state & IS_BROKE)) + msglog("sendto(%s%s%s): %s", + ifp != 0 ? ifp->int_name : "", + ifp != 0 ? ", " : "", + inet_ntoa(sin.sin_addr), + strerror(errno)); + if (ifp != 0) + if_sick(ifp); + } +} + + +/* Send an advertisement + */ +static void +send_adv(struct interface *ifp, + naddr dst, /* 0 or unicast destination */ + int type) /* 0=unicast, 1=bcast, 2=mcast */ +{ + union ad_u u; + n_long pref; + + + bzero(&u,sizeof(u.ad)); + + u.ad.icmp_type = ICMP_ROUTERADVERT; + u.ad.icmp_ad_num = 1; + u.ad.icmp_ad_asize = sizeof(u.ad.icmp_ad_info[0])/4; + + u.ad.icmp_ad_life = stopint ? 0 : htons(ifp->int_rdisc_int*3); + pref = ifp->int_rdisc_pref ^ MIN_PreferenceLevel; + pref = PREF(pref, ifp) ^ MIN_PreferenceLevel; + u.ad.icmp_ad_info[0].icmp_ad_pref = htonl(pref); + + u.ad.icmp_ad_info[0].icmp_ad_addr = ifp->int_addr; + + u.ad.icmp_cksum = in_cksum((u_short*)&u.ad, sizeof(u.ad)); + + send_rdisc(&u, sizeof(u.ad), ifp, dst, type); +} + + +/* Advertise for Router Discovery + */ +void +rdisc_adv(void) +{ + struct interface *ifp; + + + rdisc_timer.tv_sec = now.tv_sec + NEVER; + + for (ifp = ifnet; ifp; ifp = ifp->int_next) { + if (0 != (ifp->int_state & (IS_NO_ADV_OUT + | IS_PASSIVE + | IS_ALIAS + | IS_BROKE))) + continue; + + if (!timercmp(&ifp->int_rdisc_timer, &now, >) + || stopint) { + send_adv(ifp, htonl(INADDR_ALLHOSTS_GROUP), + (ifp->int_state&IS_BCAST_RDISC) ? 1 : 2); + ifp->int_rdisc_cnt++; + + intvl_random(&ifp->int_rdisc_timer, + (ifp->int_rdisc_int*3)/4, + ifp->int_rdisc_int); + if (ifp->int_rdisc_cnt < MAX_INITIAL_ADVERTS + && (ifp->int_rdisc_timer.tv_sec + > MAX_INITIAL_ADVERT_INTERVAL)) { + ifp->int_rdisc_timer.tv_sec + = MAX_INITIAL_ADVERT_INTERVAL; + } + timevaladd(&ifp->int_rdisc_timer, &now); + } + + if (timercmp(&rdisc_timer, &ifp->int_rdisc_timer, >)) + rdisc_timer = ifp->int_rdisc_timer; + } +} + + +/* Solicit for Router Discovery + */ +void +rdisc_sol(void) +{ + struct interface *ifp; + union ad_u u; + + + rdisc_timer.tv_sec = now.tv_sec + NEVER; + + for (ifp = ifnet; ifp; ifp = ifp->int_next) { + if (0 != (ifp->int_state & (IS_NO_SOL_OUT + | IS_PASSIVE + | IS_ALIAS + | IS_BROKE)) + || ifp->int_rdisc_cnt >= MAX_SOLICITATIONS) + continue; + + if (!timercmp(&ifp->int_rdisc_timer, &now, >)) { + bzero(&u,sizeof(u.so)); + u.so.icmp_type = ICMP_ROUTERSOLICIT; + u.so.icmp_cksum = in_cksum((u_short*)&u.so, + sizeof(u.so)); + send_rdisc(&u, sizeof(u.so), ifp, + htonl(INADDR_ALLROUTERS_GROUP), + ((ifp->int_state&IS_BCAST_RDISC) ? 1 : 2)); + + if (++ifp->int_rdisc_cnt >= MAX_SOLICITATIONS) + continue; + + ifp->int_rdisc_timer.tv_sec = SOLICITATION_INTERVAL; + ifp->int_rdisc_timer.tv_usec = 0; + timevaladd(&ifp->int_rdisc_timer, &now); + } + + if (timercmp(&rdisc_timer, &ifp->int_rdisc_timer, >)) + rdisc_timer = ifp->int_rdisc_timer; + } +} + + +/* check the IP header of a possible Router Discovery ICMP packet */ +static struct interface * /* 0 if bad */ +ck_icmp(char *act, + naddr from, + naddr to, + union ad_u *p, + u_int len) +{ + struct interface *ifp; + char *type; + + + /* If we could tell the interface on which a packet from address 0 + * arrived, we could deal with such solicitations. + */ + + ifp = ((from == 0) ? 0 : iflookup(from)); + + if (p->icmp.icmp_type == ICMP_ROUTERADVERT) { + type = "advertisement"; + } else if (p->icmp.icmp_type == ICMP_ROUTERSOLICIT) { + type = "solicitation"; + } else { + return 0; + } + + if (p->icmp.icmp_code != 0) { + trace_pkt("unrecognized ICMP Router" + " %s code=%d from %s to %s\n", + type, p->icmp.icmp_code, + naddr_ntoa(from), naddr_ntoa(to)); + return 0; + } + + trace_rdisc(act, from, to, ifp, p, len); + + if (ifp == 0) + trace_pkt("unknown interface for router-discovery %s" + " from %s to %s", + type, naddr_ntoa(from), naddr_ntoa(to)); + + return ifp; +} + + +/* read packets from the router discovery socket + */ +void +read_d(void) +{ + static naddr bad_asize, bad_len; + struct sockaddr_in from; + int n, fromlen, cc, hlen; + union { + struct ip ip; + u_short s[512/2]; + u_char b[512]; + } pkt; + union ad_u *p; + n_long *wp; + struct interface *ifp; + + + for (;;) { + fromlen = sizeof(from); + cc = recvfrom(rdisc_sock, &pkt, sizeof(pkt), 0, + (struct sockaddr*)&from, + &fromlen); + if (cc <= 0) { + if (cc < 0 && errno != EWOULDBLOCK) + LOGERR("recvfrom(rdisc_sock)"); + break; + } + if (fromlen != sizeof(struct sockaddr_in)) + logbad(1,"impossible recvfrom(rdisc_sock) fromlen=%d", + fromlen); + + hlen = pkt.ip.ip_hl << 2; + if (cc < hlen + ICMP_MINLEN) + continue; + p = (union ad_u *)&pkt.b[hlen]; + cc -= hlen; + + ifp = ck_icmp("Recv", + from.sin_addr.s_addr, pkt.ip.ip_dst.s_addr, + p, cc); + if (ifp == 0) + continue; + if (ifwithaddr(from.sin_addr.s_addr, 0, 0)) { + trace_pkt("\tdiscard our own Router Discovery msg\n"); + continue; + } + + switch (p->icmp.icmp_type) { + case ICMP_ROUTERADVERT: + if (p->ad.icmp_ad_asize*4 + < sizeof(p->ad.icmp_ad_info[0])) { + if (bad_asize != from.sin_addr.s_addr) { + msglog("intolerable rdisc address" + " size=%d", + p->ad.icmp_ad_asize); + bad_asize = from.sin_addr.s_addr; + } + continue; + } + if (p->ad.icmp_ad_num == 0) { + trace_pkt("\tempty?\n"); + continue; + } + if (cc != (sizeof(p->ad) - sizeof(p->ad.icmp_ad_info) + + (p->ad.icmp_ad_num + * sizeof(p->ad.icmp_ad_info[0])))) { + if (bad_len != from.sin_addr.s_addr) { + msglog("rdisc length %d does not" + " match ad_num %d", + cc, p->ad.icmp_ad_num); + bad_len = from.sin_addr.s_addr; + } + continue; + } + if (supplier) + continue; + if (ifp->int_state & IS_NO_ADV_IN) + continue; + + wp = &p->ad.icmp_ad_info[0].icmp_ad_addr; + for (n = 0; n < p->ad.icmp_ad_num; n++) { + parse_ad(from.sin_addr.s_addr, + wp[0], wp[1], + ntohs(p->ad.icmp_ad_life), + ifp); + wp += p->ad.icmp_ad_asize; + } + break; + + + case ICMP_ROUTERSOLICIT: + if (!supplier) + continue; + if (ifp->int_state & IS_NO_ADV_OUT) + continue; + + /* XXX + * We should handle messages from address 0. + */ + + /* Respond with a point-to-point advertisement */ + send_adv(ifp, from.sin_addr.s_addr, 0); + break; + } + } + + rdisc_sort(); +} diff --git a/sbin/routed/routed.8 b/sbin/routed/routed.8 new file mode 100644 index 000000000000..de3abf34e84e --- /dev/null +++ b/sbin/routed/routed.8 @@ -0,0 +1,605 @@ +.\" Copyright (c) 1983, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)routed.8 8.2 (Berkeley) 12/11/93 +.\" +.Dd June 1, 1996 +.Dt ROUTED 8 +.Os BSD 4.4 +.Sh NAME +.Nm routed +.Nd network RIP and router discovery routing daemon +.Sh SYNOPSIS +.Nm +.Op Fl sqdghmpAt +.Op Fl T Ar tracefile +.Oo +.Fl F +.Ar net Ns Op /mask Ns Op ,metric +.Oc +.OP Fl P Ar parms +.Sh DESCRIPTION +.Nm Routed +is a dameon invoked at boot time to manage the network +routing tables. +It uses Routing Information Protocol, RIPv1 (RFC\ 1058), +RIPv2 (RFC\ 1723), +and Internet Router Discovery Protocol (RFC 1256) +to maintain the kernel routing table. +The RIPv1 protocol is based on the reference 4.3BSD daemon. +.Pp +It listens on the +.Xr udp 4 +socket for the +.Xr route 8 +service (see +.Xr services 5 ) +for Routing Information Protocol packets. +It also sends and receives multicast Router Discovery ICMP messages. +If the host is a router, +.Nm +periodically supplies copies +of its routing tables to any directly connected hosts and networks. +It also advertise or solicits default routes using Router Discovery +ICMP messages. +.Pp +When started (or when a network interface is later turned on), +.Nm +uses an AF_ROUTE address family facility to find those +directly connected interfaces configured into the +system and marked "up". +It adds necessary routes for the interfaces +to the kernel routing table. +Soon after being first started, and provided there is at least one +interface on which RIP has not been disabled, +.Nm +deletes all pre-existing +non-static routes in kernel table. +Static routes in the kernel table are preserved and +included in RIP responses if they have a valid RIP metric +(see +.Xr route 8 ). +.Pp +If more than one interface is present (not counting the loopback interface), +it is assumed that the host should forward packets among the +connected networks. +After transmitting a RIP +.Em request +and +Router Discovery Advertisements or Solicitations on a new interface, +the daemon enters a loop, listening for +RIP request and response and Router Discover packets from other hosts. +.Pp +When a +.Em request +packet is received, +.Nm +formulates a reply based on the information maintained in its +internal tables. +The +.Em response +packet generated contains a list of known routes, each marked +with a "hop count" metric (a count of 16 or greater is +considered "infinite"). +Advertised metrics reflect the metric associated with interface +(see +.Xr ifconfig 8 ), +so setting the metric on an interface +is an effective way to steer traffic. +.Pp +Responses do not contain routes with a first hop on the requesting +network to implement in part +.Em split-horizon . +Requests from query programs +such as +.Xr rtquery 8 +are answered with the complete table. +.Pp +The routing table maintained by the daemon +includes space for several gateways for each destination +to speed recovery from a failing router. +RIP +.Em response +packets received are used to update the routing tables provided they are +from one of the several currently recognized gateways or +advertise a better metric than at least one of the existing +gateways. +.Pp +When an update is applied, +.Nm +records the change in its own tables and updates the kernel routing table +if the best route to the destination changes. +The change in the kernel routing tableis reflected in the next batch of +.Em response +packets sent. +If the next response is not scheduled for a while, a +.Em flash update +response containing only recently changed routes is sent. +.Pp +In addition to processing incoming packets, +.Nm +also periodically checks the routing table entries. +If an entry has not been updated for 3 minutes, the entry's metric +is set to infinity and marked for deletion. +Deletions are delayed until the route has been advertised with +an infinite metric to insure the invalidation +is propagated throughout the local internet. +This is a form of +.Em poison reverse . +.Pp +Routes in the kernel table that are added or changed as a result +of ICMP Redirect messages are deleted after a while to minimize +.Em black-holes . +When a TCP connection suffers a timeout, +the kernel tells +.Nm routed , +which deletes all redirected routes +through the gateway involved, advances the age of all RIP routes through +the gateway to allow an alternate to be chosen, and advances of the +age of any relevant Router Discovery Protocol default routes. +.Pp +Hosts acting as internetwork routers gratuitously supply their +routing tables every 30 seconds to all directly connected hosts +and networks. +These RIP responses are sent to the broadcast address on nets that support +broadcasting, +to the destination address on point-to-point links, and to the router's +own address on other networks. +If RIPv2 is enabled, multicast packets are sent on interfaces that +support multicasting. +.Pp +If no response is received on a remote interface, if there are errors +while sending responses, +or if there are more errors than input or output (see +.Xr netstat 8 ), +then the cable or some other part of the interface is assumed to be +disconnected or broken, and routes are adjusted appropriately. +.Pp +The +.Em Internet Router Discovery Protocol +is handled similarly. +When the daemon is supplying RIP routes, it also listens for +Router Discovery Solicitations and sends Advertisements. +When it is quiet and only listening to other RIP routers, it +sends Solicitations and listens for Advertisements. +If it receives +a good Advertisement, it stops listening for broadcast or multicast +RIP responses. +It tracks several advertising routers to speed recovery when the +currently chosen router dies. +If all discovered routers disappear, +the daemon resumes listening to RIP responses. +.Pp +While using Router Discovery (which happens by default when +the system has a single network interface and a Router Discover Advertisement +is received), there is a single default route and a variable number of +redirected host routes in the kernel table. +.Pp +The Router Discover standard requires that advertisements +have a default "lifetime" of 30 minutes. That means should +something happen, a client can be without a good route for +30 minutes. It is a good idea to reduce the default to 45 +seconds using +.Fl P Cm rdisc_interval=45 +on the command line or +.Cm rdisc_interval=45 +in the +.Pa /etc/gateways +file. +.Pp +While using Router Discovery (which happens by default when +the system has a single network interface and a Router Discover Advertisement +is received), there is a single default route and a variable number of +redirected host routes in the kernel table. +.Pp +See the +.Cm pm_rdisc +facility described below to support "legacy" systems +that can handle neither RIPv2 nor Router Discovery. +.Pp +By default, neither Router Discovery advertisements nor solicications +are sent over point to point links (e.g. PPP). + +.Pp +Options supported by +.Nm routed : +.Bl -tag -width Ds +.It Fl s +this option forces +.Nm +to supply routing information. +This is the default if multiple network interfaces are present on which +RIP or Router Discovery have not been disabled, and if the kernel switch +ipforwarding=1. +.It Fl q +is the opposite of the +.Fl s +option. +.It Fl d +Do not run in the background. +This option is meant for interactive use. +.It Fl g +This flag is used on internetwork routers to offer a route +to the "default" destination. +It is equivalent to +.Fl F +.Cm 0/0,1 +and is present mostly for historical reasons. +A better choice is +.Fl P Cm pm_rdisc +on the command line or +.CM pm_rdisc in the +.Pa /etc/gateways +file. +since a larger metric +will be used, reducing the spread of the potentially dangerous +default route. +This is typically used on a gateway to the Internet, +or on a gateway that uses another routing protocol whose routes +are not reported to other local routers. +Notice that because a metric of 1 is used, this feature is +dangerous. It is more commonly accidently used to create chaos with routing +loop than to solve problems. +.It Fl h +This causes host or point-to-point routes to not be advertised, +provided there is a network route going the same direction. +That is a limited kind of aggregation. +This option is useful on gateways to ethernets that have other gateway +machines connected with point-to-point links such as SLIP. +.It Fl m +This causes the machine to advertise a host or point-to-point route to +its primary interface. +It is useful on multi-homed machines such as NFS servers. +This option should not be used except when the cost of +the host routes it generates is justified by the popularity of +the server. +It is effective only when the machine is supplying +routing information, because there is more than one interface. +The +.Fl m +option overrides the +.Fl q +option to the limited extent of advertising the host route. +.It Fl A +do not ignore RIPv2 authentication if we do not care about RIPv2 +authentication. +This option is required for conformance with RFC 1723. +However, it makes no sense and breaks using RIP as a discovery protocol +to ignore all RIPv2 packets that carry authentication when this machine +does not care about authentication. +.It Fl T Ar tracefile +increases the debugging level to at least 1 and +causes debugging information to be appended to the trace file. +Note that because of security concerns, it is wisest to not run +.Nm routed +routinely with tracing directed to a file. +.It Fl t +increases the debugging level, which causes more information to be logged +on the tracefile specified with +.Fl T +or standard out. +The debugging level can be increased or decreased +with the +.Em SIGUSR1 +or +.Em SIGUSR2 +signals or with the +.Cm rtquery +command. +.It Fl F Ar net[/mask][,metric] +minimize routes in transmissions via interfaces with addresses that match +.Em net/mask , +and synthesizes a default route to this machine with the +.Em metric . +The intent is to reduce RIP traffic on slow, point-to-point links +such as PPP links by replacing many large UDP packets of RIP information +with a single, small packet containing a "fake" default route. +If +.Em metric +is absent, a value of 14 is assumed to limit +the spread of the "fake" default route. + +This is a dangerous feature that when used carelessly can cause routing +loops. +Notice also that more than one interface can match the specified network +number and mask. +See also +.Fl g . +.It Fl P Ar parms +is equivalent to adding the parameter +line +.Em parms +to the +.Pa /etc/gateways +file. +.El +.Pp +Any other argument supplied is interpreted as the name +of a file in which the actions of +.Nm +should be logged. +It is better to use +.Fl T +instead of +appending the name of the trace file to the command. +.Pp +.Nm +also supports the notion of +"distant" +.Em passive +or +.Em active +gateways. +When +.Nm +is started, it reads the file +.Pa /etc/gateways +to find such distant gateways which may not be located using +only information from a routing socket, to discover if some +of the local gateways are +.Em passive , +and to obtain other parameters. +Gateways specified in this manner should be marked passive +if they are not expected to exchange routing information, +while gateways marked active +should be willing to exchange RIP packets. +Routes through +.Em passive +gateways are installed in the +kernel's routing tables once upon startup and are not included in +transmitted RIP responses. +.Pp +Distant active gateways are treated like network interfaces. +RIP responses are sent +to the distant +.Em active +gateway. +If no responses are received, the associated route is deleted from +the kernel table and RIP responses advertised via other interfaces. +If the distant gateway resumes sending RIP responses, the associated +route is restored. +.Pp +Such gateways can be useful on media that do not support broadcasts +or multicasts but otherwise act like classic shared media like +Ethernets such as some ATM networks. +One can list all RIP routers reachable on the ATM network in +.Pa /etc/gateways +with a series of +"host" lines. +.Pp +Gateways marked +.Em external +are also passive, but are not placed in the kernel +routing table nor are they included in routing updates. +The function of external entries is to indicate +that another routing process +will install such a route if ncessary, +and that alternate routes to that destination should not be installed +by +.Nm routed . +Such entries are only required when both routers may learn of routes +to the same destination. +.Pp +The +.Em /etc/gateways +file is comprised of a series of lines, each in +one of the following formats or consist of parameters described below: +.Pp +.Bd -ragged +.Cm net +.Ar Nname[/mask] +.Cm gateway +.Ar Gname +.Cm metric +.Ar value +.Pf < Cm passive No \&| +.Cm active No \&| +.Cm extern Ns > +.Ed +.Bd -ragged +.Cm host +.Ar Hname +.Cm gateway +.Ar Gname +.Cm metric +.Ar value +.Pf < Cm passive No \&| +.Cm active No \&| +.Cm extern Ns > +.Ed +.Pp +.Ar Nname +or +.Ar Hname +is the name of the destination network or host. +It may be a symbolic network name or an Internet address +specified in "dot" notation (see +.Xr inet 3 ). +(If it is a name, then it must either be defined in +.Pa /etc/networks +or +.Pa /etc/hosts , +or +.Xr named 8 , +must have been started before +.Xr routed Ns .) +.Pp +.Ar mask +is an optional number between 1 and 32 indicating the netmask associated +with +.Ar Nname . +.Pp +.Ar Gname +is the name or address of the gateway to which RIP responses should +be forwarded. +.Pp +.Ar Value +is the hop count to the destination host or network. +.Ar " host hname " +is equivalent to +.Ar " net nname/32 ". +.Pp +One of the keywords +.Cm passive , +.Cm active +or +.Cm external +must be present to indicate whether the gateway should be treated as +.Cm passive +or +.Cm active +(as described above), +or whether the gateway is +.Cm external +to the scope of the RIP protocol. +.Pp +Lines that start with neither "net" nor "host" must consist of one +or more of the following parameter settings, separated by commas or +blanks: +.Bl -tag -width Ds +.It Cm if Ns \&= Ns Ar ifname +indicates that the other parameters on the line apply to the interface +name +.Ar ifname . +.It Cm subnet Ns \&= Ns Ar nname[/mask][,metric] +advertises a route to network +.AR nname +with mask +.AR mask +and the supplied metric (default 1). +This is useful for filling "holes" in CIDR allocations. +This parameter must appear by itself on a line. +.Pp +Do not use this feature unless necessary. It is dangerous. +.It Cm passwd Ns \&= Ns Ar XXX +specifies a RIPv2 password that will be included on all RIPv2 +responses sent and checked on all RIPv2 responses received. +The password must not contain any blanks, tab characters, commas +or '#' characters. +.It Cm no_ag +turns off aggregation of subnets in RIPv1 and RIPv2 responses. +.It Cm no_super_ag +turns off aggregation of networks into supernets in RIPv2 responses. +.It Cm passive +is equivalent +.Cm no_rip Cm no_rdisc . +.It Cm no_rip +disables all RIP processing on the specified interface. +If no interfaces are allowed to process RIP packets, +.Nm +acts purely as a router discovery daemon. +.Cm No_rip +is equivalent to +.Cm no_ripv1_in no_ripv2_in no_ripv1_out no_ripv2_out . + +Note that turning off RIP without explicitly turning on router +discovery advertisements with +.Cm rdisc_adv +or +.Fl s +causes +.Nm routed +to act as a client router discovery daemon, not adveritising. +.It Cm no_ripv1_in +causes RIPv1 received responses to be ignored. +.It Cm no_ripv2_in +causes RIPv2 received responses to be ignored. +.It Cm ripv2_out +turns off RIPv1 output and causes RIPv2 advertisements to be +multicast when possible. +.It Cm no_rdisc +disables the Internet Router Discovery Protocol. +.It Cm no_solicit +disables the tranmission of Router Discovery Solicitations. +.It Cm send_solicit +specifies that Router Discovery solicitations should be sent, +even on point-to-point links, +which by default only listen to Router Discovery messages. +.It Cm no_rdisc_adv +disables the transmission of Router Discovery Advertisements +.It Cm rdisc_adv +specifies that Router Discovery advertisements should be sent, +even on point-to-point links, +which by default only listen to Router Discovery messages +.It Cm bcast_rdisc +specifies that Router Discovery packets should be broadcast instead of +multicast. +.It Cm rdisc_pref Ns \&= Ns Ar N +sets the preference in Router Discovery Advertisements to the integer +.Ar N . +.It Cm rdisc_interval Ns \&= Ns Ar N +sets the nominal interval with which Router Discovery Advertisements +are transmitted to N seconds and their lifetime to 3*N. +.It Cm fake_default Ns \&= Ns Ar metric +has an identical effect to +.Fl F Ar net[/mask][,metric] +with the network and mask coming from the sepcified interface. +.It Cm pm_rdisc +is similar to +.Cm fake_default . +When RIPv2 routes are multicast, so that RIPv1 listeners cannot +receive them, this feature causes a RIPv1 default route to be +broadcast to RIPv1 listeners. +Unless modified with +.Cm fake_default , +the default route is broadcast with a metric of 14. +That serves as a "poor man's router discovery" protocol. +.El +.Pp +Note that the netmask associated with point-to-point links (such as SLIP +or PPP, with the IFF_POINTOPOINT flag) is used by +.Nm routed +to infer the netmask used by the remote system when RIPv1 is used. +.Pp +.Sh FILES +.Bl -tag -width /etc/gateways -compact +.It Pa /etc/gateways +for distant gateways +.El +.Sh SEE ALSO +.Xr gated 8 , +.Xr udp 4 , +.Xr icmp 4 , +.Xr htable 8 , +.Xr rtquery 8 . +.Rs +.%T Internet Transport Protocols +.%R XSIS 028112 +.%Q Xerox System Integration Standard +.Re +.Sh BUGS +It does not always detect unidirectional failures in network interfaces +(e.g., when the output side fails). +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.2 . diff --git a/sbin/routed/routed.h b/sbin/routed/routed.h new file mode 100644 index 000000000000..8bfc0a191257 --- /dev/null +++ b/sbin/routed/routed.h @@ -0,0 +1,162 @@ +/*- + * 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 + * + * $NetBSD$ + */ + +#ifndef _ROUTED_H_ +#define _ROUTED_H_ +#ifdef __cplusplus +extern "C" { +#endif +#ident "$Revision: 1.9 $" + +/* + * 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. 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 */ + u_int32_t rip_metric; /* cost of route */ +}; +#else +struct netinfo { + u_int16_t n_family; +#define RIP_AF_INET htons(AF_INET) +#define RIP_AF_UNSPEC 0 +#define RIP_AF_AUTH 0xffff + u_int16_t n_tag; /* optional in RIPv2 */ + u_int32_t n_dst; /* destination net or host */ +#define RIP_DEFAULT 0 + 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_int16_t a_type; +#define RIP_AUTH_PW htons(2) /* password type */ + union { +#define RIP_AUTH_PW_LEN 16 + int8_t au_pw[RIP_AUTH_PW_LEN]; + } au; +}; + +struct rip { + 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]; + int8_t 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_int32_t)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_ */ diff --git a/sbin/routed/rtquery/Makefile b/sbin/routed/rtquery/Makefile new file mode 100644 index 000000000000..f9aee5c55d06 --- /dev/null +++ b/sbin/routed/rtquery/Makefile @@ -0,0 +1,8 @@ +# @(#)Makefile 8.1 (Berkeley) 6/5/93 + +PROG= rtquery +MAN8= rtquery.0 +#COPTS= -g -DDEBUG -Wall + +.include "../../Makefile.inc" +.include diff --git a/sbin/routed/rtquery/rtquery.8 b/sbin/routed/rtquery/rtquery.8 new file mode 100644 index 000000000000..d77ca30b9c8f --- /dev/null +++ b/sbin/routed/rtquery/rtquery.8 @@ -0,0 +1,103 @@ +.Dd June 1, 1996 +.Dt RTQUERY 8 +.Os BSD 4.4 +.Sh NAME +.Nm rtquery +.Nd query routing daemons for their routing tables +.Sh SYNOPSIS +.Nm +.Op Fl np1 +.Op Fl w Ar timeout +.Op Fl r Ar addr +.Ar host ... +.Sh DESCRIPTION +.Nm Rtquery +is used to query a network routing daemon, +.Xr routed 8 +or +.Xr gated 8 , +for its routing table by sending a +.Em request +or +.Em poll +command. The routing information in any routing +.Em response +packets returned is displayed numerically and symbolically. +.Pp +.Em Rtquery +by default uses the +.Em request +command. +When the +.Ar -p +option is specified, +.Nm rtquery +uses the +.Em poll +command, an +undocumented extension to the RIP protocol supported by +.Xr gated 8 . +When querying gated, the +.Em poll +command is preferred over the +.I Request +command because the response is not subject to Split Horizon and/or +Poisoned Reverse, and because some versions of gated do not answer +the Request command. Routed does not answer the Poll command, but +recognizes Requests coming from rtquery and so answers completely. +.Pp +.Em Rtquery +is also used to turn tracing on or off in +.Em routed . +.Pp +Options supported by +.Nm rtquery : +.Bl -tag -width Ds +.It Fl n +Normally network and host numbers are displayed both symbolically +and numerically. +The +.Fl n +option displays only the numeric network and host numbers. +.It Fl p +Uses the +.Em Poll +command to request full routing information from +.Xr gated 8 , +This is an undocumented extension RIP protocol supported only by +.Xr gated 8 . +.It Fl 1 +query using RIP version 1 instead of RIP version 2. +.It Fl w Ar timeout +changes the delay for an answer from each host. +By default, each host is given 15 seconds to respond. +.It Fl r Ar addr +ask about the route to destination +.Em addr . +.It Fl t Ar op +change tracing, where +.Em op +is one of the following. +Requests from processes not running with UID 0 or on distant networks +are generally ignored by the daemon except for a message in the system log. +.El +.Bl -tag -width Ds -offset indent-two +.It Em on=tracefile +turn tracing on into the specified file. That file must usually +have been specified when the daemon was started or be the same +as a fixed name, often +.Pa /etc/routed.trace . +.It Em more +increases the debugging level. +.It Em off +turns off tracing. +.It Em dump +dumps the daemon's routing table to the current tracefile. +.El +.Sh SEE ALSO +.Xr routed 8 , +.Xr gated 8 . +.br +RFC\ 1058 - Routing Information Protocol, RIPv1 +.br +RFC\ 1723 - Routing Information Protocol, RIPv2 diff --git a/sbin/routed/rtquery/rtquery.c b/sbin/routed/rtquery/rtquery.c new file mode 100644 index 000000000000..7d6913a048d7 --- /dev/null +++ b/sbin/routed/rtquery/rtquery.c @@ -0,0 +1,654 @@ +/*- + * Copyright (c) 1982, 1986, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +char copyright[] = +"@(#) Copyright (c) 1982, 1986, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; + +#if !defined(lint) && !defined(sgi) && !defined(__NetBSD__) +static char sccsid[] = "@(#)query.c 8.1 (Berkeley) 6/5/93"; +#elif defined(__NetBSD__) +static char rcsid[] = "$NetBSD$"; +#endif +#ident "$Revision: 1.9 $" + +#include +#include +#include +#include +#include +#define RIPVERSION RIPv2 +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef sgi +#include +#include +#endif + +#ifndef sgi +#define _HAVE_SIN_LEN +#endif + +#define WTIME 15 /* Time to wait for all responses */ +#define STIME (250*1000) /* usec to wait for another response */ + +int s; + +char *pgmname; + +union { + struct rip rip; + char packet[MAXPACKETSIZE+MAXPATHLEN]; +} omsg_buf; +#define OMSG omsg_buf.rip +int omsg_len = sizeof(struct rip); + +union { + struct rip rip; + char packet[MAXPACKETSIZE+1024]; + } imsg_buf; +#define IMSG imsg_buf.rip + +int nflag; /* numbers, no names */ +int pflag; /* play the `gated` game */ +int ripv2 = 1; /* use RIP version 2 */ +int wtime = WTIME; +int rflag; /* 1=ask about a particular route */ +int trace; +int not_trace; + +struct timeval sent; /* when query sent */ + +static void rip_input(struct sockaddr_in*, int); +static int out(char *); +static void trace_loop(char *argv[]); +static void query_loop(char *argv[], int); +static int getnet(char *, struct netinfo *); +static u_int std_mask(u_int); + + +int +main(int argc, + char *argv[]) +{ + int ch, bsize; + char *p, *options, *value; + + OMSG.rip_nets[0].n_dst = RIP_DEFAULT; + OMSG.rip_nets[0].n_family = RIP_AF_UNSPEC; + OMSG.rip_nets[0].n_metric = htonl(HOPCNT_INFINITY); + + pgmname = argv[0]; + while ((ch = getopt(argc, argv, "np1w:r:t:")) != EOF) + switch (ch) { + case 'n': + not_trace = 1; + nflag = 1; + break; + + case 'p': + not_trace = 1; + pflag = 1; + break; + + case '1': + ripv2 = 0; + break; + + case 'w': + not_trace = 1; + wtime = (int)strtoul(optarg, &p, 0); + if (*p != '\0' + || wtime <= 0) + goto usage; + break; + + case 'r': + not_trace = 1; + if (rflag) + goto usage; + rflag = getnet(optarg, &OMSG.rip_nets[0]); + if (!rflag) { + struct hostent *hp = gethostbyname(optarg); + if (hp == 0) { + fprintf(stderr, "%s: %s:", + pgmname, optarg); + herror(0); + exit(1); + } + bcopy(hp->h_addr, &OMSG.rip_nets[0].n_dst, + sizeof(OMSG.rip_nets[0].n_dst)); + OMSG.rip_nets[0].n_family = RIP_AF_INET; + OMSG.rip_nets[0].n_mask = -1; + rflag = 1; + } + break; + + case 't': + trace = 1; + options = optarg; + while (*options != '\0') { + char *traceopts[] = { +# define TRACE_ON 0 + "on", +# define TRACE_MORE 1 + "more", +# define TRACE_OFF 2 + "off", +# define TRACE_DUMP 3 + "dump", + 0 + }; + switch (getsubopt(&options,traceopts,&value)) { + case TRACE_ON: + OMSG.rip_cmd = RIPCMD_TRACEON; + if (!value + || strlen(value) > MAXPATHLEN) + goto usage; + break; + case TRACE_MORE: + if (value) + goto usage; + OMSG.rip_cmd = RIPCMD_TRACEON; + value = ""; + break; + case TRACE_OFF: + if (value) + goto usage; + OMSG.rip_cmd = RIPCMD_TRACEOFF; + value = ""; + break; + case TRACE_DUMP: + if (value) + goto usage; + OMSG.rip_cmd = RIPCMD_TRACEON; + value = "dump/../table"; + break; + default: + goto usage; + } + strcpy((char*)OMSG.rip_tracefile, value); + omsg_len += strlen(value) - sizeof(OMSG.ripun); + } + break; + + default: + goto usage; + } + argv += optind; + argc -= optind; + if ((not_trace && trace) || argc == 0) { +usage: fprintf(stderr, "%s: [-np1v] [-r tgt_rt] [-w wtime]" + " host1 [host2 ...]\n" + "or\t-t {on=filename|more|off} host1 host2 ...\n", + pgmname); + exit(1); + } + + s = socket(AF_INET, SOCK_DGRAM, 0); + if (s < 0) { + perror("socket"); + exit(2); + } + + /* be prepared to receive a lot of routes */ + for (bsize = 127*1024; ; bsize -= 1024) { + if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, + &bsize, sizeof(bsize)) == 0) + break; + if (bsize <= 4*1024) { + perror("setsockopt SO_RCVBUF"); + break; + } + } + + if (trace) + trace_loop(argv); + else + query_loop(argv, argc); + /* NOTREACHED */ +} + + +/* tell the target hosts about tracing + */ +static void +trace_loop(char *argv[]) +{ + struct sockaddr_in myaddr; + int res; + + if (geteuid() != 0) { + (void)fprintf(stderr, "-t requires UID 0\n"); + exit(1); + } + + if (ripv2) { + OMSG.rip_vers = RIPv2; + } else { + OMSG.rip_vers = RIPv1; + } + + bzero(&myaddr, sizeof(myaddr)); + myaddr.sin_family = AF_INET; +#ifdef _HAVE_SIN_LEN + myaddr.sin_len = sizeof(myaddr); +#endif + myaddr.sin_port = htons(IPPORT_RESERVED-1); + while (bind(s, (struct sockaddr *)&myaddr, sizeof(myaddr)) < 0) { + if (errno != EADDRINUSE + || myaddr.sin_port == 0) { + perror("bind"); + exit(2); + } + myaddr.sin_port = htons(ntohs(myaddr.sin_port)-1); + } + + res = 1; + while (*argv != 0) { + if (out(*argv++) <= 0) + res = 0; + } + exit(res); +} + + +/* query all of the listed hosts + */ +static void +query_loop(char *argv[], int argc) +{ + struct seen { + struct seen *next; + struct in_addr addr; + } *seen, *sp; + int answered = 0; + int cc; + fd_set bits; + struct timeval now, delay; + struct sockaddr_in from; + int fromlen; + + + OMSG.rip_cmd = (pflag) ? RIPCMD_POLL : RIPCMD_REQUEST; + if (ripv2) { + OMSG.rip_vers = RIPv2; + } else { + OMSG.rip_vers = RIPv1; + OMSG.rip_nets[0].n_mask = 0; + } + + /* ask the first (valid) host */ + seen = 0; + while (0 > out(*argv++)) { + if (*argv == 0) + exit(-1); + answered++; + } + + FD_ZERO(&bits); + for (;;) { + FD_SET(s, &bits); + delay.tv_sec = 0; + delay.tv_usec = STIME; + cc = select(s+1, &bits, 0,0, &delay); + if (cc > 0) { + fromlen = sizeof(from); + cc = recvfrom(s, imsg_buf.packet, + sizeof(imsg_buf.packet), 0, + (struct sockaddr *)&from, &fromlen); + if (cc < 0) { + perror("recvfrom"); + exit(1); + } + /* count the distinct responding hosts. + * You cannot match responding hosts with + * addresses to which queries were transmitted, + * because a router might respond with a + * different source address. + */ + for (sp = seen; sp != 0; sp = sp->next) { + if (sp->addr.s_addr == from.sin_addr.s_addr) + break; + } + if (sp == 0) { + sp = malloc(sizeof(*sp)); + sp->addr = from.sin_addr; + sp->next = seen; + seen = sp; + answered++; + } + + rip_input(&from, cc); + continue; + } + + if (cc < 0) { + if ( errno == EINTR) + continue; + perror("select"); + exit(1); + } + + /* After a pause in responses, probe another host. + * This reduces the intermingling of answers. + */ + while (*argv != 0 && 0 > out(*argv++)) + answered++; + + /* continue until no more packets arrive + * or we have heard from all hosts + */ + if (answered >= argc) + break; + + /* or until we have waited a long time + */ + if (gettimeofday(&now, 0) < 0) { + perror("gettimeofday(now)"); + exit(1); + } + if (sent.tv_sec + wtime <= now.tv_sec) + break; + } + + /* fail if there was no answer */ + exit (answered >= argc ? 0 : 1); +} + + +/* sent do one host + */ +static int +out(char *host) +{ + struct sockaddr_in router; + struct hostent *hp; + + if (gettimeofday(&sent, 0) < 0) { + perror("gettimeofday(sent)"); + return -1; + } + + bzero(&router, sizeof(router)); + router.sin_family = AF_INET; +#ifdef _HAVE_SIN_LEN + router.sin_len = sizeof(router); +#endif + if (!inet_aton(host, &router.sin_addr)) { + hp = gethostbyname(host); + if (hp == 0) { + herror(host); + return -1; + } + bcopy(hp->h_addr, &router.sin_addr, sizeof(router.sin_addr)); + } + router.sin_port = htons(RIP_PORT); + + if (sendto(s, &omsg_buf, omsg_len, 0, + (struct sockaddr *)&router, sizeof(router)) < 0) { + perror(host); + return -1; + } + + return 0; +} + + +/* + * Handle an incoming RIP packet. + */ +static void +rip_input(struct sockaddr_in *from, + int size) +{ + struct netinfo *n, *lim; + struct in_addr in; + char *name; + char net_buf[80]; + u_int mask, dmask; + char *sp; + int i; + struct hostent *hp; + struct netent *np; + struct netauth *a; + + + if (nflag) { + printf("%s:", inet_ntoa(from->sin_addr)); + } else { + hp = gethostbyaddr((char*)&from->sin_addr, + sizeof(struct in_addr), AF_INET); + if (hp == 0) { + printf("%s:", + inet_ntoa(from->sin_addr)); + } else { + printf("%s (%s):", hp->h_name, + inet_ntoa(from->sin_addr)); + } + } + if (IMSG.rip_cmd != RIPCMD_RESPONSE) { + printf("\n unexpected response type %d\n", IMSG.rip_cmd); + return; + } + printf(" RIPv%d%s %d bytes\n", IMSG.rip_vers, + (IMSG.rip_vers != RIPv1 && IMSG.rip_vers != RIPv2) ? " ?" : "", + size); + if (size > MAXPACKETSIZE) { + if (size > sizeof(imsg_buf) - sizeof(*n)) { + printf(" at least %d bytes too long\n", + size-MAXPACKETSIZE); + size = sizeof(imsg_buf) - sizeof(*n); + } else { + printf(" %d bytes too long\n", + size-MAXPACKETSIZE); + } + } else if (size%sizeof(*n) != sizeof(struct rip)%sizeof(*n)) { + printf(" response of bad length=%d\n", size); + } + + n = IMSG.rip_nets; + lim = (struct netinfo *)((char*)n + size) - 1; + for (; n <= lim; n++) { + name = ""; + if (n->n_family == RIP_AF_INET) { + in.s_addr = n->n_dst; + (void)strcpy(net_buf, inet_ntoa(in)); + + mask = ntohl(n->n_mask); + dmask = mask & -mask; + if (mask != 0) { + sp = &net_buf[strlen(net_buf)]; + if (IMSG.rip_vers == RIPv1) { + (void)sprintf(sp," mask=%#x ? ",mask); + mask = 0; + } else if (mask + dmask == 0) { + for (i = 0; + (i != 32 + && ((1<n_name; + else if (in.s_addr == 0) + name = "default"; + } + if (name[0] == '\0' + && ((in.s_addr & ~mask) != 0 + || mask == 0xffffffff)) { + hp = gethostbyaddr((char*)&in, + sizeof(in), + AF_INET); + if (hp != 0) + name = hp->h_name; + } + } + + } else if (n->n_family == RIP_AF_AUTH) { + a = (struct netauth*)n; + (void)printf(" authentication type %d: ", + a->a_type); + for (i = 0; i < sizeof(a->au.au_pw); i++) + (void)printf("%02x ", a->au.au_pw[i]); + putc('\n', stdout); + continue; + + } else { + (void)sprintf(net_buf, "(af %#x) %d.%d.%d.%d", + ntohs(n->n_family), + (char)(n->n_dst >> 24), + (char)(n->n_dst >> 16), + (char)(n->n_dst >> 8), + (char)n->n_dst); + } + + (void)printf(" %-18s metric %2d %-10s", + net_buf, ntohl(n->n_metric), name); + + if (n->n_nhop != 0) { + in.s_addr = n->n_nhop; + if (nflag) + hp = 0; + else + hp = gethostbyaddr((char*)&in, sizeof(in), + AF_INET); + (void)printf(" nhop=%-15s%s", + (hp != 0) ? hp->h_name : inet_ntoa(in), + (IMSG.rip_vers == RIPv1) ? " ?" : ""); + } + if (n->n_tag != 0) + (void)printf(" tag=%#x%s", n->n_tag, + (IMSG.rip_vers == RIPv1) ? " ?" : ""); + putc('\n', stdout); + } +} + + +/* Return the classical netmask for an IP address. + */ +static u_int +std_mask(u_int addr) /* in network order */ +{ + NTOHL(addr); /* was a host, not a network */ + + if (addr == 0) /* default route has mask 0 */ + return 0; + if (IN_CLASSA(addr)) + return IN_CLASSA_NET; + if (IN_CLASSB(addr)) + return IN_CLASSB_NET; + return IN_CLASSC_NET; +} + + +/* get a network number as a name or a number, with an optional "/xx" + * netmask. + */ +static int /* 0=bad */ +getnet(char *name, + struct netinfo *rt) +{ + int i; + struct netent *nentp; + u_int mask; + struct in_addr in; + char hname[MAXHOSTNAMELEN+1]; + char *mname, *p; + + + /* Detect and separate "1.2.3.4/24" + */ + if (0 != (mname = rindex(name,'/'))) { + i = (int)(mname - name); + if (i > sizeof(hname)-1) /* name too long */ + return 0; + bcopy(name, hname, i); + hname[i] = '\0'; + mname++; + name = hname; + } + + nentp = getnetbyname(name); + if (nentp != 0) { + in.s_addr = nentp->n_net; + } else if (inet_aton(name, &in) == 1) { + NTOHL(in.s_addr); + } else { + return 0; + } + + if (mname == 0) { + mask = std_mask(in.s_addr); + if ((~mask & in.s_addr) != 0) + mask = 0xffffffff; + } else { + mask = (u_int)strtoul(mname, &p, 0); + if (*p != '\0' || mask > 32) + return 0; + mask = 0xffffffff << (32-mask); + } + + rt->n_dst = htonl(in.s_addr); + rt->n_family = RIP_AF_INET; + rt->n_mask = htonl(mask); + return 1; +} diff --git a/sbin/routed/table.c b/sbin/routed/table.c new file mode 100644 index 000000000000..6ad97f0ada15 --- /dev/null +++ b/sbin/routed/table.c @@ -0,0 +1,1970 @@ +/* + * Copyright (c) 1983, 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if !defined(lint) && !defined(sgi) && !defined(__NetBSD__) +static char sccsid[] = "@(#)tables.c 8.1 (Berkeley) 6/5/93"; +#elif defined(__NetBSD__) +static char rcsid[] = "$NetBSD$"; +#endif +#ident "$Revision: 1.25 $" + +#include "defs.h" + +static struct rt_spare *rts_better(struct rt_entry *); + +struct radix_node_head *rhead; /* root of the radix tree */ + +int need_flash = 1; /* flash update needed + * start =1 to suppress the 1st + */ + +struct timeval age_timer; /* next check of old routes */ +struct timeval need_kern = { /* need to update kernel table */ + EPOCH+MIN_WAITTIME-1 +}; + +int stopint; + +int total_routes; + +naddr age_bad_gate; + + +/* It is desirable to "aggregate" routes, to combine differing routes of + * the same metric and next hop into a common route with a smaller netmask + * or to suppress redundant routes, routes that add no information to + * routes with smaller netmasks. + * + * A route is redundant if and only if any and all routes with smaller + * but matching netmasks and nets are the same. Since routes are + * kept sorted in the radix tree, redundant routes always come second. + * + * There are two kinds of aggregations. First, two routes of the same bit + * mask and differing only in the least significant bit of the network + * number can be combined into a single route with a coarser mask. + * + * Second, a route can be suppressed in favor of another route with a more + * coarse mask provided no incompatible routes with intermediate masks + * are present. The second kind of aggregation involves suppressing routes. + * A route must not be suppressed if an incompatible route exists with + * an intermediate mask, since the suppressed route would be covered + * by the intermediate. + * + * This code relies on the radix tree walk encountering routes + * sorted first by address, with the smallest address first. + */ + +struct ag_info ag_slots[NUM_AG_SLOTS], *ag_avail, *ag_corsest, *ag_finest; + +/* #define DEBUG_AG */ +#ifdef DEBUG_AG +#define CHECK_AG() {int acnt = 0; struct ag_info *cag; \ + for (cag = ag_avail; cag != 0; cag = cag->ag_fine) \ + acnt++; \ + for (cag = ag_corsest; cag != 0; cag = cag->ag_fine) \ + acnt++; \ + if (acnt != NUM_AG_SLOTS) { \ + (void)fflush(stderr); \ + abort(); \ + } \ +} +#else +#define CHECK_AG() +#endif + + +/* Output the contents of an aggregation table slot. + * This function must always be immediately followed with the deletion + * of the target slot. + */ +static void +ag_out(struct ag_info *ag, + void (*out)(struct ag_info *)) +{ + struct ag_info *ag_cors; + naddr bit; + + + /* If we output both the even and odd twins, then the immediate parent, + * if it is present, is redundant, unless the parent manages to + * aggregate into something coarser. + * On successive calls, this code detects the even and odd twins, + * and marks the parent. + * + * Note that the order in which the radix tree code emits routes + * ensures that the twins are seen before the parent is emitted. + */ + ag_cors = ag->ag_cors; + if (ag_cors != 0 + && ag_cors->ag_mask == ag->ag_mask<<1 + && ag_cors->ag_dst_h == (ag->ag_dst_h & ag_cors->ag_mask)) { + ag_cors->ag_state |= ((ag_cors->ag_dst_h == ag->ag_dst_h) + ? AGS_REDUN0 + : AGS_REDUN1); + } + + /* Skip it if this route is itself redundant. + * + * It is ok to change the contents of the slot here, since it is + * always deleted next. + */ + if (ag->ag_state & AGS_REDUN0) { + if (ag->ag_state & AGS_REDUN1) + return; + bit = (-ag->ag_mask) >> 1; + ag->ag_dst_h |= bit; + ag->ag_mask |= bit; + + } else if (ag->ag_state & AGS_REDUN1) { + bit = (-ag->ag_mask) >> 1; + ag->ag_mask |= bit; + } + out(ag); +} + + +static void +ag_del(struct ag_info *ag) +{ + CHECK_AG(); + + if (ag->ag_cors == 0) + ag_corsest = ag->ag_fine; + else + ag->ag_cors->ag_fine = ag->ag_fine; + + if (ag->ag_fine == 0) + ag_finest = ag->ag_cors; + else + ag->ag_fine->ag_cors = ag->ag_cors; + + ag->ag_fine = ag_avail; + ag_avail = ag; + + CHECK_AG(); +} + + +/* Flush routes waiting for aggretation. + * This must not suppress a route unless it is known that among all + * routes with coarser masks that match it, the one with the longest + * mask is appropriate. This is ensured by scanning the routes + * in lexical order, and with the most restritive mask first + * among routes to the same destination. + */ +void +ag_flush(naddr lim_dst_h, /* flush routes to here */ + naddr lim_mask, /* matching this mask */ + void (*out)(struct ag_info *)) +{ + struct ag_info *ag, *ag_cors; + naddr dst_h; + + + for (ag = ag_finest; + ag != 0 && ag->ag_mask >= lim_mask; + ag = ag_cors) { + ag_cors = ag->ag_cors; + + /* work on only the specified routes */ + dst_h = ag->ag_dst_h; + if ((dst_h & lim_mask) != lim_dst_h) + continue; + + if (!(ag->ag_state & AGS_SUPPRESS)) + ag_out(ag, out); + + else for ( ; ; ag_cors = ag_cors->ag_cors) { + /* Look for a route that can suppress the + * current route */ + if (ag_cors == 0) { + /* failed, so output it and look for + * another route to work on + */ + ag_out(ag, out); + break; + } + + if ((dst_h & ag_cors->ag_mask) == ag_cors->ag_dst_h) { + /* We found a route with a coarser mask that + * aggregates the current target. + * + * If it has a different next hop, it + * cannot replace the target, so output + * the target. + */ + if (ag->ag_gate != ag_cors->ag_gate + && !(ag->ag_state & AGS_FINE_GATE) + && !(ag_cors->ag_state & AGS_CORS_GATE)) { + ag_out(ag, out); + break; + } + + /* If the coarse route has a good enough + * metric, it suppresses the target. + */ + if (ag_cors->ag_pref <= ag->ag_pref) { + if (ag_cors->ag_seqno > ag->ag_seqno) + ag_cors->ag_seqno = ag->ag_seqno; + if (AG_IS_REDUN(ag->ag_state) + && ag_cors->ag_mask==ag->ag_mask<<1) { + if (ag_cors->ag_dst_h == dst_h) + ag_cors->ag_state |= AGS_REDUN0; + else + ag_cors->ag_state |= AGS_REDUN1; + } + if (ag->ag_tag != ag_cors->ag_tag) + ag_cors->ag_tag = 0; + if (ag->ag_nhop != ag_cors->ag_nhop) + ag_cors->ag_nhop = 0; + break; + } + } + } + + /* That route has either been output or suppressed */ + ag_cors = ag->ag_cors; + ag_del(ag); + } + + CHECK_AG(); +} + + +/* Try to aggregate a route with previous routes. + */ +void +ag_check(naddr dst, + naddr mask, + naddr gate, + naddr nhop, + char metric, + char pref, + u_int seqno, + u_short tag, + u_short state, + void (*out)(struct ag_info *)) /* output using this */ +{ + struct ag_info *ag, *nag, *ag_cors; + naddr xaddr; + int x; + + NTOHL(dst); + + /* Punt non-contiguous subnet masks. + * + * (X & -X) contains a single bit if and only if X is a power of 2. + * (X + (X & -X)) == 0 if and only if X is a power of 2. + */ + if ((mask & -mask) + mask != 0) { + struct ag_info nc_ag; + + nc_ag.ag_dst_h = dst; + nc_ag.ag_mask = mask; + nc_ag.ag_gate = gate; + nc_ag.ag_nhop = nhop; + nc_ag.ag_metric = metric; + nc_ag.ag_pref = pref; + nc_ag.ag_tag = tag; + nc_ag.ag_state = state; + nc_ag.ag_seqno = seqno; + out(&nc_ag); + return; + } + + /* Search for the right slot in the aggregation table. + */ + ag_cors = 0; + ag = ag_corsest; + while (ag != 0) { + if (ag->ag_mask >= mask) + break; + + /* Suppress old routes (i.e. combine with compatible routes + * with coarser masks) as we look for the right slot in the + * aggregation table for the new route. + * A route to an address less than the current destination + * will not be affected by the current route or any route + * seen hereafter. That means it is safe to suppress it. + * This check keeps poor routes (eg. with large hop counts) + * from preventing suppresion of finer routes. + */ + if (ag_cors != 0 + && ag->ag_dst_h < dst + && (ag->ag_state & AGS_SUPPRESS) + && ag_cors->ag_pref <= ag->ag_pref + && (ag->ag_dst_h & ag_cors->ag_mask) == ag_cors->ag_dst_h + && (ag_cors->ag_gate == ag->ag_gate + || (ag->ag_state & AGS_FINE_GATE) + || (ag_cors->ag_state & AGS_CORS_GATE))) { + if (ag_cors->ag_seqno > ag->ag_seqno) + ag_cors->ag_seqno = ag->ag_seqno; + if (AG_IS_REDUN(ag->ag_state) + && ag_cors->ag_mask==ag->ag_mask<<1) { + if (ag_cors->ag_dst_h == dst) + ag_cors->ag_state |= AGS_REDUN0; + else + ag_cors->ag_state |= AGS_REDUN1; + } + if (ag->ag_tag != ag_cors->ag_tag) + ag_cors->ag_tag = 0; + if (ag->ag_nhop != ag_cors->ag_nhop) + ag_cors->ag_nhop = 0; + ag_del(ag); + CHECK_AG(); + } else { + ag_cors = ag; + } + ag = ag_cors->ag_fine; + } + + /* If we find the even/odd twin of the new route, and if the + * masks and so forth are equal, we can aggregate them. + * We can probably promote one of the pair. + * + * Since the routes are encountered in lexical order, + * the new route must be odd. However, the second or later + * times around this loop, it could be the even twin promoted + * from the even/odd pair of twins of the finer route. + */ + while (ag != 0 + && ag->ag_mask == mask + && ((ag->ag_dst_h ^ dst) & (mask<<1)) == 0) { + + /* Here we know the target route and the route in the current + * slot have the same netmasks and differ by at most the + * last bit. They are either for the same destination, or + * for an even/odd pair of destinations. + */ + if (ag->ag_dst_h == dst) { + /* We have two routes to the same destination. + * Routes are encountered in lexical order, so a + * route is never promoted until the parent route is + * already present. So we know that the new route is + * a promoted pair and the route already in the slot + * is the explicit route. + * + * Prefer the best route if their metrics differ, + * or the promoted one if not, following a sort + * of longest-match rule. + */ + if (pref <= ag->ag_pref) { + ag->ag_gate = gate; + ag->ag_nhop = nhop; + ag->ag_tag = tag; + ag->ag_metric = metric; + ag->ag_pref = pref; + x = ag->ag_state; + ag->ag_state = state; + state = x; + } + + /* The sequence number controls flash updating, + * and should be the smaller of the two. + */ + if (ag->ag_seqno > seqno) + ag->ag_seqno = seqno; + + /* some bits are set if they are set on either route */ + ag->ag_state |= (state & (AGS_PROMOTE_EITHER + | AGS_REDUN0 | AGS_REDUN1)); + return; + } + + /* If one of the routes can be promoted and the other can + * be suppressed, it may be possible to combine them or + * worthwhile to promote one. + * + * Note that any route that can be promoted is always + * marked to be eligible to be suppressed. + */ + if (!((state & AGS_PROMOTE) + && (ag->ag_state & AGS_SUPPRESS)) + && !((ag->ag_state & AGS_PROMOTE) + && (state & AGS_SUPPRESS))) + break; + + /* A pair of even/odd twin routes can be combined + * if either is redundant, or if they are via the + * same gateway and have the same metric. + */ + if (AG_IS_REDUN(ag->ag_state) + || AG_IS_REDUN(state) + || (ag->ag_gate == gate + && ag->ag_pref == pref + && (state & ag->ag_state & AGS_PROMOTE) != 0)) { + + /* We have both the even and odd pairs. + * Since the routes are encountered in order, + * the route in the slot must be the even twin. + * + * Combine and promote the pair of routes. + */ + if (seqno > ag->ag_seqno) + seqno = ag->ag_seqno; + if (!AG_IS_REDUN(state)) + state &= ~AGS_REDUN1; + if (AG_IS_REDUN(ag->ag_state)) + state |= AGS_REDUN0; + else + state &= ~AGS_REDUN0; + state |= (ag->ag_state & AGS_PROMOTE_EITHER); + if (ag->ag_tag != tag) + tag = 0; + if (ag->ag_nhop != nhop) + nhop = 0; + + /* Get rid of the even twin that was already + * in the slot. + */ + ag_del(ag); + + } else if (ag->ag_pref >= pref + && (ag->ag_state & AGS_PROMOTE)) { + /* If we cannot combine the pair, maybe the route + * with the worse metric can be promoted. + * + * Promote the old, even twin, by giving its slot + * in the table to the new, odd twin. + */ + ag->ag_dst_h = dst; + + xaddr = ag->ag_gate; + ag->ag_gate = gate; + gate = xaddr; + + xaddr = ag->ag_nhop; + ag->ag_nhop = nhop; + nhop = xaddr; + + x = ag->ag_tag; + ag->ag_tag = tag; + tag = x; + + x = ag->ag_state; + ag->ag_state = state; + state = x; + if (!AG_IS_REDUN(state)) + state &= ~AGS_REDUN0; + + x = ag->ag_metric; + ag->ag_metric = metric; + metric = x; + + x = ag->ag_pref; + ag->ag_pref = pref; + pref = x; + + if (seqno >= ag->ag_seqno) + seqno = ag->ag_seqno; + else + ag->ag_seqno = seqno; + + } else { + if (!(state & AGS_PROMOTE)) + break; /* cannot promote either twin */ + + /* promote the new, odd twin by shaving its + * mask and address. + */ + if (seqno > ag->ag_seqno) + seqno = ag->ag_seqno; + else + ag->ag_seqno = seqno; + if (!AG_IS_REDUN(state)) + state &= ~AGS_REDUN1; + } + + mask <<= 1; + dst &= mask; + + if (ag_cors == 0) { + ag = ag_corsest; + break; + } + ag = ag_cors; + ag_cors = ag->ag_cors; + } + + /* When we can no longer promote and combine routes, + * flush the old route in the target slot. Also flush + * any finer routes that we know will never be aggregated by + * the new route. + * + * In case we moved toward coarser masks, + * get back where we belong + */ + if (ag != 0 + && ag->ag_mask < mask) { + ag_cors = ag; + ag = ag->ag_fine; + } + + /* Empty the target slot + */ + if (ag != 0 && ag->ag_mask == mask) { + ag_flush(ag->ag_dst_h, ag->ag_mask, out); + ag = (ag_cors == 0) ? ag_corsest : ag_cors->ag_fine; + } + +#ifdef DEBUG_AG + (void)fflush(stderr); + if (ag == 0 && ag_cors != ag_finest) + abort(); + if (ag_cors == 0 && ag != ag_corsest) + abort(); + if (ag != 0 && ag->ag_cors != ag_cors) + abort(); + if (ag_cors != 0 && ag_cors->ag_fine != ag) + abort(); + CHECK_AG(); +#endif + + /* Save the new route on the end of the table. + */ + nag = ag_avail; + ag_avail = nag->ag_fine; + + nag->ag_dst_h = dst; + nag->ag_mask = mask; + nag->ag_gate = gate; + nag->ag_nhop = nhop; + nag->ag_metric = metric; + nag->ag_pref = pref; + nag->ag_tag = tag; + nag->ag_state = state; + nag->ag_seqno = seqno; + + nag->ag_fine = ag; + if (ag != 0) + ag->ag_cors = nag; + else + ag_finest = nag; + nag->ag_cors = ag_cors; + if (ag_cors == 0) + ag_corsest = nag; + else + ag_cors->ag_fine = nag; + CHECK_AG(); +} + + +static char * +rtm_type_name(u_char type) +{ + static char *rtm_types[] = { + "RTM_ADD", + "RTM_DELETE", + "RTM_CHANGE", + "RTM_GET", + "RTM_LOSING", + "RTM_REDIRECT", + "RTM_MISS", + "RTM_LOCK", + "RTM_OLDADD", + "RTM_OLDDEL", + "RTM_RESOLVE", + "RTM_NEWADDR", + "RTM_DELADDR", + "RTM_IFINFO" + }; + static char name0[10]; + + + if (type > sizeof(rtm_types)/sizeof(rtm_types[0]) + || type == 0) { + sprintf(name0, "RTM type %#x", type); + return name0; + } else { + return rtm_types[type-1]; + } +} + + +/* Trim a mask in a sockaddr + * Produce a length of 0 for an address of 0. + * Otherwise produce the index of the first zero byte. + */ +void +#ifdef _HAVE_SIN_LEN +masktrim(struct sockaddr_in *ap) +#else +masktrim(struct sockaddr_in_new *ap) +#endif +{ + register char *cp; + + if (ap->sin_addr.s_addr == 0) { + ap->sin_len = 0; + return; + } + cp = (char *)(&ap->sin_addr.s_addr+1); + while (*--cp == 0) + continue; + ap->sin_len = cp - (char*)ap + 1; +} + + +/* Tell the kernel to add, delete or change a route + */ +static void +rtioctl(int action, /* RTM_DELETE, etc */ + naddr dst, + naddr gate, + naddr mask, + int metric, + int flags) +{ + struct { + struct rt_msghdr w_rtm; + struct sockaddr_in w_dst; + struct sockaddr_in w_gate; +#ifdef _HAVE_SA_LEN + struct sockaddr_in w_mask; +#else + struct sockaddr_in_new w_mask; +#endif + } w; + long cc; + +again: + bzero(&w, sizeof(w)); + w.w_rtm.rtm_msglen = sizeof(w); + w.w_rtm.rtm_version = RTM_VERSION; + w.w_rtm.rtm_type = action; + w.w_rtm.rtm_flags = flags; + w.w_rtm.rtm_seq = ++rt_sock_seqno; + w.w_rtm.rtm_addrs = RTA_DST|RTA_GATEWAY; + if (metric != 0) { + w.w_rtm.rtm_rmx.rmx_hopcount = metric; + w.w_rtm.rtm_inits |= RTV_HOPCOUNT; + } + w.w_dst.sin_family = AF_INET; + w.w_dst.sin_addr.s_addr = dst; + w.w_gate.sin_family = AF_INET; + w.w_gate.sin_addr.s_addr = gate; +#ifdef _HAVE_SA_LEN + w.w_dst.sin_len = sizeof(w.w_dst); + w.w_gate.sin_len = sizeof(w.w_gate); +#endif + if (mask == HOST_MASK) { + w.w_rtm.rtm_flags |= RTF_HOST; + w.w_rtm.rtm_msglen -= sizeof(w.w_mask); + } else { + w.w_rtm.rtm_addrs |= RTA_NETMASK; + w.w_mask.sin_addr.s_addr = htonl(mask); +#ifdef _HAVE_SA_LEN + masktrim(&w.w_mask); + if (w.w_mask.sin_len == 0) + w.w_mask.sin_len = sizeof(long); + w.w_rtm.rtm_msglen -= (sizeof(w.w_mask) - w.w_mask.sin_len); +#endif + } + + if (TRACEKERNEL) + trace_kernel("write kernel %s %s->%s metric=%d flags=%#x\n", + rtm_type_name(action), + addrname(dst, mask, 0), naddr_ntoa(gate), + metric, flags); + +#ifndef NO_INSTALL + cc = write(rt_sock, &w, w.w_rtm.rtm_msglen); + if (cc == w.w_rtm.rtm_msglen) + return; + if (cc < 0) { + if (errno == ESRCH + && (action == RTM_CHANGE || action == RTM_DELETE)) { + trace_act("route to %s disappeared before %s\n", + addrname(dst, mask, 0), + rtm_type_name(action)); + if (action == RTM_CHANGE) { + action = RTM_ADD; + goto again; + } + return; + } + msglog("write(rt_sock) %s %s --> %s: %s", + rtm_type_name(action), + addrname(dst, mask, 0), naddr_ntoa(gate), + strerror(errno)); + } else { + msglog("write(rt_sock) wrote %d instead of %d", + cc, w.w_rtm.rtm_msglen); + } +#endif +} + + +#define KHASH_SIZE 71 /* should be prime */ +#define KHASH(a,m) khash_bins[((a) ^ (m)) % KHASH_SIZE] +static struct khash { + struct khash *k_next; + naddr k_dst; + naddr k_mask; + naddr k_gate; + short k_metric; + u_short k_state; +#define KS_NEW 0x001 +#define KS_DELETE 0x002 +#define KS_ADD 0x004 /* add to the kernel */ +#define KS_CHANGE 0x008 /* tell kernel to change the route */ +#define KS_DEL_ADD 0x010 /* delete & add to change the kernel */ +#define KS_STATIC 0x020 /* Static flag in kernel */ +#define KS_GATEWAY 0x040 /* G flag in kernel */ +#define KS_DYNAMIC 0x080 /* result of redirect */ +#define KS_DELETED 0x100 /* already deleted */ + time_t k_keep; +#define K_KEEP_LIM 30 + time_t k_redirect_time; +} *khash_bins[KHASH_SIZE]; + + +static struct khash* +kern_find(naddr dst, naddr mask, struct khash ***ppk) +{ + struct khash *k, **pk; + + for (pk = &KHASH(dst,mask); (k = *pk) != 0; pk = &k->k_next) { + if (k->k_dst == dst && k->k_mask == mask) + break; + } + if (ppk != 0) + *ppk = pk; + return k; +} + + +static struct khash* +kern_add(naddr dst, naddr mask) +{ + struct khash *k, **pk; + + k = kern_find(dst, mask, &pk); + if (k != 0) + return k; + + k = (struct khash *)malloc(sizeof(*k)); + + bzero(k, sizeof(*k)); + k->k_dst = dst; + k->k_mask = mask; + k->k_state = KS_NEW; + k->k_keep = now.tv_sec; + *pk = k; + + return k; +} + + +/* If a kernel route has a non-zero metric, check that it is still in the + * daemon table, and not deleted by interfaces coming and going. + */ +static void +kern_check_static(struct khash *k, + struct interface *ifp) +{ + struct rt_entry *rt; + naddr int_addr; + + if (k->k_metric == 0) + return; + + int_addr = (ifp != 0) ? ifp->int_addr : loopaddr; + + rt = rtget(k->k_dst, k->k_mask); + if (rt != 0) { + if (!(rt->rt_state & RS_STATIC)) + rtchange(rt, rt->rt_state | RS_STATIC, + k->k_gate, int_addr, + k->k_metric, 0, ifp, now.tv_sec, 0); + } else { + rtadd(k->k_dst, k->k_mask, k->k_gate, int_addr, + k->k_metric, 0, RS_STATIC, ifp); + } +} + + +/* add a route the kernel told us + */ +static void +rtm_add(struct rt_msghdr *rtm, + struct rt_addrinfo *info, + time_t keep) +{ + struct khash *k; + struct interface *ifp; + naddr mask; + + + if (rtm->rtm_flags & RTF_HOST) { + mask = HOST_MASK; + } else if (INFO_MASK(info) != 0) { + mask = ntohl(S_ADDR(INFO_MASK(info))); + } else { + msglog("punt %s without mask", + rtm_type_name(rtm->rtm_type)); + return; + } + + if (INFO_GATE(info) == 0 + || INFO_GATE(info)->sa_family != AF_INET) { + msglog("punt %s without gateway", + rtm_type_name(rtm->rtm_type)); + return; + } + + k = kern_add(S_ADDR(INFO_DST(info)), mask); + if (k->k_state & KS_NEW) + k->k_keep = now.tv_sec+keep; + k->k_gate = S_ADDR(INFO_GATE(info)); + k->k_metric = rtm->rtm_rmx.rmx_hopcount; + if (k->k_metric < 0) + k->k_metric = 0; + else if (k->k_metric > HOPCNT_INFINITY) + k->k_metric = HOPCNT_INFINITY; + k->k_state &= ~(KS_DELETED | KS_GATEWAY | KS_STATIC | KS_NEW); + if (rtm->rtm_flags & RTF_GATEWAY) + k->k_state |= KS_GATEWAY; + if (rtm->rtm_flags & RTF_STATIC) + k->k_state |= KS_STATIC; + + if (0 != (rtm->rtm_flags & (RTF_DYNAMIC | RTF_MODIFIED))) { + if (supplier) { + /* Routers are not supposed to listen to redirects, + * so delete it. + */ + k->k_state &= ~KS_DYNAMIC; + k->k_state |= KS_DELETE; + LIM_SEC(need_kern, 0); + trace_act("mark redirected %s --> %s for deletion" + " since this is a router\n", + addrname(k->k_dst, k->k_mask, 0), + naddr_ntoa(k->k_gate)); + } else { + k->k_state |= KS_DYNAMIC; + k->k_redirect_time = now.tv_sec; + } + return; + } + + /* If it is not a static route, quit until the next comparison + * between the kernel and daemon tables, when it will be deleted. + */ + if (!(k->k_state & KS_STATIC)) { + k->k_state |= KS_DELETE; + LIM_SEC(need_kern, k->k_keep); + return; + } + + /* Put static routes with real metrics into the daemon table so + * they can be advertised. + * + * Find the interface concerned + */ + ifp = iflookup(k->k_gate); + if (ifp == 0) { + /* if there is no known interface, + * maybe there is a new interface + */ + ifinit(); + ifp = iflookup(k->k_gate); + if (ifp == 0) + msglog("static route %s --> %s impossibly lacks ifp", + addrname(S_ADDR(INFO_DST(info)), mask, 0), + naddr_ntoa(k->k_gate)); + } + + kern_check_static(k, ifp); +} + + +/* deal with packet loss + */ +static void +rtm_lose(struct rt_msghdr *rtm, + struct rt_addrinfo *info) +{ + if (INFO_GATE(info) == 0 + || INFO_GATE(info)->sa_family != AF_INET) { + msglog("punt %s without gateway", + rtm_type_name(rtm->rtm_type)); + return; + } + + if (!supplier) + rdisc_age(S_ADDR(INFO_GATE(info))); + + age(S_ADDR(INFO_GATE(info))); +} + + +/* Clean the kernel table by copying it to the daemon image. + * Eventually the daemon will delete any extra routes. + */ +void +flush_kern(void) +{ + size_t needed; + int mib[6]; + char *buf, *next, *lim; + struct rt_msghdr *rtm; + struct interface *ifp; + static struct sockaddr_in gate_sa; + struct rt_addrinfo info; + + + mib[0] = CTL_NET; + mib[1] = PF_ROUTE; + mib[2] = 0; /* protocol */ + mib[3] = 0; /* wildcard address family */ + mib[4] = NET_RT_DUMP; + mib[5] = 0; /* no flags */ + if (sysctl(mib, 6, 0, &needed, 0, 0) < 0) { + DBGERR(1,"RT_DUMP-sysctl-estimate"); + return; + } + buf = malloc(needed); + if (sysctl(mib, 6, buf, &needed, 0, 0) < 0) + BADERR(1,"RT_DUMP"); + lim = buf + needed; + for (next = buf; next < lim; next += rtm->rtm_msglen) { + rtm = (struct rt_msghdr *)next; + + rt_xaddrs(&info, + (struct sockaddr *)(rtm+1), + (struct sockaddr *)(next + rtm->rtm_msglen), + rtm->rtm_addrs); + + if (INFO_DST(&info) == 0 + || INFO_DST(&info)->sa_family != AF_INET) + continue; + + /* ignore ARP table entries on systems with a merged route + * and ARP table. + */ + if (rtm->rtm_flags & RTF_LLINFO) + continue; + + if (INFO_GATE(&info) == 0) + continue; + if (INFO_GATE(&info)->sa_family != AF_INET) { + if (INFO_GATE(&info)->sa_family != AF_LINK) + continue; + ifp = ifwithindex(((struct sockaddr_dl *) + INFO_GATE(&info))->sdl_index); + if (ifp == 0) + continue; + if ((ifp->int_if_flags & IFF_POINTOPOINT) + || S_ADDR(INFO_DST(&info)) == ifp->int_addr) + gate_sa.sin_addr.s_addr = ifp->int_addr; + else + gate_sa.sin_addr.s_addr = htonl(ifp->int_net); +#ifdef _HAVE_SA_LEN + gate_sa.sin_len = sizeof(gate_sa); +#endif + gate_sa.sin_family = AF_INET; + INFO_GATE(&info) = (struct sockaddr *)&gate_sa; + } + + /* ignore multicast addresses + */ + if (IN_MULTICAST(ntohl(S_ADDR(INFO_DST(&info))))) + continue; + + /* Note static routes and interface routes, and also + * preload the image of the kernel table so that + * we can later clean it, as well as avoid making + * unneeded changes. Keep the old kernel routes for a + * few seconds to allow a RIP or router-discovery + * response to be heard. + */ + rtm_add(rtm,&info,MIN_WAITTIME); + } + free(buf); +} + + +/* Listen to announcements from the kernel + */ +void +read_rt(void) +{ + long cc; + struct interface *ifp; + naddr mask; + union { + struct { + struct rt_msghdr rtm; + struct sockaddr addrs[RTAX_MAX]; + } r; + struct if_msghdr ifm; + } m; + char str[100], *strp; + struct rt_addrinfo info; + + + for (;;) { + cc = read(rt_sock, &m, sizeof(m)); + if (cc <= 0) { + if (cc < 0 && errno != EWOULDBLOCK) + LOGERR("read(rt_sock)"); + return; + } + + if (m.r.rtm.rtm_version != RTM_VERSION) { + msglog("bogus routing message version %d", + m.r.rtm.rtm_version); + continue; + } + + /* Ignore our own results. + */ + if (m.r.rtm.rtm_type <= RTM_CHANGE + && m.r.rtm.rtm_pid == mypid) { + static int complained = 0; + if (!complained) { + msglog("receiving our own change messages"); + complained = 1; + } + continue; + } + + if (m.r.rtm.rtm_type == RTM_IFINFO + || m.r.rtm.rtm_type == RTM_NEWADDR + || m.r.rtm.rtm_type == RTM_DELADDR) { + ifp = ifwithindex(m.ifm.ifm_index); + if (ifp == 0) + trace_act("note %s with flags %#x" + " for index #%d\n", + rtm_type_name(m.r.rtm.rtm_type), + m.ifm.ifm_flags, + m.ifm.ifm_index); + else + trace_act("note %s with flags %#x for %s\n", + rtm_type_name(m.r.rtm.rtm_type), + m.ifm.ifm_flags, + ifp->int_name); + + /* After being informed of a change to an interface, + * check them all now if the check would otherwise + * be a long time from now, if the interface is + * not known, or if the interface has been turned + * off or on. + */ + if (ifinit_timer.tv_sec-now.tv_sec>=CHECK_BAD_INTERVAL + || ifp == 0 + || ((ifp->int_if_flags ^ m.ifm.ifm_flags) + & IFF_UP_RUNNING) != 0) + ifinit_timer.tv_sec = now.tv_sec; + continue; + } + + strcpy(str, rtm_type_name(m.r.rtm.rtm_type)); + strp = &str[strlen(str)]; + if (m.r.rtm.rtm_type <= RTM_CHANGE) + strp += sprintf(strp," from pid %d",m.r.rtm.rtm_pid); + + rt_xaddrs(&info, m.r.addrs, &m.r.addrs[RTAX_MAX], + m.r.rtm.rtm_addrs); + + if (INFO_DST(&info) == 0) { + trace_act("ignore %s without dst\n", str); + continue; + } + + if (INFO_DST(&info)->sa_family != AF_INET) { + trace_act("ignore %s for AF %d\n", str, + INFO_DST(&info)->sa_family); + continue; + } + + mask = ((INFO_MASK(&info) != 0) + ? ntohl(S_ADDR(INFO_MASK(&info))) + : (m.r.rtm.rtm_flags & RTF_HOST) + ? HOST_MASK + : std_mask(S_ADDR(INFO_DST(&info)))); + + strp += sprintf(strp, ": %s", + addrname(S_ADDR(INFO_DST(&info)), mask, 0)); + + if (IN_MULTICAST(ntohl(S_ADDR(INFO_DST(&info))))) { + trace_act("ignore multicast %s\n", str); + continue; + } + + if (INFO_GATE(&info) != 0 + && INFO_GATE(&info)->sa_family == AF_INET) + strp += sprintf(strp, " --> %s", + saddr_ntoa(INFO_GATE(&info))); + + if (INFO_AUTHOR(&info) != 0) + strp += sprintf(strp, " by authority of %s", + saddr_ntoa(INFO_AUTHOR(&info))); + + switch (m.r.rtm.rtm_type) { + case RTM_ADD: + case RTM_CHANGE: + case RTM_REDIRECT: + if (m.r.rtm.rtm_errno != 0) { + trace_act("ignore %s with \"%s\" error\n", + str, strerror(m.r.rtm.rtm_errno)); + } else { + trace_act("%s\n", str); + rtm_add(&m.r.rtm,&info,0); + } + break; + + case RTM_DELETE: + if (m.r.rtm.rtm_errno != 0) { + trace_act("ignore %s with \"%s\" error\n", + str, strerror(m.r.rtm.rtm_errno)); + } else { + trace_act("%s\n", str); + del_static(S_ADDR(INFO_DST(&info)), mask, 1); + } + break; + + case RTM_LOSING: + trace_act("%s\n", str); + rtm_lose(&m.r.rtm,&info); + break; + + default: + trace_act("ignore %s\n", str); + break; + } + } +} + + +/* after aggregating, note routes that belong in the kernel + */ +static void +kern_out(struct ag_info *ag) +{ + struct khash *k; + + + /* Do not install bad routes if they are not already present. + * This includes routes that had RS_NET_SYN for interfaces that + * recently died. + */ + if (ag->ag_metric == HOPCNT_INFINITY) { + k = kern_find(htonl(ag->ag_dst_h), ag->ag_mask, 0); + if (k == 0) + return; + } else { + k = kern_add(htonl(ag->ag_dst_h), ag->ag_mask); + } + + if (k->k_state & KS_NEW) { + /* will need to add new entry to the kernel table */ + k->k_state = KS_ADD; + if (ag->ag_state & AGS_GATEWAY) + k->k_state |= KS_GATEWAY; + k->k_gate = ag->ag_gate; + k->k_metric = ag->ag_metric; + return; + } + + if (k->k_state & KS_STATIC) + return; + + /* modify existing kernel entry if necessary */ + if (k->k_gate != ag->ag_gate + || k->k_metric != ag->ag_metric) { + k->k_gate = ag->ag_gate; + k->k_metric = ag->ag_metric; + k->k_state |= KS_CHANGE; + } + + if (k->k_state & KS_DYNAMIC) { + k->k_state &= ~KS_DYNAMIC; + k->k_state |= (KS_ADD | KS_DEL_ADD); + } + + if ((k->k_state & KS_GATEWAY) + && !(ag->ag_state & AGS_GATEWAY)) { + k->k_state &= ~KS_GATEWAY; + k->k_state |= (KS_ADD | KS_DEL_ADD); + } else if (!(k->k_state & KS_GATEWAY) + && (ag->ag_state & AGS_GATEWAY)) { + k->k_state |= KS_GATEWAY; + k->k_state |= (KS_ADD | KS_DEL_ADD); + } + + /* Deleting-and-adding is necessary to change aspects of a route. + * Just delete instead of deleting and then adding a bad route. + * Otherwise, we want to keep the route in the kernel. + */ + if (k->k_metric == HOPCNT_INFINITY + && (k->k_state & KS_DEL_ADD)) + k->k_state |= KS_DELETE; + else + k->k_state &= ~KS_DELETE; +#undef RT +} + + +/* ARGSUSED */ +static int +walk_kern(struct radix_node *rn, + struct walkarg *w) +{ +#define RT ((struct rt_entry *)rn) + char metric, pref; + u_int ags = 0; + + + /* Do not install synthetic routes */ + if (RT->rt_state & RS_NET_SYN) + return 0; + + if (!(RT->rt_state & RS_IF)) { + ags |= (AGS_GATEWAY | AGS_SUPPRESS | AGS_PROMOTE); + + } else { + /* Do not install routes for "external" remote interfaces. + */ + if (RT->rt_ifp != 0 && (RT->rt_ifp->int_state & IS_EXTERNAL)) + return 0; + + ags |= AGS_IF; + + /* If it is not an interface, or an alias for an interface, + * it must be a "gateway." + * + * If it is a "remote" interface, it is also a "gateway" to + * the kernel if is not a alias. + */ + if (RT->rt_ifp == 0 + || ((RT->rt_ifp->int_state & IS_REMOTE) + && RT->rt_ifp->int_metric == 0)) + ags |= (AGS_GATEWAY | AGS_SUPPRESS | AGS_PROMOTE); + } + + if (RT->rt_state & RS_RDISC) + ags |= AGS_CORS_GATE; + + /* aggregate good routes without regard to their metric */ + pref = 1; + metric = RT->rt_metric; + if (metric == HOPCNT_INFINITY) { + /* if the route is dead, so try hard to aggregate. */ + pref = HOPCNT_INFINITY; + ags |= (AGS_FINE_GATE | AGS_SUPPRESS); + } + + ag_check(RT->rt_dst, RT->rt_mask, RT->rt_gate, 0, + metric,pref, 0, 0, ags, kern_out); + return 0; +#undef RT +} + + +/* Update the kernel table to match the daemon table. + */ +static void +fix_kern(void) +{ + int i, flags; + struct khash *k, **pk; + + + need_kern = age_timer; + + /* Walk daemon table, updating the copy of the kernel table. + */ + (void)rn_walktree(rhead, walk_kern, 0); + ag_flush(0,0,kern_out); + + for (i = 0; i < KHASH_SIZE; i++) { + for (pk = &khash_bins[i]; (k = *pk) != 0; ) { + /* Do not touch static routes */ + if (k->k_state & KS_STATIC) { + kern_check_static(k,0); + pk = &k->k_next; + continue; + } + + /* check hold on routes deleted by the operator */ + if (k->k_keep > now.tv_sec) { + LIM_SEC(need_kern, k->k_keep); + k->k_state |= KS_DELETE; + pk = &k->k_next; + continue; + } + + if ((k->k_state & (KS_DELETE | KS_DYNAMIC)) + == KS_DELETE) { + if (!(k->k_state & KS_DELETED)) + rtioctl(RTM_DELETE, + k->k_dst, k->k_gate, k->k_mask, + 0, 0); + *pk = k->k_next; + free(k); + continue; + } + + if (0 != (k->k_state&(KS_ADD|KS_CHANGE|KS_DEL_ADD))) { + if (k->k_state & KS_DEL_ADD) { + rtioctl(RTM_DELETE, + k->k_dst,k->k_gate,k->k_mask, + 0, 0); + k->k_state &= ~KS_DYNAMIC; + } + + flags = 0; + if (0 != (k->k_state&(KS_GATEWAY|KS_DYNAMIC))) + flags |= RTF_GATEWAY; + + if (k->k_state & KS_ADD) { + rtioctl(RTM_ADD, + k->k_dst, k->k_gate, k->k_mask, + k->k_metric, flags); + } else if (k->k_state & KS_CHANGE) { + rtioctl(RTM_CHANGE, + k->k_dst,k->k_gate,k->k_mask, + k->k_metric, flags); + } + k->k_state &= ~(KS_ADD|KS_CHANGE|KS_DEL_ADD); + } + + /* Mark this route to be deleted in the next cycle. + * This deletes routes that disappear from the + * daemon table, since the normal aging code + * will clear the bit for routes that have not + * disappeared from the daemon table. + */ + k->k_state |= KS_DELETE; + pk = &k->k_next; + } + } +} + + +/* Delete a static route in the image of the kernel table. + */ +void +del_static(naddr dst, + naddr mask, + int gone) +{ + struct khash *k; + struct rt_entry *rt; + + /* Just mark it in the table to be deleted next time the kernel + * table is updated. + * If it has already been deleted, mark it as such, and set its + * keep-timer so that it will not be deleted again for a while. + * This lets the operator delete a route added by the daemon + * and add a replacement. + */ + k = kern_find(dst, mask, 0); + if (k != 0) { + k->k_state &= ~(KS_STATIC | KS_DYNAMIC); + k->k_state |= KS_DELETE; + if (gone) { + k->k_state |= KS_DELETED; + k->k_keep = now.tv_sec + K_KEEP_LIM; + } + } + + rt = rtget(dst, mask); + if (rt != 0 && (rt->rt_state & RS_STATIC)) + rtbad(rt); +} + + +/* Delete all routes generated from ICMP Redirects that use a given gateway, + * as well as old redirected routes. + */ +void +del_redirects(naddr bad_gate, + time_t old) +{ + int i; + struct khash *k; + + + for (i = 0; i < KHASH_SIZE; i++) { + for (k = khash_bins[i]; k != 0; k = k->k_next) { + if (!(k->k_state & KS_DYNAMIC) + || (k->k_state & KS_STATIC)) + continue; + + if (k->k_gate != bad_gate + && k->k_redirect_time > old + && !supplier) + continue; + + k->k_state |= KS_DELETE; + k->k_state &= ~KS_DYNAMIC; + need_kern.tv_sec = now.tv_sec; + trace_act("mark redirected %s --> %s for deletion\n", + addrname(k->k_dst, k->k_mask, 0), + naddr_ntoa(k->k_gate)); + } + } +} + + +/* Start the daemon tables. + */ +void +rtinit(void) +{ + extern int max_keylen; + int i; + struct ag_info *ag; + + /* Initialize the radix trees */ + max_keylen = sizeof(struct sockaddr_in); + rn_init(); + rn_inithead((void**)&rhead, 32); + + /* mark all of the slots in the table free */ + ag_avail = ag_slots; + for (ag = ag_slots, i = 1; i < NUM_AG_SLOTS; i++) { + ag->ag_fine = ag+1; + ag++; + } +} + + +#ifdef _HAVE_SIN_LEN +static struct sockaddr_in dst_sock = {sizeof(dst_sock), AF_INET}; +static struct sockaddr_in mask_sock = {sizeof(mask_sock), AF_INET}; +#else +static struct sockaddr_in_new dst_sock = {_SIN_ADDR_SIZE, AF_INET}; +static struct sockaddr_in_new mask_sock = {_SIN_ADDR_SIZE, AF_INET}; +#endif + + +void +set_need_flash(void) +{ + if (!need_flash) { + need_flash = 1; + /* Do not send the flash update immediately. Wait a little + * while to hear from other routers. + */ + no_flash.tv_sec = now.tv_sec + MIN_WAITTIME; + } +} + + +/* Get a particular routing table entry + */ +struct rt_entry * +rtget(naddr dst, naddr mask) +{ + struct rt_entry *rt; + + dst_sock.sin_addr.s_addr = dst; + mask_sock.sin_addr.s_addr = mask; + masktrim(&mask_sock); + rt = (struct rt_entry *)rhead->rnh_lookup(&dst_sock,&mask_sock,rhead); + if (!rt + || rt->rt_dst != dst + || rt->rt_mask != mask) + return 0; + + return rt; +} + + +/* Find a route to dst as the kernel would. + */ +struct rt_entry * +rtfind(naddr dst) +{ + dst_sock.sin_addr.s_addr = dst; + return (struct rt_entry *)rhead->rnh_matchaddr(&dst_sock, rhead); +} + + +/* add a route to the table + */ +void +rtadd(naddr dst, + naddr mask, + naddr gate, /* forward packets here */ + naddr router, /* on the authority of this router */ + int metric, + u_short tag, + u_int state, /* rs_state for the entry */ + struct interface *ifp) +{ + struct rt_entry *rt; + naddr smask; + int i; + struct rt_spare *rts; + + rt = (struct rt_entry *)rtmalloc(sizeof (*rt), "rtadd"); + bzero(rt, sizeof(*rt)); + for (rts = rt->rt_spares, i = NUM_SPARES; i != 0; i--, rts++) + rts->rts_metric = HOPCNT_INFINITY; + + rt->rt_nodes->rn_key = (caddr_t)&rt->rt_dst_sock; + rt->rt_dst = dst; + rt->rt_dst_sock.sin_family = AF_INET; +#ifdef _HAVE_SIN_LEN + rt->rt_dst_sock.sin_len = dst_sock.sin_len; +#endif + if (mask != HOST_MASK) { + smask = std_mask(dst); + if ((smask & ~mask) == 0 && mask > smask) + state |= RS_SUBNET; + } + mask_sock.sin_addr.s_addr = mask; + masktrim(&mask_sock); + rt->rt_mask = mask; + rt->rt_state = state; + rt->rt_gate = gate; + rt->rt_router = router; + rt->rt_time = now.tv_sec; + rt->rt_metric = metric; + rt->rt_poison_metric = HOPCNT_INFINITY; + rt->rt_tag = tag; + rt->rt_ifp = ifp; + rt->rt_seqno = update_seqno; + + if (++total_routes == MAX_ROUTES) + msglog("have maximum (%d) routes", total_routes); + if (TRACEACTIONS) + trace_add_del("Add", rt); + + need_kern.tv_sec = now.tv_sec; + set_need_flash(); + + if (0 == rhead->rnh_addaddr(&rt->rt_dst_sock, &mask_sock, + rhead, rt->rt_nodes)) { + msglog("rnh_addaddr() failed for %s mask=%#x", + naddr_ntoa(dst), mask); + } +} + + +/* notice a changed route + */ +void +rtchange(struct rt_entry *rt, + u_int state, /* new state bits */ + naddr gate, /* now forward packets here */ + naddr router, /* on the authority of this router */ + int metric, /* new metric */ + u_short tag, + struct interface *ifp, + time_t new_time, + char *label) +{ + if (rt->rt_metric != metric) { + /* Fix the kernel immediately if it seems the route + * has gone bad, since there may be a working route that + * aggregates this route. + */ + if (metric == HOPCNT_INFINITY) { + need_kern.tv_sec = now.tv_sec; + if (new_time >= now.tv_sec - EXPIRE_TIME) + new_time = now.tv_sec - EXPIRE_TIME; + } + rt->rt_seqno = update_seqno; + set_need_flash(); + } + + if (rt->rt_gate != gate) { + need_kern.tv_sec = now.tv_sec; + rt->rt_seqno = update_seqno; + set_need_flash(); + } + + state |= (rt->rt_state & RS_SUBNET); + + /* Keep various things from deciding ageless routes are stale. + */ + if (!AGE_RT(state, ifp)) + new_time = now.tv_sec; + + if (TRACEACTIONS) + trace_change(rt, state, gate, router, metric, tag, ifp, + new_time, + label ? label : "Chg "); + + rt->rt_state = state; + rt->rt_gate = gate; + rt->rt_router = router; + rt->rt_metric = metric; + rt->rt_tag = tag; + rt->rt_ifp = ifp; + rt->rt_time = new_time; +} + + +/* check for a better route among the spares + */ +static struct rt_spare * +rts_better(struct rt_entry *rt) +{ + struct rt_spare *rts, *rts1; + int i; + + /* find the best alternative among the spares */ + rts = rt->rt_spares+1; + for (i = NUM_SPARES, rts1 = rts+1; i > 2; i--, rts1++) { + if (BETTER_LINK(rt,rts1,rts)) + rts = rts1; + } + + return rts; +} + + +/* switch to a backup route + */ +void +rtswitch(struct rt_entry *rt, + struct rt_spare *rts) +{ + struct rt_spare swap; + char label[10]; + + + /* Do not change permanent routes */ + if (0 != (rt->rt_state & (RS_MHOME | RS_STATIC | RS_RDISC + | RS_NET_SYN | RS_IF))) + return; + + /* find the best alternative among the spares */ + if (rts == 0) + rts = rts_better(rt); + + /* Do not bother if it is not worthwhile. + */ + if (!BETTER_LINK(rt, rts, rt->rt_spares)) + return; + + swap = rt->rt_spares[0]; + (void)sprintf(label, "Use #%d", rts - rt->rt_spares); + rtchange(rt, rt->rt_state & ~(RS_NET_SYN | RS_RDISC), + rts->rts_gate, rts->rts_router, rts->rts_metric, + rts->rts_tag, rts->rts_ifp, rts->rts_time, label); + *rts = swap; +} + + +void +rtdelete(struct rt_entry *rt) +{ + struct khash *k; + + + if (TRACEACTIONS) + trace_add_del("Del", rt); + + k = kern_find(rt->rt_dst, rt->rt_mask, 0); + if (k != 0) { + k->k_state |= KS_DELETE; + need_kern.tv_sec = now.tv_sec; + } + + dst_sock.sin_addr.s_addr = rt->rt_dst; + mask_sock.sin_addr.s_addr = rt->rt_mask; + masktrim(&mask_sock); + if (rt != (struct rt_entry *)rhead->rnh_deladdr(&dst_sock, &mask_sock, + rhead)) { + msglog("rnh_deladdr() failed"); + } else { + free(rt); + total_routes--; + } +} + + +/* Get rid of a bad route, and try to switch to a replacement. + */ +void +rtbad(struct rt_entry *rt) +{ + /* Poison the route */ + rtchange(rt, rt->rt_state & ~(RS_IF | RS_LOCAL | RS_STATIC), + rt->rt_gate, rt->rt_router, HOPCNT_INFINITY, rt->rt_tag, + 0, rt->rt_time, 0); + + rtswitch(rt, 0); +} + + +/* Junk a RS_NET_SYN or RS_LOCAL route, + * unless it is needed by another interface. + */ +void +rtbad_sub(struct rt_entry *rt) +{ + struct interface *ifp, *ifp1; + struct intnet *intnetp; + u_int state; + + + ifp1 = 0; + state = 0; + + if (rt->rt_state & RS_LOCAL) { + /* Is this the route through loopback for the interface? + * If so, see if it is used by any other interfaces, such + * as a point-to-point interface with the same local address. + */ + for (ifp = ifnet; ifp != 0; ifp = ifp->int_next) { + /* Retain it if another interface needs it. + */ + if (ifp->int_addr == rt->rt_ifp->int_addr) { + state |= RS_LOCAL; + ifp1 = ifp; + break; + } + } + + } + + if (!(state & RS_LOCAL)) { + /* Retain RIPv1 logical network route if there is another + * interface that justifies it. + */ + if (rt->rt_state & RS_NET_SYN) { + for (ifp = ifnet; ifp != 0; ifp = ifp->int_next) { + if ((ifp->int_state & IS_NEED_NET_SYN) + && rt->rt_mask == ifp->int_std_mask + && rt->rt_dst == ifp->int_std_addr) { + state |= RS_NET_SYN; + ifp1 = ifp; + break; + } + } + } + + /* or if there is an authority route that needs it. */ + for (intnetp = intnets; + intnetp != 0; + intnetp = intnetp->intnet_next) { + if (intnetp->intnet_addr == rt->rt_dst + && intnetp->intnet_mask == rt->rt_mask) { + state |= (RS_NET_SYN | RS_NET_INT); + break; + } + } + } + + if (ifp1 != 0 || (state & RS_NET_SYN)) { + rtchange(rt, ((rt->rt_state & ~(RS_NET_SYN | RS_LOCAL)) + | state), + rt->rt_gate, rt->rt_router, rt->rt_metric, + rt->rt_tag, ifp1, rt->rt_time, 0); + } else { + rtbad(rt); + } +} + + +/* Called while walking the table looking for sick interfaces + * or after a time change. + */ +/* ARGSUSED */ +int +walk_bad(struct radix_node *rn, + struct walkarg *w) +{ +#define RT ((struct rt_entry *)rn) + struct rt_spare *rts; + int i; + time_t new_time; + + + /* fix any spare routes through the interface + */ + rts = RT->rt_spares; + for (i = NUM_SPARES; i != 1; i--) { + rts++; + + if (rts->rts_ifp != 0 + && (rts->rts_ifp->int_state & IS_BROKE)) { + /* mark the spare route to be deleted immediately */ + new_time = rts->rts_time; + if (new_time >= now_garbage) + new_time = now_garbage-1; + trace_upslot(RT, rts, rts->rts_gate, + rts->rts_router, 0, + HOPCNT_INFINITY, rts->rts_tag, + new_time); + rts->rts_ifp = 0; + rts->rts_metric = HOPCNT_INFINITY; + rts->rts_time = new_time; + } + } + + /* Deal with the main route + */ + /* finished if it has been handled before or if its interface is ok + */ + if (RT->rt_ifp == 0 || !(RT->rt_ifp->int_state & IS_BROKE)) + return 0; + + /* Bad routes for other than interfaces are easy. + */ + if (0 == (RT->rt_state & (RS_IF | RS_NET_SYN | RS_LOCAL))) { + rtbad(RT); + return 0; + } + + rtbad_sub(RT); + return 0; +#undef RT +} + + +/* Check the age of an individual route. + */ +/* ARGSUSED */ +static int +walk_age(struct radix_node *rn, + struct walkarg *w) +{ +#define RT ((struct rt_entry *)rn) + struct interface *ifp; + struct rt_spare *rts; + int i; + + + /* age all of the spare routes, including the primary route + * currently in use + */ + rts = RT->rt_spares; + for (i = NUM_SPARES; i != 0; i--, rts++) { + + ifp = rts->rts_ifp; + if (i == NUM_SPARES) { + if (!AGE_RT(RT->rt_state, ifp)) { + /* Keep various things from deciding ageless + * routes are stale + */ + rts->rts_time = now.tv_sec; + continue; + } + + /* forget RIP routes after RIP has been turned off. + */ + if (rip_sock < 0) { + rtdelete(RT); + return 0; + } + } + + /* age failing routes + */ + if (age_bad_gate == rts->rts_gate + && rts->rts_time >= now_stale) { + rts->rts_time -= SUPPLY_INTERVAL; + } + + /* trash the spare routes when they go bad */ + if (rts->rts_metric < HOPCNT_INFINITY + && now_garbage > rts->rts_time) { + trace_upslot(RT, rts, rts->rts_gate, + rts->rts_router, rts->rts_ifp, + HOPCNT_INFINITY, rts->rts_tag, + rts->rts_time); + rts->rts_metric = HOPCNT_INFINITY; + } + } + + + /* finished if the active route is still fresh */ + if (now_stale <= RT->rt_time) + return 0; + + /* try to switch to an alternative */ + rtswitch(RT, 0); + + /* Delete a dead route after it has been publically mourned. */ + if (now_garbage > RT->rt_time) { + rtdelete(RT); + return 0; + } + + /* Start poisoning a bad route before deleting it. */ + if (now.tv_sec - RT->rt_time > EXPIRE_TIME) + rtchange(RT, RT->rt_state, RT->rt_gate, RT->rt_router, + HOPCNT_INFINITY, RT->rt_tag, RT->rt_ifp, + RT->rt_time, 0); + return 0; +} + + +/* Watch for dead routes and interfaces. + */ +void +age(naddr bad_gate) +{ + struct interface *ifp; + + + age_timer.tv_sec = now.tv_sec + (rip_sock < 0 + ? NEVER + : SUPPLY_INTERVAL); + + for (ifp = ifnet; ifp; ifp = ifp->int_next) { + /* Check for dead IS_REMOTE interfaces by timing their + * transmissions. + */ + if ((ifp->int_state & IS_REMOTE) + && !(ifp->int_state & IS_PASSIVE) + && (ifp->int_state & IS_ACTIVE)) { + LIM_SEC(age_timer, now.tv_sec+SUPPLY_INTERVAL); + + if (now.tv_sec - ifp->int_act_time > EXPIRE_TIME + && !(ifp->int_state & IS_BROKE)) { + msglog("remote interface %s to %s timed out" + "--turned off", + ifp->int_name, + naddr_ntoa(ifp->int_addr)); + if_bad(ifp); + } + } + } + + /* Age routes. */ + age_bad_gate = bad_gate; + (void)rn_walktree(rhead, walk_age, 0); + + /* Update the kernel routing table. */ + fix_kern(); +} diff --git a/sbin/routed/trace.c b/sbin/routed/trace.c new file mode 100644 index 000000000000..8a0b59ccb023 --- /dev/null +++ b/sbin/routed/trace.c @@ -0,0 +1,887 @@ +/* + * Copyright (c) 1983, 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if !defined(lint) && !defined(sgi) && !defined(__NetBSD__) +static char sccsid[] = "@(#)trace.c 8.1 (Berkeley) 6/5/93"; +#elif defined(__NetBSD__) +static char rcsid[] = "$NetBSD$"; +#endif +#ident "$Revision: 1.13 $" + +#define RIPCMDS +#include "defs.h" +#include "pathnames.h" +#include +#include +#include + + +#ifdef sgi +/* use *stat64 for files on large filesystems */ +#define stat stat64 +#endif + +#define NRECORDS 50 /* size of circular trace buffer */ + +u_int tracelevel, new_tracelevel; +FILE *ftrace = stdout; /* output trace file */ +static char *tracelevel_pat = "%s\n"; + +char savetracename[MAXPATHLEN+1]; + +static void trace_dump(void); + + +/* convert IP address to a string, but not into a single buffer + */ +char * +naddr_ntoa(naddr a) +{ +#define NUM_BUFS 4 + static int bufno; + static struct { + char str[16]; /* xxx.xxx.xxx.xxx\0 */ + } bufs[NUM_BUFS]; + char *s; + struct in_addr addr; + + addr.s_addr = a; + s = strcpy(bufs[bufno].str, inet_ntoa(addr)); + bufno = (bufno+1) % NUM_BUFS; + return s; +#undef NUM_BUFS +} + + +char * +saddr_ntoa(struct sockaddr *sa) +{ + return (sa == 0) ? "?" : naddr_ntoa(S_ADDR(sa)); +} + + +static char * +ts(time_t secs) { + static char s[20]; + + secs += epoch.tv_sec; +#ifdef sgi + (void)cftime(s, "%T", &secs); +#else + bcopy(ctime(&secs)+11, s, 8); + s[8] = '\0'; +#endif + return s; +} + + +/* On each event, display a time stamp. + * This assumes that 'now' is update once for each event, and + * that at least now.tv_usec changes. + */ +void +lastlog(void) +{ + static struct timeval last; + + if (last.tv_sec != now.tv_sec + || last.tv_usec != now.tv_usec) { + (void)fprintf(ftrace, "-- %s --\n", ts(now.tv_sec)); + last = now; + } +} + + +static void +tmsg(char *p, ...) +{ + va_list args; + + if (ftrace != 0) { + lastlog(); + va_start(args, p); + vfprintf(ftrace, p, args); + fflush(ftrace); + } +} + + +static void +trace_close(void) +{ + int fd; + + + fflush(stdout); + fflush(stderr); + + if (ftrace != 0 + && savetracename[0] != '\0') { + fd = open(_PATH_DEVNULL, O_RDWR); + (void)dup2(fd, STDOUT_FILENO); + (void)dup2(fd, STDERR_FILENO); + (void)close(fd); + fclose(ftrace); + ftrace = 0; + } +} + + +void +trace_flush(void) +{ + if (ftrace != 0) { + fflush(ftrace); + if (ferror(ftrace)) + trace_off("tracing off: ", strerror(ferror(ftrace))); + } +} + + +void +trace_off(char *p, ...) +{ + va_list args; + + + if (ftrace != 0) { + lastlog(); + va_start(args, p); + vfprintf(ftrace, p, args); + fflush(ftrace); + } + trace_close(); + + new_tracelevel = tracelevel = 0; +} + + +void +trace_on(char *filename, + int trusted) +{ + struct stat stbuf; + FILE *n_ftrace; + + + /* Given a null filename when tracing is already on, increase the + * debugging level and re-open the file in case it has been unlinked. + */ + if (filename[0] == '\0') { + if (tracelevel != 0) { + new_tracelevel++; + tracelevel_pat = "trace command: %s\n"; + } else if (savetracename[0] == '\0') { + msglog("missing trace file name"); + return; + } + filename = savetracename; + + } else if (!strcmp(filename,"dump/../table")) { + trace_dump(); + return; + + } else { + if (stat(filename, &stbuf) >= 0 + && (stbuf.st_mode & S_IFMT) != S_IFREG) { + msglog("wrong type (%#x) of trace file \"%s\"", + stbuf.st_mode, filename); + return; + } + + if (!trusted +#ifdef _PATH_TRACE + && (strncmp(filename, _PATH_TRACE, sizeof(_PATH_TRACE)-1) + || strstr(filename,"../") + || 0 > stat(_PATH_TRACE, &stbuf)) +#endif + && strcmp(filename, savetracename)) { + msglog("wrong directory for trace file \"%s\"", + filename); + return; + } + } + + n_ftrace = fopen(filename, "a"); + if (n_ftrace == 0) { + msglog("failed to open trace file \"%s\" %s", + filename, strerror(errno)); + return; + } + + tmsg("switch to trace file %s\n", filename); + trace_close(); + if (filename != savetracename) + strncpy(savetracename, filename, sizeof(savetracename)-1); + ftrace = n_ftrace; + + fflush(stdout); + fflush(stderr); + dup2(fileno(ftrace), STDOUT_FILENO); + dup2(fileno(ftrace), STDERR_FILENO); + + if (new_tracelevel == 0) + new_tracelevel = 1; + set_tracelevel(); +} + + +/* ARGSUSED */ +void +sigtrace_on(int s) +{ + new_tracelevel++; + tracelevel_pat = "SIGUSR1: %s\n"; +} + + +/* ARGSUSED */ +void +sigtrace_off(int s) +{ + new_tracelevel--; + tracelevel_pat = "SIGUSR2: %s\n"; +} + + +/* Move to next higher level of tracing when -t option processed or + * SIGUSR1 is received. Successive levels are: + * actions + * actions + packets + * actions + packets + contents + */ +void +set_tracelevel(void) +{ + static char *off_msgs[MAX_TRACELEVEL] = { + "Tracing actions stopped", + "Tracing packets stopped", + "Tracing packet contents stopped", + "Tracing kernel changes stopped", + }; + static char *on_msgs[MAX_TRACELEVEL] = { + "Tracing actions started", + "Tracing packets started", + "Tracing packet contents started", + "Tracing kernel changes started", + }; + + + if (new_tracelevel > MAX_TRACELEVEL) { + new_tracelevel = MAX_TRACELEVEL; + if (new_tracelevel == tracelevel) { + tmsg(tracelevel_pat, on_msgs[tracelevel-1]); + return; + } + } + while (new_tracelevel != tracelevel) { + if (new_tracelevel < tracelevel) { + if (--tracelevel == 0) + trace_off(tracelevel_pat, off_msgs[0]); + else + tmsg(tracelevel_pat, off_msgs[tracelevel]); + } else { + if (ftrace == 0) { + if (savetracename[0] != '\0') + trace_on(savetracename, 1); + else + ftrace = stdout; + } + tmsg(tracelevel_pat, on_msgs[tracelevel++]); + } + } + tracelevel_pat = "%s\n"; +} + + +/* display an address + */ +char * +addrname(naddr addr, /* in network byte order */ + naddr mask, + int force) /* 0=show mask if nonstandard, */ +{ /* 1=always show mask, 2=never */ +#define NUM_BUFS 4 + static int bufno; + static struct { + char str[15+20]; + } bufs[NUM_BUFS]; + char *s, *sp; + naddr dmask; + int i; + + s = strcpy(bufs[bufno].str, naddr_ntoa(addr)); + bufno = (bufno+1) % NUM_BUFS; + + if (force == 1 || (force == 0 && mask != std_mask(addr))) { + sp = &s[strlen(s)]; + + dmask = mask & -mask; + if (mask + dmask == 0) { + for (i = 0; i != 32 && ((1<bits_mask) != 0) { + if ((b & field) == b) { + if (tbl->bits_name[0] != '\0') { + if (c) + (void)putc(c, ftrace); + (void)fprintf(ftrace, "%s", tbl->bits_name); + c = '|'; + } + if (0 == (field &= ~(b | tbl->bits_clear))) + break; + } + tbl++; + } + if (field != 0 && tbl->bits_name != 0) { + if (c) + (void)putc(c, ftrace); + (void)fprintf(ftrace, tbl->bits_name, field); + c = '|'; + } + + if (c != '<' || force) + (void)fputs("> ", ftrace); +} + + +static char * +trace_pair(naddr dst, + naddr mask, + char *gate) +{ + static char buf[3*4+3+1+2+3 /* "xxx.xxx.xxx.xxx/xx-->" */ + +3*4+3+1]; /* "xxx.xxx.xxx.xxx" */ + int i; + + i = sprintf(buf, "%-16s-->", addrname(dst, mask, 0)); + (void)sprintf(&buf[i], "%-*s", 15+20-MAX(20,i), gate); + return buf; +} + + +void +trace_if(char *act, + struct interface *ifp) +{ + if (!TRACEACTIONS || ftrace == 0) + return; + + lastlog(); + (void)fprintf(ftrace, "%s interface %-4s ", act, ifp->int_name); + (void)fprintf(ftrace, "%-15s-->%-15s ", + naddr_ntoa(ifp->int_addr), + addrname(htonl((ifp->int_if_flags & IFF_POINTOPOINT) + ? ifp->int_dstaddr + : ifp->int_net), + ifp->int_mask, 1)); + if (ifp->int_metric != 0) + (void)fprintf(ftrace, "metric=%d ", ifp->int_metric); + trace_bits(if_bits, ifp->int_if_flags, 0); + trace_bits(is_bits, ifp->int_state, 0); + (void)fputc('\n',ftrace); +} + + +void +trace_upslot(struct rt_entry *rt, + struct rt_spare *rts, + naddr gate, + naddr router, + struct interface *ifp, + int metric, + u_short tag, + time_t new_time) +{ + if (!TRACEACTIONS || ftrace == 0) + return; + if (rts->rts_gate == gate + && rts->rts_router == router + && rts->rts_metric == metric + && rts->rts_tag == tag) + return; + + lastlog(); + if (rts->rts_gate != RIP_DEFAULT) { + (void)fprintf(ftrace, "Chg #%d %-35s ", + rts - rt->rt_spares, + trace_pair(rt->rt_dst, rt->rt_mask, + naddr_ntoa(rts->rts_gate))); + if (rts->rts_gate != rts->rts_gate) + (void)fprintf(ftrace, "router=%s ", + naddr_ntoa(rts->rts_gate)); + if (rts->rts_tag != 0) + (void)fprintf(ftrace, "tag=%#x ", ntohs(rts->rts_tag)); + (void)fprintf(ftrace, "metric=%-2d ", rts->rts_metric); + if (rts->rts_ifp != 0) + (void)fprintf(ftrace, "%s ", + rts->rts_ifp->int_name); + (void)fprintf(ftrace, "%s\n", ts(rts->rts_time)); + + (void)fprintf(ftrace, " %19s%-16s ", + "", + gate != rts->rts_gate ? naddr_ntoa(gate) : ""); + if (gate != router) + (void)fprintf(ftrace,"router=%s ",naddr_ntoa(router)); + if (tag != rts->rts_tag) + (void)fprintf(ftrace, "tag=%#x ", ntohs(tag)); + if (metric != rts->rts_metric) + (void)fprintf(ftrace, "metric=%-2d ", metric); + if (ifp != rts->rts_ifp && ifp != 0 ) + (void)fprintf(ftrace, "%s ", ifp->int_name); + (void)fprintf(ftrace, "%s\n", + new_time != rts->rts_time ? ts(new_time) : ""); + + } else { + (void)fprintf(ftrace, "Add #%d %-35s ", + rts - rt->rt_spares, + trace_pair(rt->rt_dst, rt->rt_mask, + naddr_ntoa(gate))); + if (gate != router) + (void)fprintf(ftrace, "router=%s ", naddr_ntoa(gate)); + if (tag != 0) + (void)fprintf(ftrace, "tag=%#x ", ntohs(tag)); + (void)fprintf(ftrace, "metric=%-2d ", metric); + if (ifp != 0) + (void)fprintf(ftrace, "%s ", ifp->int_name); + (void)fprintf(ftrace, "%s\n", ts(new_time)); + } +} + + +/* talk about a change made to the kernel table + */ +void +trace_kernel(char *p, ...) +{ + va_list args; + + if (!TRACEKERNEL || ftrace == 0) + return; + + lastlog(); + va_start(args, p); + vfprintf(ftrace, p, args); +} + + +/* display a message if tracing actions + */ +void +trace_act(char *p, ...) +{ + va_list args; + + if (!TRACEACTIONS || ftrace == 0) + return; + + lastlog(); + va_start(args, p); + vfprintf(ftrace, p, args); +} + + +/* display a message if tracing packets + */ +void +trace_pkt(char *p, ...) +{ + va_list args; + + if (!TRACEPACKETS || ftrace == 0) + return; + + lastlog(); + va_start(args, p); + vfprintf(ftrace, p, args); +} + + +void +trace_change(struct rt_entry *rt, + u_int state, + naddr gate, /* forward packets here */ + naddr router, /* on the authority of this router */ + int metric, + u_short tag, + struct interface *ifp, + time_t new_time, + char *label) +{ + if (ftrace == 0) + return; + + if (rt->rt_metric == metric + && rt->rt_gate == gate + && rt->rt_router == router + && rt->rt_state == state + && rt->rt_tag == tag) + return; + + lastlog(); + (void)fprintf(ftrace, "%s %-35s metric=%-2d ", + label, + trace_pair(rt->rt_dst, rt->rt_mask, + naddr_ntoa(rt->rt_gate)), + rt->rt_metric); + if (rt->rt_router != rt->rt_gate) + (void)fprintf(ftrace, "router=%s ", + naddr_ntoa(rt->rt_router)); + if (rt->rt_tag != 0) + (void)fprintf(ftrace, "tag=%#x ", ntohs(rt->rt_tag)); + trace_bits(rs_bits, rt->rt_state, rt->rt_state != state); + (void)fprintf(ftrace, "%s ", + rt->rt_ifp == 0 ? "?" : rt->rt_ifp->int_name); + (void)fprintf(ftrace, "%s\n", + AGE_RT(rt->rt_state, rt->rt_ifp) ? ts(rt->rt_time) : ""); + + (void)fprintf(ftrace, "%*s %19s%-16s ", + strlen(label), "", "", + rt->rt_gate != gate ? naddr_ntoa(gate) : ""); + if (rt->rt_metric != metric) + (void)fprintf(ftrace, "metric=%-2d ", metric); + if (router != gate) + (void)fprintf(ftrace, "router=%s ", naddr_ntoa(router)); + if (rt->rt_tag != tag) + (void)fprintf(ftrace, "tag=%#x ", ntohs(tag)); + if (rt->rt_state != state) + trace_bits(rs_bits, state, 1); + if (rt->rt_ifp != ifp) + (void)fprintf(ftrace, "%s ", + ifp != 0 ? ifp->int_name : "?"); + (void)fprintf(ftrace, "%s\n", + ((rt->rt_time == new_time || !AGE_RT(rt->rt_state, ifp)) + ? "" : ts(new_time))); +} + + +void +trace_add_del(char * action, struct rt_entry *rt) +{ + u_int state = rt->rt_state; + + if (ftrace == 0) + return; + + lastlog(); + (void)fprintf(ftrace, "%s %-35s metric=%-2d ", + action, + trace_pair(rt->rt_dst, rt->rt_mask, + naddr_ntoa(rt->rt_gate)), + rt->rt_metric); + if (rt->rt_router != rt->rt_gate) + (void)fprintf(ftrace, "router=%s ", + naddr_ntoa(rt->rt_router)); + if (rt->rt_tag != 0) + (void)fprintf(ftrace, "tag=%#x ", ntohs(rt->rt_tag)); + trace_bits(rs_bits, state, 0); + (void)fprintf(ftrace, "%s ", + rt->rt_ifp != 0 ? rt->rt_ifp->int_name : "?"); + (void)fprintf(ftrace, "%s\n", ts(rt->rt_time)); +} + + +/* ARGSUSED */ +static int +walk_trace(struct radix_node *rn, + struct walkarg *w) +{ +#define RT ((struct rt_entry *)rn) + struct rt_spare *rts; + int i, age; + + (void)fprintf(ftrace, " %-35s metric=%-2d ", + trace_pair(RT->rt_dst, RT->rt_mask, + naddr_ntoa(RT->rt_gate)), + RT->rt_metric); + if (RT->rt_router != RT->rt_gate) + (void)fprintf(ftrace, "router=%s ", + naddr_ntoa(RT->rt_router)); + if (RT->rt_tag != 0) + (void)fprintf(ftrace, "tag=%#x ", + ntohs(RT->rt_tag)); + trace_bits(rs_bits, RT->rt_state, 0); + (void)fprintf(ftrace, "%s ", + RT->rt_ifp == 0 ? "?" : RT->rt_ifp->int_name); + age = AGE_RT(RT->rt_state, RT->rt_ifp); + if (age) + (void)fprintf(ftrace, "%s", ts(RT->rt_time)); + + rts = &RT->rt_spares[1]; + for (i = 1; i < NUM_SPARES; i++, rts++) { + if (rts->rts_metric != HOPCNT_INFINITY) { + (void)fprintf(ftrace,"\n #%d%15s%-16s metric=%-2d ", + i, "", naddr_ntoa(rts->rts_gate), + rts->rts_metric); + if (rts->rts_router != rts->rts_gate) + (void)fprintf(ftrace, "router=%s ", + naddr_ntoa(rts->rts_router)); + if (rts->rts_tag != 0) + (void)fprintf(ftrace, "tag=%#x ", + ntohs(rts->rts_tag)); + (void)fprintf(ftrace, "%s ", + (rts->rts_ifp == 0 + ? "?" : rts->rts_ifp->int_name)); + if (age) + (void)fprintf(ftrace, "%s", ts(rts->rts_time)); + } + } + (void)fputc('\n',ftrace); + + return 0; +} + + +static void +trace_dump(void) +{ + if (ftrace == 0) + return; + lastlog(); + + (void)rn_walktree(rhead, walk_trace, 0); +} + + +void +trace_rip(char *dir1, char *dir2, + struct sockaddr_in *who, + struct interface *ifp, + struct rip *msg, + int size) /* total size of message */ +{ + struct netinfo *n, *lim; + struct netauth *a; + int i; + + if (!TRACEPACKETS || ftrace == 0) + return; + + lastlog(); + if (msg->rip_cmd >= RIPCMD_MAX + || msg->rip_vers == 0) { + (void)fprintf(ftrace, "%s bad RIPv%d cmd=%d %s" + " %s.%d size=%d\n", + dir1, msg->rip_vers, msg->rip_cmd, dir2, + naddr_ntoa(who->sin_addr.s_addr), + ntohs(who->sin_port), + size); + return; + } + + (void)fprintf(ftrace, "%s RIPv%d %s %s %s.%d%s%s\n", + dir1, msg->rip_vers, ripcmds[msg->rip_cmd], dir2, + naddr_ntoa(who->sin_addr.s_addr), ntohs(who->sin_port), + ifp ? " via " : "", ifp ? ifp->int_name : ""); + if (!TRACECONTENTS) + return; + + switch (msg->rip_cmd) { + case RIPCMD_REQUEST: + case RIPCMD_RESPONSE: + n = msg->rip_nets; + lim = (struct netinfo *)((char*)msg + size); + for (; n < lim; n++) { + if (n->n_family == RIP_AF_UNSPEC + && ntohl(n->n_metric) == HOPCNT_INFINITY + && n+1 == lim + && n == msg->rip_nets + && msg->rip_cmd == RIPCMD_REQUEST) { + (void)fputs("\tQUERY ", ftrace); + if (n->n_dst != 0) + (void)fprintf(ftrace, "%s ", + naddr_ntoa(n->n_dst)); + if (n->n_mask != 0) + (void)fprintf(ftrace, "mask=%#x ", + (u_int)ntohl(n->n_mask)); + if (n->n_nhop != 0) + (void)fprintf(ftrace, " nhop=%s ", + naddr_ntoa(n->n_nhop)); + if (n->n_tag != 0) + (void)fprintf(ftrace, "tag=%#x", + ntohs(n->n_tag)); + (void)fputc('\n',ftrace); + continue; + } + + if (n->n_family == RIP_AF_AUTH) { + a = (struct netauth*)n; + (void)fprintf(ftrace, + "\tAuthentication type %d: ", + ntohs(a->a_type)); + for (i = 0; + i < sizeof(a->au.au_pw); + i++) + (void)fprintf(ftrace, "%02x ", + a->au.au_pw[i]); + (void)fputc('\n',ftrace); + continue; + } + + if (n->n_family != RIP_AF_INET) { + (void)fprintf(ftrace, + "\t(af %d) %-18s mask=%#x", + ntohs(n->n_family), + naddr_ntoa(n->n_dst), + (u_int)ntohl(n->n_mask)); + } else if (msg->rip_vers == RIPv1) { + (void)fprintf(ftrace, "\t%-18s ", + addrname(n->n_dst, + ntohl(n->n_mask), + n->n_mask==0 ? 2 : 1)); + } else { + (void)fprintf(ftrace, "\t%-18s ", + addrname(n->n_dst, + ntohl(n->n_mask), + n->n_mask==0 ? 2 : 0)); + } + (void)fprintf(ftrace, "metric=%-2d ", + (u_int)ntohl(n->n_metric)); + if (n->n_nhop != 0) + (void)fprintf(ftrace, " nhop=%s ", + naddr_ntoa(n->n_nhop)); + if (n->n_tag != 0) + (void)fprintf(ftrace, "tag=%#x", + ntohs(n->n_tag)); + (void)fputc('\n',ftrace); + } + if (size != (char *)n - (char *)msg) + (void)fprintf(ftrace, "truncated record, len %d\n", + size); + break; + + case RIPCMD_TRACEON: + fprintf(ftrace, "\tfile=%*s\n", size-4, msg->rip_tracefile); + break; + + case RIPCMD_TRACEOFF: + break; + } +} diff --git a/usr.sbin/routed/Makefile b/usr.sbin/routed/Makefile deleted file mode 100644 index c567e68e87ad..000000000000 --- a/usr.sbin/routed/Makefile +++ /dev/null @@ -1,22 +0,0 @@ -# @(#)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.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/Makefile.inc b/usr.sbin/routed/Makefile.inc deleted file mode 100644 index 10fa13f1ed80..000000000000 --- a/usr.sbin/routed/Makefile.inc +++ /dev/null @@ -1 +0,0 @@ -.include "../../Makefile.inc" diff --git a/usr.sbin/routed/defs.h b/usr.sbin/routed/defs.h deleted file mode 100644 index f64991222191..000000000000 --- a/usr.sbin/routed/defs.h +++ /dev/null @@ -1,562 +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. - * - * @(#)defs.h 8.1 (Berkeley) 6/5/93 - * - * $NetBSD$ - */ - -#ifndef __NetBSD__ -#ident "$Revision: 1.13 $" -#endif - -/* Definitions for RIPv2 routing process. - * - * This code is based on the 4.4BSD `routed` daemon, with extensions to - * support: - * RIPv2, including variable length subnet masks. - * Router Discovery - * aggregate routes in the kernel tables. - * aggregate advertised routes. - * maintain spare routes for faster selection of another gateway - * when the current gateway dies. - * timers on routes with second granularity so that selection - * of a new route does not wait 30-60 seconds. - * tolerance of static routes. - * tell the kernel hop counts - * do not advertise if ipforwarding=0 - * - * The vestigual support for other protocols has been removed. There - * is no likelihood that IETF RIPv1 or RIPv2 will ever be used with - * other protocols. The result is far smaller, faster, cleaner, and - * perhaps understandable. - * - * The accumulation of special flags and kludges added over the many - * years have been simplified and integrated. - */ - -#include -#include -#include -#include -#include -#include -#ifdef sgi -#include -#include -#endif -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef sgi -#include -#else -#include "radix.h" -#endif -#include -#include -#include -#include -#include -#define RIPVERSION RIPv2 -#include - - -/* Type of an IP address. - * Some systems do not like to pass structures, so do not use in_addr. - * Some systems think a long has 64 bits, which would be a gross waste. - * So define it here so it can be changed for the target system. - * It should be defined somewhere netinet/in.h, but it is not. - */ -#ifdef sgi -#define naddr __uint32_t -#else -#ifdef __NetBSD__ -#define naddr u_int32_t -#else -#define naddr u_long -#endif -#define _HAVE_SA_LEN -#define _HAVE_SIN_LEN -#endif - -/* Turn on if IP_DROP_MEMBERSHIP and IP_ADD_MEMBERSHIP do not look at - * the dstaddr of point-to-point interfaces. - */ -/* #define MCAST_PPP_BUG */ - -#define NEVER (24*60*60) /* a long time */ -#define EPOCH NEVER /* bias time by this to avoid <0 */ - -/* Scan the kernel regularly to see if any interfaces have appeared or been - * turned off. These must be less than STALE_TIME. - */ -#define CHECK_BAD_INTERVAL 5 /* when an interface is known bad */ -#define CHECK_ACT_INTERVAL 30 /* when advertising */ -#define CHECK_QUIET_INTERVAL 300 /* when not */ - -#define LIM_SEC(s,l) ((s).tv_sec = MIN((s).tv_sec, (l))) - - -/* Router Discovery parameters */ -#ifndef sgi -#define INADDR_ALLROUTERS_GROUP 0xe0000002 /* 224.0.0.2 */ -#endif -#define MaxMaxAdvertiseInterval 1800 -#define MinMaxAdvertiseInterval 4 -#define DefMaxAdvertiseInterval 600 -#define DEF_PreferenceLevel 0 -#define MIN_PreferenceLevel 0x80000000 - -#define MAX_INITIAL_ADVERT_INTERVAL 16 -#define MAX_INITIAL_ADVERTS 3 -#define MAX_RESPONSE_DELAY 2 - -#define MAX_SOLICITATION_DELAY 1 -#define SOLICITATION_INTERVAL 3 -#define MAX_SOLICITATIONS 3 - - -/* typical packet buffers */ -union pkt_buf { - char packet[MAXPACKETSIZE+1]; - struct rip rip; -}; - - -/* no more routes than this, to protect ourself in case something goes - * whacko and starts broadcast zillions of bogus routes. - */ -#define MAX_ROUTES (128*1024) -extern int total_routes; - -/* Main, daemon routing table structure - */ -struct rt_entry { - struct radix_node rt_nodes[2]; /* radix tree glue */ - u_int rt_state; -# define RS_IF 0x001 /* for network interface */ -# define RS_NET_INT 0x002 /* authority route */ -# define RS_NET_SYN 0x004 /* fake net route for subnet */ -# define RS_NO_NET_SYN (RS_LOCAL | RS_LOCAL | RS_IF) -# define RS_SUBNET 0x008 /* subnet route from any source */ -# define RS_LOCAL 0x010 /* loopback for pt-to-pt */ -# define RS_MHOME 0x020 /* from -m */ -# define RS_STATIC 0x040 /* from the kernel */ -# define RS_RDISC 0x080 /* from router discovery */ -# define RS_PERMANENT (RS_MHOME | RS_STATIC | RS_NET_SYN | RS_RDISC) - struct sockaddr_in rt_dst_sock; - naddr rt_mask; - struct rt_spare { - struct interface *rts_ifp; - naddr rts_gate; /* forward packets here */ - naddr rts_router; /* on the authority of this router */ - char rts_metric; - u_short rts_tag; - time_t rts_time; /* timer to junk stale routes */ -#define NUM_SPARES 4 - } rt_spares[NUM_SPARES]; - u_int rt_seqno; /* when last changed */ - char rt_poison_metric; /* to notice maximum recently */ - time_t rt_poison_time; /* advertised metric */ -}; -#define rt_dst rt_dst_sock.sin_addr.s_addr -#define rt_ifp rt_spares[0].rts_ifp -#define rt_gate rt_spares[0].rts_gate -#define rt_router rt_spares[0].rts_router -#define rt_metric rt_spares[0].rts_metric -#define rt_tag rt_spares[0].rts_tag -#define rt_time rt_spares[0].rts_time - -#define HOST_MASK 0xffffffff -#define RT_ISHOST(rt) ((rt)->rt_mask == HOST_MASK) - -/* age all routes that - * are not from -g, -m, or static routes from the kernel - * not unbroken interface routes - * but not broken interfaces - * nor non-passive, remote interfaces that are not aliases - * (i.e. remote & metric=0) - */ -#define AGE_RT(rt,ifp) (0 == ((rt)->rt_state & RS_PERMANENT) \ - && (!((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 - * - or it is a host route advertised by a system for itself - */ -#define BETTER_LINK(rt,A,B) ((A)->rts_metric < HOPCNT_INFINITY \ - && now_stale <= (A)->rts_time \ - && ((A)->rts_metric < (B)->rts_metric \ - || ((A)->rts_gate == (A)->rts_router \ - && (B)->rts_gate != (B)->rts_router) \ - || ((A)->rts_metric == (B)->rts_metric \ - && now_stale > (B)->rts_time) \ - || (RT_ISHOST(rt) \ - && (rt)->rt_dst == (A)->rts_router \ - && (A)->rts_metric == (B)->rts_metric))) - - -/* An "interface" is similar to a kernel ifnet structure, except it also - * handles "logical" or "IS_REMOTE" interfaces (remote gateways). - */ -struct interface { - struct interface *int_next, *int_prev; - char int_name[IFNAMSIZ+15+1]; /* big enough for IS_REMOTE */ - u_short int_index; - naddr int_addr; /* address on this host (net order) */ - naddr int_brdaddr; /* broadcast address (n) */ - naddr int_dstaddr; /* other end of pt-to-pt link (n) */ - naddr int_net; /* working network # (host order)*/ - naddr int_mask; /* working net mask (host order) */ - naddr int_ripv1_mask; /* for inferring a mask (n) */ - naddr int_std_addr; /* class A/B/C address (n) */ - naddr int_std_net; /* class A/B/C network (h) */ - naddr int_std_mask; /* class A/B/C netmask (h) */ - int int_rip_sock; /* for queries */ - int int_if_flags; /* copied from kernel */ - u_int int_state; - time_t int_act_time; /* last thought healthy */ - u_short int_transitions; /* times gone up-down */ - char int_metric; - char int_d_metric; /* for faked default route */ - struct int_data { - u_int ipackets; /* previous network stats */ - u_int ierrors; - u_int opackets; - u_int oerrors; -#ifdef sgi - u_int odrops; -#endif - time_t ts; /* timestamp on network stats */ - } int_data; - char int_passwd[RIP_AUTH_PW_LEN]; /* RIPv2 password */ - int int_rdisc_pref; /* advertised rdisc preference */ - int int_rdisc_int; /* MaxAdvertiseInterval */ - int int_rdisc_cnt; - struct timeval int_rdisc_timer; -}; - -#define IS_ALIAS 0x0000001 /* interface alias */ -#define IS_SUBNET 0x0000002 /* interface on subnetted network */ -#define IS_REMOTE 0x0000004 /* interface is not on this machine */ -#define IS_PASSIVE 0x0000008 /* remote and does not do RIP */ -#define IS_EXTERNAL 0x0000010 /* handled by EGP or something */ -#define IS_CHECKED 0x0000020 /* still exists */ -#define IS_ALL_HOSTS 0x0000040 /* in INADDR_ALLHOSTS_GROUP */ -#define IS_ALL_ROUTERS 0x0000080 /* in INADDR_ALLROUTERS_GROUP */ -#define IS_RIP_QUERIED 0x0000100 /* query broadcast */ -#define IS_BROKE 0x0000200 /* seems to be broken */ -#define IS_SICK 0x0000400 /* seems to be broken */ -#define IS_DUP 0x0000800 /* has a duplicate address */ -#define IS_ACTIVE 0x0001000 /* heard from it at least once */ -#define IS_NEED_NET_SYN 0x0002000 /* need RS_NET_SYN route */ -#define IS_NO_AG 0x0004000 /* do not aggregate subnets */ -#define IS_NO_SUPER_AG 0x0008000 /* do not aggregate networks */ -#define IS_NO_RIPV1_IN 0x0010000 /* no RIPv1 input at all */ -#define IS_NO_RIPV2_IN 0x0020000 /* no RIPv2 input at all */ -#define IS_NO_RIP_IN (IS_NO_RIPV1_IN | IS_NO_RIPV2_IN) -#define IS_RIP_IN_OFF(s) (((s) & IS_NO_RIP_IN) == IS_NO_RIP_IN) -#define IS_NO_RIPV1_OUT 0x0040000 /* no RIPv1 output at all */ -#define IS_NO_RIPV2_OUT 0x0080000 /* no RIPv2 output at all */ -#define IS_NO_RIP_OUT (IS_NO_RIPV1_OUT | IS_NO_RIPV2_OUT) -#define IS_NO_RIP (IS_NO_RIP_OUT | IS_NO_RIP_IN) -#define IS_RIP_OUT_OFF(s) (((s) & IS_NO_RIP_OUT) == IS_NO_RIP_OUT) -#define IS_RIP_OFF(s) (((s) & IS_NO_RIP) == IS_NO_RIP) -#define IS_NO_ADV_IN 0x0100000 -#define IS_NO_SOL_OUT 0x0200000 /* no solicitations */ -#define IS_SOL_OUT 0x0400000 /* send solicitations */ -#define GROUP_IS_SOL (IS_NO_ADV_IN|IS_NO_SOL_OUT) -#define IS_NO_ADV_OUT 0x0800000 /* do not advertise rdisc */ -#define IS_ADV_OUT 0x1000000 /* advertise rdisc */ -#define GROUP_IS_ADV (IS_NO_ADV_OUT|IS_ADV_OUT) -#define IS_BCAST_RDISC 0x2000000 /* broadcast instead of multicast */ -#define IS_NO_RDISC (IS_NO_ADV_IN | IS_NO_SOL_OUT | IS_NO_ADV_OUT) -#define IS_PM_RDISC 0x4000000 /* poor-man's router discovery */ - -#ifdef sgi -#define IFF_UP_RUNNING (IFF_RUNNING|IFF_UP) -#else -#define IFF_UP_RUNNING IFF_UP -#endif -#define iff_alive(f) (((f) & IFF_UP_RUNNING) == IFF_UP_RUNNING) - - -/* Information for aggregating routes */ -#define NUM_AG_SLOTS 32 -struct ag_info { - struct ag_info *ag_fine; /* slot with finer netmask */ - struct ag_info *ag_cors; /* more coarse netmask */ - naddr ag_dst_h; /* destination in host byte order */ - naddr ag_mask; - naddr ag_gate; - naddr ag_nhop; - char ag_metric; /* metric to be advertised */ - char ag_pref; /* aggregate based on this */ - u_int ag_seqno; - u_short ag_tag; - u_short ag_state; -#define AGS_SUPPRESS 0x001 /* combine with coaser mask */ -#define AGS_PROMOTE 0x002 /* synthesize combined routes */ -#define AGS_REDUN0 0x004 /* redundant, finer routes output */ -#define AGS_REDUN1 0x008 -#define AG_IS_REDUN(state) (((state) & (AGS_REDUN0 | AGS_REDUN1)) \ - == (AGS_REDUN0 | AGS_REDUN1)) -#define AGS_GATEWAY 0x010 /* tell kernel RTF_GATEWAY */ -#define AGS_IF 0x020 /* for an interface */ -#define AGS_RIPV2 0x040 /* send only as RIPv2 */ -#define AGS_FINE_GATE 0x080 /* ignore differing ag_gate when this - * has the finer netmask */ -#define AGS_CORS_GATE 0x100 /* ignore differing gate when this - * has the coarser netmaks */ -#define AGS_SPLIT_HZ 0x200 /* suppress for split horizon */ - - /* some bits are set if they are set on either route */ -#define AGS_PROMOTE_EITHER (AGS_RIPV2 | AGS_GATEWAY | \ - AGS_SUPPRESS | AGS_CORS_GATE) -}; - - -/* parameters for interfaces */ -extern struct parm { - struct parm *parm_next; - char parm_name[IFNAMSIZ+1]; - naddr parm_addr_h; - naddr parm_mask; - - char parm_d_metric; - u_int parm_int_state; - int parm_rdisc_pref; - int parm_rdisc_int; - char parm_passwd[RIP_AUTH_PW_LEN+1]; -} *parms; - -/* authority for internal networks */ -extern struct intnet { - struct intnet *intnet_next; - naddr intnet_addr; - naddr intnet_mask; - char intnet_metric; -} *intnets; - - - -extern pid_t mypid; -extern naddr myaddr; /* main address of this system */ - -extern int stopint; /* !=0 to stop */ - -extern int sock_max; -extern int rip_sock; /* RIP socket */ -extern struct interface *rip_sock_mcast; /* current multicast interface */ -extern int rt_sock; /* routing socket */ -extern int rt_sock_seqno; -extern int rdisc_sock; /* router-discovery raw socket */ - -extern int seqno; /* sequence number for messages */ -extern int supplier; /* process should supply updates */ -extern int lookforinterfaces; /* 1=probe for new up interfaces */ -extern int supplier_set; /* -s or -q requested */ -extern int ridhosts; /* 1=reduce host routes */ -extern int mhome; /* 1=want multi-homed host route */ -extern int advertise_mhome; /* 1=must continue adverising it */ -extern int auth_ok; /* 1=ignore auth if we do not care */ - -extern struct timeval epoch; /* when started */ -extern struct timeval now; /* current idea of time */ -extern time_t now_stale; -extern time_t now_garbage; - -extern struct timeval next_bcast; /* next general broadcast */ -extern struct timeval age_timer; /* next check of old routes */ -extern struct timeval no_flash; /* inhibit flash update until then */ -extern struct timeval rdisc_timer; /* next advert. or solicitation */ -extern int rdisc_ok; /* using solicited route */ - -extern struct timeval ifinit_timer; /* time to check interfaces */ - -extern naddr loopaddr; /* our address on loopback */ -extern int tot_interfaces; /* # of remote and local interfaces */ -extern int rip_interfaces; /* # of interfaces doing RIP */ -extern struct interface *ifnet; /* all interfaces */ -extern int have_ripv1_out; /* have a RIPv1 interface */ -extern int have_ripv1_in; -extern int need_flash; /* flash update needed */ -extern struct timeval need_kern; /* need to update kernel table */ -extern int update_seqno; /* a route has changed */ - -extern u_int tracelevel, new_tracelevel; -#define MAX_TRACELEVEL 3 -#define TRACECONTENTS (tracelevel >= 3) /* display packet contents */ -#define TRACEPACKETS (tracelevel >= 2) /* note packets */ -#define TRACEACTIONS (tracelevel != 0) -extern FILE *ftrace; /* output trace file */ - -extern struct radix_node_head *rhead; - - -#ifdef sgi -/* Fix conflicts */ -#define dup2(x,y) BSDdup2(x,y) -#endif /* sgi */ - -extern void fix_sock(int, char *); -extern void fix_select(void); -extern void rip_off(void); -extern void rip_on(struct interface *); - -enum output_type {OUT_QUERY, OUT_UNICAST, OUT_BROADCAST, OUT_MULTICAST, - NO_OUT_MULTICAST, NO_OUT_RIPV2}; -extern int output(enum output_type, struct sockaddr_in *, - struct interface *, struct rip *, int); -extern void rip_query(void); -extern void rip_bcast(int); -extern void supply(struct sockaddr_in *, struct interface *, - enum output_type, int, int); - -extern void msglog(char *, ...); -#define LOGERR(msg) msglog(msg ": %s", strerror(errno)) -extern void logbad(int, char *, ...); -#define BADERR(dump,msg) logbad(dump,msg ": %s", strerror(errno)) -#ifdef DEBUG -#define DBGERR(dump,msg) BADERR(dump,msg) -#else -#define DBGERR(dump,msg) LOGERR(msg) -#endif -extern char *naddr_ntoa(naddr); -extern char *saddr_ntoa(struct sockaddr *); - -extern void *rtmalloc(size_t, char *); -extern void timevaladd(struct timeval *, struct timeval *); -extern void intvl_random(struct timeval *, u_long, u_long); -extern int getnet(char *, naddr *, naddr *); -extern int gethost(char *, naddr *); -extern void gwkludge(void); -extern char *parse_parms(char *); -extern char *check_parms(struct parm *); -extern void get_parms(struct interface *); - -extern void lastlog(void); -extern void trace_on(char *, int); -extern void trace_off(char*, ...); -extern void trace_flush(void); -extern void set_tracelevel(void); -extern void trace_act(char *, ...); -extern void trace_pkt(char *, ...); -extern void trace_add_del(char *, struct rt_entry *); -extern void trace_change(struct rt_entry *, u_int, naddr, naddr, int, - u_short, struct interface *, time_t, char *); -extern void trace_if(char *, struct interface *); -extern void trace_upslot(struct rt_entry *, struct rt_spare *, - naddr, naddr, - struct interface *, int, u_short, time_t); -extern void trace_rip(char*, char*, struct sockaddr_in *, - struct interface *, struct rip *, int); -extern char *addrname(naddr, naddr, int); - -extern void rdisc_age(naddr); -extern void set_rdisc_mg(struct interface *, int); -extern void set_supplier(void); -extern void if_bad_rdisc(struct interface *); -extern void if_ok_rdisc(struct interface *); -extern void read_rip(int, struct interface *); -extern void read_rt(void); -extern void read_d(void); -extern void rdisc_adv(void); -extern void rdisc_sol(void); - -extern void sigalrm(int); -extern void sigterm(int); - -extern void sigtrace_on(int); -extern void sigtrace_off(int); - -extern void flush_kern(void); -extern void age(naddr); - -extern void ag_flush(naddr, naddr, void (*)(struct ag_info *)); -extern void ag_check(naddr, naddr, naddr, naddr, char, char, u_int, - u_short, u_short, void (*)(struct ag_info *)); -extern void del_static(naddr, naddr, int); -extern void del_redirects(naddr, time_t); -extern struct rt_entry *rtget(naddr, naddr); -extern struct rt_entry *rtfind(naddr); -extern void rtinit(void); -extern void rtadd(naddr, naddr, naddr, naddr, - int, u_short, u_int, struct interface *); -extern void rtchange(struct rt_entry *, u_int, naddr,naddr, int, u_short, - struct interface *ifp, time_t, char *); -extern void rtdelete(struct rt_entry *); -extern void rtbad_sub(struct rt_entry *); -extern void rtswitch(struct rt_entry *, struct rt_spare *); -extern void rtbad(struct rt_entry *); - - -#define S_ADDR(x) (((struct sockaddr_in *)(x))->sin_addr.s_addr) -#define INFO_DST(I) ((I)->rti_info[RTAX_DST]) -#define INFO_GATE(I) ((I)->rti_info[RTAX_GATEWAY]) -#define INFO_MASK(I) ((I)->rti_info[RTAX_NETMASK]) -#define INFO_IFA(I) ((I)->rti_info[RTAX_IFA]) -#define INFO_IFP(I) ((I)->rti_info[RTAX_IFP]) -#define INFO_AUTHOR(I) ((I)->rti_info[RTAX_AUTHOR]) -#define INFO_BRD(I) ((I)->rti_info[RTAX_BRD]) -void rt_xaddrs(struct rt_addrinfo *, struct sockaddr *, struct sockaddr *, - int); - -extern naddr std_mask(naddr); -extern naddr ripv1_mask_net(naddr, struct interface *); -extern naddr ripv1_mask_host(naddr,struct interface *); -#define on_net(a,net,mask) (((ntohl(a) ^ (net)) & (mask)) == 0) -extern int check_dst(naddr); -extern void addrouteforif(register struct interface *); -extern void ifinit(void); -extern int walk_bad(struct radix_node *, struct walkarg *); -extern int if_ok(struct interface *, char *); -extern void if_sick(struct interface *); -extern void if_bad(struct interface *); -extern struct interface *ifwithaddr(naddr, int, int); -extern struct interface *ifwithname(char *, naddr); -extern struct interface *ifwithindex(u_short); -extern struct interface *iflookup(naddr); diff --git a/usr.sbin/routed/if.c b/usr.sbin/routed/if.c deleted file mode 100644 index 7143b319c54c..000000000000 --- a/usr.sbin/routed/if.c +++ /dev/null @@ -1,1132 +0,0 @@ -/* - * Copyright (c) 1983, 1993 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#if !defined(lint) && !defined(sgi) && !defined(__NetBSD__) -static char sccsid[] = "@(#)if.c 8.1 (Berkeley) 6/5/93"; -#elif defined(__NetBSD__) -static char rcsid[] = "$NetBSD$"; -#endif -#ident "$Revision: 1.16 $" - -#include "defs.h" -#include "pathnames.h" - -struct interface *ifnet; /* all interfaces */ -int tot_interfaces; /* # of remote and local interfaces */ -int rip_interfaces; /* # of interfaces doing RIP */ -int foundloopback; /* valid flag for loopaddr */ -naddr loopaddr; /* our address on loopback */ - -struct timeval ifinit_timer; - -int have_ripv1_out; /* have a RIPv1 interface */ -int have_ripv1_in; - - -/* Find the interface with an address - */ -struct interface * -ifwithaddr(naddr addr, - int bcast, /* notice IFF_BROADCAST address */ - int remote) /* include IS_REMOTE interfaces */ -{ - struct interface *ifp, *possible = 0; - - for (ifp = ifnet; ifp; ifp = ifp->int_next) { - if (ifp->int_addr == addr - || ((ifp->int_if_flags & IFF_BROADCAST) - && ifp->int_brdaddr == addr - && bcast)) { - if ((ifp->int_state & IS_REMOTE) && !remote) - continue; - - if (!(ifp->int_state & IS_BROKE) - && !(ifp->int_state & IS_PASSIVE)) - return ifp; - - possible = ifp; - } - } - - return possible; -} - - -/* find the interface with a name - */ -struct interface * -ifwithname(char *name, /* "ec0" or whatever */ - naddr addr) /* 0 or network address */ -{ - struct interface *ifp; - - - for (ifp = ifnet; 0 != ifp; ifp = ifp->int_next) { - if (!strcmp(ifp->int_name, name) - && (ifp->int_addr == addr - || (addr == 0 && !(ifp->int_state & IS_ALIAS)))) - return ifp; - } - return 0; -} - - -struct interface * -ifwithindex(u_short index) -{ - struct interface *ifp; - - - for (ifp = ifnet; 0 != ifp; ifp = ifp->int_next) { - if (ifp->int_index == index) - return ifp; - } - return 0; -} - - -/* Find an interface from which the specified address - * should have come from. Used for figuring out which - * interface a packet came in on -- for tracing. - */ -struct interface * -iflookup(naddr addr) -{ - struct interface *ifp, *maybe; - - maybe = 0; - for (ifp = ifnet; ifp; ifp = ifp->int_next) { - if (ifp->int_if_flags & IFF_POINTOPOINT) { - if (ifp->int_dstaddr == addr) - /* finished with a match */ - return ifp; - - } else { - /* finished with an exact match */ - if (ifp->int_addr == addr) - return ifp; - if ((ifp->int_if_flags & IFF_BROADCAST) - && ifp->int_brdaddr == addr) - return ifp; - - /* Look for the longest approximate match. - */ - if (on_net(addr, ifp->int_net, ifp->int_mask) - && (maybe == 0 - || ifp->int_mask > maybe->int_mask)) - maybe = ifp; - } - } - - return maybe; -} - - -/* Return the classical netmask for an IP address. - */ -naddr -std_mask(naddr addr) /* in network order */ -{ - NTOHL(addr); /* was a host, not a network */ - - if (addr == 0) /* default route has mask 0 */ - return 0; - if (IN_CLASSA(addr)) - return IN_CLASSA_NET; - if (IN_CLASSB(addr)) - return IN_CLASSB_NET; - return IN_CLASSC_NET; -} - - -/* Find the netmask that would be inferred by RIPv1 listeners - * on the given interface for a given network. - * If no interface is specified, look for the best fitting interface. - */ -naddr -ripv1_mask_net(naddr addr, /* in network byte order */ - struct interface *ifp) /* as seen on this interface */ -{ - naddr mask = 0; - - if (addr == 0) /* default always has 0 mask */ - return mask; - - if (ifp != 0) { - /* If the target network is that of the associated interface - * on which it arrived, then use the netmask of the interface. - */ - if (on_net(addr, ifp->int_net, ifp->int_std_mask)) - mask = ifp->int_ripv1_mask; - - } else { - /* Examine all interfaces, and if it the target seems - * to have the same network number of an interface, use the - * netmask of that interface. If there is more than one - * such interface, prefer the interface with the longest - * match. - */ - for (ifp = ifnet; ifp != 0; ifp = ifp->int_next) { - if (on_net(addr, ifp->int_std_net, ifp->int_std_mask) - && ifp->int_ripv1_mask > mask) - mask = ifp->int_ripv1_mask; - } - } - - /* Otherwise, make the classic A/B/C guess. - */ - if (mask == 0) - mask = std_mask(addr); - - return mask; -} - - -naddr -ripv1_mask_host(naddr addr, /* in network byte order */ - struct interface *ifp) /* as seen on this interface */ -{ - naddr mask = ripv1_mask_net(addr, ifp); - - - /* If the computed netmask does not mask the address, - * then assume it is a host address - */ - if ((ntohl(addr) & ~mask) != 0) - mask = HOST_MASK; - return mask; -} - - -/* See if a IP address looks reasonable as a destination - */ -int /* 0=bad */ -check_dst(naddr addr) -{ - NTOHL(addr); - - if (IN_CLASSA(addr)) { - if (addr == 0) - return 1; /* default */ - - addr >>= IN_CLASSA_NSHIFT; - return (addr != 0 && addr != IN_LOOPBACKNET); - } - - return (IN_CLASSB(addr) || IN_CLASSC(addr)); -} - - -/* Delete an interface. - */ -static void -ifdel(struct interface *ifp) -{ - struct ip_mreq m; - struct interface *ifp1; - - - trace_if("Del", ifp); - - ifp->int_state |= IS_BROKE; - - /* unlink the interface - */ - if (rip_sock_mcast == ifp) - rip_sock_mcast = 0; - if (ifp->int_next != 0) - ifp->int_next->int_prev = ifp->int_prev; - if (ifp->int_prev != 0) - ifp->int_prev->int_next = ifp->int_next; - else - ifnet = ifp->int_next; - - if (!(ifp->int_state & IS_ALIAS)) { - /* delete aliases - */ - for (ifp1 = ifnet; 0 != ifp1; ifp1 = ifp1->int_next) { - if (ifp1 != ifp - && !strcmp(ifp->int_name, ifp1->int_name)) - ifdel(ifp1); - } - - if ((ifp->int_if_flags & IFF_MULTICAST) -#ifdef MCAST_PPP_BUG - && !(ifp->int_if_flags & IFF_POINTOPOINT) -#endif - && rip_sock >= 0) { - m.imr_multiaddr.s_addr = htonl(INADDR_RIP_GROUP); - m.imr_interface.s_addr = ((ifp->int_if_flags - & IFF_POINTOPOINT) - ? ifp->int_dstaddr - : ifp->int_addr); - if (setsockopt(rip_sock,IPPROTO_IP,IP_DROP_MEMBERSHIP, - &m, sizeof(m)) < 0 - && errno != EADDRNOTAVAIL - && !TRACEACTIONS) - LOGERR("setsockopt(IP_DROP_MEMBERSHIP RIP)"); - } - if (ifp->int_rip_sock >= 0) { - (void)close(ifp->int_rip_sock); - ifp->int_rip_sock = -1; - fix_select(); - } - - tot_interfaces--; - if (!IS_RIP_OFF(ifp->int_state)) - rip_interfaces--; - - /* Zap all routes associated with this interface. - * Assume routes just using gateways beyond this interface will - * timeout naturally, and have probably already died. - */ - (void)rn_walktree(rhead, walk_bad, 0); - - set_rdisc_mg(ifp, 0); - if_bad_rdisc(ifp); - } - - free(ifp); -} - - -/* Mark an interface ill. - */ -void -if_sick(struct interface *ifp) -{ - if (0 == (ifp->int_state & (IS_SICK | IS_BROKE))) { - ifp->int_state |= IS_SICK; - trace_if("Chg", ifp); - - LIM_SEC(ifinit_timer, now.tv_sec+CHECK_BAD_INTERVAL); - } -} - - -/* Mark an interface dead. - */ -void -if_bad(struct interface *ifp) -{ - struct interface *ifp1; - - - if (ifp->int_state & IS_BROKE) - return; - - LIM_SEC(ifinit_timer, now.tv_sec+CHECK_BAD_INTERVAL); - - ifp->int_state |= (IS_BROKE | IS_SICK); - ifp->int_state &= ~(IS_RIP_QUERIED | IS_ACTIVE); - ifp->int_data.ts = 0; - - trace_if("Chg", ifp); - - if (!(ifp->int_state & IS_ALIAS)) { - for (ifp1 = ifnet; 0 != ifp1; ifp1 = ifp1->int_next) { - if (ifp1 != ifp - && !strcmp(ifp->int_name, ifp1->int_name)) - if_bad(ifp1); - } - (void)rn_walktree(rhead, walk_bad, 0); - if_bad_rdisc(ifp); - } -} - - -/* Mark an interface alive - */ -int /* 1=it was dead */ -if_ok(struct interface *ifp, - char *type) -{ - struct interface *ifp1; - - - if (!(ifp->int_state & IS_BROKE)) { - if (ifp->int_state & IS_SICK) { - trace_act("%sinterface %s to %s working better\n", - type, - ifp->int_name, naddr_ntoa(ifp->int_addr)); - ifp->int_state &= ~IS_SICK; - } - return 0; - } - - msglog("%sinterface %s to %s restored", - type, ifp->int_name, naddr_ntoa(ifp->int_addr)); - ifp->int_state &= ~(IS_BROKE | IS_SICK); - ifp->int_data.ts = 0; - - if (!(ifp->int_state & IS_ALIAS)) { - for (ifp1 = ifnet; 0 != ifp1; ifp1 = ifp1->int_next) { - if (ifp1 != ifp - && !strcmp(ifp->int_name, ifp1->int_name)) - if_ok(ifp1, type); - } - if_ok_rdisc(ifp); - } - return 1; -} - - -/* disassemble routing message - */ -void -rt_xaddrs(struct rt_addrinfo *info, - struct sockaddr *sa, - struct sockaddr *lim, - int addrs) -{ - int i; -#ifdef _HAVE_SA_LEN - static struct sockaddr sa_zero; -#endif -#ifdef sgi -#define ROUNDUP(a) ((a) > 0 ? (1 + (((a) - 1) | (sizeof(__uint64_t) - 1))) \ - : sizeof(__uint64_t)) -#else -#define ROUNDUP(a) ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) \ - : sizeof(long)) -#endif - - - bzero(info, sizeof(*info)); - info->rti_addrs = addrs; - for (i = 0; i < RTAX_MAX && sa < lim; i++) { - if ((addrs & (1 << i)) == 0) - continue; -#ifdef _HAVE_SA_LEN - info->rti_info[i] = (sa->sa_len != 0) ? sa : &sa_zero; - sa = (struct sockaddr *)((char*)(sa) - + ROUNDUP(sa->sa_len)); -#else - info->rti_info[i] = sa; - sa = (struct sockaddr *)((char*)(sa) - + ROUNDUP(_FAKE_SA_LEN_DST(sa))); -#endif - } -} - - -/* Find the network interfaces which have configured themselves. - * This must be done regularly, if only for extra addresses - * that come and go on interfaces. - */ -void -ifinit(void) -{ - static char *sysctl_buf; - static size_t sysctl_buf_size = 0; - uint complaints = 0; - static u_int prev_complaints = 0; -# define COMP_NOT_INET 0x001 -# define COMP_WIERD 0x002 -# define COMP_NOADDR 0x004 -# define COMP_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; - size_t needed; - int mib[6]; - struct if_msghdr *ifm; - struct ifa_msghdr *ifam, *ifam_lim, *ifam2; - struct sockaddr_dl *sdl; - int in, ierr, out, oerr; - struct intnet *intnetp; - struct rt_addrinfo info; -#ifdef SIOCGIFMETRIC - struct ifreq ifr; -#endif - - - ifinit_timer.tv_sec = now.tv_sec + (supplier - ? CHECK_ACT_INTERVAL - : CHECK_QUIET_INTERVAL); - - /* mark all interfaces so we can get rid of thost that disappear */ - for (ifp = ifnet; 0 != ifp; ifp = ifp->int_next) - ifp->int_state &= ~(IS_CHECKED | IS_DUP); - - /* Fetch the interface list, without too many system calls - * since we do it repeatedly. - */ - mib[0] = CTL_NET; - mib[1] = PF_ROUTE; - mib[2] = 0; - mib[3] = AF_INET; - mib[4] = NET_RT_IFLIST; - mib[5] = 0; - for (;;) { - if ((needed = sysctl_buf_size) != 0) { - if (sysctl(mib, 6, sysctl_buf,&needed, 0, 0) >= 0) - break; - if (errno != ENOMEM && errno != EFAULT) - BADERR(1, "ifinit: get interface table"); - free(sysctl_buf); - needed = 0; - } - if (sysctl(mib, 6, 0, &needed, 0, 0) < 0) - BADERR(1,"ifinit: route-sysctl-estimate"); - sysctl_buf = rtmalloc(sysctl_buf_size = needed, "ifinit"); - } - - ifam_lim = (struct ifa_msghdr *)(sysctl_buf + needed); - for (ifam = (struct ifa_msghdr *)sysctl_buf; - ifam < ifam_lim; - ifam = ifam2) { - - ifam2 = (struct ifa_msghdr*)((char*)ifam + ifam->ifam_msglen); - - if (ifam->ifam_type == RTM_IFINFO) { - ifm = (struct if_msghdr *)ifam; - /* make prototype structure for the IP aliases - */ - bzero(&ifs0, sizeof(ifs0)); - ifs0.int_rip_sock = -1; - ifs0.int_index = ifm->ifm_index; - ifs0.int_if_flags = ifm->ifm_flags; - ifs0.int_state = IS_CHECKED; - ifs0.int_act_time = now.tv_sec; - ifs0.int_data.ts = now.tv_sec; - ifs0.int_data.ipackets = ifm->ifm_data.ifi_ipackets; - ifs0.int_data.ierrors = ifm->ifm_data.ifi_ierrors; - ifs0.int_data.opackets = ifm->ifm_data.ifi_opackets; - ifs0.int_data.oerrors = ifm->ifm_data.ifi_oerrors; -#ifdef sgi - ifs0.int_data.odrops = ifm->ifm_data.ifi_odrops; -#endif - sdl = (struct sockaddr_dl *)(ifm + 1); - sdl->sdl_data[sdl->sdl_nlen] = 0; - continue; - } - if (ifam->ifam_type != RTM_NEWADDR) { - logbad(1,"ifinit: out of sync"); - continue; - } - - rt_xaddrs(&info, (struct sockaddr *)(ifam+1), - (struct sockaddr *)ifam2, - ifam->ifam_addrs); - - if (INFO_IFA(&info) == 0) { - if (iff_alive(ifs.int_if_flags)) { - if (!(prev_complaints & COMP_NOADDR)) - msglog("%s has a bad address", - sdl->sdl_data); - complaints |= COMP_NOADDR; - } - continue; - } - if (INFO_IFA(&info)->sa_family != AF_INET) { - if (iff_alive(ifs.int_if_flags)) { - if (!(prev_complaints & COMP_NOT_INET)) - trace_act("%s: not AF_INET\n", - sdl->sdl_data); - complaints |= COMP_NOT_INET; - } - continue; - } - - bcopy(&ifs0, &ifs, sizeof(ifs0)); - ifs0.int_state |= IS_ALIAS; /* next will be an alias */ - - ifs.int_addr = S_ADDR(INFO_IFA(&info)); - - if (ifs.int_if_flags & IFF_BROADCAST) { - if (INFO_MASK(&info) == 0) { - if (iff_alive(ifs.int_if_flags)) { - if (!(prev_complaints & COMP_NOMASK)) - msglog("%s has no netmask", - sdl->sdl_data); - complaints |= COMP_NOMASK; - } - continue; - } - ifs.int_dstaddr = ifs.int_addr; - ifs.int_mask = ntohl(S_ADDR(INFO_MASK(&info))); - ifs.int_ripv1_mask = ifs.int_mask; - ifs.int_net = ntohl(ifs.int_addr) & ifs.int_mask; - ifs.int_std_mask = std_mask(ifs.int_addr); - if (ifs.int_mask != ifs.int_std_mask) - ifs.int_state |= IS_SUBNET; - - if (INFO_BRD(&info) == 0) { - if (iff_alive(ifs.int_if_flags)) { - if (!(prev_complaints & COMP_NOBADR)) - msglog("%s has no" - " broadcast address", - sdl->sdl_data); - complaints |= COMP_NOBADR; - } - continue; - } - ifs.int_brdaddr = S_ADDR(INFO_BRD(&info)); - - } else if (ifs.int_if_flags & IFF_POINTOPOINT) { - if (INFO_BRD(&info) == 0 - || INFO_BRD(&info)->sa_family != AF_INET) { - if (iff_alive(ifs.int_if_flags)) { - if (!(prev_complaints & COMP_NODST)) - msglog("%s has a bad" - " destination address", - sdl->sdl_data); - complaints |= COMP_NODST; - } - continue; - } - ifs.int_dstaddr = S_ADDR(INFO_BRD(&info)); - ifs.int_mask = HOST_MASK; - ifs.int_ripv1_mask = ntohl(S_ADDR(INFO_MASK(&info))); - ifs.int_net = ntohl(ifs.int_dstaddr); - ifs.int_std_mask = std_mask(ifs.int_dstaddr); - - } else if (ifs.int_if_flags & IFF_LOOPBACK) { - ifs.int_state |= IS_PASSIVE | IS_NO_RIP; - ifs.int_dstaddr = ifs.int_addr; - ifs.int_mask = HOST_MASK; - ifs.int_ripv1_mask = HOST_MASK; - ifs.int_net = ntohl(ifs.int_dstaddr); - ifs.int_std_mask = std_mask(ifs.int_dstaddr); - if (!foundloopback) { - foundloopback = 1; - loopaddr = ifs.int_addr; - } - - } else { - if (!(prev_complaints & COMP_WIERD)) - trace_act("%s is neither broadcast" - " nor point-to-point nor loopback", - sdl->sdl_data); - complaints |= COMP_WIERD; - continue; - } - ifs.int_std_net = ifs.int_net & ifs.int_std_mask; - ifs.int_std_addr = htonl(ifs.int_std_net); - - /* Use a minimum metric of one. Treat the interface metric - * (default 0) as an increment to the hop count of one. - * - * The metric obtained from the routing socket dump of - * interface addresses is wrong. It is not set by the - * SIOCSIFMETRIC ioctl. - */ -#ifdef SIOCGIFMETRIC - strncpy(ifr.ifr_name, sdl->sdl_data, sizeof(ifr.ifr_name)); - if (ioctl(rt_sock, SIOCGIFMETRIC, &ifr) < 0) { - DBGERR(1, "ioctl(SIOCGIFMETRIC)"); - ifs.int_metric = 0; - } else { - ifs.int_metric = ifr.ifr_metric; - } -#else - ifs.int_metric = ifam->ifam_metric; -#endif - if (ifs.int_metric > HOPCNT_INFINITY) { - ifs.int_metric = 0; - if (!(prev_complaints & COMP_BAD_METRIC) - && iff_alive(ifs.int_if_flags)) { - complaints |= COMP_BAD_METRIC; - msglog("%s has a metric of %d", - sdl->sdl_data, ifs.int_metric); - } - } - - /* See if this is a familiar interface. - * If so, stop worrying about it if it is the same. - * Start it over if it now is to somewhere else, as happens - * frequently with PPP and SLIP. - */ - ifp = ifwithname(sdl->sdl_data, ((ifs.int_state & IS_ALIAS) - ? ifs.int_addr - : 0)); - if (ifp != 0) { - ifp->int_state |= IS_CHECKED; - - if (0 != ((ifp->int_if_flags ^ ifs.int_if_flags) - & (IFF_BROADCAST - | IFF_LOOPBACK - | IFF_POINTOPOINT - | IFF_MULTICAST)) - || 0 != ((ifp->int_state ^ ifs.int_state) - & IS_ALIAS) - || ifp->int_addr != ifs.int_addr - || ifp->int_brdaddr != ifs.int_brdaddr - || ifp->int_dstaddr != ifs.int_dstaddr - || ifp->int_mask != ifs.int_mask - || ifp->int_metric != ifs.int_metric) { - /* Forget old information about - * a changed interface. - */ - trace_act("interface %s has changed\n", - ifp->int_name); - ifdel(ifp); - ifp = 0; - } - } - - if (ifp != 0) { - /* The primary representative of an alias worries - * about how things are working. - */ - if (ifp->int_state & IS_ALIAS) - continue; - - /* note interfaces that have been turned off - */ - if (!iff_alive(ifs.int_if_flags)) { - if (iff_alive(ifp->int_if_flags)) { - msglog("interface %s to %s turned off", - ifp->int_name, - naddr_ntoa(ifp->int_addr)); - if_bad(ifp); - ifp->int_if_flags &= ~IFF_UP_RUNNING; - } - continue; - } - /* or that were off and are now ok */ - if (!iff_alive(ifp->int_if_flags)) { - ifp->int_if_flags |= IFF_UP_RUNNING; - (void)if_ok(ifp, ""); - } - - /* If it has been long enough, - * see if the interface is broken. - */ - if (now.tv_sec < ifp->int_data.ts+CHECK_BAD_INTERVAL) - continue; - - in = ifs.int_data.ipackets - ifp->int_data.ipackets; - ierr = ifs.int_data.ierrors - ifp->int_data.ierrors; - out = ifs.int_data.opackets - ifp->int_data.opackets; - oerr = ifs.int_data.oerrors - ifp->int_data.oerrors; -#ifdef sgi - /* Through at least IRIX 6.2, PPP and SLIP - * count packets dropped by the filters. - * But FDDI rings stuck non-operational count - * dropped packets as they wait for improvement. - */ - if (!(ifp->int_if_flags & IFF_POINTOPOINT)) - oerr += (ifs.int_data.odrops - - ifp->int_data.odrops); -#endif - /* If the interface just awoke, restart the counters. - */ - if (ifp->int_data.ts == 0) { - ifp->int_data = ifs.int_data; - continue; - } - ifp->int_data = ifs.int_data; - - /* Withhold judgement when the short error - * counters wrap or the interface is reset. - */ - if (ierr < 0 || in < 0 || oerr < 0 || out < 0) { - LIM_SEC(ifinit_timer, - now.tv_sec+CHECK_BAD_INTERVAL); - continue; - } - - /* Withhold judgement when there is no traffic - */ - if (in == 0 && out == 0 && ierr == 0 && oerr == 0) - continue; - - /* It is bad if input or output is not working. - * Require presistent problems before marking it dead. - */ - if ((in <= ierr && ierr > 0) - || (out <= oerr && oerr > 0)) { - if (!(ifp->int_state & IS_SICK)) { - trace_act("interface %s to %s" - " sick: in=%d ierr=%d" - " out=%d oerr=%d\n", - ifp->int_name, - naddr_ntoa(ifp->int_addr), - in, ierr, out, oerr); - if_sick(ifp); - continue; - } - if (!(ifp->int_state & IS_BROKE)) { - msglog("interface %s to %s bad:" - " in=%d ierr=%d out=%d oerr=%d", - ifp->int_name, - naddr_ntoa(ifp->int_addr), - in, ierr, out, oerr); - if_bad(ifp); - } - continue; - } - - /* otherwise, it is active and healthy - */ - ifp->int_act_time = now.tv_sec; - (void)if_ok(ifp, ""); - continue; - } - - /* This is a new interface. - * If it is dead, forget it. - */ - if (!iff_alive(ifs.int_if_flags)) - continue; - - /* See if it duplicates an existing interface. - */ - for (ifp = ifnet; 0 != ifp; ifp = ifp->int_next) { - if (ifp->int_mask != ifs.int_mask) - continue; - if (((ifp->int_addr != ifs.int_addr - && ifs.int_mask != HOST_MASK) - || (ifp->int_dstaddr != ifs.int_dstaddr - && ifs.int_mask == HOST_MASK))) - continue; - if (!iff_alive(ifp->int_if_flags)) - continue; - /* Let one of our real interfaces be marked - * passive. - */ - if ((ifp->int_state & IS_PASSIVE) - && !(ifp->int_state & IS_EXTERNAL)) - continue; - - /* It does duplicate an existing interface, - * so complain about it, mark the other one - * duplicated, and for get this one. - */ - if (!(prev_complaints & COMP_DUP)) { - complaints |= COMP_DUP; - msglog("%s is duplicated by %s at %s", - sdl->sdl_data, ifp->int_name, - naddr_ntoa(ifp->int_addr)); - } - ifp->int_state |= IS_DUP; - break; - } - if (ifp != 0) - continue; - - /* It is new and ok. So make it real - */ - strncpy(ifs.int_name, sdl->sdl_data, - MIN(sizeof(ifs.int_name)-1, sdl->sdl_nlen)); - get_parms(&ifs); - - /* Add it to the list of interfaces - */ - ifp = (struct interface *)rtmalloc(sizeof(*ifp), "ifinit"); - bcopy(&ifs, ifp, sizeof(*ifp)); - if (ifnet != 0) { - ifp->int_next = ifnet; - ifnet->int_prev = ifp; - } - ifnet = ifp; - trace_if("Add", ifp); - - /* Notice likely bad netmask. - */ - if (!(prev_complaints & COMP_NETMASK) - && !(ifp->int_if_flags & IFF_POINTOPOINT)) { - for (ifp1 = ifnet; 0 != ifp1; ifp1 = ifp1->int_next) { - if (ifp1->int_mask == ifp->int_mask) - continue; - if (ifp1->int_if_flags & IFF_POINTOPOINT) - continue; - if (on_net(ifp->int_addr, - ifp1->int_net, ifp1->int_mask) - || on_net(ifp1->int_addr, - ifp->int_net, ifp->int_mask)) { - msglog("possible netmask problem" - " betwen %s:%s and %s:%s", - ifp->int_name, - addrname(htonl(ifp->int_net), - ifp->int_mask, 1), - ifp1->int_name, - addrname(htonl(ifp1->int_net), - ifp1->int_mask, 1)); - complaints |= COMP_NETMASK; - } - } - } - - /* Count the # of directly connected networks. - */ - if (!(ifp->int_state & IS_ALIAS)) { - if (!(ifp->int_if_flags & IFF_LOOPBACK)) - tot_interfaces++; - if (!IS_RIP_OFF(ifp->int_state)) - rip_interfaces++; - } - - if_ok_rdisc(ifp); - rip_on(ifp); - } - - /* If we are multi-homed and have at least one interface - * listening to RIP, then output by default. - */ - if (!supplier_set && rip_interfaces > 1) - set_supplier(); - - /* If we are multi-homed, optionally advertise a route to - * our main address. - */ - if (advertise_mhome - || (tot_interfaces > 1 - && mhome - && (ifp = ifwithaddr(myaddr, 0, 0)) != 0 - && foundloopback)) { - advertise_mhome = 1; - rt = rtget(myaddr, HOST_MASK); - if (rt != 0) { - if (rt->rt_ifp != ifp - || rt->rt_router != loopaddr) { - rtdelete(rt); - rt = 0; - } else { - rtchange(rt, rt->rt_state | RS_MHOME, - loopaddr, loopaddr, - 0, 0, ifp, rt->rt_time, 0); - } - } - if (rt == 0) - rtadd(myaddr, HOST_MASK, loopaddr, loopaddr, - 0, 0, RS_MHOME, ifp); - } - - for (ifp = ifnet; ifp != 0; ifp = ifp1) { - ifp1 = ifp->int_next; /* because we may delete it */ - - /* Forget any interfaces that have disappeared. - */ - if (!(ifp->int_state & (IS_CHECKED | IS_REMOTE))) { - trace_act("interface %s has disappeared\n", - ifp->int_name); - ifdel(ifp); - continue; - } - - if ((ifp->int_state & IS_BROKE) - && !(ifp->int_state & IS_PASSIVE)) - LIM_SEC(ifinit_timer, now.tv_sec+CHECK_BAD_INTERVAL); - - /* If we ever have a RIPv1 interface, assume we always will. - * It might come back if it ever goes away. - */ - if (!(ifp->int_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; - } - } - - for (ifp = ifnet; ifp != 0; ifp = ifp->int_next) { - /* Ensure there is always a network route for interfaces, - * after any dead interfaces have been deleted, which - * might affect routes for point-to-point links. - */ - addrouteforif(ifp); - - /* Add routes to the local end of point-to-point interfaces - * using loopback. - */ - if ((ifp->int_if_flags & IFF_POINTOPOINT) - && !(ifp->int_state & IS_REMOTE) - && foundloopback) { - /* Delete any routes to the network address through - * foreign routers. Remove even static routes. - */ - del_static(ifp->int_addr, HOST_MASK, 0); - rt = rtget(ifp->int_addr, HOST_MASK); - if (rt != 0 && rt->rt_router != loopaddr) { - rtdelete(rt); - rt = 0; - } - if (rt != 0) { - if (!(rt->rt_state & RS_LOCAL) - || rt->rt_metric > ifp->int_metric) { - ifp1 = ifp; - } else { - ifp1 = rt->rt_ifp; - } - rtchange(rt,((rt->rt_state & ~RS_NET_SYN) - | (RS_IF|RS_LOCAL)), - loopaddr, loopaddr, - 0, 0, ifp1, rt->rt_time, 0); - } else { - rtadd(ifp->int_addr, HOST_MASK, - loopaddr, loopaddr, - 0, 0, (RS_IF | RS_LOCAL), ifp); - } - } - } - - /* add the authority routes */ - for (intnetp = intnets; intnetp!=0; intnetp = intnetp->intnet_next) { - rt = rtget(intnetp->intnet_addr, intnetp->intnet_mask); - if (rt != 0 - && !(rt->rt_state & RS_NO_NET_SYN) - && !(rt->rt_state & RS_NET_INT)) { - rtdelete(rt); - rt = 0; - } - if (rt == 0) - rtadd(intnetp->intnet_addr, intnetp->intnet_mask, - loopaddr, loopaddr, intnetp->intnet_metric-1, - 0, RS_NET_SYN | RS_NET_INT, 0); - } - - prev_complaints = complaints; -} - - -static void -check_net_syn(struct interface *ifp) -{ - struct rt_entry *rt; - - - /* Turn on the need to automatically synthesize a network route - * for this interface only if we are running RIPv1 on some other - * interface that is on a different class-A,B,or C network. - */ - if (have_ripv1_out || have_ripv1_in) { - ifp->int_state |= IS_NEED_NET_SYN; - rt = rtget(ifp->int_std_addr, ifp->int_std_mask); - if (rt != 0 - && 0 == (rt->rt_state & RS_NO_NET_SYN) - && (!(rt->rt_state & RS_NET_SYN) - || rt->rt_metric > ifp->int_metric)) { - rtdelete(rt); - rt = 0; - } - if (rt == 0) - rtadd(ifp->int_std_addr, ifp->int_std_mask, - ifp->int_addr, ifp->int_addr, - ifp->int_metric, 0, RS_NET_SYN, ifp); - - } else { - ifp->int_state &= ~IS_NEED_NET_SYN; - - rt = rtget(ifp->int_std_addr, - ifp->int_std_mask); - if (rt != 0 - && (rt->rt_state & RS_NET_SYN) - && rt->rt_ifp == ifp) - rtbad_sub(rt); - } -} - - -/* Add route for interface if not currently installed. - * Create route to other end if a point-to-point link, - * otherwise a route to this (sub)network. - */ -void -addrouteforif(struct interface *ifp) -{ - struct rt_entry *rt; - naddr dst, gate; - - - /* skip sick interfaces - */ - if (ifp->int_state & IS_BROKE) - return; - - /* If the interface on a subnet, then install a RIPv1 route to - * the network as well (unless it is sick). - */ - if (ifp->int_state & IS_SUBNET) - check_net_syn(ifp); - - if (ifp->int_state & IS_REMOTE) { - dst = ifp->int_addr; - gate = ifp->int_dstaddr; - /* If we are going to send packets to the gateway, - * it must be reachable using our physical interfaces - */ - if (!(ifp->int_state && IS_EXTERNAL) - && !rtfind(ifp->int_dstaddr) - && ifp->int_transitions == 0) { - msglog("unreachable gateway %s in " - _PATH_GATEWAYS" entry %s", - naddr_ntoa(gate), ifp->int_name); - return; - } - - } else { - dst = (0 != (ifp->int_if_flags & (IFF_POINTOPOINT - | IFF_LOOPBACK)) - ? ifp->int_dstaddr - : htonl(ifp->int_net)); - gate = ifp->int_addr; - } - - /* We are finished if the correct main interface route exists. - * The right route must be for the right interface, not synthesized - * from a subnet, be a "gateway" or not as appropriate, and so forth. - */ - del_static(dst, ifp->int_mask, 0); - rt = rtget(dst, ifp->int_mask); - if (rt != 0) { - if ((rt->rt_ifp != ifp - || rt->rt_router != ifp->int_addr) - && (!(ifp->int_state & IS_DUP) - || rt->rt_ifp == 0 - || (rt->rt_ifp->int_state & IS_BROKE))) { - rtdelete(rt); - rt = 0; - } else { - rtchange(rt, ((rt->rt_state | RS_IF) - & ~(RS_NET_SYN | RS_LOCAL)), - ifp->int_addr, ifp->int_addr, - ifp->int_metric, 0, ifp, now.tv_sec, 0); - } - } - if (rt == 0) { - if (ifp->int_transitions++ > 0) - trace_act("re-install interface %s\n", - ifp->int_name); - - rtadd(dst, ifp->int_mask, gate, gate, - ifp->int_metric, 0, RS_IF, ifp); - } -} diff --git a/usr.sbin/routed/input.c b/usr.sbin/routed/input.c deleted file mode 100644 index 6bbc572af577..000000000000 --- a/usr.sbin/routed/input.c +++ /dev/null @@ -1,714 +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. - */ - -#if !defined(lint) && !defined(sgi) && !defined(__NetBSD__) -static char sccsid[] = "@(#)input.c 8.1 (Berkeley) 6/5/93"; -#elif defined(__NetBSD__) -static char rcsid[] = "$NetBSD$"; -#endif -#ident "$Revision: 1.13 $" - -#include "defs.h" - -static void input(struct sockaddr_in *, struct interface*, struct rip *, int); -static void input_route(struct interface *, naddr, - naddr, naddr, naddr, struct netinfo *); - - -/* process RIP input - */ -void -read_rip(int sock, - struct interface *ifp) -{ - struct sockaddr_in from; - int fromlen, cc; - union pkt_buf inbuf; - - - for (;;) { - fromlen = sizeof(from); - cc = recvfrom(sock, &inbuf, sizeof(inbuf), 0, - (struct sockaddr*)&from, &fromlen); - if (cc <= 0) { - if (cc < 0 && errno != EWOULDBLOCK) - LOGERR("recvfrom(rip)"); - break; - } - if (fromlen != sizeof(struct sockaddr_in)) - logbad(1,"impossible recvfrom(rip) fromlen=%d", - fromlen); - - input(&from, - (ifp != 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; - - 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; - trace_pkt("discard authenticated RIPv2 message\n"); - return; - } - - switch (rip->rip_cmd) { - case RIPCMD_REQUEST: - /* did the request come from a router? - */ - if (from->sin_port == htons(RIP_PORT)) { - /* yes, ignore it if RIP is off so that it does not - * depend on us. - */ - if (rip_sock < 0) { - trace_pkt("ignore request while RIP off\n"); - return; - } - - /* Ignore the request if we talking to ourself - * (and not a remote gateway). - */ - if (ifwithaddr(FROM_NADDR, 0, 0) != 0) { - trace_pkt("discard our own RIP request\n"); - return; - } - } - - /* According to RFC 1723, we should ignore unathenticated - * queries. That is too silly to bother with. Sheesh! - * Are forwarding tables supposed to be secret? When - * a bad guy can infer them with test traffic? - * Maybe on firewalls you'd care, but not enough to - * give up the diagnostic facilities of remote probing. - */ - - if (n >= lim - || size%sizeof(*n) != sizeof(struct rip)%sizeof(*n)) { - if (from->sin_addr.s_addr != bad_len) - msglog("request of bad length (%d) from %s", - size, naddr_ntoa(FROM_NADDR)); - bad_len = from->sin_addr.s_addr; - } - for (; n < lim; n++) { - n->n_metric = ntohl(n->n_metric); - - /* A single entry with family RIP_AF_UNSPEC and - * metric HOPCNT_INFINITY means "all routes". - * We respond to routers only if we are acting - * as a supplier, or to anyone other than a router - * (i.e. a query). - * - * 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 - */ - 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); - } - return; - } - - if (n->n_family != RIP_AF_INET) { - if (from->sin_addr.s_addr != bad_router) - msglog("request from %s" - " for unsupported (af %d) %s", - naddr_ntoa(FROM_NADDR), - ntohs(n->n_family), - naddr_ntoa(n->n_dst)); - bad_router = from->sin_addr.s_addr; - return; - } - - dst = n->n_dst; - if (!check_dst(dst)) { - if (from->sin_addr.s_addr != bad_router) - msglog("bad queried destination" - " %s from %s", - naddr_ntoa(dst), - naddr_ntoa(FROM_NADDR)); - bad_router = from->sin_addr.s_addr; - return; - } - - if (rip->rip_vers == RIPv1 - || 0 == (mask = ntohl(n->n_mask)) - || 0 != (ntohl(dst) & ~mask)) - mask = ripv1_mask_host(dst,ifp); - - rt = rtget(dst, mask); - if (!rt && dst != RIP_DEFAULT) - rt = rtfind(n->n_dst); - - n->n_tag = 0; - n->n_nhop = 0; - if (rip->rip_vers == RIPv1) { - n->n_mask = 0; - } else { - n->n_mask = mask; - } - if (rt == 0) { - n->n_metric = HOPCNT_INFINITY; - } else { - n->n_metric = rt->rt_metric+1; - n->n_metric += (ifp!=0) ? ifp->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 - && on_net(rt->rt_gate, - ifp->int_net, - ifp->int_mask) - && rt->rt_gate != ifp->int_addr) - n->n_nhop = rt->rt_gate; - } - } - HTONL(n->n_metric); - } - /* Answer about specific routes. - * Only answer a router if we are a supplier - * to keep an unwary host that is just starting - * from picking us an a router. - */ - rip->rip_cmd = RIPCMD_RESPONSE; - rip->rip_res1 = 0; - if (rip->rip_vers != RIPv1) - rip->rip_vers = RIPv2; - if (from->sin_port != htons(RIP_PORT)) { - /* query */ - (void)output(OUT_QUERY, from, 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((char*)rip->rip_tracefile, 0); - } else { - trace_off("tracing turned off by %s\n", - naddr_ntoa(FROM_NADDR)); - } - return; - - case RIPCMD_RESPONSE: - if (size%sizeof(*n) != sizeof(struct rip)%sizeof(*n)) { - if (from->sin_addr.s_addr != bad_len) - msglog("response of bad length (%d) from %s", - size, naddr_ntoa(FROM_NADDR)); - bad_len = from->sin_addr.s_addr; - } - - /* verify message came from a router */ - if (from->sin_port != ntohs(RIP_PORT)) { - trace_pkt("discard RIP response from unknown port\n"); - return; - } - - if (rip_sock < 0) { - trace_pkt("discard response while RIP off\n"); - return; - } - - /* Are we talking to ourself or a remote gateway? - */ - ifp1 = ifwithaddr(FROM_NADDR, 0, 1); - if (ifp1) { - if (ifp1->int_state & IS_REMOTE) { - if (ifp1->int_state & IS_PASSIVE) { - msglog("bogus input from %s on" - " supposedly passive %s", - naddr_ntoa(FROM_NADDR), - ifp1->int_name); - } else { - ifp1->int_act_time = now.tv_sec; - if (if_ok(ifp1, "remote ")) - addrouteforif(ifp1); - } - } else { - trace_pkt("discard our own RIP response\n"); - } - return; - } - - /* Check the router from which message originated. We accept - * routing packets from routers directly connected via - * broadcast or point-to-point networks, and from - * those listed in /etc/gateways. - */ - if (!ifp) { - if (from->sin_addr.s_addr != unk_router) - 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 - */ - if (((ifp->int_state & IS_NO_RIPV1_IN) - && rip->rip_vers == RIPv1) - || ((ifp->int_state & IS_NO_RIPV2_IN) - && rip->rip_vers != RIPv1)) { - trace_pkt("discard RIPv%d response\n", - rip->rip_vers); - return; - } - - /* Ignore routes via dead interface. - */ - if (ifp->int_state & IS_BROKE) { - trace_pkt("discard response via broken interface %s\n", - ifp->int_name); - return; - } - - /* Authenticate the packet if we have a secret. - */ - 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++) { - 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; - } - } - - 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" - " %#x with %s", - naddr_ntoa(FROM_NADDR), - mask, - naddr_ntoa(dst)); - bad_mask = from->sin_addr.s_addr; - } - continue; - } - if (rip->rip_vers == RIPv1) - n->n_tag = 0; - - /* Adjust metric according to incoming interface.. - */ - n->n_metric += 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. - * 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_metric >= ifp->int_d_metric) - continue; - - /* We can receive aggregated RIPv2 routes that must - * be broken down before they are transmitted by - * RIPv1 via an interface on a subnet. - * We might also receive the same routes aggregated - * via other RIPv2 interfaces. - * This could cause duplicate routes to be sent on - * the RIPv1 interfaces. "Longest matching variable - * length netmasks" lets RIPv2 listeners understand, - * but breaking down the aggregated routes for RIPv1 - * listeners can produce duplicate routes. - * - * Breaking down aggregated routes here bloats - * the daemon table, but does not hurt the kernel - * table, since routes are always aggregated for - * the kernel. - * - * Notice that this does not break down network - * routes corresponding to subnets. This is part - * of the defense against RS_NET_SYN. - */ - if (have_ripv1_out - && (v1_mask = ripv1_mask_net(dst,0)) > mask - && (((rt = rtget(dst,mask)) == 0 - || !(rt->rt_state & RS_NET_SYN)))) { - ddst_h = v1_mask & -v1_mask; - i = (v1_mask & ~mask)/ddst_h; - if (i >= 511) { - /* Punt if we would have to generate - * an unreasonable number of routes. - */ -#ifdef DEBUG - msglog("accept %s from %s as 1" - " instead of %d routes", - addrname(dst,mask,0), - naddr_ntoa(FROM_NADDR), - i+1); -#endif - i = 0; - } else { - mask = v1_mask; - } - } else { - i = 0; - } - - for (;;) { - input_route(ifp, FROM_NADDR, - dst, mask, gate, n); - if (i-- == 0) - break; - dst = htonl(ntohl(dst) + ddst_h); - } - } - break; - } -} - - -/* Process a single input route. - */ -static void -input_route(struct interface *ifp, - naddr from, - naddr dst, - naddr mask, - naddr gate, - struct netinfo *n) -{ - int i; - struct rt_entry *rt; - struct rt_spare *rts, *rts0; - struct interface *ifp1; - time_t new_time; - - - /* See if the other guy is telling us to send our packets to him. - * Sometimes network routes arrive over a point-to-point link for - * the network containing the address(es) of the link. - * - * If our interface is broken, switch to using the other guy. - */ - ifp1 = ifwithaddr(dst, 1, 1); - if (ifp1 != 0 - && !(ifp1->int_state & IS_BROKE)) - return; - - /* Look for the route in our table. - */ - rt = rtget(dst, mask); - - /* Consider adding the route if we do not already have it. - */ - if (rt == 0) { - /* Ignore unknown routes being poisoned. - */ - if (n->n_metric == HOPCNT_INFINITY) - return; - - if (total_routes < MAX_ROUTES) - rtadd(dst, mask, gate, from, n->n_metric, - n->n_tag, 0, ifp); - return; - } - - /* We already know about the route. Consider this update. - * - * If (rt->rt_state & RS_NET_SYN), then this route - * is the same as a network route we have inferred - * for subnets we know, in order to tell RIPv1 routers - * about the subnets. - * - * It is impossible to tell if the route is coming - * from a distant RIPv2 router with the standard - * netmask because that router knows about the entire - * network, or if it is a round-about echo of a - * synthetic, RIPv1 network route of our own. - * The worst is that both kinds of routes might be - * received, and the bad one might have the smaller - * metric. Partly solve this problem by 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(rt, rts0, rts)) - rts0 = rts; - } - if (i != 0) { - /* Found the router - */ - int old_metric = rts->rts_metric; - - /* Keep poisoned routes around only long enough to pass - * the poison on. Get a new timestamp for good routes. - */ - new_time =((old_metric == HOPCNT_INFINITY) - ? rts->rts_time - : now.tv_sec); - - /* If this is an update for the router we currently prefer, - * then note it. - */ - if (i == NUM_SPARES) { - rtchange(rt,rt->rt_state, gate,rt->rt_router, - n->n_metric, n->n_tag, ifp, new_time, 0); - /* If the route got worse, check for something better. - */ - if (n->n_metric > old_metric) - rtswitch(rt, 0); - return; - } - - /* This is an update for a spare route. - * Finished if the route is unchanged. - */ - if (rts->rts_gate == gate - && old_metric == n->n_metric - && rts->rts_tag == n->n_tag) { - rts->rts_time = new_time; - return; - } - - } else { - /* The update is for a route we know about, - * but not from a familiar router. - */ - rts = rts0; - - /* Save the route as a spare only if it has - * a better metric than our worst spare. - * This also ignores poisoned routes (those - * received with metric HOPCNT_INFINITY). - */ - if (n->n_metric >= rts->rts_metric) - return; - - new_time = now.tv_sec; - } - - trace_upslot(rt, rts, gate, from, ifp, n->n_metric,n->n_tag, new_time); - - rts->rts_gate = gate; - rts->rts_router = from; - rts->rts_metric = n->n_metric; - rts->rts_tag = n->n_tag; - rts->rts_time = new_time; - rts->rts_ifp = ifp; - - /* try to switch to a better route */ - rtswitch(rt, rts); -} diff --git a/usr.sbin/routed/main.c b/usr.sbin/routed/main.c deleted file mode 100644 index 700762ed5dcb..000000000000 --- a/usr.sbin/routed/main.c +++ /dev/null @@ -1,824 +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. - */ - -char copyright[] = -"@(#) Copyright (c) 1983, 1988, 1993\n\ - The Regents of the University of California. All rights reserved.\n"; -#if !defined(lint) && !defined(sgi) && !defined(__NetBSD__) -static char sccsid[] = "@(#)main.c 8.1 (Berkeley) 6/5/93"; -#elif defined(__NetBSD__) -static char rcsid[] = "$NetBSD$"; -#endif -#ident "$Revision: 1.14 $" - -#include "defs.h" -#include "pathnames.h" -#ifdef sgi -#include "math.h" -#endif -#include -#include -#include - -pid_t mypid; - -naddr myaddr; /* system address */ -char myname[MAXHOSTNAMELEN+1]; - -int supplier; /* supply or broadcast updates */ -int supplier_set; -int ipforwarding = 1; /* kernel forwarding on */ - -int default_gateway; /* 1=advertise default */ -int background = 1; -int ridhosts; /* 1=reduce host routes */ -int mhome; /* 1=want multi-homed host route */ -int advertise_mhome; /* 1=must continue adverising it */ -int auth_ok = 1; /* 1=ignore auth if we do not care */ - -struct timeval epoch; /* when started */ -struct timeval clk, prev_clk; -struct timeval now; /* current idea of time */ -time_t now_stale; -time_t now_garbage; - -struct timeval next_bcast; /* next general broadcast */ -struct timeval no_flash = {EPOCH+SUPPLY_INTERVAL}; /* inhibit flash update */ - -fd_set fdbits; -int sock_max; -int rip_sock = -1; /* RIP socket */ -struct interface *rip_sock_mcast; /* current multicast interface */ -int rt_sock; /* routing socket */ -int rt_sock_seqno; - - -static int get_rip_sock(naddr, int); -static void timevalsub(struct timeval *, struct timeval *, struct timeval *); - -int -main(int argc, - char *argv[]) -{ - int n, mib[4], off; - size_t len; - char *p, *q; - struct timeval wtime, t2; - time_t dt; - fd_set ibits; - naddr p_addr, p_mask; - struct interface *ifp; - struct parm parm; - char *tracename = 0; - - - openlog("routed", LOG_PID | LOG_ODELAY, LOG_DAEMON); - ftrace = stdout; - - gettimeofday(&clk, 0); - prev_clk = clk; - epoch = clk; - epoch.tv_sec -= EPOCH; - now.tv_sec = EPOCH; - now_stale = EPOCH - STALE_TIME; - now_garbage = EPOCH - GARBAGE_TIME; - wtime.tv_sec = 0; - - (void)gethostname(myname, sizeof(myname)-1); - (void)gethost(myname, &myaddr); - - while ((n = getopt(argc, argv, "sqdghmpAtT:F:P:")) != EOF) { - switch (n) { - case 's': - supplier = 1; - supplier_set = 1; - break; - - case 'q': - supplier = 0; - supplier_set = 1; - break; - - case 'd': - background = 0; - break; - - case 'g': - bzero(&parm, sizeof(parm)); - parm.parm_d_metric = 1; - p = check_parms(&parm); - if (p != 0) - msglog("bad -g: %s", p); - else - default_gateway = 1; - break; - - case 'h': /* suppress extra host routes */ - ridhosts = 1; - break; - - case 'm': /* advertise host route */ - mhome = 1; /* on multi-homed hosts */ - break; - - case 'A': - /* Ignore authentication if we do not care. - * Crazy as it is, that is what RFC 1723 requires. - */ - auth_ok = 0; - break; - - case 't': - new_tracelevel++; - break; - - case 'T': - tracename = optarg; - break; - - case 'F': /* minimal routes for SLIP */ - n = HOPCNT_INFINITY-2; - p = strchr(optarg,','); - if (p && *p != '\0') { - n = (int)strtoul(p+1, &q, 0); - if (*q == '\0' - && n <= HOPCNT_INFINITY-1 - && n >= 1) - *p = '\0'; - } - if (!getnet(optarg, &p_addr, &p_mask)) { - msglog("bad network; \"-F %s\"", - optarg); - break; - } - bzero(&parm, sizeof(parm)); - parm.parm_addr_h = ntohl(p_addr); - parm.parm_mask = p_mask; - parm.parm_d_metric = n; - p = check_parms(&parm); - if (p != 0) - msglog("bad -F: %s", p); - break; - - case 'P': - /* handle arbirary, (usually) per-interface - * parameters. - */ - p = parse_parms(optarg); - if (p != 0) - msglog("bad \"%s\" in \"%s\"", - p, optarg); - break; - - default: - goto usage; - } - } - argc -= optind; - argv += optind; - - if (tracename == 0 && argc >= 1) { - tracename = *argv++; - argc--; - } - if (argc != 0) { -usage: - logbad(0, "usage: routed [-sqdghmpAt] [-T /tracefile]" - " [-F net[,metric]] [-P parms]"); - } - if (geteuid() != 0) - logbad(0, "requires UID 0"); - - mib[0] = CTL_NET; - mib[1] = PF_INET; - mib[2] = IPPROTO_IP; - mib[3] = IPCTL_FORWARDING; - len = sizeof(ipforwarding); - if (sysctl(mib, 4, &ipforwarding, &len, 0, 0) < 0) - LOGERR("sysctl(IPCTL_FORWARDING)"); - - if (!ipforwarding) { - if (supplier) - msglog("-s incompatible with ipforwarding=0"); - if (default_gateway) { - msglog("-g incompatible with ipforwarding=0"); - default_gateway = 0; - } - supplier = 0; - supplier_set = 1; - } - if (default_gateway) { - if (supplier_set && !supplier) { - msglog("-g and -q incompatible"); - } else { - supplier = 1; - supplier_set = 1; - } - } - - - /* get into the background */ - if (background) { -#ifdef sgi - if (0 > _daemonize(_DF_NOCHDIR, - new_tracelevel == 0 ? -1 : STDOUT_FILENO, - new_tracelevel == 0 ? -1 : STDERR_FILENO, - -1)) - BADERR(0, "_daemonize()"); -#else - if (daemon(1, 1) < 0) - BADERR(0,"daemon()"); -#endif - } - - mypid = getpid(); - srandom((int)(clk.tv_sec ^ clk.tv_usec ^ mypid)); - - /* prepare socket connected to the kernel. - */ - rt_sock = socket(AF_ROUTE, SOCK_RAW, 0); - if (rt_sock < 0) - BADERR(1,"rt_sock = socket()"); - if (fcntl(rt_sock, F_SETFL, O_NONBLOCK) == -1) - logbad(1, "fcntl(rt_sock) O_NONBLOCK: %s", strerror(errno)); - off = 0; - if (setsockopt(rt_sock, SOL_SOCKET,SO_USELOOPBACK, - &off,sizeof(off)) < 0) - LOGERR("setsockopt(SO_USELOOPBACK,0)"); - - fix_select(); - - - if (background && new_tracelevel == 0) - ftrace = 0; - if (tracename != 0) { - trace_on(tracename, 1); - if (new_tracelevel == 0) /* use stdout if file is bad */ - new_tracelevel = 1; - } - set_tracelevel(); - - /* initialize radix tree */ - rtinit(); - - /* Pick a random part of the second for our output to minimize - * collisions. - * - * Start broadcasting after hearing from other routers, and - * at a random time so a bunch of systems do not get synchronized - * after a power failure. - */ - intvl_random(&next_bcast, EPOCH+MIN_WAITTIME, EPOCH+SUPPLY_INTERVAL); - age_timer.tv_usec = next_bcast.tv_usec; - age_timer.tv_sec = EPOCH+MIN_WAITTIME; - rdisc_timer = next_bcast; - ifinit_timer.tv_usec = next_bcast.tv_usec; - - 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. - */ - gwkludge(); - ifinit(); - flush_kern(); - - /* Ask for routes */ - rip_query(); - if (!supplier) - rdisc_sol(); - - /* Loop forever, listening and broadcasting. - */ - for (;;) { - prev_clk = clk; - gettimeofday(&clk, 0); - timevalsub(&t2, &clk, &prev_clk); - if (t2.tv_sec < 0 - || t2.tv_sec > wtime.tv_sec + 5) { - /* Deal with time changes before other housekeeping to - * keep everything straight. - */ - dt = t2.tv_sec; - if (dt > 0) - dt -= wtime.tv_sec; - trace_act("time changed by %d sec\n", dt); - epoch.tv_sec += dt; - } - timevalsub(&now, &clk, &epoch); - now_stale = now.tv_sec - STALE_TIME; - now_garbage = now.tv_sec - GARBAGE_TIME; - - /* deal with interrupts that should affect tracing */ - set_tracelevel(); - - if (stopint != 0) { - if (supplier) { - rip_bcast(0); - rdisc_adv(); - } - trace_off("exiting with signal %d\n", stopint); - exit(stopint | 128); - } - - /* look for new or dead interfaces */ - timevalsub(&wtime, &ifinit_timer, &now); - if (wtime.tv_sec <= 0) { - wtime.tv_sec = 0; - ifinit(); - rip_query(); - continue; - } - - /* If it is time, then broadcast our routes. - */ - if (supplier || advertise_mhome) { - timevalsub(&t2, &next_bcast, &now); - if (t2.tv_sec <= 0) { - /* Synchronize the aging and broadcast - * timers to minimize awakenings - */ - age(0); - - rip_bcast(0); - - /* It is desirable to send routing updates - * regularly. So schedule the next update - * 30 seconds after the previous one was - * secheduled, instead of 30 seconds after - * the previous update was finished. - * Even if we just started after discovering - * a 2nd interface or were otherwise delayed, - * pick a 30-second aniversary of the - * original broadcast time. - */ - n = 1 + (0-t2.tv_sec)/SUPPLY_INTERVAL; - next_bcast.tv_sec += n*SUPPLY_INTERVAL; - - continue; - } - - if (timercmp(&t2, &wtime, <)) - wtime = t2; - } - - /* If we need a flash update, either do it now or - * set the delay to end when it is time. - * - * If we are within MIN_WAITTIME seconds of a full update, - * do not bother. - */ - if (need_flash - && supplier - && no_flash.tv_sec+MIN_WAITTIME < next_bcast.tv_sec) { - /* accurate to the millisecond */ - if (!timercmp(&no_flash, &now, >)) - rip_bcast(1); - timevalsub(&t2, &no_flash, &now); - if (timercmp(&t2, &wtime, <)) - wtime = t2; - } - - /* trigger the main aging timer. - */ - timevalsub(&t2, &age_timer, &now); - if (t2.tv_sec <= 0) { - age(0); - continue; - } - if (timercmp(&t2, &wtime, <)) - wtime = t2; - - /* update the kernel routing table - */ - timevalsub(&t2, &need_kern, &now); - if (t2.tv_sec <= 0) { - age(0); - continue; - } - if (timercmp(&t2, &wtime, <)) - wtime = t2; - - /* take care of router discovery, - * but do it to the millisecond - */ - if (!timercmp(&rdisc_timer, &now, >)) { - rdisc_age(0); - continue; - } - timevalsub(&t2, &rdisc_timer, &now); - if (timercmp(&t2, &wtime, <)) - wtime = t2; - - - /* wait for input or a timer to expire. - */ - trace_flush(); - ibits = fdbits; - n = select(sock_max, &ibits, 0, 0, &wtime); - if (n <= 0) { - if (n < 0 && errno != EINTR && errno != EAGAIN) - BADERR(1,"select"); - continue; - } - - if (FD_ISSET(rt_sock, &ibits)) { - read_rt(); - n--; - } - if (rdisc_sock >= 0 && FD_ISSET(rdisc_sock, &ibits)) { - read_d(); - n--; - } - if (rip_sock >= 0 && FD_ISSET(rip_sock, &ibits)) { - read_rip(rip_sock, 0); - n--; - } - - for (ifp = ifnet; n > 0 && 0 != ifp; ifp = ifp->int_next) { - if (ifp->int_rip_sock >= 0 - && FD_ISSET(ifp->int_rip_sock, &ibits)) { - read_rip(ifp->int_rip_sock, ifp); - n--; - } - } - } -} - - -/* ARGSUSED */ -void -sigalrm(int sig) -{ - /* Historically, SIGALRM would cause the daemon to check for - * new and broken interfaces. - */ - ifinit_timer.tv_sec = now.tv_sec; - trace_act("SIGALRM\n"); -} - - -/* watch for fatal signals */ -void -sigterm(int sig) -{ - stopint = sig; - (void)signal(sig, SIG_DFL); /* catch it only once */ -} - - -void -fix_select(void) -{ - struct interface *ifp; - - - FD_ZERO(&fdbits); - sock_max = 0; - - FD_SET(rt_sock, &fdbits); - if (sock_max <= rt_sock) - sock_max = rt_sock+1; - if (rip_sock >= 0) { - FD_SET(rip_sock, &fdbits); - if (sock_max <= rip_sock) - sock_max = rip_sock+1; - } - for (ifp = ifnet; 0 != ifp; ifp = ifp->int_next) { - if (ifp->int_rip_sock >= 0) { - FD_SET(ifp->int_rip_sock, &fdbits); - if (sock_max <= ifp->int_rip_sock) - sock_max = ifp->int_rip_sock+1; - } - } - if (rdisc_sock >= 0) { - FD_SET(rdisc_sock, &fdbits); - if (sock_max <= rdisc_sock) - sock_max = rdisc_sock+1; - } -} - - -void -fix_sock(int sock, - char *name) -{ - int on; -#define MIN_SOCKBUF (4*1024) - static int rbuf; - - if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1) - logbad(1, "fcntl(%s) O_NONBLOCK: %s", - name, strerror(errno)); - on = 1; - if (setsockopt(sock, SOL_SOCKET,SO_BROADCAST, - &on,sizeof(on)) < 0) - msglog("setsockopt(%s,SO_BROADCAST): %s", - name, strerror(errno)); - if (rbuf >= MIN_SOCKBUF) { - if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, - &rbuf, sizeof(rbuf)) < 0) - msglog("setsockopt(%s,SO_RCVBUF=%d): %s", - name, rbuf, strerror(errno)); - } else { - for (rbuf = 60*1024; ; rbuf -= 4096) { - if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, - &rbuf, sizeof(rbuf)) == 0) { - trace_act("RCVBUF=%d\n", rbuf); - break; - } - if (rbuf < MIN_SOCKBUF) { - msglog("setsockopt(%s,SO_RCVBUF = %d): %s", - name, rbuf, strerror(errno)); - break; - } - } - } -} - - -/* get a rip socket - */ -static int /* <0 or file descriptor */ -get_rip_sock(naddr addr, - int serious) /* 1=failure to bind is serious */ -{ - struct sockaddr_in sin; - unsigned char ttl; - int s; - - - if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) - BADERR(1,"rip_sock = socket()"); - - bzero(&sin,sizeof(sin)); -#ifdef _HAVE_SIN_LEN - sin.sin_len = sizeof(sin); -#endif - sin.sin_family = AF_INET; - sin.sin_port = htons(RIP_PORT); - sin.sin_addr.s_addr = addr; - if (bind(s, (struct sockaddr *)&sin,sizeof(sin)) < 0) { - if (serious) - BADERR(errno != EADDRINUSE, "bind(rip_sock)"); - return -1; - } - fix_sock(s,"rip_sock"); - - ttl = 1; - if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_TTL, - &ttl, sizeof(ttl)) < 0) - DBGERR(1,"rip_sock setsockopt(IP_MULTICAST_TTL)"); - - return s; -} - - -/* turn off main RIP socket */ -void -rip_off(void) -{ - struct interface *ifp; - register naddr addr; - - - if (rip_sock >= 0 && !mhome) { - trace_act("turn off RIP\n"); - - (void)close(rip_sock); - rip_sock = -1; - - /* get non-broadcast sockets to listen to queries. - */ - for (ifp = ifnet; ifp != 0; ifp = ifp->int_next) { - if (ifp->int_rip_sock < 0 - && !(ifp->int_state & IS_ALIAS)) { - addr = ((ifp->int_if_flags & IFF_POINTOPOINT) - ? ifp->int_dstaddr - : ifp->int_addr); - ifp->int_rip_sock = get_rip_sock(addr, 0); - } - } - - fix_select(); - - age(0); - } -} - - -/* turn on RIP multicast input via an interface - */ -static void -rip_mcast_on(struct interface *ifp) -{ - struct ip_mreq m; - - if (!IS_RIP_IN_OFF(ifp->int_state) - && (ifp->int_if_flags & IFF_MULTICAST) -#ifdef MCAST_PPP_BUG - && !(ifp->int_if_flags & IFF_POINTOPOINT) -#endif - && !(ifp->int_state & IS_ALIAS)) { - m.imr_multiaddr.s_addr = htonl(INADDR_RIP_GROUP); - m.imr_interface.s_addr = ((ifp->int_if_flags & IFF_POINTOPOINT) - ? ifp->int_dstaddr - : ifp->int_addr); - if (setsockopt(rip_sock,IPPROTO_IP, IP_ADD_MEMBERSHIP, - &m, sizeof(m)) < 0) - LOGERR("setsockopt(IP_ADD_MEMBERSHIP RIP)"); - } -} - - -/* Prepare socket used for RIP. - */ -void -rip_on(struct interface *ifp) -{ - /* If the main RIP socket is already alive, only start receiving - * multicasts for this interface. - */ - if (rip_sock >= 0) { - if (ifp != 0) - rip_mcast_on(ifp); - return; - } - - /* If the main RIP socket is off, and it makes sense to turn it on, - * turn it on for all of the interfaces. - */ - if (rip_interfaces > 0 && !rdisc_ok) { - trace_act("turn on RIP\n"); - - /* Close all of the query sockets so that we can open - * the main socket. SO_REUSEPORT is not a solution, - * since that would let two daemons bind to the broadcast - * socket. - */ - for (ifp = ifnet; ifp != 0; ifp = ifp->int_next) { - if (ifp->int_rip_sock >= 0) { - (void)close(ifp->int_rip_sock); - ifp->int_rip_sock = -1; - } - } - - rip_sock = get_rip_sock(INADDR_ANY, 1); - rip_sock_mcast = 0; - - /* Do not advertise anything until we have heard something - */ - if (next_bcast.tv_sec < now.tv_sec+MIN_WAITTIME) - next_bcast.tv_sec = now.tv_sec+MIN_WAITTIME; - - for (ifp = ifnet; ifp != 0; ifp = ifp->int_next) { - if (!IS_RIP_IN_OFF(ifp->int_state)) - ifp->int_state &= ~IS_RIP_QUERIED; - rip_mcast_on(ifp); - } - - ifinit_timer.tv_sec = now.tv_sec; - - fix_select(); - - } else if (ifp != 0 - && ifp->int_rip_sock < 0 - && !(ifp->int_state & IS_ALIAS)) { - /* RIP is off, so ensure there are sockets on which - * to listen for queries. - */ - ifp->int_rip_sock = get_rip_sock(ifp->int_addr, 0); - - fix_select(); - } -} - - -/* die if malloc(3) fails - */ -void * -rtmalloc(size_t size, - char *msg) -{ - void *p = malloc(size); - if (p == 0) - logbad(1,"malloc() failed in %s", msg); - return p; -} - - -/* get a random instant in an interval - */ -void -intvl_random(struct timeval *tp, /* put value here */ - u_long lo, /* value is after this second */ - u_long hi) /* and before this */ -{ - tp->tv_sec = (time_t)(hi == lo - ? lo - : (lo + random() % ((hi - lo)))); - tp->tv_usec = random() % 1000000; -} - - -void -timevaladd(struct timeval *t1, - struct timeval *t2) -{ - - t1->tv_sec += t2->tv_sec; - if ((t1->tv_usec += t2->tv_usec) > 1000000) { - t1->tv_sec++; - t1->tv_usec -= 1000000; - } -} - - -/* t1 = t2 - t3 - */ -static void -timevalsub(struct timeval *t1, - struct timeval *t2, - struct timeval *t3) -{ - t1->tv_sec = t2->tv_sec - t3->tv_sec; - if ((t1->tv_usec = t2->tv_usec - t3->tv_usec) < 0) { - t1->tv_sec--; - t1->tv_usec += 1000000; - } -} - - -void -msglog(char *p, ...) -{ - va_list args; - - trace_flush(); - - va_start(args, p); - vsyslog(LOG_ERR, p, args); - - if (ftrace != 0) { - if (ftrace == stdout) - (void)fputs("routed: ", ftrace); - (void)vfprintf(ftrace, p, args); - (void)fputc('\n', ftrace); - } -} - - -void -logbad(int dump, char *p, ...) -{ - va_list args; - - trace_flush(); - - va_start(args, p); - vsyslog(LOG_ERR, p, args); - - (void)fputs("routed: ", stderr); - (void)vfprintf(stderr, p, args); - (void)fputs("; giving up\n",stderr); - (void)fflush(stderr); - - if (dump) - abort(); - exit(1); -} diff --git a/usr.sbin/routed/output.c b/usr.sbin/routed/output.c deleted file mode 100644 index be30609743a7..000000000000 --- a/usr.sbin/routed/output.c +++ /dev/null @@ -1,858 +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. - */ - -#if !defined(lint) && !defined(sgi) && !defined(__NetBSD__) -static char sccsid[] = "@(#)output.c 8.1 (Berkeley) 6/5/93"; -#elif defined(__NetBSD__) -static char rcsid[] = "$NetBSD$"; -#endif -#ident "$Revision: 1.16 $" - -#include "defs.h" - - -int update_seqno; - - -/* walk the tree of routes with this for output - */ -struct { - struct sockaddr_in to; - naddr to_mask; - naddr to_net; - naddr to_std_mask; - naddr to_std_net; - struct interface *ifp; /* usually output interface */ - struct ws_buf { /* info for each buffer */ - struct rip *buf; - struct netinfo *n; - struct netinfo *base; - struct netinfo *lim; - enum output_type type; - } v12, v2; - char metric; /* adjust metrics by interface */ - int npackets; - int gen_limit; - u_int state; -#define WS_ST_FLASH 0x001 /* send only changed routes */ -#define WS_ST_RIP2_SAFE 0x002 /* send RIPv2 safe for RIPv1 */ -#define WS_ST_RIP2_ALL 0x004 /* send full featured RIPv2 */ -#define WS_ST_AG 0x008 /* ok to aggregate subnets */ -#define WS_ST_SUPER_AG 0x010 /* ok to aggregate networks */ -#define WS_ST_SUB_AG 0x020 /* aggregate subnets in odd case */ -#define WS_ST_QUERY 0x040 /* responding to a query */ -#define WS_ST_TO_ON_NET 0x080 /* sending onto one of our nets */ -#define WS_ST_DEFAULT 0x100 /* faking a default */ -#define WS_ST_PM_RDISC 0x200 /* poor-man's router discovery */ -} ws; - -/* A buffer for what can be heard by both RIPv1 and RIPv2 listeners */ -union pkt_buf ripv12_buf; - -/* Another for only RIPv2 listeners */ -union pkt_buf rip_v2_buf; - - - -/* Send the contents of the global buffer via the non-multicast socket - */ -int /* <0 on failure */ -output(enum output_type type, - struct sockaddr_in *dst, /* send to here */ - struct interface *ifp, - struct rip *buf, - int size) /* this many bytes */ -{ - struct sockaddr_in sin; - int flags; - char *msg; - int res; - naddr tgt_mcast; - int soc; - int serrno; - - sin = *dst; - if (sin.sin_port == 0) - sin.sin_port = htons(RIP_PORT); -#ifdef _HAVE_SIN_LEN - if (sin.sin_len == 0) - sin.sin_len = sizeof(sin); -#endif - - soc = rip_sock; - flags = 0; - - switch (type) { - case OUT_QUERY: - msg = "Answer Query"; - if (soc < 0) - soc = ifp->int_rip_sock; - break; - case OUT_UNICAST: - msg = "Send"; - if (soc < 0) - soc = ifp->int_rip_sock; - flags = MSG_DONTROUTE; - break; - case OUT_BROADCAST: - if (ifp->int_if_flags & IFF_POINTOPOINT) { - msg = "Send"; - } else { - msg = "Send bcast"; - } - flags = MSG_DONTROUTE; - break; - case OUT_MULTICAST: - if (ifp->int_if_flags & IFF_POINTOPOINT) { - msg = "Send pt-to-pt"; - } else if (ifp->int_state & IS_DUP) { - trace_act("abort multicast output via %s" - " with duplicate address\n", - ifp->int_name); - return 0; - } else { - msg = "Send mcast"; - if (rip_sock_mcast != ifp) { -#ifdef MCAST_PPP_BUG - /* Do not specifiy the primary interface - * explicitly if we have the multicast - * point-to-point kernel bug, since the - * kernel will do the wrong thing if the - * local address of a point-to-point link - * is the same as the address of an ordinary - * interface. - */ - if (ifp->int_addr == myaddr) { - tgt_mcast = 0; - } else -#endif - tgt_mcast = ifp->int_addr; - if (0 > setsockopt(rip_sock, - IPPROTO_IP, IP_MULTICAST_IF, - &tgt_mcast, - sizeof(tgt_mcast))) { - serrno = errno; - LOGERR("setsockopt(rip_sock," - "IP_MULTICAST_IF)"); - errno = serrno; - ifp = 0; - return -1; - } - rip_sock_mcast = ifp; - } - sin.sin_addr.s_addr = htonl(INADDR_RIP_GROUP); - } - - case NO_OUT_MULTICAST: - case NO_OUT_RIPV2: - break; - } - - trace_rip(msg, "to", &sin, ifp, buf, size); - - res = sendto(soc, buf, size, flags, - (struct sockaddr *)&sin, sizeof(sin)); - if (res < 0 - && (ifp == 0 || !(ifp->int_state & IS_BROKE))) { - serrno = errno; - msglog("%s sendto(%s%s%s.%d): %s", msg, - ifp != 0 ? ifp->int_name : "", - ifp != 0 ? ", " : "", - inet_ntoa(sin.sin_addr), - ntohs(sin.sin_port), - strerror(errno)); - errno = serrno; - } - - return res; -} - - -/* install authentication if appropriate - */ -static void -set_auth(struct ws_buf *w) -{ - if (ws.ifp != 0 - && ws.ifp->int_passwd[0] != '\0' - && (ws.state & WS_ST_RIP2_SAFE)) { - w->n->n_family = RIP_AF_AUTH; - ((struct netauth*)w->n)->a_type = RIP_AUTH_PW; - bcopy(ws.ifp->int_passwd, ((struct netauth*)w->n)->au.au_pw, - sizeof(((struct netauth*)w->n)->au.au_pw)); - w->n++; - } -} - - -/* Send the buffer - */ -static void -supply_write(struct ws_buf *wb) -{ - /* Output multicast only if legal. - * If we would multcast and it would be illegal, then discard the - * packet. - */ - switch (wb->type) { - case NO_OUT_MULTICAST: - trace_pkt("skip multicast to %s because impossible\n", - naddr_ntoa(ws.to.sin_addr.s_addr)); - break; - case NO_OUT_RIPV2: - break; - default: - if (output(wb->type, &ws.to, ws.ifp, wb->buf, - ((char *)wb->n - (char*)wb->buf)) < 0 - && ws.ifp != 0) - if_sick(ws.ifp); - ws.npackets++; - break; - } - - bzero(wb->n = wb->base, sizeof(*wb->n)*NETS_LEN); - if (wb->buf->rip_vers == RIPv2) - set_auth(wb); -} - - -/* put an entry into the packet - */ -static void -supply_out(struct ag_info *ag) -{ - int i; - naddr mask, v1_mask, s_mask, dst_h, ddst_h; - struct ws_buf *wb; - - - /* Skip this route if doing a flash update and it and the routes - * it aggregates have not changed recently. - */ - if (ag->ag_seqno < update_seqno - && (ws.state & WS_ST_FLASH)) - return; - - /* Skip this route if required by split-horizon - */ - if (ag->ag_state & AGS_SPLIT_HZ) - return; - - dst_h = ag->ag_dst_h; - mask = ag->ag_mask; - v1_mask = ripv1_mask_host(htonl(dst_h), - (ws.state & WS_ST_TO_ON_NET) ? ws.ifp : 0); - s_mask = std_mask(htonl(dst_h)); - i = 0; - - /* If we are sending RIPv2 packets that cannot (or must not) be - * heard by RIPv1 listeners, do not worry about sub- or supernets. - * Subnets (from other networks) can only be sent via multicast. - * A pair of subnet routes might have been promoted so that they - * are legal to send by RIPv1. - * If RIPv1 is off, use the multicast buffer, unless this is the - * fake default route and it is acting as a poor-man's router- - * discovery mechanism. - */ - if (((ws.state & WS_ST_RIP2_ALL) - && (dst_h != RIP_DEFAULT || !(ws.state & WS_ST_PM_RDISC))) - || ((ag->ag_state & AGS_RIPV2) && v1_mask != mask)) { - /* use the RIPv2-only buffer */ - wb = &ws.v2; - - } else { - /* use the RIPv1-or-RIPv2 buffer */ - wb = &ws.v12; - - /* Convert supernet route into corresponding set of network - * routes for RIPv1, but leave non-contiguous netmasks - * to ag_check(). - */ - if (v1_mask > mask - && mask + (mask & -mask) == 0) { - ddst_h = v1_mask & -v1_mask; - i = (v1_mask & ~mask)/ddst_h; - - if (i > ws.gen_limit) { - /* Punt if we would have to generate an - * unreasonable number of routes. - */ -#ifdef DEBUG - msglog("sending %s to %s as 1 instead" - " of %d routes", - addrname(htonl(dst_h),mask,1), - naddr_ntoa(ws.to.sin_addr.s_addr), - i+1); -#endif - i = 0; - - } else { - mask = v1_mask; - ws.gen_limit -= i; - } - } - } - - do { - wb->n->n_family = RIP_AF_INET; - wb->n->n_dst = htonl(dst_h); - /* If the route is from router-discovery or we are - * shutting down, admit only a bad metric. - */ - wb->n->n_metric = ((stopint || ag->ag_metric < 1) - ? HOPCNT_INFINITY - : ag->ag_metric); - HTONL(wb->n->n_metric); - if (wb->buf->rip_vers == RIPv2) { - if (ag->ag_nhop != 0 - && (ws.state & WS_ST_RIP2_SAFE) - && ((ws.state & WS_ST_QUERY) - || (ag->ag_nhop != ws.ifp->int_addr - && on_net(ag->ag_nhop, - ws.ifp->int_net, - ws.ifp->int_mask)))) - wb->n->n_nhop = ag->ag_nhop; - if ((ws.state & WS_ST_RIP2_ALL) - || mask != s_mask) - wb->n->n_mask = htonl(mask); - wb->n->n_tag = ag->ag_tag; - } - dst_h += ddst_h; - - if (++wb->n >= wb->lim) - supply_write(wb); - } while (i-- != 0); -} - - -/* supply one route from the table - */ -/* ARGSUSED */ -static int -walk_supply(struct radix_node *rn, - struct walkarg *w) -{ -#define RT ((struct rt_entry *)rn) - u_short ags = 0; - char metric, pref; - 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_state & RS_MHOME)) - return 0; - - /* If being quiet about our ability to forward, then - * do not say anything unless responding to a query. - */ - if (!supplier && !(ws.state & WS_ST_QUERY)) - return 0; - - dst = RT->rt_dst; - - /* do not collide with the fake default route */ - if (dst == RIP_DEFAULT - && (ws.state & WS_ST_DEFAULT)) - return 0; - - if (RT->rt_state & RS_NET_SYN) { - if (RT->rt_state & RS_NET_INT) { - /* Do not send manual synthetic network routes - * into the subnet. - */ - if (on_net(ws.to.sin_addr.s_addr, - ntohl(dst), RT->rt_mask)) - return 0; - - } else { - /* Do not send automatic synthetic network routes - * if they are not needed becaus no RIPv1 listeners - * can hear them. - */ - if (ws.state & WS_ST_RIP2_ALL) - return 0; - - /* Do not send automatic synthetic network routes to - * the real subnet. - */ - if (on_net(ws.to.sin_addr.s_addr, - ntohl(dst), RT->rt_mask)) - return 0; - } - nhop = 0; - - } else { - /* Advertise the next hop if this is not a route for one - * of our interfaces and the next hop is on the same - * network as the target. - */ - if (!(RT->rt_state & RS_IF) - && RT->rt_gate != myaddr - && RT->rt_gate != loopaddr) - nhop = RT->rt_gate; - else - nhop = 0; - } - - /* 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 in general, then do so. - * Avoid advertising host routes onto their own network, - * where they should be handled by proxy-ARP. - */ - if ((RT->rt_state & RS_LOCAL) - || ridhosts - || (ws.state & WS_ST_SUPER_AG) - || on_net(dst, ws.to_net, ws.to_mask)) - ags |= AGS_SUPPRESS; - - if (ws.state & WS_ST_SUPER_AG) - ags |= AGS_PROMOTE; - - } else if (ws.state & WS_ST_AG) { - /* Aggregate network routes, if we are allowed. - */ - ags |= AGS_SUPPRESS; - - /* Generate supernets if allowed. - * If we can be heard by RIPv1 systems, we will - * later convert back to ordinary nets. - * This unifies dealing with received supernets. - */ - if ((RT->rt_state & RS_SUBNET) - || (ws.state & WS_ST_SUPER_AG)) - ags |= AGS_PROMOTE; - - } - - /* Do not send RIPv1 advertisements of subnets to other - * networks. If possible, multicast them by RIPv2. - */ - if ((RT->rt_state & RS_SUBNET) - && !(ws.state & WS_ST_RIP2_ALL) - && !on_net(dst, ws.to_std_net, ws.to_std_mask)) { - ags |= AGS_RIPV2 | AGS_PROMOTE; - if (ws.state & WS_ST_SUB_AG) - ags |= AGS_SUPPRESS; - } - - /* Do not send a route back to where it came from, except in - * response to a query. This is "split-horizon". That means not - * advertising back to the same network and so via the same interface. - * - * We want to suppress routes that might have been fragmented - * from this route by a RIPv1 router and sent back to us, and so we - * cannot forget this route here. Let the split-horizon route - * aggregate (suppress) the fragmented routes and then itself be - * forgotten. - * - * Include the routes for both ends of point-to-point interfaces - * since the other side presumably knows them as well as we do. - */ - if (RT->rt_ifp == ws.ifp && ws.ifp != 0 - && !(ws.state & WS_ST_QUERY) - && (ws.state & WS_ST_TO_ON_NET) - && (!(RT->rt_state & RS_IF) - || ws.ifp->int_if_flags & IFF_POINTOPOINT)) { - ags |= AGS_SPLIT_HZ; - ags &= ~(AGS_PROMOTE | AGS_SUPPRESS); - } - - ag_check(dst, RT->rt_mask, 0, nhop, metric, pref, - RT->rt_seqno, RT->rt_tag, ags, supply_out); - return 0; -#undef RT -} - - -/* Supply dst with the contents of the routing tables. - * If this won't fit in one packet, chop it up into several. - */ -void -supply(struct sockaddr_in *dst, - struct interface *ifp, /* output interface */ - enum output_type type, - int flash, /* 1=flash update */ - int vers) /* RIP version */ -{ - static int init = 1; - struct rt_entry *rt; - - - ws.state = 0; - ws.gen_limit = 1024; - - ws.to = *dst; - ws.to_std_mask = std_mask(ws.to.sin_addr.s_addr); - ws.to_std_net = ntohl(ws.to.sin_addr.s_addr) & ws.to_std_mask; - - if (ifp != 0) { - ws.to_mask = ifp->int_mask; - ws.to_net = ifp->int_net; - if (on_net(ws.to.sin_addr.s_addr, ws.to_net, ws.to_mask)) - ws.state |= WS_ST_TO_ON_NET; - - } else { - ws.to_mask = ripv1_mask_net(ws.to.sin_addr.s_addr, 0); - ws.to_net = ntohl(ws.to.sin_addr.s_addr) & ws.to_mask; - rt = rtfind(dst->sin_addr.s_addr); - if (rt) - ifp = rt->rt_ifp; - } - - ws.npackets = 0; - if (flash) - ws.state |= WS_ST_FLASH; - if (type == OUT_QUERY) - ws.state |= WS_ST_QUERY; - - if ((ws.ifp = ifp) == 0) { - ws.metric = 1; - } else { - /* Adjust the advertised metric by the outgoing interface - * metric. - */ - ws.metric = ifp->int_metric+1; - } - - if (init) { - init = 0; - - bzero(&ripv12_buf, sizeof(ripv12_buf)); - ripv12_buf.rip.rip_cmd = RIPCMD_RESPONSE; - ws.v12.buf = &ripv12_buf.rip; - ws.v12.base = &ws.v12.buf->rip_nets[0]; - ws.v12.lim = ws.v12.base + NETS_LEN; - - bzero(&rip_v2_buf, sizeof(rip_v2_buf)); - rip_v2_buf.rip.rip_cmd = RIPCMD_RESPONSE; - rip_v2_buf.rip.rip_vers = RIPv2; - ws.v2.buf = &rip_v2_buf.rip; - ws.v2.base = &ws.v2.buf->rip_nets[0]; - ws.v2.lim = ws.v2.base + NETS_LEN; - } - ripv12_buf.rip.rip_vers = vers; - - ws.v12.n = ws.v12.base; - set_auth(&ws.v12); - ws.v2.n = ws.v2.base; - set_auth(&ws.v2); - - switch (type) { - case OUT_BROADCAST: - ws.v2.type = ((ws.ifp != 0 - && (ws.ifp->int_if_flags & IFF_MULTICAST)) - ? OUT_MULTICAST - : NO_OUT_MULTICAST); - ws.v12.type = OUT_BROADCAST; - break; - case OUT_MULTICAST: - ws.v2.type = ((ws.ifp != 0 - && (ws.ifp->int_if_flags & IFF_MULTICAST)) - ? OUT_MULTICAST - : NO_OUT_MULTICAST); - ws.v12.type = OUT_BROADCAST; - break; - case OUT_UNICAST: - case OUT_QUERY: - ws.v2.type = (vers == RIPv2) ? type : NO_OUT_RIPV2; - ws.v12.type = type; - break; - default: - ws.v2.type = type; - ws.v12.type = type; - break; - } - - if (vers == RIPv2) { - /* if asked to send RIPv2, send at least that which can - * be safely heard by RIPv1 listeners. - */ - ws.state |= WS_ST_RIP2_SAFE; - - /* full RIPv2 only if cannot be heard by RIPv1 listeners */ - if (type != OUT_BROADCAST) - ws.state |= WS_ST_RIP2_ALL; - if (!(ws.state & WS_ST_TO_ON_NET)) { - ws.state |= (WS_ST_AG | WS_ST_SUPER_AG); - } else if (ws.ifp == 0 || !(ws.ifp->int_state & IS_NO_AG)) { - ws.state |= WS_ST_AG; - if (type != OUT_BROADCAST - && (ws.ifp == 0 - || !(ws.ifp->int_state & IS_NO_SUPER_AG))) - ws.state |= WS_ST_SUPER_AG; - } - - } else if (ws.ifp == 0 || !(ws.ifp->int_state & IS_NO_AG)) { - ws.state |= WS_ST_SUB_AG; - } - - if (supplier) { - /* Fake a default route if asked, and if there is not - * a better, real default route. - */ - if (ifp->int_d_metric != 0 - && (0 == (rt = rtget(RIP_DEFAULT, 0)) - || rt->rt_metric+ws.metric >= ifp->int_d_metric)) { - ws.state |= WS_ST_DEFAULT; - ag_check(0, 0, 0, 0, - ifp->int_d_metric,ifp->int_d_metric, - 0, 0, 0, supply_out); - } - if ((ws.state & WS_ST_RIP2_ALL) - && (ifp->int_state & IS_PM_RDISC)) { - ws.state |= WS_ST_PM_RDISC; - ripv12_buf.rip.rip_vers = RIPv1; - } - } - - (void)rn_walktree(rhead, walk_supply, 0); - ag_flush(0,0,supply_out); - - /* Flush the packet buffers, provided they are not empty and - * do not contain only the password. - */ - if (ws.v12.n != ws.v12.base - && (ws.v12.n > ws.v12.base+1 - || ws.v12.n->n_family != RIP_AF_AUTH)) - supply_write(&ws.v12); - if (ws.v2.n != ws.v2.base - && (ws.v2.n > ws.v2.base+1 - || ws.v2.n->n_family != RIP_AF_AUTH)) - supply_write(&ws.v2); - - /* If we sent nothing and this is an answer to a query, send - * an empty buffer. - */ - if (ws.npackets == 0 - && (ws.state & WS_ST_QUERY)) - supply_write(&ws.v12); -} - - -/* send all of the routing table or just do a flash update - */ -void -rip_bcast(int flash) -{ -#ifdef _HAVE_SIN_LEN - static struct sockaddr_in dst = {sizeof(dst), AF_INET}; -#else - static struct sockaddr_in dst = {AF_INET}; -#endif - struct interface *ifp; - enum output_type type; - int vers; - struct timeval rtime; - - - need_flash = 0; - intvl_random(&rtime, MIN_WAITTIME, MAX_WAITTIME); - no_flash = rtime; - timevaladd(&no_flash, &now); - - if (rip_sock < 0) - return; - - trace_act("send %s and inhibit dynamic updates for %.3f sec\n", - flash ? "dynamic update" : "all routes", - rtime.tv_sec + ((float)rtime.tv_usec)/1000000.0); - - for (ifp = ifnet; ifp != 0; ifp = ifp->int_next) { - /* skip interfaces not doing RIP, those already queried, - * and aliases. Do try broken interfaces to see - * if they have healed. - */ - if (0 != (ifp->int_state & (IS_PASSIVE | IS_ALIAS))) - continue; - - /* skip turned off interfaces */ - if (!iff_alive(ifp->int_if_flags)) - continue; - - /* default to RIPv1 output */ - if (ifp->int_state & IS_NO_RIPV1_OUT) { - /* Say nothing if this interface is turned off */ - if (ifp->int_state & IS_NO_RIPV2_OUT) - continue; - vers = RIPv2; - } else { - vers = RIPv1; - } - - if (ifp->int_if_flags & IFF_BROADCAST) { - /* ordinary, hardware interface */ - dst.sin_addr.s_addr = ifp->int_brdaddr; - /* if RIPv1 is not turned off, then broadcast so - * that RIPv1 listeners can hear. - */ - if (vers == RIPv2 - && (ifp->int_state & IS_NO_RIPV1_OUT)) { - type = OUT_MULTICAST; - } else { - type = OUT_BROADCAST; - } - - } else if (ifp->int_if_flags & IFF_POINTOPOINT) { - /* point-to-point hardware interface */ - dst.sin_addr.s_addr = ifp->int_dstaddr; - type = OUT_UNICAST; - - } else { - /* remote interface */ - dst.sin_addr.s_addr = ifp->int_addr; - type = OUT_UNICAST; - } - - supply(&dst, ifp, type, flash, vers); - } - - update_seqno++; /* all routes are up to date */ -} - - -/* Ask for routes - * Do it only once to an interface, and not even after the interface - * was broken and recovered. - */ -void -rip_query(void) -{ -#ifdef _HAVE_SIN_LEN - static struct sockaddr_in dst = {sizeof(dst), AF_INET}; -#else - static struct sockaddr_in dst = {AF_INET}; -#endif - struct interface *ifp; - struct rip buf; - enum output_type type; - - - if (rip_sock < 0) - return; - - bzero(&buf, sizeof(buf)); - - for (ifp = ifnet; ifp; ifp = ifp->int_next) { - /* skip interfaces not doing RIP, those already queried, - * and aliases. Do try broken interfaces to see - * if they have healed. - */ - if (0 != (ifp->int_state & (IS_RIP_QUERIED - | IS_PASSIVE | IS_ALIAS))) - continue; - - /* skip turned off interfaces */ - if (!iff_alive(ifp->int_if_flags)) - continue; - - /* default to RIPv1 output */ - if (ifp->int_state & IS_NO_RIPV2_OUT) { - /* Say nothing if this interface is turned off */ - if (ifp->int_state & IS_NO_RIPV1_OUT) - continue; - buf.rip_vers = RIPv1; - } else { - buf.rip_vers = RIPv2; - } - - buf.rip_cmd = RIPCMD_REQUEST; - buf.rip_nets[0].n_family = RIP_AF_UNSPEC; - buf.rip_nets[0].n_metric = htonl(HOPCNT_INFINITY); - - if (ifp->int_if_flags & IFF_BROADCAST) { - /* ordinary, hardware interface */ - dst.sin_addr.s_addr = ifp->int_brdaddr; - /* if RIPv1 is not turned off, then broadcast so - * that RIPv1 listeners can hear. - */ - if (buf.rip_vers == RIPv2 - && (ifp->int_state & IS_NO_RIPV1_OUT)) { - type = OUT_MULTICAST; - } else { - type = OUT_BROADCAST; - } - - } else if (ifp->int_if_flags & IFF_POINTOPOINT) { - /* point-to-point hardware interface */ - dst.sin_addr.s_addr = ifp->int_dstaddr; - type = OUT_UNICAST; - - } else { - /* remote interface */ - dst.sin_addr.s_addr = ifp->int_addr; - type = OUT_UNICAST; - } - - ifp->int_state |= IS_RIP_QUERIED; - if (output(type, &dst, ifp, &buf, sizeof(buf)) < 0) - if_sick(ifp); - } -} diff --git a/usr.sbin/routed/parms.c b/usr.sbin/routed/parms.c deleted file mode 100644 index fcd37a6b6c3a..000000000000 --- a/usr.sbin/routed/parms.c +++ /dev/null @@ -1,622 +0,0 @@ -/* - * Copyright (c) 1983, 1993 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#if !defined(lint) && !defined(sgi) && !defined(__NetBSD__) -static char sccsid[] = "@(#)if.c 8.1 (Berkeley) 6/5/93"; -#elif defined(__NetBSD__) -static char rcsid[] = "$NetBSD$"; -#endif -#ident "$Revision: 1.8 $" - -#include "defs.h" -#include "pathnames.h" - - -struct parm *parms; -struct intnet *intnets; - - -/* use configured parameters - */ -void -get_parms(struct interface *ifp) -{ - struct parm *parmp; - - /* get all relevant parameters - */ - for (parmp = parms; parmp != 0; parmp = parmp->parm_next) { - if ((parmp->parm_name[0] == '\0' - && on_net(ifp->int_addr, - parmp->parm_addr_h, parmp->parm_mask)) - || (parmp->parm_name[0] != '\0' - && !strcmp(ifp->int_name, parmp->parm_name))) { - /* this group of parameters is relevant, - * so get its settings - */ - ifp->int_state |= parmp->parm_int_state; - if (parmp->parm_passwd[0] != '\0') - bcopy(parmp->parm_passwd, ifp->int_passwd, - sizeof(ifp->int_passwd)); - if (parmp->parm_rdisc_pref != 0) - ifp->int_rdisc_pref = parmp->parm_rdisc_pref; - if (parmp->parm_rdisc_int != 0) - ifp->int_rdisc_int = parmp->parm_rdisc_int; - if (parmp->parm_d_metric != 0) - ifp->int_d_metric = parmp->parm_d_metric; - } - } - /* default poor-man's router discovery to a metric that will - * be heard by old versions of routed. - */ - if ((ifp->int_state & IS_PM_RDISC) - && ifp->int_d_metric == 0) - ifp->int_d_metric = HOPCNT_INFINITY-2; - - if (IS_RIP_IN_OFF(ifp->int_state)) - ifp->int_state |= IS_NO_RIP_OUT; - - if (ifp->int_rdisc_int == 0) - ifp->int_rdisc_int = DefMaxAdvertiseInterval; - - if (!(ifp->int_if_flags & IFF_MULTICAST) - && !(ifp->int_if_flags & IFF_POINTOPOINT)) - ifp->int_state |= IS_NO_RIPV2_OUT; - - if (!(ifp->int_if_flags & IFF_MULTICAST)) - ifp->int_state |= IS_BCAST_RDISC; - - if (ifp->int_if_flags & IFF_POINTOPOINT) { - ifp->int_state |= IS_BCAST_RDISC; - /* By default, point-to-point links should be passive - * about router-discovery for the sake of demand-dialing. - */ - if (0 == (ifp->int_state & GROUP_IS_SOL)) - ifp->int_state |= IS_NO_SOL_OUT; - if (0 == (ifp->int_state & GROUP_IS_ADV)) - ifp->int_state |= IS_NO_ADV_OUT; - } - - if (0 != (ifp->int_state & (IS_PASSIVE | IS_REMOTE))) - ifp->int_state |= IS_NO_RDISC; - if (ifp->int_state & IS_PASSIVE) - ifp->int_state |= (IS_NO_RIP | IS_NO_RDISC); - if ((ifp->int_state & (IS_NO_RIP | IS_NO_RDISC)) - == (IS_NO_RIP|IS_NO_RDISC)) - ifp->int_state |= IS_PASSIVE; -} - - -/* Read a list of gateways from /etc/gateways and add them to our tables. - * - * This file contains a list of "remote" gateways. That is usually - * a gateway which we cannot immediately determine if it is present or - * not as we can do for those provided by directly connected hardware. - * - * If a gateway is marked "passive" in the file, then we assume it - * does not understand RIP and assume it is always present. Those - * not marked passive are treated as if they were directly connected - * and assumed to be broken if they do not send us advertisements. - * All remote interfaces are added to our list, and those not marked - * passive are sent routing updates. - * - * A passive interface can also be local, hardware interface exempt - * from RIP. - */ -void -gwkludge(void) -{ - FILE *fp; - char *p, *lptr; - char lbuf[200], net_host[5], dname[64+1+64+1], gname[64+1], qual[9]; - struct interface *ifp; - naddr dst, netmask, gate; - int metric, n; - u_int state; - char *type; - struct parm *parmp; - - - fp = fopen(_PATH_GATEWAYS, "r"); - if (fp == 0) - return; - - for (;;) { - if (0 == fgets(lbuf, sizeof(lbuf)-1, fp)) - break; - lptr = lbuf; - while (*lptr == ' ') - lptr++; - if (*lptr == '\n' /* ignore null and comment lines */ - || *lptr == '#') - continue; - p = lptr+strlen(lptr)-1; - while (*p == '\n' - || *p == ' ') - *p-- = '\0'; - - /* notice newfangled parameter lines - */ - if (strncasecmp("net", lptr, 3) - && strncasecmp("host", lptr, 4)) { - p = parse_parms(lptr); - if (p != 0) { - if (strcmp(p,lptr)) - msglog("bad \"%s\" in "_PATH_GATEWAYS - " entry \"%s\"", lptr, p); - else - msglog("bad \"%s\" in "_PATH_GATEWAYS, - lptr); - } - continue; - } - -/* {net | host} XX[/M] XX gateway XX metric DD [passive | external]\n */ - n = sscanf(lptr, "%4s %129[^ \t] gateway" - " %64[^ / \t] metric %d %8s\n", - net_host, dname, gname, &metric, qual); - if (n != 5) { - msglog("bad "_PATH_GATEWAYS" entry \"%s\"", lptr); - continue; - } - if (metric < 0 || metric >= HOPCNT_INFINITY) { - msglog("bad metric in "_PATH_GATEWAYS" entry \"%s\"", - lptr); - continue; - } - if (!strcmp(net_host, "host")) { - if (!gethost(dname, &dst)) { - msglog("bad host \"%s\" in "_PATH_GATEWAYS - " entry \"%s\"", dname, lptr); - continue; - } - netmask = HOST_MASK; - } else if (!strcmp(net_host, "net")) { - if (!getnet(dname, &dst, &netmask)) { - msglog("bad net \"%s\" in "_PATH_GATEWAYS - " entry \"%s\"", dname, lptr); - continue; - } - } else { - msglog("bad \"%s\" in "_PATH_GATEWAYS - " entry \"%s\"", lptr); - continue; - } - - if (!gethost(gname, &gate)) { - msglog("bad gateway \"%s\" in "_PATH_GATEWAYS - " entry \"%s\"", gname, lptr); - continue; - } - - if (strcmp(qual, type = "passive") == 0) { - /* Passive entries are not placed in our tables, - * only the kernel's, so we don't copy all of the - * external routing information within a net. - * Internal machines should use the default - * route to a suitable gateway (like us). - */ - state = IS_REMOTE | IS_PASSIVE; - if (metric == 0) - metric = 1; - - } else if (strcmp(qual, type = "external") == 0) { - /* External entries are handled by other means - * such as EGP, and are placed only in the daemon - * tables to prevent overriding them with something - * else. - */ - state = IS_REMOTE | IS_PASSIVE | IS_EXTERNAL; - if (metric == 0) - metric = 1; - - } else if (qual[0] == '\0') { - if (metric != 0) { - /* Entries that are neither "passive" nor - * "external" are "remote" and must behave - * like physical interfaces. If they are not - * heard from regularly, they are deleted. - */ - state = IS_REMOTE; - type = "remote"; - } else { - /* "remote" entries with a metric of 0 - * are aliases for our own interfaces - */ - state = IS_REMOTE | IS_PASSIVE; - type = "alias"; - } - - } else { - msglog("bad "_PATH_GATEWAYS" entry \"%s\"", lptr); - continue; - } - - /* Remember to advertise the corresponding logical network. - */ - if (!(state & IS_EXTERNAL) - && netmask != std_mask(dst)) - state |= IS_SUBNET; - - if (0 != (state & (IS_PASSIVE | IS_REMOTE))) - state |= IS_NO_RDISC; - if (state & IS_PASSIVE) - state |= (IS_NO_RIP | IS_NO_RDISC); - if ((state & (IS_NO_RIP | IS_NO_RDISC)) - == (IS_NO_RIP|IS_NO_RDISC)) - state |= IS_PASSIVE; - - parmp = (struct parm*)malloc(sizeof(*parmp)); - bzero(parmp, sizeof(*parmp)); - parmp->parm_next = parms; - parms = parmp; - parmp->parm_addr_h = ntohl(dst); - parmp->parm_mask = -1; - parmp->parm_d_metric = 0; - parmp->parm_int_state = state; - - /* See if this new interface duplicates an existing - * interface. - */ - for (ifp = ifnet; 0 != ifp; ifp = ifp->int_next) { - if (ifp->int_mask == netmask - && ((ifp->int_addr == dst - && netmask != HOST_MASK) - || (ifp->int_dstaddr == dst - && netmask == HOST_MASK))) - break; - } - if (ifp != 0) { - /* Let one of our real interfaces be marked passive. - */ - if ((state & IS_PASSIVE) && !(state & IS_EXTERNAL)) { - ifp->int_state |= state; - } else { - msglog("%s is duplicated in "_PATH_GATEWAYS - " by %s", - ifp->int_name, lptr); - } - continue; - } - - tot_interfaces++; - - ifp = (struct interface *)malloc(sizeof(*ifp)); - bzero(ifp, sizeof(*ifp)); - if (ifnet != 0) { - ifp->int_next = ifnet; - ifnet->int_prev = ifp; - } - ifnet = ifp; - - ifp->int_state = state; - ifp->int_net = ntohl(dst) & netmask; - ifp->int_mask = netmask; - if (netmask == HOST_MASK) - ifp->int_if_flags |= IFF_POINTOPOINT; - ifp->int_dstaddr = dst; - ifp->int_addr = gate; - ifp->int_metric = metric; - (void)sprintf(ifp->int_name, "%s-%s", type, naddr_ntoa(dst)); - ifp->int_index = -1; - - get_parms(ifp); - - trace_if("Add", ifp); - } -} - - -/* parse a set of parameters for an interface - */ -char * /* 0 or error message */ -parse_parms(char *line) -{ -#define PARS(str) (0 == (tgt = str, strcasecmp(tok, tgt))) -#define PARSE(str) (0 == (tgt = str, strncasecmp(tok, str "=", sizeof(str)))) -#define CKF(g,b) {if (0 != (parm.parm_int_state & ((g) & ~(b)))) break; \ - parm.parm_int_state |= (b);} -#define DELIMS " ,\t\n" - struct parm parm; - struct intnet *intnetp; - char *tok, *tgt, *p; - - - /* "subnet=x.y.z.u/mask" must be alone on the line */ - if (!strncasecmp("subnet=",line,7)) { - intnetp = (struct intnet*)malloc(sizeof(*intnetp)); - intnetp->intnet_metric = 1; - if ((p = strrchr(line,','))) { - *p++ = '\0'; - intnetp->intnet_metric = (int)strtol(p,&p,0); - if (*p != '\0' - || intnetp->intnet_metric <= 0 - || intnetp->intnet_metric >= HOPCNT_INFINITY) - return line; - } - if (!getnet(&line[7], &intnetp->intnet_addr, - &intnetp->intnet_mask) - || intnetp->intnet_mask == HOST_MASK - || intnetp->intnet_addr == RIP_DEFAULT) { - free(intnetp); - return line; - } - intnetp->intnet_next = intnets; - intnets = intnetp; - return 0; - } - - bzero(&parm, sizeof(parm)); - - tgt = "null"; - for (tok = strtok(line, DELIMS); - tok != 0 && tok[0] != '\0'; - tgt = 0, tok = strtok(0,DELIMS)) { - if (PARSE("if")) { - if (parm.parm_name[0] != '\0' - || tok[3] == '\0' - || strlen(tok) > IFNAMSIZ+3) - break; - strcpy(parm.parm_name, tok+3); - - } else if (PARSE("passwd")) { - if (tok[7] == '\0' - || strlen(tok) > RIP_AUTH_PW_LEN+7) - break; - strcpy(parm.parm_passwd, tok+7); - - } else if (PARS("no_ag")) { - parm.parm_int_state |= (IS_NO_AG | IS_NO_SUPER_AG); - - } else if (PARS("no_super_ag")) { - parm.parm_int_state |= IS_NO_SUPER_AG; - - } else if (PARS("no_ripv1_in")) { - parm.parm_int_state |= IS_NO_RIPV1_IN; - - } else if (PARS("no_ripv2_in")) { - parm.parm_int_state |= IS_NO_RIPV2_IN; - - } else if (PARS("ripv2_out")) { - if (parm.parm_int_state & IS_NO_RIPV2_OUT) - break; - parm.parm_int_state |= IS_NO_RIPV1_OUT; - - } else if (PARS("no_rip")) { - parm.parm_int_state |= IS_NO_RIP; - - } else if (PARS("no_rdisc")) { - CKF((GROUP_IS_SOL|GROUP_IS_ADV), IS_NO_RDISC); - - } else if (PARS("no_solicit")) { - CKF(GROUP_IS_SOL, IS_NO_SOL_OUT); - - } else if (PARS("send_solicit")) { - CKF(GROUP_IS_SOL, IS_SOL_OUT); - - } else if (PARS("no_rdisc_adv")) { - CKF(GROUP_IS_ADV, IS_NO_ADV_OUT); - - } else if (PARS("rdisc_adv")) { - CKF(GROUP_IS_ADV, IS_ADV_OUT); - - } else if (PARS("bcast_rdisc")) { - parm.parm_int_state |= IS_BCAST_RDISC; - - } else if (PARS("passive")) { - CKF((GROUP_IS_SOL|GROUP_IS_ADV), IS_NO_RDISC); - parm.parm_int_state |= IS_NO_RIP; - - } else if (PARSE("rdisc_pref")) { - if (parm.parm_rdisc_pref != 0 - || tok[11] == '\0' - || (parm.parm_rdisc_pref = (int)strtol(&tok[11], - &p,0), - *p != '\0')) - break; - - } else if (PARS("pm_rdisc")) { - parm.parm_int_state |= IS_PM_RDISC; - - } else if (PARSE("rdisc_interval")) { - if (parm.parm_rdisc_int != 0 - || tok[15] == '\0' - || (parm.parm_rdisc_int = (int)strtol(&tok[15], - &p,0), - *p != '\0') - || parm.parm_rdisc_int < MinMaxAdvertiseInterval - || parm.parm_rdisc_int > MaxMaxAdvertiseInterval) - break; - - } else if (PARSE("fake_default")) { - if (parm.parm_d_metric != 0 - || tok[13] == '\0' - || (parm.parm_d_metric=(int)strtol(&tok[13],&p,0), - *p != '\0') - || parm.parm_d_metric > HOPCNT_INFINITY-1) - break; - - } else { - tgt = tok; - break; - } - } - if (tgt != 0) - return tgt; - - 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 *addrp, /* host byte order */ - naddr *maskp) -{ - int i; - struct netent *np; - naddr mask; - struct in_addr in; - char hname[MAXHOSTNAMELEN+1]; - char *mname, *p; - - - /* Detect and separate "1.2.3.4/24" - */ - if (0 != (mname = rindex(name,'/'))) { - i = (int)(mname - name); - if (i > sizeof(hname)-1) /* name too long */ - return 0; - bcopy(name, hname, i); - hname[i] = '\0'; - mname++; - name = hname; - } - - np = getnetbyname(name); - if (np != 0) { - in.s_addr = (naddr)np->n_net; - } else if (inet_aton(name, &in) == 1) { - HTONL(in.s_addr); - } else { - return 0; - } - - if (mname == 0) { - /* we cannot use the interfaces here because we have not - * looked at them yet. - */ - mask = std_mask(in.s_addr); - if ((~mask & ntohl(in.s_addr)) != 0) - mask = HOST_MASK; - } else { - mask = (naddr)strtoul(mname, &p, 0); - if (*p != '\0' || mask > 32) - return 0; - mask = HOST_MASK << (32-mask); - } - if (mask != 0 && in.s_addr == RIP_DEFAULT) - return 0; - if ((~mask & ntohl(in.s_addr)) != 0) - return 0; - - *addrp = in.s_addr; - *maskp = mask; - return 1; -} - - -int /* 0=bad */ -gethost(char *name, - naddr *addrp) -{ - struct hostent *hp; - struct in_addr in; - - - /* Try for a number first, even in IRIX where gethostbyname() - * is smart. This avoids hitting the name server which - * might be sick because routing is. - */ - if (inet_aton(name, &in) == 1) { - *addrp = in.s_addr; - return 1; - } - - hp = gethostbyname(name); - if (hp) { - bcopy(hp->h_addr, addrp, sizeof(*addrp)); - return 1; - } - - return 0; -} diff --git a/usr.sbin/routed/pathnames.h b/usr.sbin/routed/pathnames.h deleted file mode 100644 index e9e6c48c6392..000000000000 --- a/usr.sbin/routed/pathnames.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 1989, 1993 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)pathnames.h 8.1 (Berkeley) 6/5/93 - * - * $NetBSD$ - */ - -#include - -#define _PATH_GATEWAYS "/etc/gateways" - -/* All remotely requested trace files must either start with this prefix - * or be the same as the tracefile specified when the daemon was started. - * If this is a directory, routed will create log files in it. That - * might be a security problem. - */ -#define _PATH_TRACE "/tmp/routed.log" diff --git a/usr.sbin/routed/radix.c b/usr.sbin/routed/radix.c deleted file mode 100644 index 7f7e1e47b79e..000000000000 --- a/usr.sbin/routed/radix.c +++ /dev/null @@ -1,895 +0,0 @@ -/* - * Copyright (c) 1988, 1989, 1993 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)radix.c 8.4 (Berkeley) 11/2/94 - */ - -/* - * Routines to build and maintain radix trees for routing lookups. - */ -#if !defined(lint) && !defined(sgi) && !defined(__NetBSD__) -static char sccsid[] = "@(#)rdisc.c 8.1 (Berkeley) x/y/95"; -#elif defined(__NetBSD__) -static char rcsid[] = "$NetBSD$"; -#endif -#ident "$Revision: 1.10 $" - -#include "defs.h" - -#define log(x, msg) syslog(x, msg) -#define panic(s) {log(LOG_ERR,s); exit(1);} -#define min(a,b) (((a)<(b))?(a):(b)) - -int max_keylen; -struct radix_mask *rn_mkfreelist; -struct radix_node_head *mask_rnhead; -static char *addmask_key; -static char normal_chars[] = {0, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, -1}; -static char *rn_zeros, *rn_ones; - -#define rn_masktop (mask_rnhead->rnh_treetop) -#undef Bcmp -#define Bcmp(a, b, l) (l == 0 ? 0 : bcmp((caddr_t)(a), (caddr_t)(b), (u_long)l)) - -static int rn_satsifies_leaf(char *, struct radix_node *, int); - -/* - * The data structure for the keys is a radix tree with one way - * branching removed. The index rn_b at an internal node n represents a bit - * position to be tested. The tree is arranged so that all descendants - * of a node n have keys whose bits all agree up to position rn_b - 1. - * (We say the index of n is rn_b.) - * - * There is at least one descendant which has a one bit at position rn_b, - * and at least one with a zero there. - * - * A route is determined by a pair of key and mask. We require that the - * bit-wise logical and of the key and mask to be the key. - * We define the index of a route to associated with the mask to be - * the first bit number in the mask where 0 occurs (with bit number 0 - * representing the highest order bit). - * - * We say a mask is normal if every bit is 0, past the index of the mask. - * If a node n has a descendant (k, m) with index(m) == index(n) == rn_b, - * and m is a normal mask, then the route applies to every descendant of n. - * If the index(m) < rn_b, this implies the trailing last few bits of k - * before bit b are all 0, (and hence consequently true of every descendant - * of n), so the route applies to all descendants of the node as well. - * - * Similar logic shows that a non-normal mask m such that - * index(m) <= index(n) could potentially apply to many children of n. - * Thus, for each non-host route, we attach its mask to a list at an internal - * node as high in the tree as we can go. - * - * The present version of the code makes use of normal routes in short- - * circuiting an explict mask and compare operation when testing whether - * a key satisfies a normal route, and also in remembering the unique leaf - * that governs a subtree. - */ - -struct radix_node * -rn_search(void *v_arg, - struct radix_node *head) -{ - register struct radix_node *x; - register caddr_t v; - - for (x = head, v = v_arg; x->rn_b >= 0;) { - if (x->rn_bmask & v[x->rn_off]) - x = x->rn_r; - else - x = x->rn_l; - } - return (x); -} - -struct radix_node * -rn_search_m(void *v_arg, - struct radix_node *head, - void *m_arg) -{ - register struct radix_node *x; - register caddr_t v = v_arg, m = m_arg; - - for (x = head; x->rn_b >= 0;) { - if ((x->rn_bmask & m[x->rn_off]) && - (x->rn_bmask & v[x->rn_off])) - x = x->rn_r; - else - x = x->rn_l; - } - return x; -} - -int -rn_refines(void* m_arg, void *n_arg) -{ - register caddr_t m = m_arg, n = n_arg; - register caddr_t lim, lim2 = lim = n + *(u_char *)n; - int longer = (*(u_char *)n++) - (int)(*(u_char *)m++); - int masks_are_equal = 1; - - if (longer > 0) - lim -= longer; - while (n < lim) { - if (*n & ~(*m)) - return 0; - if (*n++ != *m++) - masks_are_equal = 0; - } - while (n < lim2) - if (*n++) - return 0; - if (masks_are_equal && (longer < 0)) - for (lim2 = m - longer; m < lim2; ) - if (*m++) - return 1; - return (!masks_are_equal); -} - -struct radix_node * -rn_lookup(v_arg, m_arg, head) - void *v_arg, *m_arg; - struct radix_node_head *head; -{ - register struct radix_node *x; - caddr_t netmask = 0; - - if (m_arg) { - if ((x = rn_addmask(m_arg, 1, head->rnh_treetop->rn_off)) == 0) - return (0); - netmask = x->rn_key; - } - x = rn_match(v_arg, head); - if (x && netmask) { - while (x && x->rn_mask != netmask) - x = x->rn_dupedkey; - } - return x; -} - -static int -rn_satsifies_leaf(char *trial, - register struct radix_node *leaf, - int skip) -{ - register char *cp = trial, *cp2 = leaf->rn_key, *cp3 = leaf->rn_mask; - char *cplim; - int length = min(*(u_char *)cp, *(u_char *)cp2); - - if (cp3 == 0) - cp3 = rn_ones; - else - length = min(length, *(u_char *)cp3); - cplim = cp + length; cp3 += skip; cp2 += skip; - for (cp += skip; cp < cplim; cp++, cp2++, cp3++) - if ((*cp ^ *cp2) & *cp3) - return 0; - return 1; -} - -struct radix_node * -rn_match(void *v_arg, - struct radix_node_head *head) -{ - caddr_t v = v_arg; - register struct radix_node *t = head->rnh_treetop, *x; - register caddr_t cp = v, cp2; - caddr_t cplim; - struct radix_node *saved_t, *top = t; - int off = t->rn_off, vlen = *(u_char *)cp, matched_off; - register int test, b, rn_b; - - /* - * Open code rn_search(v, top) to avoid overhead of extra - * subroutine call. - */ - for (; t->rn_b >= 0; ) { - if (t->rn_bmask & cp[t->rn_off]) - t = t->rn_r; - else - t = t->rn_l; - } - /* - * See if we match exactly as a host destination - * or at least learn how many bits match, for normal mask finesse. - * - * It doesn't hurt us to limit how many bytes to check - * to the length of the mask, since if it matches we had a genuine - * match and the leaf we have is the most specific one anyway; - * if it didn't match with a shorter length it would fail - * with a long one. This wins big for class B&C netmasks which - * are probably the most common case... - */ - if (t->rn_mask) - vlen = *(u_char *)t->rn_mask; - cp += off; cp2 = t->rn_key + off; cplim = v + vlen; - for (; cp < cplim; cp++, cp2++) - if (*cp != *cp2) - goto on1; - /* - * This extra grot is in case we are explicitly asked - * to look up the default. Ugh! - * Or 255.255.255.255 - * - * In this case, we have a complete match of the key. Unless - * the node is one of the roots, we are finished. - * If it is the zeros root, then take what we have, prefering - * any real data. - * If it is the ones root, then pretend the target key was followed - * by a byte of zeros. - */ - if (!(t->rn_flags & RNF_ROOT)) - return t; /* not a root */ - if (t->rn_dupedkey) { - t = t->rn_dupedkey; - return t; /* have some real data */ - } - if (*(cp-1) == 0) - return t; /* not the ones root */ - b = 0; /* fake a zero after 255.255.255.255 */ - goto on2; -on1: - test = (*cp ^ *cp2) & 0xff; /* find first bit that differs */ - for (b = 7; (test >>= 1) > 0;) - b--; -on2: - matched_off = cp - v; - b += matched_off << 3; - rn_b = -1 - b; - /* - * If there is a host route in a duped-key chain, it will be first. - */ - if ((saved_t = t)->rn_mask == 0) - t = t->rn_dupedkey; - for (; t; t = t->rn_dupedkey) - /* - * Even if we don't match exactly as a host, - * we may match if the leaf we wound up at is - * a route to a net. - */ - if (t->rn_flags & RNF_NORMAL) { - if (rn_b <= t->rn_b) - return t; - } else if (rn_satsifies_leaf(v, t, matched_off)) - return t; - t = saved_t; - /* start searching up the tree */ - do { - register struct radix_mask *m; - t = t->rn_p; - if ((m = t->rn_mklist)) { - /* - * If non-contiguous masks ever become important - * we can restore the masking and open coding of - * the search and satisfaction test and put the - * calculation of "off" back before the "do". - */ - do { - if (m->rm_flags & RNF_NORMAL) { - if (rn_b <= m->rm_b) - return (m->rm_leaf); - } else { - off = min(t->rn_off, matched_off); - x = rn_search_m(v, t, m->rm_mask); - while (x && x->rn_mask != m->rm_mask) - x = x->rn_dupedkey; - if (x && rn_satsifies_leaf(v, x, off)) - return x; - } - } while ((m = m->rm_mklist)); - } - } while (t != top); - return 0; -} - -#ifdef RN_DEBUG -int rn_nodenum; -struct radix_node *rn_clist; -int rn_saveinfo; -int rn_debug = 1; -#endif - -struct radix_node * -rn_newpair(void *v, int b, struct radix_node nodes[2]) -{ - register struct radix_node *tt = nodes, *t = tt + 1; - t->rn_b = b; t->rn_bmask = 0x80 >> (b & 7); - t->rn_l = tt; t->rn_off = b >> 3; - tt->rn_b = -1; tt->rn_key = (caddr_t)v; tt->rn_p = t; - tt->rn_flags = t->rn_flags = RNF_ACTIVE; -#ifdef RN_DEBUG - tt->rn_info = rn_nodenum++; t->rn_info = rn_nodenum++; - tt->rn_twin = t; tt->rn_ybro = rn_clist; rn_clist = tt; -#endif - return t; -} - -struct radix_node * -rn_insert(void* v_arg, - struct radix_node_head *head, - int *dupentry, - struct radix_node nodes[2]) -{ - caddr_t v = v_arg; - struct radix_node *top = head->rnh_treetop; - int head_off = top->rn_off, vlen = (int)*((u_char *)v); - register struct radix_node *t = rn_search(v_arg, top); - register caddr_t cp = v + head_off; - register int b; - struct radix_node *tt; - - /* - * Find first bit at which v and t->rn_key differ - */ - { - register caddr_t cp2 = t->rn_key + head_off; - register int cmp_res; - caddr_t cplim = v + vlen; - - while (cp < cplim) - if (*cp2++ != *cp++) - goto on1; - /* handle adding 255.255.255.255 */ - if (!(t->rn_flags & RNF_ROOT) || *(cp2-1) == 0) { - *dupentry = 1; - return t; - } -on1: - *dupentry = 0; - cmp_res = (cp[-1] ^ cp2[-1]) & 0xff; - for (b = (cp - v) << 3; cmp_res; b--) - cmp_res >>= 1; - } - { - register struct radix_node *p, *x = top; - cp = v; - do { - p = x; - if (cp[x->rn_off] & x->rn_bmask) - x = x->rn_r; - else x = x->rn_l; - } while (b > (unsigned) x->rn_b); /* x->rn_b < b && x->rn_b >= 0 */ -#ifdef RN_DEBUG - if (rn_debug) - log(LOG_DEBUG, "rn_insert: Going In:\n"), traverse(p); -#endif - t = rn_newpair(v_arg, b, nodes); tt = t->rn_l; - if ((cp[p->rn_off] & p->rn_bmask) == 0) - p->rn_l = t; - else - p->rn_r = t; - x->rn_p = t; t->rn_p = p; /* frees x, p as temp vars below */ - if ((cp[t->rn_off] & t->rn_bmask) == 0) { - t->rn_r = x; - } else { - t->rn_r = tt; t->rn_l = x; - } -#ifdef RN_DEBUG - if (rn_debug) - log(LOG_DEBUG, "rn_insert: Coming Out:\n"), traverse(p); -#endif - } - return (tt); -} - -struct radix_node * -rn_addmask(void *n_arg, int search, int skip) -{ - caddr_t netmask = (caddr_t)n_arg; - register struct radix_node *x; - register caddr_t cp, cplim; - register int b = 0, mlen, j; - int maskduplicated, m0, isnormal; - struct radix_node *saved_x; - static int last_zeroed = 0; - - if ((mlen = *(u_char *)netmask) > max_keylen) - mlen = max_keylen; - if (skip == 0) - skip = 1; - if (mlen <= skip) - return (mask_rnhead->rnh_nodes); - if (skip > 1) - Bcopy(rn_ones + 1, addmask_key + 1, skip - 1); - if ((m0 = mlen) > skip) - Bcopy(netmask + skip, addmask_key + skip, mlen - skip); - /* - * Trim trailing zeroes. - */ - for (cp = addmask_key + mlen; (cp > addmask_key) && cp[-1] == 0;) - cp--; - mlen = cp - addmask_key; - if (mlen <= skip) { - if (m0 >= last_zeroed) - last_zeroed = mlen; - return (mask_rnhead->rnh_nodes); - } - if (m0 < last_zeroed) - Bzero(addmask_key + m0, last_zeroed - m0); - *addmask_key = last_zeroed = mlen; - x = rn_search(addmask_key, rn_masktop); - if (Bcmp(addmask_key, x->rn_key, mlen) != 0) - x = 0; - if (x || search) - return (x); - R_Malloc(x, struct radix_node *, max_keylen + 2 * sizeof (*x)); - if ((saved_x = x) == 0) - return (0); - Bzero(x, max_keylen + 2 * sizeof (*x)); - netmask = cp = (caddr_t)(x + 2); - Bcopy(addmask_key, cp, mlen); - x = rn_insert(cp, mask_rnhead, &maskduplicated, x); - if (maskduplicated) { - log(LOG_ERR, "rn_addmask: mask impossibly already in tree"); - Free(saved_x); - return (x); - } - /* - * Calculate index of mask, and check for normalcy. - */ - cplim = netmask + mlen; isnormal = 1; - for (cp = netmask + skip; (cp < cplim) && *(u_char *)cp == 0xff;) - cp++; - if (cp != cplim) { - for (j = 0x80; (j & *cp) != 0; j >>= 1) - b++; - if (*cp != normal_chars[b] || cp != (cplim - 1)) - isnormal = 0; - } - b += (cp - netmask) << 3; - x->rn_b = -1 - b; - if (isnormal) - x->rn_flags |= RNF_NORMAL; - return (x); -} - -static int /* XXX: arbitrary ordering for non-contiguous masks */ -rn_lexobetter(void *m_arg, void *n_arg) -{ - register u_char *mp = m_arg, *np = n_arg, *lim; - - if (*mp > *np) - return 1; /* not really, but need to check longer one first */ - if (*mp == *np) - for (lim = mp + *mp; mp < lim;) - if (*mp++ > *np++) - return 1; - return 0; -} - -static struct radix_mask * -rn_new_radix_mask(register struct radix_node *tt, - register struct radix_mask *next) -{ - register struct radix_mask *m; - - MKGet(m); - if (m == 0) { - log(LOG_ERR, "Mask for route not entered\n"); - return (0); - } - Bzero(m, sizeof *m); - m->rm_b = tt->rn_b; - m->rm_flags = tt->rn_flags; - if (tt->rn_flags & RNF_NORMAL) - m->rm_leaf = tt; - else - m->rm_mask = tt->rn_mask; - m->rm_mklist = next; - tt->rn_mklist = m; - return m; -} - -struct radix_node * -rn_addroute(void *v_arg, - void *n_arg, - struct radix_node_head *head, - struct radix_node treenodes[2]) -{ - caddr_t v = (caddr_t)v_arg, netmask = (caddr_t)n_arg; - register struct radix_node *t, *x = 0, *tt; - struct radix_node *saved_tt, *top = head->rnh_treetop; - short b = 0, b_leaf = 0; - int keyduplicated; - caddr_t mmask; - struct radix_mask *m, **mp; - - /* - * In dealing with non-contiguous masks, there may be - * many different routes which have the same mask. - * We will find it useful to have a unique pointer to - * the mask to speed avoiding duplicate references at - * nodes and possibly save time in calculating indices. - */ - if (netmask) { - if ((x = rn_addmask(netmask, 0, top->rn_off)) == 0) - return (0); - b_leaf = x->rn_b; - b = -1 - x->rn_b; - netmask = x->rn_key; - } - /* - * Deal with duplicated keys: attach node to previous instance - */ - saved_tt = tt = rn_insert(v, head, &keyduplicated, treenodes); - if (keyduplicated) { - for (t = tt; tt; t = tt, tt = tt->rn_dupedkey) { - if (tt->rn_mask == netmask) - return (0); - if (netmask == 0 || - (tt->rn_mask && - ((b_leaf < tt->rn_b) || /* index(netmask) > node */ - rn_refines(netmask, tt->rn_mask) || - rn_lexobetter(netmask, tt->rn_mask)))) - break; - } - /* - * If the mask is not duplicated, we wouldn't - * find it among possible duplicate key entries - * anyway, so the above test doesn't hurt. - * - * We sort the masks for a duplicated key the same way as - * in a masklist -- most specific to least specific. - * This may require the unfortunate nuisance of relocating - * the head of the list. - */ - if (tt == saved_tt) { - struct radix_node *xx = x; - /* link in at head of list */ - (tt = treenodes)->rn_dupedkey = t; - tt->rn_flags = t->rn_flags; - tt->rn_p = x = t->rn_p; - if (x->rn_l == t) x->rn_l = tt; else x->rn_r = tt; - saved_tt = tt; x = xx; - } else { - (tt = treenodes)->rn_dupedkey = t->rn_dupedkey; - t->rn_dupedkey = tt; - } -#ifdef RN_DEBUG - t=tt+1; tt->rn_info = rn_nodenum++; t->rn_info = rn_nodenum++; - tt->rn_twin = t; tt->rn_ybro = rn_clist; rn_clist = tt; -#endif - tt->rn_key = (caddr_t) v; - tt->rn_b = -1; - tt->rn_flags = RNF_ACTIVE; - } - /* - * Put mask in tree. - */ - if (netmask) { - tt->rn_mask = netmask; - tt->rn_b = x->rn_b; - tt->rn_flags |= x->rn_flags & RNF_NORMAL; - } - t = saved_tt->rn_p; - if (keyduplicated) - goto on2; - b_leaf = -1 - t->rn_b; - if (t->rn_r == saved_tt) x = t->rn_l; else x = t->rn_r; - /* Promote general routes from below */ - if (x->rn_b < 0) { - for (mp = &t->rn_mklist; x; x = x->rn_dupedkey) - if (x->rn_mask && (x->rn_b >= b_leaf) && x->rn_mklist == 0) { - if ((*mp = m = rn_new_radix_mask(x, 0))) - mp = &m->rm_mklist; - } - } else if (x->rn_mklist) { - /* - * Skip over masks whose index is > that of new node - */ - for (mp = &x->rn_mklist; (m = *mp); mp = &m->rm_mklist) - if (m->rm_b >= b_leaf) - break; - t->rn_mklist = m; *mp = 0; - } -on2: - /* Add new route to highest possible ancestor's list */ - if ((netmask == 0) || (b > t->rn_b )) - return tt; /* can't lift at all */ - b_leaf = tt->rn_b; - do { - x = t; - t = t->rn_p; - } while (b <= t->rn_b && x != top); - /* - * Search through routes associated with node to - * insert new route according to index. - * Need same criteria as when sorting dupedkeys to avoid - * double loop on deletion. - */ - for (mp = &x->rn_mklist; (m = *mp); mp = &m->rm_mklist) { - if (m->rm_b < b_leaf) - continue; - if (m->rm_b > b_leaf) - break; - if (m->rm_flags & RNF_NORMAL) { - mmask = m->rm_leaf->rn_mask; - if (tt->rn_flags & RNF_NORMAL) { - log(LOG_ERR, - "Non-unique normal route, mask not entered"); - return tt; - } - } else - mmask = m->rm_mask; - if (mmask == netmask) { - m->rm_refs++; - tt->rn_mklist = m; - return tt; - } - if (rn_refines(netmask, mmask) || rn_lexobetter(netmask, mmask)) - break; - } - *mp = rn_new_radix_mask(tt, *mp); - return tt; -} - -struct radix_node * -rn_delete(void *v_arg, - void *netmask_arg, - struct radix_node_head *head) -{ - register struct radix_node *t, *p, *x, *tt; - struct radix_mask *m, *saved_m, **mp; - struct radix_node *dupedkey, *saved_tt, *top; - caddr_t v, netmask; - int b, head_off, vlen; - - v = v_arg; - netmask = netmask_arg; - x = head->rnh_treetop; - tt = rn_search(v, x); - head_off = x->rn_off; - vlen = *(u_char *)v; - saved_tt = tt; - top = x; - if (tt == 0 || - Bcmp(v + head_off, tt->rn_key + head_off, vlen - head_off)) - return (0); - /* - * Delete our route from mask lists. - */ - if (netmask) { - if ((x = rn_addmask(netmask, 1, head_off)) == 0) - return (0); - netmask = x->rn_key; - while (tt->rn_mask != netmask) - if ((tt = tt->rn_dupedkey) == 0) - return (0); - } - if (tt->rn_mask == 0 || (saved_m = m = tt->rn_mklist) == 0) - goto on1; - if (tt->rn_flags & RNF_NORMAL) { - if (m->rm_leaf != tt || m->rm_refs > 0) { - log(LOG_ERR, "rn_delete: inconsistent annotation\n"); - return 0; /* dangling ref could cause disaster */ - } - } else { - if (m->rm_mask != tt->rn_mask) { - log(LOG_ERR, "rn_delete: inconsistent annotation\n"); - goto on1; - } - if (--m->rm_refs >= 0) - goto on1; - } - b = -1 - tt->rn_b; - t = saved_tt->rn_p; - if (b > t->rn_b) - goto on1; /* Wasn't lifted at all */ - do { - x = t; - t = t->rn_p; - } while (b <= t->rn_b && x != top); - for (mp = &x->rn_mklist; (m = *mp); mp = &m->rm_mklist) - if (m == saved_m) { - *mp = m->rm_mklist; - MKFree(m); - break; - } - if (m == 0) { - log(LOG_ERR, "rn_delete: couldn't find our annotation\n"); - if (tt->rn_flags & RNF_NORMAL) - return (0); /* Dangling ref to us */ - } -on1: - /* - * Eliminate us from tree - */ - if (tt->rn_flags & RNF_ROOT) - return (0); -#ifdef RN_DEBUG - /* Get us out of the creation list */ - for (t = rn_clist; t && t->rn_ybro != tt; t = t->rn_ybro) {} - if (t) t->rn_ybro = tt->rn_ybro; -#endif - t = tt->rn_p; - if ((dupedkey = saved_tt->rn_dupedkey)) { - if (tt == saved_tt) { - x = dupedkey; x->rn_p = t; - if (t->rn_l == tt) t->rn_l = x; else t->rn_r = x; - } else { - for (x = p = saved_tt; p && p->rn_dupedkey != tt;) - p = p->rn_dupedkey; - if (p) p->rn_dupedkey = tt->rn_dupedkey; - else log(LOG_ERR, "rn_delete: couldn't find us\n"); - } - t = tt + 1; - if (t->rn_flags & RNF_ACTIVE) { -#ifndef RN_DEBUG - *++x = *t; p = t->rn_p; -#else - b = t->rn_info; *++x = *t; t->rn_info = b; p = t->rn_p; -#endif - if (p->rn_l == t) p->rn_l = x; else p->rn_r = x; - x->rn_l->rn_p = x; x->rn_r->rn_p = x; - } - goto out; - } - if (t->rn_l == tt) x = t->rn_r; else x = t->rn_l; - p = t->rn_p; - if (p->rn_r == t) p->rn_r = x; else p->rn_l = x; - x->rn_p = p; - /* - * Demote routes attached to us. - */ - if (t->rn_mklist) { - if (x->rn_b >= 0) { - for (mp = &x->rn_mklist; (m = *mp);) - mp = &m->rm_mklist; - *mp = t->rn_mklist; - } else { - /* If there are any key,mask pairs in a sibling - duped-key chain, some subset will appear sorted - in the same order attached to our mklist */ - for (m = t->rn_mklist; m && x; x = x->rn_dupedkey) - if (m == x->rn_mklist) { - struct radix_mask *mm = m->rm_mklist; - x->rn_mklist = 0; - if (--(m->rm_refs) < 0) - MKFree(m); - m = mm; - } - if (m) - syslog(LOG_ERR, "%s %lx at %lx\n", - "rn_delete: Orphaned Mask", - (unsigned long)m, - (unsigned long)x); - } - } - /* - * We may be holding an active internal node in the tree. - */ - x = tt + 1; - if (t != x) { -#ifndef RN_DEBUG - *t = *x; -#else - b = t->rn_info; *t = *x; t->rn_info = b; -#endif - t->rn_l->rn_p = t; t->rn_r->rn_p = t; - p = x->rn_p; - if (p->rn_l == x) p->rn_l = t; else p->rn_r = t; - } -out: - tt->rn_flags &= ~RNF_ACTIVE; - tt[1].rn_flags &= ~RNF_ACTIVE; - return (tt); -} - -int -rn_walktree(struct radix_node_head *h, - register int (*f)(struct radix_node *, struct walkarg*), - struct walkarg *w) -{ - int error; - struct radix_node *base, *next; - register struct radix_node *rn = h->rnh_treetop; - /* - * This gets complicated because we may delete the node - * while applying the function f to it, so we need to calculate - * the successor node in advance. - */ - /* First time through node, go left */ - while (rn->rn_b >= 0) - rn = rn->rn_l; - for (;;) { - base = rn; - /* If at right child go back up, otherwise, go right */ - while (rn->rn_p->rn_r == rn && (rn->rn_flags & RNF_ROOT) == 0) - rn = rn->rn_p; - /* Find the next *leaf* since next node might vanish, too */ - for (rn = rn->rn_p->rn_r; rn->rn_b >= 0;) - rn = rn->rn_l; - next = rn; - /* Process leaves */ - while ((rn = base)) { - base = rn->rn_dupedkey; - if (!(rn->rn_flags & RNF_ROOT) && (error = (*f)(rn, w))) - return (error); - } - rn = next; - if (rn->rn_flags & RNF_ROOT) - return (0); - } - /* NOTREACHED */ -} - -int -rn_inithead(void **head, int off) -{ - register struct radix_node_head *rnh; - register struct radix_node *t, *tt, *ttt; - if (*head) - return (1); - R_Malloc(rnh, struct radix_node_head *, sizeof (*rnh)); - if (rnh == 0) - return (0); - Bzero(rnh, sizeof (*rnh)); - *head = rnh; - t = rn_newpair(rn_zeros, off, rnh->rnh_nodes); - ttt = rnh->rnh_nodes + 2; - t->rn_r = ttt; - t->rn_p = t; - tt = t->rn_l; - tt->rn_flags = t->rn_flags = RNF_ROOT | RNF_ACTIVE; - tt->rn_b = -1 - off; - *ttt = *tt; - ttt->rn_key = rn_ones; - rnh->rnh_addaddr = rn_addroute; - rnh->rnh_deladdr = rn_delete; - rnh->rnh_matchaddr = rn_match; - rnh->rnh_lookup = rn_lookup; - rnh->rnh_walktree = rn_walktree; - rnh->rnh_treetop = t; - return (1); -} - -void -rn_init(void) -{ - char *cp, *cplim; - if (max_keylen == 0) { - printf("rn_init: radix functions require max_keylen be set\n"); - return; - } - R_Malloc(rn_zeros, char *, 3 * max_keylen); - if (rn_zeros == NULL) - panic("rn_init"); - Bzero(rn_zeros, 3 * max_keylen); - rn_ones = cp = rn_zeros + max_keylen; - addmask_key = cplim = rn_ones + max_keylen; - while (cp < cplim) - *cp++ = -1; - if (rn_inithead((void **)&mask_rnhead, 0) == 0) - panic("rn_init 2"); -} - diff --git a/usr.sbin/routed/radix.h b/usr.sbin/routed/radix.h deleted file mode 100644 index fddf02ead7a6..000000000000 --- a/usr.sbin/routed/radix.h +++ /dev/null @@ -1,161 +0,0 @@ -/* - * 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 deleted file mode 100644 index da1784c793e4..000000000000 --- a/usr.sbin/routed/rdisc.c +++ /dev/null @@ -1,1032 +0,0 @@ -/* - * Copyright (c) 1995 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#if !defined(lint) && !defined(sgi) && !defined(__NetBSD__) -static char sccsid[] = "@(#)rdisc.c 8.1 (Berkeley) x/y/95"; -#elif defined(__NetBSD__) -static char rcsid[] = "$NetBSD$"; -#endif -#ident "$Revision: 1.16 $" - -#include "defs.h" -#include -#include -#include - -/* router advertisement ICMP packet */ -struct icmp_ad { - u_int8_t icmp_type; /* type of message */ - u_int8_t icmp_code; /* type sub code */ - u_int16_t icmp_cksum; /* ones complement cksum of struct */ - u_int8_t icmp_ad_num; /* # of following router addresses */ - u_int8_t icmp_ad_asize; /* 2--words in each advertisement */ - u_int16_t icmp_ad_life; /* seconds of validity */ - struct icmp_ad_info { - n_long icmp_ad_addr; - n_long icmp_ad_pref; - } icmp_ad_info[1]; -}; - -/* router solicitation ICMP packet */ -struct icmp_so { - u_int8_t icmp_type; /* type of message */ - u_int8_t icmp_code; /* type sub code */ - u_int16_t icmp_cksum; /* ones complement cksum of struct */ - n_long icmp_so_rsvd; -}; - -union ad_u { - struct icmp icmp; - struct icmp_ad ad; - struct icmp_so so; -}; - - -int rdisc_sock = -1; /* router-discovery raw socket */ -struct interface *rdisc_sock_mcast; /* current multicast interface */ - -struct timeval rdisc_timer; -int rdisc_ok; /* using solicited route */ - - -#define MAX_ADS 5 -struct dr { /* accumulated advertisements */ - struct interface *dr_ifp; - naddr dr_gate; /* gateway */ - time_t dr_ts; /* when received */ - time_t dr_life; /* lifetime */ - n_long dr_recv_pref; /* received but biased preference */ - n_long dr_pref; /* preference adjusted by metric */ -} *cur_drp, drs[MAX_ADS]; - -/* adjust preference by interface metric without driving it to infinity */ -#define PREF(p, ifp) ((p) <= (ifp)->int_metric ? ((p) != 0 ? 1 : 0) \ - : (p) - ((ifp)->int_metric)) - -static void rdisc_sort(void); - - -/* dump an ICMP Router Discovery Advertisement Message - */ -static void -trace_rdisc(char *act, - naddr from, - naddr to, - struct interface *ifp, - union ad_u *p, - u_int len) -{ - int i; - n_long *wp, *lim; - - - if (!TRACEPACKETS || ftrace == 0) - return; - - lastlog(); - - if (p->icmp.icmp_type == ICMP_ROUTERADVERT) { - (void)fprintf(ftrace, "%s Router Ad" - " from %s to %s via %s life=%d\n", - act, naddr_ntoa(from), naddr_ntoa(to), - ifp ? ifp->int_name : "?", - ntohs(p->ad.icmp_ad_life)); - if (!TRACECONTENTS) - return; - - wp = &p->ad.icmp_ad_info[0].icmp_ad_addr; - lim = &wp[(len - sizeof(p->ad)) / sizeof(*wp)]; - for (i = 0; i < p->ad.icmp_ad_num && wp <= lim; i++) { - (void)fprintf(ftrace, "\t%s preference=%#x", - naddr_ntoa(wp[0]), (int)ntohl(wp[1])); - wp += p->ad.icmp_ad_asize; - } - (void)fputc('\n',ftrace); - - } else { - trace_act("%s Router Solic. from %s to %s via %s" - " value=%#x\n", - act, naddr_ntoa(from), naddr_ntoa(to), - ifp ? ifp->int_name : "?", - ntohl(p->so.icmp_so_rsvd)); - } -} - -/* prepare Router Discovery socket. - */ -static void -get_rdisc_sock(void) -{ - if (rdisc_sock < 0) { - rdisc_sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); - if (rdisc_sock < 0) - BADERR(1,"rdisc_sock = socket()"); - fix_sock(rdisc_sock,"rdisc_sock"); - fix_select(); - } -} - - -/* Pick multicast group for router-discovery socket - */ -void -set_rdisc_mg(struct interface *ifp, - int on) { /* 0=turn it off */ - struct ip_mreq m; - - if (rdisc_sock < 0) { - /* Create the raw socket so that we can hear at least - * broadcast router discovery packets. - */ - if ((ifp->int_state & IS_NO_RDISC) == IS_NO_RDISC - || !on) - return; - get_rdisc_sock(); - } - - if (!(ifp->int_if_flags & IFF_MULTICAST) - || (ifp->int_state & IS_ALIAS)) { - ifp->int_state &= ~(IS_ALL_HOSTS | IS_ALL_ROUTERS); - return; - } - -#ifdef MCAST_PPP_BUG - if (ifp->int_if_flags & IFF_POINTOPOINT) - return; -#endif - bzero(&m, sizeof(m)); - m.imr_interface.s_addr = ((ifp->int_if_flags & IFF_POINTOPOINT) - ? ifp->int_dstaddr - : ifp->int_addr); - if (supplier - || (ifp->int_state & IS_NO_ADV_IN) - || !on) { - /* stop listening to advertisements - */ - if (ifp->int_state & IS_ALL_HOSTS) { - m.imr_multiaddr.s_addr = htonl(INADDR_ALLHOSTS_GROUP); - if (setsockopt(rdisc_sock, IPPROTO_IP, - IP_DROP_MEMBERSHIP, - &m, sizeof(m)) < 0) - LOGERR("IP_DROP_MEMBERSHIP ALLHOSTS"); - ifp->int_state &= ~IS_ALL_HOSTS; - } - - } else if (!(ifp->int_state & IS_ALL_HOSTS)) { - /* start listening to advertisements - */ - m.imr_multiaddr.s_addr = htonl(INADDR_ALLHOSTS_GROUP); - if (setsockopt(rdisc_sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, - &m, sizeof(m)) < 0) { - LOGERR("IP_ADD_MEMBERSHIP ALLHOSTS"); - } else { - ifp->int_state |= IS_ALL_HOSTS; - } - } - - if (!supplier - || (ifp->int_state & IS_NO_ADV_OUT) - || !on) { - /* stop listening to solicitations - */ - if (ifp->int_state & IS_ALL_ROUTERS) { - m.imr_multiaddr.s_addr=htonl(INADDR_ALLROUTERS_GROUP); - if (setsockopt(rdisc_sock, IPPROTO_IP, - IP_DROP_MEMBERSHIP, - &m, sizeof(m)) < 0) - LOGERR("IP_DROP_MEMBERSHIP ALLROUTERS"); - ifp->int_state &= ~IS_ALL_ROUTERS; - } - - } else if (!(ifp->int_state & IS_ALL_ROUTERS)) { - /* start hearing solicitations - */ - m.imr_multiaddr.s_addr=htonl(INADDR_ALLROUTERS_GROUP); - if (setsockopt(rdisc_sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, - &m, sizeof(m)) < 0) { - LOGERR("IP_ADD_MEMBERSHIP ALLROUTERS"); - } else { - ifp->int_state |= IS_ALL_ROUTERS; - } - } -} - - -/* start supplying routes - */ -void -set_supplier(void) -{ - struct interface *ifp; - struct dr *drp; - - if (supplier_set) - return; - - trace_act("start suppying routes\n"); - - /* Forget discovered routes. - */ - for (drp = drs; drp < &drs[MAX_ADS]; drp++) { - drp->dr_recv_pref = 0; - drp->dr_life = 0; - } - rdisc_age(0); - - supplier_set = 1; - supplier = 1; - - /* Do not start advertising until we have heard some RIP routes */ - LIM_SEC(rdisc_timer, now.tv_sec+MIN_WAITTIME); - - /* Switch router discovery multicast groups from soliciting - * to advertising. - */ - for (ifp = ifnet; ifp; ifp = ifp->int_next) { - if (ifp->int_state & IS_BROKE) - continue; - ifp->int_rdisc_cnt = 0; - ifp->int_rdisc_timer.tv_usec = rdisc_timer.tv_usec; - ifp->int_rdisc_timer.tv_sec = now.tv_sec+MIN_WAITTIME; - set_rdisc_mg(ifp, 1); - } - - /* get rid of any redirects */ - del_redirects(0,0); -} - - -/* age discovered routes and find the best one - */ -void -rdisc_age(naddr bad_gate) -{ - time_t sec; - struct dr *drp; - - - /* If only adverising, then do only that. */ - if (supplier) { - /* if switching from client to server, get rid of old - * default routes. - */ - if (cur_drp != 0) - rdisc_sort(); - rdisc_adv(); - return; - } - - /* If we are being told about a bad router, - * then age the discovered default route, and if there is - * no alternative, solicite a replacement. - */ - if (bad_gate != 0) { - /* Look for the bad discovered default route. - * Age it and note its interface. - */ - for (drp = drs; drp < &drs[MAX_ADS]; drp++) { - if (drp->dr_ts == 0) - continue; - - /* When we find the bad router, then age the route - * to at most SUPPLY_INTERVAL. - * This is contrary to RFC 1256, but defends against - * black holes. - */ - if (drp->dr_gate == bad_gate) { - sec = (now.tv_sec - drp->dr_life - + SUPPLY_INTERVAL); - if (drp->dr_ts > sec) { - trace_act("age 0.0.0.0 --> %s" - " via %s\n", - naddr_ntoa(drp->dr_gate), - drp->dr_ifp->int_name); - drp->dr_ts = sec; - } - break; - } - } - } - - /* delete old redirected routes to keep the kernel table small - */ - sec = (cur_drp == 0) ? MaxMaxAdvertiseInterval : cur_drp->dr_life; - del_redirects(bad_gate, now.tv_sec-sec); - - rdisc_sol(); - - rdisc_sort(); -} - - -/* Zap all routes discovered via an interface that has gone bad - * This should only be called when !(ifp->int_state & IS_ALIAS) - */ -void -if_bad_rdisc(struct interface *ifp) -{ - struct dr *drp; - - for (drp = drs; drp < &drs[MAX_ADS]; drp++) { - if (drp->dr_ifp != ifp) - continue; - drp->dr_recv_pref = 0; - drp->dr_life = 0; - } - - rdisc_sort(); -} - - -/* mark an interface ok for router discovering. - */ -void -if_ok_rdisc(struct interface *ifp) -{ - set_rdisc_mg(ifp, 1); - - ifp->int_rdisc_cnt = 0; - ifp->int_rdisc_timer.tv_sec = now.tv_sec + (supplier - ? MIN_WAITTIME - : MAX_SOLICITATION_DELAY); - if (timercmp(&rdisc_timer, &ifp->int_rdisc_timer, >)) - rdisc_timer = ifp->int_rdisc_timer; -} - - -/* get rid of a dead discovered router - */ -static void -del_rdisc(struct dr *drp) -{ - struct interface *ifp; - int i; - - - del_redirects(drp->dr_gate, 0); - drp->dr_ts = 0; - drp->dr_life = 0; - - - /* Count the other discovered routes on the interface. - */ - i = 0; - ifp = drp->dr_ifp; - for (drp = drs; drp < &drs[MAX_ADS]; drp++) { - if (drp->dr_ts != 0 - && drp->dr_ifp == ifp) - i++; - } - - /* If that was the last good discovered router on the interface, - * then solicit a new one. - * This is contrary to RFC 1256, but defends against black holes. - */ - if (i == 0 - && ifp->int_rdisc_cnt >= MAX_SOLICITATIONS) { - trace_act("discovered route is bad" - "--re-solicit routers via %s\n", ifp->int_name); - ifp->int_rdisc_cnt = 0; - ifp->int_rdisc_timer.tv_sec = 0; - rdisc_sol(); - } -} - - -/* Find the best discovered route, - * and discard stale routers. - */ -static void -rdisc_sort(void) -{ - struct dr *drp, *new_drp; - struct rt_entry *rt; - struct interface *ifp; - u_int new_st; - n_long new_pref; - - - /* Find the best discovered route. - */ - new_drp = 0; - for (drp = drs; drp < &drs[MAX_ADS]; drp++) { - if (drp->dr_ts == 0) - continue; - ifp = drp->dr_ifp; - - /* Get rid of expired discovered routers. - */ - if (drp->dr_ts + drp->dr_life <= now.tv_sec) { - del_rdisc(drp); - continue; - } - - LIM_SEC(rdisc_timer, drp->dr_ts+drp->dr_life+1); - - /* Update preference with possibly changed interface - * metric. - */ - drp->dr_pref = PREF(drp->dr_recv_pref, ifp); - - /* Prefer the current route to prevent thrashing. - * Prefer shorter lifetimes to speed the detection of - * bad routers. - * Avoid sick interfaces. - */ - if (new_drp == 0 - || (!((new_st ^ drp->dr_ifp->int_state) & IS_SICK) - && (new_pref < drp->dr_pref - || (new_pref == drp->dr_pref - && (drp == cur_drp - || (new_drp != cur_drp - && new_drp->dr_life > drp->dr_life))))) - || ((new_st & IS_SICK) - && !(drp->dr_ifp->int_state & IS_SICK))) { - new_drp = drp; - new_st = drp->dr_ifp->int_state; - new_pref = drp->dr_pref; - } - } - - /* switch to a better default route - */ - if (new_drp != cur_drp) { - rt = rtget(RIP_DEFAULT, 0); - - /* Stop using discovered routes if they are all bad - */ - if (new_drp == 0) { - trace_act("turn off Router Discovery client\n"); - rdisc_ok = 0; - - if (rt != 0 - && (rt->rt_state & RS_RDISC)) { - rtchange(rt, rt->rt_state & ~RS_RDISC, - rt->rt_gate, rt->rt_router, - HOPCNT_INFINITY, 0, rt->rt_ifp, - now.tv_sec - GARBAGE_TIME, 0); - rtswitch(rt, 0); - } - - /* turn on RIP if permitted */ - rip_on(0); - - } else { - if (cur_drp == 0) { - trace_act("turn on Router Discovery client" - " using %s via %s\n", - naddr_ntoa(new_drp->dr_gate), - new_drp->dr_ifp->int_name); - - rdisc_ok = 1; - - } else { - trace_act("switch Router Discovery from" - " %s via %s to %s via %s\n", - naddr_ntoa(cur_drp->dr_gate), - cur_drp->dr_ifp->int_name, - naddr_ntoa(new_drp->dr_gate), - new_drp->dr_ifp->int_name); - } - - if (rt != 0) { - rtchange(rt, rt->rt_state | RS_RDISC, - new_drp->dr_gate, new_drp->dr_gate, - 0,0, new_drp->dr_ifp, - now.tv_sec, 0); - } else { - rtadd(RIP_DEFAULT, 0, - new_drp->dr_gate, new_drp->dr_gate, - 0, 0, RS_RDISC, new_drp->dr_ifp); - } - - /* Now turn off RIP and delete RIP routes, - * which might otherwise include the default - * we just modified. - */ - rip_off(); - } - - cur_drp = new_drp; - } -} - - -/* handle a single address in an advertisement - */ -static void -parse_ad(naddr from, - naddr gate, - n_long pref, - u_short life, - struct interface *ifp) -{ - static naddr bad_gate; - struct dr *drp, *new_drp; - - - if (gate == RIP_DEFAULT - || !check_dst(gate)) { - if (bad_gate != from) { - msglog("router %s advertising bad gateway %s", - naddr_ntoa(from), - naddr_ntoa(gate)); - bad_gate = from; - } - return; - } - - /* ignore pointers to ourself and routes via unreachable networks - */ - if (ifwithaddr(gate, 1, 0) != 0) { - trace_pkt("\tdiscard Router Discovery Ad pointing at us\n"); - return; - } - if (!on_net(gate, ifp->int_net, ifp->int_mask)) { - trace_pkt("\tdiscard Router Discovery Ad" - " toward unreachable net\n"); - return; - } - - /* Convert preference to an unsigned value - * and later bias it by the metric of the interface. - */ - pref = ntohl(pref) ^ MIN_PreferenceLevel; - - if (pref == 0 || life == 0) { - pref = 0; - life = 0; - } - - for (new_drp = 0, drp = drs; drp < &drs[MAX_ADS]; drp++) { - /* accept new info for a familiar entry - */ - if (drp->dr_gate == gate) { - new_drp = drp; - break; - } - - if (life == 0) - continue; /* do not worry about dead ads */ - - if (drp->dr_ts == 0) { - new_drp = drp; /* use unused entry */ - - } else if (new_drp == 0) { - /* look for an entry worse than the new one to - * reuse. - */ - if ((!(ifp->int_state & IS_SICK) - && (drp->dr_ifp->int_state & IS_SICK)) - || (pref > drp->dr_pref - && !((ifp->int_state ^ drp->dr_ifp->int_state) - & IS_SICK))) - new_drp = drp; - - } else if (new_drp->dr_ts != 0) { - /* look for the least valueable entry to reuse - */ - if ((!(new_drp->dr_ifp->int_state & IS_SICK) - && (drp->dr_ifp->int_state & IS_SICK)) - || (new_drp->dr_pref > drp->dr_pref - && !((new_drp->dr_ifp->int_state - ^ drp->dr_ifp->int_state) - & IS_SICK))) - new_drp = drp; - } - } - - /* forget it if all of the current entries are better */ - if (new_drp == 0) - return; - - new_drp->dr_ifp = ifp; - new_drp->dr_gate = gate; - new_drp->dr_ts = now.tv_sec; - new_drp->dr_life = ntohs(life); - new_drp->dr_recv_pref = pref; - /* bias functional preference by metric of the interface */ - new_drp->dr_pref = PREF(pref,ifp); - - /* after hearing a good advertisement, stop asking - */ - if (!(ifp->int_state & IS_SICK)) - ifp->int_rdisc_cnt = MAX_SOLICITATIONS; -} - - -/* Compute the IP checksum - * This assumes the packet is less than 32K long. - */ -static u_short -in_cksum(u_short *p, - u_int len) -{ - u_int sum = 0; - int nwords = len >> 1; - - while (nwords-- != 0) - sum += *p++; - - if (len & 1) - sum += *(u_char *)p; - - /* end-around-carry */ - sum = (sum >> 16) + (sum & 0xffff); - sum += (sum >> 16); - return (~sum); -} - - -/* Send a router discovery advertisement or solicitation ICMP packet. - */ -static void -send_rdisc(union ad_u *p, - int p_size, - struct interface *ifp, - naddr dst, /* 0 or unicast destination */ - int type) /* 0=unicast, 1=bcast, 2=mcast */ -{ - struct sockaddr_in sin; - int flags; - char *msg; - naddr tgt_mcast; - - - bzero(&sin, sizeof(sin)); - sin.sin_addr.s_addr = dst; - sin.sin_family = AF_INET; -#ifdef _HAVE_SIN_LEN - sin.sin_len = sizeof(sin); -#endif - flags = MSG_DONTROUTE; - - switch (type) { - case 0: /* unicast */ - msg = "Send"; - break; - - case 1: /* broadcast */ - if (ifp->int_if_flags & IFF_POINTOPOINT) { - msg = "Send pt-to-pt"; - sin.sin_addr.s_addr = ifp->int_dstaddr; - } else { - msg = "Send broadcast"; - sin.sin_addr.s_addr = ifp->int_brdaddr; - } - break; - - case 2: /* multicast */ - msg = "Send multicast"; - if (ifp->int_state & IS_DUP) { - trace_act("abort multicast output via %s" - " with duplicate address\n", - ifp->int_name); - return; - } - if (rdisc_sock_mcast != ifp) { - /* select the right interface. */ -#ifdef MCAST_PPP_BUG - /* Do not specifiy the primary interface explicitly - * if we have the multicast point-to-point kernel - * bug, since the kernel will do the wrong thing - * if the local address of a point-to-point link - * is the same as the address of an ordinary - * interface. - */ - if (ifp->int_addr == myaddr) { - tgt_mcast = 0; - } else -#endif - tgt_mcast = ifp->int_addr; - if (0 > setsockopt(rdisc_sock, - IPPROTO_IP, IP_MULTICAST_IF, - &tgt_mcast, sizeof(tgt_mcast))) { - LOGERR("setsockopt(rdisc_sock," - "IP_MULTICAST_IF)"); - rdisc_sock_mcast = 0; - return; - } - rdisc_sock_mcast = ifp; - } - flags = 0; - break; - } - - if (rdisc_sock < 0) - get_rdisc_sock(); - - trace_rdisc(msg, ifp->int_addr, sin.sin_addr.s_addr, ifp, - p, p_size); - - if (0 > sendto(rdisc_sock, p, p_size, flags, - (struct sockaddr *)&sin, sizeof(sin))) { - if (ifp == 0 || !(ifp->int_state & IS_BROKE)) - msglog("sendto(%s%s%s): %s", - ifp != 0 ? ifp->int_name : "", - ifp != 0 ? ", " : "", - inet_ntoa(sin.sin_addr), - strerror(errno)); - if (ifp != 0) - if_sick(ifp); - } -} - - -/* Send an advertisement - */ -static void -send_adv(struct interface *ifp, - naddr dst, /* 0 or unicast destination */ - int type) /* 0=unicast, 1=bcast, 2=mcast */ -{ - union ad_u u; - n_long pref; - - - bzero(&u,sizeof(u.ad)); - - u.ad.icmp_type = ICMP_ROUTERADVERT; - u.ad.icmp_ad_num = 1; - u.ad.icmp_ad_asize = sizeof(u.ad.icmp_ad_info[0])/4; - - u.ad.icmp_ad_life = stopint ? 0 : htons(ifp->int_rdisc_int*3); - pref = ifp->int_rdisc_pref ^ MIN_PreferenceLevel; - pref = PREF(pref, ifp) ^ MIN_PreferenceLevel; - u.ad.icmp_ad_info[0].icmp_ad_pref = htonl(pref); - - u.ad.icmp_ad_info[0].icmp_ad_addr = ifp->int_addr; - - u.ad.icmp_cksum = in_cksum((u_short*)&u.ad, sizeof(u.ad)); - - send_rdisc(&u, sizeof(u.ad), ifp, dst, type); -} - - -/* Advertise for Router Discovery - */ -void -rdisc_adv(void) -{ - struct interface *ifp; - - - rdisc_timer.tv_sec = now.tv_sec + NEVER; - - for (ifp = ifnet; ifp; ifp = ifp->int_next) { - if (0 != (ifp->int_state & (IS_NO_ADV_OUT - | IS_PASSIVE - | IS_ALIAS - | IS_BROKE))) - continue; - - if (!timercmp(&ifp->int_rdisc_timer, &now, >) - || stopint) { - send_adv(ifp, htonl(INADDR_ALLHOSTS_GROUP), - (ifp->int_state&IS_BCAST_RDISC) ? 1 : 2); - ifp->int_rdisc_cnt++; - - intvl_random(&ifp->int_rdisc_timer, - (ifp->int_rdisc_int*3)/4, - ifp->int_rdisc_int); - if (ifp->int_rdisc_cnt < MAX_INITIAL_ADVERTS - && (ifp->int_rdisc_timer.tv_sec - > MAX_INITIAL_ADVERT_INTERVAL)) { - ifp->int_rdisc_timer.tv_sec - = MAX_INITIAL_ADVERT_INTERVAL; - } - timevaladd(&ifp->int_rdisc_timer, &now); - } - - if (timercmp(&rdisc_timer, &ifp->int_rdisc_timer, >)) - rdisc_timer = ifp->int_rdisc_timer; - } -} - - -/* Solicit for Router Discovery - */ -void -rdisc_sol(void) -{ - struct interface *ifp; - union ad_u u; - - - rdisc_timer.tv_sec = now.tv_sec + NEVER; - - for (ifp = ifnet; ifp; ifp = ifp->int_next) { - if (0 != (ifp->int_state & (IS_NO_SOL_OUT - | IS_PASSIVE - | IS_ALIAS - | IS_BROKE)) - || ifp->int_rdisc_cnt >= MAX_SOLICITATIONS) - continue; - - if (!timercmp(&ifp->int_rdisc_timer, &now, >)) { - bzero(&u,sizeof(u.so)); - u.so.icmp_type = ICMP_ROUTERSOLICIT; - u.so.icmp_cksum = in_cksum((u_short*)&u.so, - sizeof(u.so)); - send_rdisc(&u, sizeof(u.so), ifp, - htonl(INADDR_ALLROUTERS_GROUP), - ((ifp->int_state&IS_BCAST_RDISC) ? 1 : 2)); - - if (++ifp->int_rdisc_cnt >= MAX_SOLICITATIONS) - continue; - - ifp->int_rdisc_timer.tv_sec = SOLICITATION_INTERVAL; - ifp->int_rdisc_timer.tv_usec = 0; - timevaladd(&ifp->int_rdisc_timer, &now); - } - - if (timercmp(&rdisc_timer, &ifp->int_rdisc_timer, >)) - rdisc_timer = ifp->int_rdisc_timer; - } -} - - -/* check the IP header of a possible Router Discovery ICMP packet */ -static struct interface * /* 0 if bad */ -ck_icmp(char *act, - naddr from, - naddr to, - union ad_u *p, - u_int len) -{ - struct interface *ifp; - char *type; - - - /* If we could tell the interface on which a packet from address 0 - * arrived, we could deal with such solicitations. - */ - - ifp = ((from == 0) ? 0 : iflookup(from)); - - if (p->icmp.icmp_type == ICMP_ROUTERADVERT) { - type = "advertisement"; - } else if (p->icmp.icmp_type == ICMP_ROUTERSOLICIT) { - type = "solicitation"; - } else { - return 0; - } - - if (p->icmp.icmp_code != 0) { - trace_pkt("unrecognized ICMP Router" - " %s code=%d from %s to %s\n", - type, p->icmp.icmp_code, - naddr_ntoa(from), naddr_ntoa(to)); - return 0; - } - - trace_rdisc(act, from, to, ifp, p, len); - - if (ifp == 0) - trace_pkt("unknown interface for router-discovery %s" - " from %s to %s", - type, naddr_ntoa(from), naddr_ntoa(to)); - - return ifp; -} - - -/* read packets from the router discovery socket - */ -void -read_d(void) -{ - static naddr bad_asize, bad_len; - struct sockaddr_in from; - int n, fromlen, cc, hlen; - union { - struct ip ip; - u_short s[512/2]; - u_char b[512]; - } pkt; - union ad_u *p; - n_long *wp; - struct interface *ifp; - - - for (;;) { - fromlen = sizeof(from); - cc = recvfrom(rdisc_sock, &pkt, sizeof(pkt), 0, - (struct sockaddr*)&from, - &fromlen); - if (cc <= 0) { - if (cc < 0 && errno != EWOULDBLOCK) - LOGERR("recvfrom(rdisc_sock)"); - break; - } - if (fromlen != sizeof(struct sockaddr_in)) - logbad(1,"impossible recvfrom(rdisc_sock) fromlen=%d", - fromlen); - - hlen = pkt.ip.ip_hl << 2; - if (cc < hlen + ICMP_MINLEN) - continue; - p = (union ad_u *)&pkt.b[hlen]; - cc -= hlen; - - ifp = ck_icmp("Recv", - from.sin_addr.s_addr, pkt.ip.ip_dst.s_addr, - p, cc); - if (ifp == 0) - continue; - if (ifwithaddr(from.sin_addr.s_addr, 0, 0)) { - trace_pkt("\tdiscard our own Router Discovery msg\n"); - continue; - } - - switch (p->icmp.icmp_type) { - case ICMP_ROUTERADVERT: - if (p->ad.icmp_ad_asize*4 - < sizeof(p->ad.icmp_ad_info[0])) { - if (bad_asize != from.sin_addr.s_addr) { - msglog("intolerable rdisc address" - " size=%d", - p->ad.icmp_ad_asize); - bad_asize = from.sin_addr.s_addr; - } - continue; - } - if (p->ad.icmp_ad_num == 0) { - trace_pkt("\tempty?\n"); - continue; - } - if (cc != (sizeof(p->ad) - sizeof(p->ad.icmp_ad_info) - + (p->ad.icmp_ad_num - * sizeof(p->ad.icmp_ad_info[0])))) { - if (bad_len != from.sin_addr.s_addr) { - msglog("rdisc length %d does not" - " match ad_num %d", - cc, p->ad.icmp_ad_num); - bad_len = from.sin_addr.s_addr; - } - continue; - } - if (supplier) - continue; - if (ifp->int_state & IS_NO_ADV_IN) - continue; - - wp = &p->ad.icmp_ad_info[0].icmp_ad_addr; - for (n = 0; n < p->ad.icmp_ad_num; n++) { - parse_ad(from.sin_addr.s_addr, - wp[0], wp[1], - ntohs(p->ad.icmp_ad_life), - ifp); - wp += p->ad.icmp_ad_asize; - } - break; - - - case ICMP_ROUTERSOLICIT: - if (!supplier) - continue; - if (ifp->int_state & IS_NO_ADV_OUT) - continue; - - /* XXX - * We should handle messages from address 0. - */ - - /* Respond with a point-to-point advertisement */ - send_adv(ifp, from.sin_addr.s_addr, 0); - break; - } - } - - rdisc_sort(); -} diff --git a/usr.sbin/routed/routed.8 b/usr.sbin/routed/routed.8 deleted file mode 100644 index 828bdd89fb1c..000000000000 --- a/usr.sbin/routed/routed.8 +++ /dev/null @@ -1,602 +0,0 @@ -.\" Copyright (c) 1983, 1991, 1993 -.\" The Regents of the University of California. All rights reserved. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" 3. All advertising materials mentioning features or use of this software -.\" must display the following acknowledgement: -.\" This product includes software developed by the University of -.\" California, Berkeley and its contributors. -.\" 4. Neither the name of the University nor the names of its contributors -.\" may be used to endorse or promote products derived from this software -.\" without specific prior written permission. -.\" -.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND -.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -.\" SUCH DAMAGE. -.\" -.\" @(#)routed.8 8.2 (Berkeley) 12/11/93 -.\" -.Dd June 1, 1996 -.Dt ROUTED 8 -.Os BSD 4.4 -.Sh NAME -.Nm routed -.Nd network RIP and router discovery routing daemon -.Sh SYNOPSIS -.Nm -.Op Fl sqdghmpAt -.Op Fl T Ar tracefile -.Oo -.Fl F -.Ar net Ns Op /mask Ns Op ,metric -.Oc -.OP Fl P Ar parms -.Sh DESCRIPTION -.Nm Routed -is a dameon invoked at boot time to manage the network -routing tables. -It uses Routing Information Protocol, RIPv1 (RFC\ 1058), -RIPv2 (RFC\ 1723), -and Internet Router Discovery Protocol (RFC 1256) -to maintain the kernel routing table. -The RIPv1 protocol is based on the reference 4.3BSD daemon. -.Pp -It listens on the -.Xr udp 4 -socket for the -.Xr route 8 -service (see -.Xr services 5 ) -for Routing Information Protocol packets. -It also sends and receives multicast Router Discovery ICMP messages. -If the host is a router, -.Nm -periodically supplies copies -of its routing tables to any directly connected hosts and networks. -It also advertise or solicits default routes using Router Discovery -ICMP messages. -.Pp -When started (or when a network interface is later turned on), -.Nm -uses an AF_ROUTE address family facility to find those -directly connected interfaces configured into the -system and marked "up". -It adds necessary routes for the interfaces -to the kernel routing table. -Soon after being first started, and provided there is at least one -interface on which RIP has not been disabled, -.Nm -deletes all pre-existing -non-static routes in kernel table. -Static routes in the kernel table are preserved and -included in RIP responses if they have a valid RIP metric -(see -.Xr route 8 ). -.Pp -If more than one interface is present (not counting the loopback interface), -it is assumed that the host should forward packets among the -connected networks. -After transmitting a RIP -.Em request -and -Router Discovery Advertisements or Solicitations on a new interface, -the daemon enters a loop, listening for -RIP request and response and Router Discover packets from other hosts. -.Pp -When a -.Em request -packet is received, -.Nm -formulates a reply based on the information maintained in its -internal tables. -The -.Em response -packet generated contains a list of known routes, each marked -with a "hop count" metric (a count of 16 or greater is -considered "infinite"). -Advertised metrics reflect the metric associated with interface -(see -.Xr ifconfig 8 ), -so setting the metric on an interface -is an effective way to steer traffic. -.Pp -Responses do not contain routes with a first hop on the requesting -network to implement in part -.Em split-horizon . -Requests from query programs -such as -.Xr rtquery 8 -are answered with the complete table. -.Pp -The routing table maintained by the daemon -includes space for several gateways for each destination -to speed recovery from a failing router. -RIP -.Em response -packets received are used to update the routing tables provided they are -from one of the several currently recognized gateways or -advertise a better metric than at least one of the existing -gateways. -.Pp -When an update is applied, -.Nm -records the change in its own tables and updates the kernel routing table -if the best route to the destination changes. -The change in the kernel routing tableis reflected in the next batch of -.Em response -packets sent. -If the next response is not scheduled for a while, a -.Em flash update -response containing only recently changed routes is sent. -.Pp -In addition to processing incoming packets, -.Nm -also periodically checks the routing table entries. -If an entry has not been updated for 3 minutes, the entry's metric -is set to infinity and marked for deletion. -Deletions are delayed until the route has been advertised with -an infinite metric to insure the invalidation -is propagated throughout the local internet. -This is a form of -.Em poison reverse . -.Pp -Routes in the kernel table that are added or changed as a result -of ICMP Redirect messages are deleted after a while to minimize -.Em black-holes . -When a TCP connection suffers a timeout, -the kernel tells -.Nm routed , -which deletes all redirected routes -through the gateway involved, advances the age of all RIP routes through -the gateway to allow an alternate to be chosen, and advances of the -age of any relevant Router Discovery Protocol default routes. -.Pp -Hosts acting as internetwork routers gratuitously supply their -routing tables every 30 seconds to all directly connected hosts -and networks. -These RIP responses are sent to the broadcast address on nets that support -broadcasting, -to the destination address on point-to-point links, and to the router's -own address on other networks. -If RIPv2 is enabled, multicast packets are sent on interfaces that -support multicasting. -.Pp -If no response is received on a remote interface, if there are errors -while sending responses, -or if there are more errors than input or output (see -.Xr netstat 8 ), -then the cable or some other part of the interface is assumed to be -disconnected or broken, and routes are adjusted appropriately. -.Pp -The -.Em Internet Router Discovery Protocol -is handled similarly. -When the daemon is supplying RIP routes, it also listens for -Router Discovery Solicitations and sends Advertisements. -When it is quiet and only listening to other RIP routers, it -sends Solicitations and listens for Advertisements. -If it receives -a good Advertisement, it stops listening for broadcast or multicast -RIP responses. -It tracks several advertising routers to speed recovery when the -currently chosen router dies. -If all discovered routers disappear, -the daemon resumes listening to RIP responses. -.Pp -While using Router Discovery (which happens by default when -the system has a single network interface and a Router Discover Advertisement -is received), there is a single default route and a variable number of -redirected host routes in the kernel table. -.Pp -The Router Discover standard requires that advertisements -have a default "lifetime" of 30 minutes. That means should -something happen, a client can be without a good route for -30 minutes. It is a good idea to reduce the default to 45 -seconds using -.Fl P Cm rdisc_interval=45 -on the command line or -.Cm rdisc_interval=45 -in the -.Pa /etc/gateways -file. -.Pp -While using Router Discovery (which happens by default when -the system has a single network interface and a Router Discover Advertisement -is received), there is a single default route and a variable number of -redirected host routes in the kernel table. -.Pp -See the -.Cm pm_rdisc -facility described below to support "legacy" systems -that can handle neither RIPv2 nor Router Discovery. -.Pp -By default, neither Router Discovery advertisements nor solicications -are sent over point to point links (e.g. PPP). - -.Pp -Options supported by -.Nm routed : -.Bl -tag -width Ds -.It Fl s -this option forces -.Nm -to supply routing information. -This is the default if multiple network interfaces are present on which -RIP or Router Discovery have not been disabled, and if the kernel switch -ipforwarding=1. -.It Fl q -is the opposite of the -.Fl s -option. -.It Fl d -Do not run in the background. -This option is meant for interactive use. -.It Fl g -This flag is used on internetwork routers to offer a route -to the "default" destination. -It is equivalent to -.Fl F -.Cm 0/0,1 -and is present mostly for historical reasons. -A better choice is -.Fl P Cm pm_rdisc -on the command line or -.CM pm_rdisc in the -.Pa /etc/gateways -file. -since a larger metric -will be used, reducing the spread of the potentially dangerous -default route. -This is typically used on a gateway to the Internet, -or on a gateway that uses another routing protocol whose routes -are not reported to other local routers. -Notice that because a metric of 1 is used, this feature is -dangerous. It is more commonly accidently used to create chaos with routing -loop than to solve problems. -.It Fl h -This causes host or point-to-point routes to not be advertised, -provided there is a network route going the same direction. -That is a limited kind of aggregation. -This option is useful on gateways to ethernets that have other gateway -machines connected with point-to-point links such as SLIP. -.It Fl m -This causes the machine to advertise a host or point-to-point route to -its primary interface. -It is useful on multi-homed machines such as NFS servers. -This option should not be used except when the cost of -the host routes it generates is justified by the popularity of -the server. -It is effective only when the machine is supplying -routing information, because there is more than one interface. -The -.Fl m -option overrides the -.Fl q -option to the limited extent of advertising the host route. -.It Fl A -do not ignore RIPv2 authentication if we do not care about RIPv2 -authentication. -This option is required for conformance with RFC 1723. -However, it makes no sense and breaks using RIP as a discovery protocol -to ignore all RIPv2 packets that carry authentication when this machine -does not care about authentication. -.It Fl T Ar tracefile -increases the debugging level to at least 1 and -causes debugging information to be appended to the trace file. -.It Fl t -increases the debugging level, which causes more information to be logged -on the tracefile specified with -.Fl T -or standard out. -The debugging level can be increased or decreased -with the -.Em SIGUSR1 -or -.Em SIGUSR2 -signals or with the -.Cm rtquery -command. -.It Fl F Ar net[/mask][,metric] -minimize routes in transmissions via interfaces with addresses that match -.Em net/mask , -and synthesizes a default route to this machine with the -.Em metric . -The intent is to reduce RIP traffic on slow, point-to-point links -such as PPP links by replacing many large UDP packets of RIP information -with a single, small packet containing a "fake" default route. -If -.Em metric -is absent, a value of 14 is assumed to limit -the spread of the "fake" default route. - -This is a dangerous feature that when used carelessly can cause routing -loops. -Notice also that more than one interface can match the specified network -number and mask. -See also -.Fl g . -.It Fl P Ar parms -is equivalent to adding the parameter -line -.Em parms -to the -.Pa /etc/gateways -file. -.El -.Pp -Any other argument supplied is interpreted as the name -of a file in which the actions of -.Nm -should be logged. -It is better to use -.Fl T -instead of -appending the name of the trace file to the command. -.Pp -.Nm -also supports the notion of -"distant" -.Em passive -or -.Em active -gateways. -When -.Nm -is started, it reads the file -.Pa /etc/gateways -to find such distant gateways which may not be located using -only information from a routing socket, to discover if some -of the local gateways are -.Em passive , -and to obtain other parameters. -Gateways specified in this manner should be marked passive -if they are not expected to exchange routing information, -while gateways marked active -should be willing to exchange RIP packets. -Routes through -.Em passive -gateways are installed in the -kernel's routing tables once upon startup and are not included in -transmitted RIP responses. -.Pp -Distant active gateways are treated like network interfaces. -RIP responses are sent -to the distant -.Em active -gateway. -If no responses are received, the associated route is deleted from -the kernel table and RIP responses advertised via other interfaces. -If the distant gateway resumes sending RIP responses, the associated -route is restored. -.Pp -Such gateways can be useful on media that do not support broadcasts -or multicasts but otherwise act like classic shared media like -Ethernets such as some ATM networks. -One can list all RIP routers reachable on the ATM network in -.Pa /etc/gateways -with a series of -"host" lines. -.Pp -Gateways marked -.Em external -are also passive, but are not placed in the kernel -routing table nor are they included in routing updates. -The function of external entries is to indicate -that another routing process -will install such a route if ncessary, -and that alternate routes to that destination should not be installed -by -.Nm routed . -Such entries are only required when both routers may learn of routes -to the same destination. -.Pp -The -.Em /etc/gateways -file is comprised of a series of lines, each in -one of the following formats or consist of parameters described below: -.Pp -.Bd -ragged -.Cm net -.Ar Nname[/mask] -.Cm gateway -.Ar Gname -.Cm metric -.Ar value -.Pf < Cm passive No \&| -.Cm active No \&| -.Cm extern Ns > -.Ed -.Bd -ragged -.Cm host -.Ar Hname -.Cm gateway -.Ar Gname -.Cm metric -.Ar value -.Pf < Cm passive No \&| -.Cm active No \&| -.Cm extern Ns > -.Ed -.Pp -.Ar Nname -or -.Ar Hname -is the name of the destination network or host. -It may be a symbolic network name or an Internet address -specified in "dot" notation (see -.Xr inet 3 ). -(If it is a name, then it must either be defined in -.Pa /etc/networks -or -.Pa /etc/hosts , -or -.Xr named 8 , -must have been started before -.Xr routed Ns .) -.Pp -.Ar mask -is an optional number between 1 and 32 indicating the netmask associated -with -.Ar Nname . -.Pp -.Ar Gname -is the name or address of the gateway to which RIP responses should -be forwarded. -.Pp -.Ar Value -is the hop count to the destination host or network. -.Ar " host hname " -is equivalent to -.Ar " net nname/32 ". -.Pp -One of the keywords -.Cm passive , -.Cm active -or -.Cm external -must be present to indicate whether the gateway should be treated as -.Cm passive -or -.Cm active -(as described above), -or whether the gateway is -.Cm external -to the scope of the RIP protocol. -.Pp -Lines that start with neither "net" nor "host" must consist of one -or more of the following parameter settings, separated by commas or -blanks: -.Bl -tag -width Ds -.It Cm if Ns \&= Ns Ar ifname -indicates that the other parameters on the line apply to the interface -name -.Ar ifname . -.It Cm subnet Ns \&= Ns Ar nname[/mask][,metric] -advertises a route to network -.AR nname -with mask -.AR mask -and the supplied metric (default 1). -This is useful for filling "holes" in CIDR allocations. -This parameter must appear by itself on a line. -.Pp -Do not use this feature unless necessary. It is dangerous. -.It Cm passwd Ns \&= Ns Ar XXX -specifies a RIPv2 password that will be included on all RIPv2 -responses sent and checked on all RIPv2 responses received. -The password must not contain any blanks, tab characters, commas -or '#' characters. -.It Cm no_ag -turns off aggregation of subnets in RIPv1 and RIPv2 responses. -.It Cm no_super_ag -turns off aggregation of networks into supernets in RIPv2 responses. -.It Cm passive -is equivalent -.Cm no_rip Cm no_rdisc . -.It Cm no_rip -disables all RIP processing on the specified interface. -If no interfaces are allowed to process RIP packets, -.Nm -acts purely as a router discovery daemon. -.Cm No_rip -is equivalent to -.Cm no_ripv1_in no_ripv2_in no_ripv1_out no_ripv2_out . - -Note that turning off RIP without explicitly turning on router -discovery advertisements with -.Cm rdisc_adv -or -.Fl s -causes -.Nm routed -to act as a client router discovery daemon, not adveritising. -.It Cm no_ripv1_in -causes RIPv1 received responses to be ignored. -.It Cm no_ripv2_in -causes RIPv2 received responses to be ignored. -.It Cm ripv2_out -turns off RIPv1 output and causes RIPv2 advertisements to be -multicast when possible. -.It Cm no_rdisc -disables the Internet Router Discovery Protocol. -.It Cm no_solicit -disables the tranmission of Router Discovery Solicitations. -.It Cm send_solicit -specifies that Router Discovery solicitations should be sent, -even on point-to-point links, -which by default only listen to Router Discovery messages. -.It Cm no_rdisc_adv -disables the transmission of Router Discovery Advertisements -.It Cm rdisc_adv -specifies that Router Discovery advertisements should be sent, -even on point-to-point links, -which by default only listen to Router Discovery messages -.It Cm bcast_rdisc -specifies that Router Discovery packets should be broadcast instead of -multicast. -.It Cm rdisc_pref Ns \&= Ns Ar N -sets the preference in Router Discovery Advertisements to the integer -.Ar N . -.It Cm rdisc_interval Ns \&= Ns Ar N -sets the nominal interval with which Router Discovery Advertisements -are transmitted to N seconds and their lifetime to 3*N. -.It Cm fake_default Ns \&= Ns Ar metric -has an identical effect to -.Fl F Ar net[/mask][,metric] -with the network and mask coming from the sepcified interface. -.It Cm pm_rdisc -is similar to -.Cm fake_default . -When RIPv2 routes are multicast, so that RIPv1 listeners cannot -receive them, this feature causes a RIPv1 default route to be -broadcast to RIPv1 listeners. -Unless modified with -.Cm fake_default , -the default route is broadcast with a metric of 14. -That serves as a "poor man's router discovery" protocol. -.El -.Pp -Note that the netmask associated with point-to-point links (such as SLIP -or PPP, with the IFF_POINTOPOINT flag) is used by -.Nm routed -to infer the netmask used by the remote system when RIPv1 is used. -.Pp -.Sh FILES -.Bl -tag -width /etc/gateways -compact -.It Pa /etc/gateways -for distant gateways -.El -.Sh SEE ALSO -.Xr gated 8 , -.Xr udp 4 , -.Xr icmp 4 , -.Xr htable 8 , -.Xr rtquery 8 . -.Rs -.%T Internet Transport Protocols -.%R XSIS 028112 -.%Q Xerox System Integration Standard -.Re -.Sh BUGS -It does not always detect unidirectional failures in network interfaces -(e.g., when the output side fails). -.Sh HISTORY -The -.Nm -command appeared in -.Bx 4.2 . diff --git a/usr.sbin/routed/routed.h b/usr.sbin/routed/routed.h deleted file mode 100644 index 8bfc0a191257..000000000000 --- a/usr.sbin/routed/routed.h +++ /dev/null @@ -1,162 +0,0 @@ -/*- - * 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 - * - * $NetBSD$ - */ - -#ifndef _ROUTED_H_ -#define _ROUTED_H_ -#ifdef __cplusplus -extern "C" { -#endif -#ident "$Revision: 1.9 $" - -/* - * 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. 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 */ - u_int32_t rip_metric; /* cost of route */ -}; -#else -struct netinfo { - u_int16_t n_family; -#define RIP_AF_INET htons(AF_INET) -#define RIP_AF_UNSPEC 0 -#define RIP_AF_AUTH 0xffff - u_int16_t n_tag; /* optional in RIPv2 */ - u_int32_t n_dst; /* destination net or host */ -#define RIP_DEFAULT 0 - 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_int16_t a_type; -#define RIP_AUTH_PW htons(2) /* password type */ - union { -#define RIP_AUTH_PW_LEN 16 - int8_t au_pw[RIP_AUTH_PW_LEN]; - } au; -}; - -struct rip { - 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]; - int8_t 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_int32_t)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_ */ diff --git a/usr.sbin/routed/rtquery/Makefile b/usr.sbin/routed/rtquery/Makefile deleted file mode 100644 index f9aee5c55d06..000000000000 --- a/usr.sbin/routed/rtquery/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -# @(#)Makefile 8.1 (Berkeley) 6/5/93 - -PROG= rtquery -MAN8= rtquery.0 -#COPTS= -g -DDEBUG -Wall - -.include "../../Makefile.inc" -.include diff --git a/usr.sbin/routed/rtquery/rtquery.8 b/usr.sbin/routed/rtquery/rtquery.8 deleted file mode 100644 index 1afb8b9478be..000000000000 --- a/usr.sbin/routed/rtquery/rtquery.8 +++ /dev/null @@ -1,101 +0,0 @@ -.Dd June 1, 1996 -.Dt RTQUERY 8 -.Os BSD 4.4 -.Sh NAME -.Nm rtquery -.Nd query routing daemons for their routing tables -.Sh SYNOPSIS -.Nm -.Op Fl np1 -.Op Fl w Ar timeout -.Op Fl r Ar addr -.Ar host ... -.Sh DESCRIPTION -.Nm Rtquery -is used to query a network routing daemon, -.Xr routed 8 -or -.Xr gated 8 , -for its routing table by sending a -.Em request -or -.Em poll -command. The routing information in any routing -.Em response -packets returned is displayed numerically and symbolically. -.Pp -.Em Rtquery -by default uses the -.Em request -command. -When the -.Ar -p -option is specified, -.Nm rtquery -uses the -.Em poll -command, an -undocumented extension to the RIP protocol supported by -.Xr gated 8 . -When querying gated, the -.Em poll -command is preferred over the -.I Request -command because the response is not subject to Split Horizon and/or -Poisoned Reverse, and because some versions of gated do not answer -the Request command. Routed does not answer the Poll command, but -recognizes Requests coming from rtquery and so answers completely. -.Pp -.Em Rtquery -is also used to turn tracing on or off in -.Em routed . -.Pp -Options supported by -.Nm rtquery : -.Bl -tag -width Ds -.It Fl n -Normally network and host numbers are displayed both symbolically -and numerically. -The -.Fl n -option displays only the numeric network and host numbers. -.It Fl p -Uses the -.Em Poll -command to request full routing information from -.Xr gated 8 , -This is an undocumented extension RIP protocol supported only by -.Xr gated 8 . -.It Fl 1 -query using RIP version 1 instead of RIP version 2. -.It Fl w Ar timeout -changes the delay for an answer from each host. -By default, each host is given 15 seconds to respond. -.It Fl r Ar addr -ask about the route to destination -.Em addr . -.It Fl t Ar op -change tracing, where -.Em op -is one of the following. -Requests from processes not running with UID 0 or on distant networks -are generally ignored. -.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 . -.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 deleted file mode 100644 index cc1be10ab228..000000000000 --- a/usr.sbin/routed/rtquery/rtquery.c +++ /dev/null @@ -1,647 +0,0 @@ -/*- - * Copyright (c) 1982, 1986, 1993 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -char copyright[] = -"@(#) Copyright (c) 1982, 1986, 1993\n\ - The Regents of the University of California. All rights reserved.\n"; - -#if !defined(lint) && !defined(sgi) && !defined(__NetBSD__) -static char sccsid[] = "@(#)query.c 8.1 (Berkeley) 6/5/93"; -#elif defined(__NetBSD__) -static char rcsid[] = "$NetBSD$"; -#endif -#ident "$Revision: 1.8 $" - -#include -#include -#include -#include -#include -#define RIPVERSION RIPv2 -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef sgi -#include -#include -#endif - -#ifndef sgi -#define _HAVE_SIN_LEN -#endif - -#define WTIME 15 /* Time to wait for all responses */ -#define STIME (250*1000) /* usec to wait for another response */ - -int s; - -char *pgmname; - -union { - struct rip rip; - char packet[MAXPACKETSIZE+MAXPATHLEN]; -} omsg_buf; -#define OMSG omsg_buf.rip -int omsg_len = sizeof(struct rip); - -union { - struct rip rip; - char packet[MAXPACKETSIZE+1024]; - } imsg_buf; -#define IMSG imsg_buf.rip - -int nflag; /* numbers, no names */ -int pflag; /* play the `gated` game */ -int ripv2 = 1; /* use RIP version 2 */ -int wtime = WTIME; -int rflag; /* 1=ask about a particular route */ -int trace; -int not_trace; - -struct timeval sent; /* when query sent */ - -static void rip_input(struct sockaddr_in*, int); -static int out(char *); -static void trace_loop(char *argv[]); -static void query_loop(char *argv[], int); -static int getnet(char *, struct netinfo *); -static u_int std_mask(u_int); - - -int -main(int argc, - char *argv[]) -{ - int ch, bsize; - char *p, *options, *value; - - OMSG.rip_nets[0].n_dst = RIP_DEFAULT; - OMSG.rip_nets[0].n_family = RIP_AF_UNSPEC; - OMSG.rip_nets[0].n_metric = htonl(HOPCNT_INFINITY); - - pgmname = argv[0]; - while ((ch = getopt(argc, argv, "np1w:r:t:")) != EOF) - switch (ch) { - case 'n': - not_trace = 1; - nflag = 1; - break; - - case 'p': - not_trace = 1; - pflag = 1; - break; - - case '1': - ripv2 = 0; - break; - - case 'w': - not_trace = 1; - wtime = (int)strtoul(optarg, &p, 0); - if (*p != '\0' - || wtime <= 0) - goto usage; - break; - - case 'r': - not_trace = 1; - if (rflag) - goto usage; - rflag = getnet(optarg, &OMSG.rip_nets[0]); - if (!rflag) { - struct hostent *hp = gethostbyname(optarg); - if (hp == 0) { - fprintf(stderr, "%s: %s:", - pgmname, optarg); - herror(0); - exit(1); - } - bcopy(hp->h_addr, &OMSG.rip_nets[0].n_dst, - sizeof(OMSG.rip_nets[0].n_dst)); - OMSG.rip_nets[0].n_family = RIP_AF_INET; - OMSG.rip_nets[0].n_mask = -1; - rflag = 1; - } - break; - - case 't': - trace = 1; - options = optarg; - while (*options != '\0') { - char *traceopts[] = { -# define TRACE_ON 0 - "on", -# define TRACE_MORE 1 - "more", -# define TRACE_OFF 2 - "off", - 0 - }; - switch (getsubopt(&options,traceopts,&value)) { - case TRACE_ON: - OMSG.rip_cmd = RIPCMD_TRACEON; - 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'; - 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 ((not_trace && trace) || argc == 0) { -usage: fprintf(stderr, "%s: [-np1v] [-r tgt_rt] [-w wtime]" - " host1 [host2 ...]\n" - "or\t-t {on=filename|more|off} host1 host2 ...\n", - pgmname); - exit(1); - } - - s = socket(AF_INET, SOCK_DGRAM, 0); - if (s < 0) { - perror("socket"); - exit(2); - } - - /* be prepared to receive a lot of routes */ - for (bsize = 127*1024; ; bsize -= 1024) { - if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, - &bsize, sizeof(bsize)) == 0) - break; - if (bsize <= 4*1024) { - perror("setsockopt SO_RCVBUF"); - break; - } - } - - if (trace) - trace_loop(argv); - else - query_loop(argv, argc); - /* NOTREACHED */ -} - - -/* tell the target hosts about tracing - */ -static void -trace_loop(char *argv[]) -{ - struct sockaddr_in myaddr; - int res; - - if (geteuid() != 0) { - (void)fprintf(stderr, "-t requires UID 0\n"); - exit(1); - } - - if (ripv2) { - OMSG.rip_vers = RIPv2; - } else { - OMSG.rip_vers = RIPv1; - } - - bzero(&myaddr, sizeof(myaddr)); - myaddr.sin_family = AF_INET; -#ifdef _HAVE_SIN_LEN - myaddr.sin_len = sizeof(myaddr); -#endif - myaddr.sin_port = htons(IPPORT_RESERVED-1); - while (bind(s, (struct sockaddr *)&myaddr, sizeof(myaddr)) < 0) { - if (errno != EADDRINUSE - || myaddr.sin_port == 0) { - perror("bind"); - exit(2); - } - myaddr.sin_port = htons(ntohs(myaddr.sin_port)-1); - } - - res = 1; - while (*argv != 0) { - if (out(*argv++) <= 0) - res = 0; - } - exit(res); -} - - -/* query all of the listed hosts - */ -static void -query_loop(char *argv[], int argc) -{ - struct seen { - struct seen *next; - struct in_addr addr; - } *seen, *sp; - int answered = 0; - int cc; - fd_set bits; - struct timeval now, delay; - struct sockaddr_in from; - int fromlen; - - - OMSG.rip_cmd = (pflag) ? RIPCMD_POLL : RIPCMD_REQUEST; - if (ripv2) { - OMSG.rip_vers = RIPv2; - } else { - OMSG.rip_vers = RIPv1; - OMSG.rip_nets[0].n_mask = 0; - } - - /* ask the first (valid) host */ - seen = 0; - while (0 > out(*argv++)) { - if (*argv == 0) - exit(-1); - answered++; - } - - FD_ZERO(&bits); - for (;;) { - FD_SET(s, &bits); - delay.tv_sec = 0; - delay.tv_usec = STIME; - cc = select(s+1, &bits, 0,0, &delay); - if (cc > 0) { - fromlen = sizeof(from); - cc = recvfrom(s, imsg_buf.packet, - sizeof(imsg_buf.packet), 0, - (struct sockaddr *)&from, &fromlen); - if (cc < 0) { - perror("recvfrom"); - exit(1); - } - /* count the distinct responding hosts. - * You cannot match responding hosts with - * addresses to which queries were transmitted, - * because a router might respond with a - * different source address. - */ - for (sp = seen; sp != 0; sp = sp->next) { - if (sp->addr.s_addr == from.sin_addr.s_addr) - break; - } - if (sp == 0) { - sp = malloc(sizeof(*sp)); - sp->addr = from.sin_addr; - sp->next = seen; - seen = sp; - answered++; - } - - rip_input(&from, cc); - continue; - } - - if (cc < 0) { - if ( errno == EINTR) - continue; - perror("select"); - exit(1); - } - - /* After a pause in responses, probe another host. - * This reduces the intermingling of answers. - */ - while (*argv != 0 && 0 > out(*argv++)) - answered++; - - /* continue until no more packets arrive - * or we have heard from all hosts - */ - if (answered >= argc) - break; - - /* or until we have waited a long time - */ - if (gettimeofday(&now, 0) < 0) { - perror("gettimeofday(now)"); - exit(1); - } - if (sent.tv_sec + wtime <= now.tv_sec) - break; - } - - /* fail if there was no answer */ - exit (answered >= argc ? 0 : 1); -} - - -/* sent do one host - */ -static int -out(char *host) -{ - struct sockaddr_in router; - struct hostent *hp; - - if (gettimeofday(&sent, 0) < 0) { - perror("gettimeofday(sent)"); - return -1; - } - - bzero(&router, sizeof(router)); - router.sin_family = AF_INET; -#ifdef _HAVE_SIN_LEN - router.sin_len = sizeof(router); -#endif - if (!inet_aton(host, &router.sin_addr)) { - hp = gethostbyname(host); - if (hp == 0) { - herror(host); - return -1; - } - bcopy(hp->h_addr, &router.sin_addr, sizeof(router.sin_addr)); - } - router.sin_port = htons(RIP_PORT); - - if (sendto(s, &omsg_buf, omsg_len, 0, - (struct sockaddr *)&router, sizeof(router)) < 0) { - perror(host); - return -1; - } - - return 0; -} - - -/* - * Handle an incoming RIP packet. - */ -static void -rip_input(struct sockaddr_in *from, - int size) -{ - struct netinfo *n, *lim; - struct in_addr in; - char *name; - char net_buf[80]; - u_int mask, dmask; - char *sp; - int i; - struct hostent *hp; - struct netent *np; - struct netauth *a; - - - if (nflag) { - printf("%s:", inet_ntoa(from->sin_addr)); - } else { - hp = gethostbyaddr((char*)&from->sin_addr, - sizeof(struct in_addr), AF_INET); - if (hp == 0) { - printf("%s:", - inet_ntoa(from->sin_addr)); - } else { - printf("%s (%s):", hp->h_name, - inet_ntoa(from->sin_addr)); - } - } - if (IMSG.rip_cmd != RIPCMD_RESPONSE) { - printf("\n unexpected response type %d\n", IMSG.rip_cmd); - return; - } - printf(" RIPv%d%s %d bytes\n", IMSG.rip_vers, - (IMSG.rip_vers != RIPv1 && IMSG.rip_vers != RIPv2) ? " ?" : "", - size); - if (size > MAXPACKETSIZE) { - if (size > sizeof(imsg_buf) - sizeof(*n)) { - printf(" at least %d bytes too long\n", - size-MAXPACKETSIZE); - size = sizeof(imsg_buf) - sizeof(*n); - } else { - printf(" %d bytes too long\n", - size-MAXPACKETSIZE); - } - } else if (size%sizeof(*n) != sizeof(struct rip)%sizeof(*n)) { - printf(" response of bad length=%d\n", size); - } - - n = IMSG.rip_nets; - lim = (struct netinfo *)((char*)n + size) - 1; - for (; n <= lim; n++) { - name = ""; - if (n->n_family == RIP_AF_INET) { - in.s_addr = n->n_dst; - (void)strcpy(net_buf, inet_ntoa(in)); - - mask = ntohl(n->n_mask); - dmask = mask & -mask; - if (mask != 0) { - sp = &net_buf[strlen(net_buf)]; - if (IMSG.rip_vers == RIPv1) { - (void)sprintf(sp," mask=%#x ? ",mask); - mask = 0; - } else if (mask + dmask == 0) { - for (i = 0; - (i != 32 - && ((1<n_name; - else if (in.s_addr == 0) - name = "default"; - } - if (name[0] == '\0' - && ((in.s_addr & ~mask) != 0 - || mask == 0xffffffff)) { - hp = gethostbyaddr((char*)&in, - sizeof(in), - AF_INET); - if (hp != 0) - name = hp->h_name; - } - } - - } else if (n->n_family == RIP_AF_AUTH) { - a = (struct netauth*)n; - (void)printf(" authentication type %d: ", - a->a_type); - for (i = 0; i < sizeof(a->au.au_pw); i++) - (void)printf("%02x ", a->au.au_pw[i]); - putc('\n', stdout); - continue; - - } else { - (void)sprintf(net_buf, "(af %#x) %d.%d.%d.%d", - ntohs(n->n_family), - (char)(n->n_dst >> 24), - (char)(n->n_dst >> 16), - (char)(n->n_dst >> 8), - (char)n->n_dst); - } - - (void)printf(" %-18s metric %2d %-10s", - net_buf, ntohl(n->n_metric), name); - - if (n->n_nhop != 0) { - in.s_addr = n->n_nhop; - if (nflag) - hp = 0; - else - hp = gethostbyaddr((char*)&in, sizeof(in), - AF_INET); - (void)printf(" nhop=%-15s%s", - (hp != 0) ? hp->h_name : inet_ntoa(in), - (IMSG.rip_vers == RIPv1) ? " ?" : ""); - } - if (n->n_tag != 0) - (void)printf(" tag=%#x%s", n->n_tag, - (IMSG.rip_vers == RIPv1) ? " ?" : ""); - putc('\n', stdout); - } -} - - -/* Return the classical netmask for an IP address. - */ -static u_int -std_mask(u_int addr) /* in network order */ -{ - NTOHL(addr); /* was a host, not a network */ - - if (addr == 0) /* default route has mask 0 */ - return 0; - if (IN_CLASSA(addr)) - return IN_CLASSA_NET; - if (IN_CLASSB(addr)) - return IN_CLASSB_NET; - return IN_CLASSC_NET; -} - - -/* get a network number as a name or a number, with an optional "/xx" - * netmask. - */ -static int /* 0=bad */ -getnet(char *name, - struct netinfo *rt) -{ - int i; - struct netent *nentp; - u_int mask; - struct in_addr in; - char hname[MAXHOSTNAMELEN+1]; - char *mname, *p; - - - /* Detect and separate "1.2.3.4/24" - */ - if (0 != (mname = rindex(name,'/'))) { - i = (int)(mname - name); - if (i > sizeof(hname)-1) /* name too long */ - return 0; - bcopy(name, hname, i); - hname[i] = '\0'; - mname++; - name = hname; - } - - nentp = getnetbyname(name); - if (nentp != 0) { - in.s_addr = nentp->n_net; - } else if (inet_aton(name, &in) == 1) { - NTOHL(in.s_addr); - } else { - return 0; - } - - if (mname == 0) { - mask = std_mask(in.s_addr); - if ((~mask & in.s_addr) != 0) - mask = 0xffffffff; - } else { - mask = (u_int)strtoul(mname, &p, 0); - if (*p != '\0' || mask > 32) - return 0; - mask = 0xffffffff << (32-mask); - } - - rt->n_dst = htonl(in.s_addr); - rt->n_family = RIP_AF_INET; - rt->n_mask = htonl(mask); - return 1; -} diff --git a/usr.sbin/routed/rttrace/Makefile b/usr.sbin/routed/rttrace/Makefile deleted file mode 100644 index df19d5c61b44..000000000000 --- 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 0d8ac6224ce7..000000000000 --- 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; -} diff --git a/usr.sbin/routed/table.c b/usr.sbin/routed/table.c deleted file mode 100644 index b1566a3792a8..000000000000 --- a/usr.sbin/routed/table.c +++ /dev/null @@ -1,1954 +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. - */ - -#if !defined(lint) && !defined(sgi) && !defined(__NetBSD__) -static char sccsid[] = "@(#)tables.c 8.1 (Berkeley) 6/5/93"; -#elif defined(__NetBSD__) -static char rcsid[] = "$NetBSD$"; -#endif -#ident "$Revision: 1.23 $" - -#include "defs.h" - -static struct rt_spare *rts_better(struct rt_entry *); - -struct radix_node_head *rhead; /* root of the radix tree */ - -int need_flash = 1; /* flash update needed - * start =1 to suppress the 1st - */ - -struct timeval age_timer; /* next check of old routes */ -struct timeval need_kern = { /* need to update kernel table */ - EPOCH+MIN_WAITTIME-1 -}; - -int stopint; - -int total_routes; - -naddr age_bad_gate; - - -/* It is desirable to "aggregate" routes, to combine differing routes of - * the same metric and next hop into a common route with a smaller netmask - * or to suppress redundant routes, routes that add no information to - * routes with smaller netmasks. - * - * A route is redundant if and only if any and all routes with smaller - * but matching netmasks and nets are the same. Since routes are - * kept sorted in the radix tree, redundant routes always come second. - * - * There are two kinds of aggregations. First, two routes of the same bit - * mask and differing only in the least significant bit of the network - * number can be combined into a single route with a coarser mask. - * - * Second, a route can be suppressed in favor of another route with a more - * coarse mask provided no incompatible routes with intermediate masks - * are present. The second kind of aggregation involves suppressing routes. - * A route must not be suppressed if an incompatible route exists with - * an intermediate mask, since the suppressed route would be covered - * by the intermediate. - * - * This code relies on the radix tree walk encountering routes - * sorted first by address, with the smallest address first. - */ - -struct ag_info ag_slots[NUM_AG_SLOTS], *ag_avail, *ag_corsest, *ag_finest; - -/* #define DEBUG_AG */ -#ifdef DEBUG_AG -#define CHECK_AG() {int acnt = 0; struct ag_info *cag; \ - for (cag = ag_avail; cag != 0; cag = cag->ag_fine) \ - acnt++; \ - for (cag = ag_corsest; cag != 0; cag = cag->ag_fine) \ - acnt++; \ - if (acnt != NUM_AG_SLOTS) { \ - (void)fflush(stderr); \ - abort(); \ - } \ -} -#else -#define CHECK_AG() -#endif - - -/* Output the contents of an aggregation table slot. - * This function must always be immediately followed with the deletion - * of the target slot. - */ -static void -ag_out(struct ag_info *ag, - void (*out)(struct ag_info *)) -{ - struct ag_info *ag_cors; - naddr bit; - - - /* If we output both the even and odd twins, then the immediate parent, - * if it is present, is redundant, unless the parent manages to - * aggregate into something coarser. - * On successive calls, this code detects the even and odd twins, - * and marks the parent. - * - * Note that the order in which the radix tree code emits routes - * ensures that the twins are seen before the parent is emitted. - */ - ag_cors = ag->ag_cors; - if (ag_cors != 0 - && ag_cors->ag_mask == ag->ag_mask<<1 - && ag_cors->ag_dst_h == (ag->ag_dst_h & ag_cors->ag_mask)) { - ag_cors->ag_state |= ((ag_cors->ag_dst_h == ag->ag_dst_h) - ? AGS_REDUN0 - : AGS_REDUN1); - } - - /* Skip it if this route is itself redundant. - * - * It is ok to change the contents of the slot here, since it is - * always deleted next. - */ - if (ag->ag_state & AGS_REDUN0) { - if (ag->ag_state & AGS_REDUN1) - return; - bit = (-ag->ag_mask) >> 1; - ag->ag_dst_h |= bit; - ag->ag_mask |= bit; - - } else if (ag->ag_state & AGS_REDUN1) { - bit = (-ag->ag_mask) >> 1; - ag->ag_mask |= bit; - } - out(ag); -} - - -static void -ag_del(struct ag_info *ag) -{ - CHECK_AG(); - - if (ag->ag_cors == 0) - ag_corsest = ag->ag_fine; - else - ag->ag_cors->ag_fine = ag->ag_fine; - - if (ag->ag_fine == 0) - ag_finest = ag->ag_cors; - else - ag->ag_fine->ag_cors = ag->ag_cors; - - ag->ag_fine = ag_avail; - ag_avail = ag; - - CHECK_AG(); -} - - -/* Flush routes waiting for aggretation. - * This must not suppress a route unless it is known that among all - * routes with coarser masks that match it, the one with the longest - * mask is appropriate. This is ensured by scanning the routes - * in lexical order, and with the most restritive mask first - * among routes to the same destination. - */ -void -ag_flush(naddr lim_dst_h, /* flush routes to here */ - naddr lim_mask, /* matching this mask */ - void (*out)(struct ag_info *)) -{ - struct ag_info *ag, *ag_cors; - naddr dst_h; - - - for (ag = ag_finest; - ag != 0 && ag->ag_mask >= lim_mask; - ag = ag_cors) { - ag_cors = ag->ag_cors; - - /* work on only the specified routes */ - dst_h = ag->ag_dst_h; - if ((dst_h & lim_mask) != lim_dst_h) - continue; - - if (!(ag->ag_state & AGS_SUPPRESS)) - ag_out(ag, out); - - else for ( ; ; ag_cors = ag_cors->ag_cors) { - /* Look for a route that can suppress the - * current route */ - if (ag_cors == 0) { - /* failed, so output it and look for - * another route to work on - */ - ag_out(ag, out); - break; - } - - if ((dst_h & ag_cors->ag_mask) == ag_cors->ag_dst_h) { - /* We found a route with a coarser mask that - * aggregates the current target. - * - * If it has a different next hop, it - * cannot replace the target, so output - * the target. - */ - if (ag->ag_gate != ag_cors->ag_gate - && !(ag->ag_state & AGS_FINE_GATE) - && !(ag_cors->ag_state & AGS_CORS_GATE)) { - ag_out(ag, out); - break; - } - - /* If the coarse route has a good enough - * metric, it suppresses the target. - */ - if (ag_cors->ag_pref <= ag->ag_pref) { - if (ag_cors->ag_seqno > ag->ag_seqno) - ag_cors->ag_seqno = ag->ag_seqno; - if (AG_IS_REDUN(ag->ag_state) - && ag_cors->ag_mask==ag->ag_mask<<1) { - if (ag_cors->ag_dst_h == dst_h) - ag_cors->ag_state |= AGS_REDUN0; - else - ag_cors->ag_state |= AGS_REDUN1; - } - if (ag->ag_tag != ag_cors->ag_tag) - ag_cors->ag_tag = 0; - if (ag->ag_nhop != ag_cors->ag_nhop) - ag_cors->ag_nhop = 0; - break; - } - } - } - - /* That route has either been output or suppressed */ - ag_cors = ag->ag_cors; - ag_del(ag); - } - - CHECK_AG(); -} - - -/* Try to aggregate a route with previous routes. - */ -void -ag_check(naddr dst, - naddr mask, - naddr gate, - naddr nhop, - char metric, - char pref, - u_int seqno, - u_short tag, - u_short state, - void (*out)(struct ag_info *)) /* output using this */ -{ - struct ag_info *ag, *nag, *ag_cors; - naddr xaddr; - int x; - - NTOHL(dst); - - /* Punt non-contiguous subnet masks. - * - * (X & -X) contains a single bit if and only if X is a power of 2. - * (X + (X & -X)) == 0 if and only if X is a power of 2. - */ - if ((mask & -mask) + mask != 0) { - struct ag_info nc_ag; - - nc_ag.ag_dst_h = dst; - nc_ag.ag_mask = mask; - nc_ag.ag_gate = gate; - nc_ag.ag_nhop = nhop; - nc_ag.ag_metric = metric; - nc_ag.ag_pref = pref; - nc_ag.ag_tag = tag; - nc_ag.ag_state = state; - nc_ag.ag_seqno = seqno; - out(&nc_ag); - return; - } - - /* Search for the right slot in the aggregation table. - */ - ag_cors = 0; - ag = ag_corsest; - while (ag != 0) { - if (ag->ag_mask >= mask) - break; - - /* Suppress old routes (i.e. combine with compatible routes - * with coarser masks) as we look for the right slot in the - * aggregation table for the new route. - * A route to an address less than the current destination - * will not be affected by the current route or any route - * seen hereafter. That means it is safe to suppress it. - * This check keeps poor routes (eg. with large hop counts) - * from preventing suppresion of finer routes. - */ - if (ag_cors != 0 - && ag->ag_dst_h < dst - && (ag->ag_state & AGS_SUPPRESS) - && ag_cors->ag_pref <= ag->ag_pref - && (ag->ag_dst_h & ag_cors->ag_mask) == ag_cors->ag_dst_h - && (ag_cors->ag_gate == ag->ag_gate - || (ag->ag_state & AGS_FINE_GATE) - || (ag_cors->ag_state & AGS_CORS_GATE))) { - if (ag_cors->ag_seqno > ag->ag_seqno) - ag_cors->ag_seqno = ag->ag_seqno; - if (AG_IS_REDUN(ag->ag_state) - && ag_cors->ag_mask==ag->ag_mask<<1) { - if (ag_cors->ag_dst_h == dst) - ag_cors->ag_state |= AGS_REDUN0; - else - ag_cors->ag_state |= AGS_REDUN1; - } - if (ag->ag_tag != ag_cors->ag_tag) - ag_cors->ag_tag = 0; - if (ag->ag_nhop != ag_cors->ag_nhop) - ag_cors->ag_nhop = 0; - ag_del(ag); - CHECK_AG(); - } else { - ag_cors = ag; - } - ag = ag_cors->ag_fine; - } - - /* If we find the even/odd twin of the new route, and if the - * masks and so forth are equal, we can aggregate them. - * We can probably promote one of the pair. - * - * Since the routes are encountered in lexical order, - * the new route must be odd. However, the second or later - * times around this loop, it could be the even twin promoted - * from the even/odd pair of twins of the finer route. - */ - while (ag != 0 - && ag->ag_mask == mask - && ((ag->ag_dst_h ^ dst) & (mask<<1)) == 0) { - - /* Here we know the target route and the route in the current - * slot have the same netmasks and differ by at most the - * last bit. They are either for the same destination, or - * for an even/odd pair of destinations. - */ - if (ag->ag_dst_h == dst) { - /* We have two routes to the same destination. - * Routes are encountered in lexical order, so a - * route is never promoted until the parent route is - * already present. So we know that the new route is - * a promoted pair and the route already in the slot - * is the explicit route. - * - * Prefer the best route if their metrics differ, - * or the promoted one if not, following a sort - * of longest-match rule. - */ - if (pref <= ag->ag_pref) { - ag->ag_gate = gate; - ag->ag_nhop = nhop; - ag->ag_tag = tag; - ag->ag_metric = metric; - ag->ag_pref = pref; - x = ag->ag_state; - ag->ag_state = state; - state = x; - } - - /* The sequence number controls flash updating, - * and should be the smaller of the two. - */ - if (ag->ag_seqno > seqno) - ag->ag_seqno = seqno; - - /* some bits are set if they are set on either route */ - ag->ag_state |= (state & (AGS_PROMOTE_EITHER - | AGS_REDUN0 | AGS_REDUN1)); - return; - } - - /* If one of the routes can be promoted and the other can - * be suppressed, it may be possible to combine them or - * worthwhile to promote one. - * - * Note that any route that can be promoted is always - * marked to be eligible to be suppressed. - */ - if (!((state & AGS_PROMOTE) - && (ag->ag_state & AGS_SUPPRESS)) - && !((ag->ag_state & AGS_PROMOTE) - && (state & AGS_SUPPRESS))) - break; - - /* A pair of even/odd twin routes can be combined - * if either is redundant, or if they are via the - * same gateway and have the same metric. - */ - if (AG_IS_REDUN(ag->ag_state) - || AG_IS_REDUN(state) - || (ag->ag_gate == gate - && ag->ag_pref == pref - && (state & ag->ag_state & AGS_PROMOTE) != 0)) { - - /* We have both the even and odd pairs. - * Since the routes are encountered in order, - * the route in the slot must be the even twin. - * - * Combine and promote the pair of routes. - */ - if (seqno > ag->ag_seqno) - seqno = ag->ag_seqno; - if (!AG_IS_REDUN(state)) - state &= ~AGS_REDUN1; - if (AG_IS_REDUN(ag->ag_state)) - state |= AGS_REDUN0; - else - state &= ~AGS_REDUN0; - state |= (ag->ag_state & AGS_PROMOTE_EITHER); - if (ag->ag_tag != tag) - tag = 0; - if (ag->ag_nhop != nhop) - nhop = 0; - - /* Get rid of the even twin that was already - * in the slot. - */ - ag_del(ag); - - } else if (ag->ag_pref >= pref - && (ag->ag_state & AGS_PROMOTE)) { - /* If we cannot combine the pair, maybe the route - * with the worse metric can be promoted. - * - * Promote the old, even twin, by giving its slot - * in the table to the new, odd twin. - */ - ag->ag_dst_h = dst; - - xaddr = ag->ag_gate; - ag->ag_gate = gate; - gate = xaddr; - - xaddr = ag->ag_nhop; - ag->ag_nhop = nhop; - nhop = xaddr; - - x = ag->ag_tag; - ag->ag_tag = tag; - tag = x; - - x = ag->ag_state; - ag->ag_state = state; - state = x; - if (!AG_IS_REDUN(state)) - state &= ~AGS_REDUN0; - - x = ag->ag_metric; - ag->ag_metric = metric; - metric = x; - - x = ag->ag_pref; - ag->ag_pref = pref; - pref = x; - - if (seqno >= ag->ag_seqno) - seqno = ag->ag_seqno; - else - ag->ag_seqno = seqno; - - } else { - if (!(state & AGS_PROMOTE)) - break; /* cannot promote either twin */ - - /* promote the new, odd twin by shaving its - * mask and address. - */ - if (seqno > ag->ag_seqno) - seqno = ag->ag_seqno; - else - ag->ag_seqno = seqno; - if (!AG_IS_REDUN(state)) - state &= ~AGS_REDUN1; - } - - mask <<= 1; - dst &= mask; - - if (ag_cors == 0) { - ag = ag_corsest; - break; - } - ag = ag_cors; - ag_cors = ag->ag_cors; - } - - /* When we can no longer promote and combine routes, - * flush the old route in the target slot. Also flush - * any finer routes that we know will never be aggregated by - * the new route. - * - * In case we moved toward coarser masks, - * get back where we belong - */ - if (ag != 0 - && ag->ag_mask < mask) { - ag_cors = ag; - ag = ag->ag_fine; - } - - /* Empty the target slot - */ - if (ag != 0 && ag->ag_mask == mask) { - ag_flush(ag->ag_dst_h, ag->ag_mask, out); - ag = (ag_cors == 0) ? ag_corsest : ag_cors->ag_fine; - } - -#ifdef DEBUG_AG - (void)fflush(stderr); - if (ag == 0 && ag_cors != ag_finest) - abort(); - if (ag_cors == 0 && ag != ag_corsest) - abort(); - if (ag != 0 && ag->ag_cors != ag_cors) - abort(); - if (ag_cors != 0 && ag_cors->ag_fine != ag) - abort(); - CHECK_AG(); -#endif - - /* Save the new route on the end of the table. - */ - nag = ag_avail; - ag_avail = nag->ag_fine; - - nag->ag_dst_h = dst; - nag->ag_mask = mask; - nag->ag_gate = gate; - nag->ag_nhop = nhop; - nag->ag_metric = metric; - nag->ag_pref = pref; - nag->ag_tag = tag; - nag->ag_state = state; - nag->ag_seqno = seqno; - - nag->ag_fine = ag; - if (ag != 0) - ag->ag_cors = nag; - else - ag_finest = nag; - nag->ag_cors = ag_cors; - if (ag_cors == 0) - ag_corsest = nag; - else - ag_cors->ag_fine = nag; - CHECK_AG(); -} - - -static char * -rtm_type_name(u_char type) -{ - static char *rtm_types[] = { - "RTM_ADD", - "RTM_DELETE", - "RTM_CHANGE", - "RTM_GET", - "RTM_LOSING", - "RTM_REDIRECT", - "RTM_MISS", - "RTM_LOCK", - "RTM_OLDADD", - "RTM_OLDDEL", - "RTM_RESOLVE", - "RTM_NEWADDR", - "RTM_DELADDR", - "RTM_IFINFO" - }; - static char name0[10]; - - - if (type > sizeof(rtm_types)/sizeof(rtm_types[0]) - || type == 0) { - sprintf(name0, "RTM type %#x", type); - return name0; - } else { - return rtm_types[type-1]; - } -} - - -/* Trim a mask in a sockaddr - * Produce a length of 0 for an address of 0. - * Otherwise produce the index of the first zero byte. - */ -void -#ifdef _HAVE_SIN_LEN -masktrim(struct sockaddr_in *ap) -#else -masktrim(struct sockaddr_in_new *ap) -#endif -{ - register char *cp; - - if (ap->sin_addr.s_addr == 0) { - ap->sin_len = 0; - return; - } - cp = (char *)(&ap->sin_addr.s_addr+1); - while (*--cp == 0) - continue; - ap->sin_len = cp - (char*)ap + 1; -} - - -/* Tell the kernel to add, delete or change a route - */ -static void -rtioctl(int action, /* RTM_DELETE, etc */ - naddr dst, - naddr gate, - naddr mask, - int metric, - int flags) -{ - struct { - struct rt_msghdr w_rtm; - struct sockaddr_in w_dst; - struct sockaddr_in w_gate; -#ifdef _HAVE_SA_LEN - struct sockaddr_in w_mask; -#else - struct sockaddr_in_new w_mask; -#endif - } w; - long cc; - -again: - bzero(&w, sizeof(w)); - w.w_rtm.rtm_msglen = sizeof(w); - w.w_rtm.rtm_version = RTM_VERSION; - w.w_rtm.rtm_type = action; - w.w_rtm.rtm_flags = flags; - w.w_rtm.rtm_seq = ++rt_sock_seqno; - w.w_rtm.rtm_addrs = RTA_DST|RTA_GATEWAY; - if (metric != 0) { - w.w_rtm.rtm_rmx.rmx_hopcount = metric; - w.w_rtm.rtm_inits |= RTV_HOPCOUNT; - } - w.w_dst.sin_family = AF_INET; - w.w_dst.sin_addr.s_addr = dst; - w.w_gate.sin_family = AF_INET; - w.w_gate.sin_addr.s_addr = gate; -#ifdef _HAVE_SA_LEN - w.w_dst.sin_len = sizeof(w.w_dst); - w.w_gate.sin_len = sizeof(w.w_gate); -#endif - if (mask == HOST_MASK) { - w.w_rtm.rtm_flags |= RTF_HOST; - w.w_rtm.rtm_msglen -= sizeof(w.w_mask); - } else { - w.w_rtm.rtm_addrs |= RTA_NETMASK; - w.w_mask.sin_addr.s_addr = htonl(mask); -#ifdef _HAVE_SA_LEN - masktrim(&w.w_mask); - if (w.w_mask.sin_len == 0) - w.w_mask.sin_len = sizeof(long); - w.w_rtm.rtm_msglen -= (sizeof(w.w_mask) - w.w_mask.sin_len); -#endif - } -#ifndef NO_INSTALL - cc = write(rt_sock, &w, w.w_rtm.rtm_msglen); - if (cc == w.w_rtm.rtm_msglen) - return; - if (cc < 0) { - if (errno == ESRCH - && (action == RTM_CHANGE || action == RTM_DELETE)) { - trace_act("route to %s disappeared before %s\n", - addrname(dst, mask, 0), - rtm_type_name(action)); - if (action == RTM_CHANGE) { - action = RTM_ADD; - goto again; - } - return; - } - msglog("write(rt_sock) %s %s --> %s: %s", - rtm_type_name(action), - addrname(dst, mask, 0), naddr_ntoa(gate), - strerror(errno)); - } else { - msglog("write(rt_sock) wrote %d instead of %d", - cc, w.w_rtm.rtm_msglen); - } -#endif -} - - -#define KHASH_SIZE 71 /* should be prime */ -#define KHASH(a,m) khash_bins[((a) ^ (m)) % KHASH_SIZE] -static struct khash { - struct khash *k_next; - naddr k_dst; - naddr k_mask; - naddr k_gate; - short k_metric; - u_short k_state; -#define KS_NEW 0x001 -#define KS_DELETE 0x002 -#define KS_ADD 0x004 /* add to the kernel */ -#define KS_CHANGE 0x008 /* tell kernel to change the route */ -#define KS_DEL_ADD 0x010 /* delete & add to change the kernel */ -#define KS_STATIC 0x020 /* Static flag in kernel */ -#define KS_GATEWAY 0x040 /* G flag in kernel */ -#define KS_DYNAMIC 0x080 /* result of redirect */ -#define KS_DELETED 0x100 /* already deleted */ - time_t k_keep; -#define K_KEEP_LIM 30 - time_t k_redirect_time; -} *khash_bins[KHASH_SIZE]; - - -static struct khash* -kern_find(naddr dst, naddr mask, struct khash ***ppk) -{ - struct khash *k, **pk; - - for (pk = &KHASH(dst,mask); (k = *pk) != 0; pk = &k->k_next) { - if (k->k_dst == dst && k->k_mask == mask) - break; - } - if (ppk != 0) - *ppk = pk; - return k; -} - - -static struct khash* -kern_add(naddr dst, naddr mask) -{ - struct khash *k, **pk; - - k = kern_find(dst, mask, &pk); - if (k != 0) - return k; - - k = (struct khash *)malloc(sizeof(*k)); - - bzero(k, sizeof(*k)); - k->k_dst = dst; - k->k_mask = mask; - k->k_state = KS_NEW; - k->k_keep = now.tv_sec; - *pk = k; - - return k; -} - - -/* If a kernel route has a non-zero metric, check that it is still in the - * daemon table, and not deleted by interfaces coming and going. - */ -static void -kern_check_static(struct khash *k, - struct interface *ifp) -{ - struct rt_entry *rt; - naddr int_addr; - - if (k->k_metric == 0) - return; - - int_addr = (ifp != 0) ? ifp->int_addr : loopaddr; - - rt = rtget(k->k_dst, k->k_mask); - if (rt != 0) { - if (!(rt->rt_state & RS_STATIC)) - rtchange(rt, rt->rt_state | RS_STATIC, - k->k_gate, int_addr, - k->k_metric, 0, ifp, now.tv_sec, 0); - } else { - rtadd(k->k_dst, k->k_mask, k->k_gate, int_addr, - k->k_metric, 0, RS_STATIC, ifp); - } -} - - -/* add a route the kernel told us - */ -static void -rtm_add(struct rt_msghdr *rtm, - struct rt_addrinfo *info, - time_t keep) -{ - struct khash *k; - struct interface *ifp; - naddr mask; - - - if (rtm->rtm_flags & RTF_HOST) { - mask = HOST_MASK; - } else if (INFO_MASK(info) != 0) { - mask = ntohl(S_ADDR(INFO_MASK(info))); - } else { - msglog("punt %s without mask", - rtm_type_name(rtm->rtm_type)); - return; - } - - if (INFO_GATE(info) == 0 - || INFO_GATE(info)->sa_family != AF_INET) { - msglog("punt %s without gateway", - rtm_type_name(rtm->rtm_type)); - return; - } - - k = kern_add(S_ADDR(INFO_DST(info)), mask); - if (k->k_state & KS_NEW) - k->k_keep = now.tv_sec+keep; - k->k_gate = S_ADDR(INFO_GATE(info)); - k->k_metric = rtm->rtm_rmx.rmx_hopcount; - if (k->k_metric < 0) - k->k_metric = 0; - else if (k->k_metric > HOPCNT_INFINITY) - k->k_metric = HOPCNT_INFINITY; - k->k_state &= ~(KS_DELETED | KS_GATEWAY | KS_STATIC | KS_NEW); - if (rtm->rtm_flags & RTF_GATEWAY) - k->k_state |= KS_GATEWAY; - if (rtm->rtm_flags & RTF_STATIC) - k->k_state |= KS_STATIC; - if (0 != (rtm->rtm_flags & (RTF_DYNAMIC | RTF_MODIFIED))) { - if (supplier) { - /* Routers are not supposed to listen to redirects, - * so delete it. - */ - k->k_state &= ~KS_DYNAMIC; - k->k_state |= KS_DELETE; - trace_act("mark redirected %s --> %s for deletion" - " since this is a router\n", - addrname(k->k_dst, k->k_mask, 0), - naddr_ntoa(k->k_gate)); - } else { - k->k_state |= KS_DYNAMIC; - k->k_redirect_time = now.tv_sec; - } - } - - /* If it is not a static route, quit until the next comparison - * between the kernel and daemon tables, when it will be deleted. - */ - if (!(k->k_state & KS_STATIC)) { - k->k_state |= KS_DELETE; - LIM_SEC(need_kern, k->k_keep); - return; - } - - /* Put static routes with real metrics into the daemon table so - * they can be advertised. - * - * Find the interface concerned - */ - ifp = iflookup(k->k_gate); - if (ifp == 0) { - /* if there is no known interface, - * maybe there is a new interface - */ - ifinit(); - ifp = iflookup(k->k_gate); - if (ifp == 0) - msglog("static route %s --> %s impossibly lacks ifp", - addrname(S_ADDR(INFO_DST(info)), mask, 0), - naddr_ntoa(k->k_gate)); - } - - kern_check_static(k, ifp); -} - - -/* deal with packet loss - */ -static void -rtm_lose(struct rt_msghdr *rtm, - struct rt_addrinfo *info) -{ - if (INFO_GATE(info) == 0 - || INFO_GATE(info)->sa_family != AF_INET) { - msglog("punt %s without gateway", - rtm_type_name(rtm->rtm_type)); - return; - } - - if (!supplier) - rdisc_age(S_ADDR(INFO_GATE(info))); - - age(S_ADDR(INFO_GATE(info))); -} - - -/* Clean the kernel table by copying it to the daemon image. - * Eventually the daemon will delete any extra routes. - */ -void -flush_kern(void) -{ - size_t needed; - int mib[6]; - char *buf, *next, *lim; - struct rt_msghdr *rtm; - struct interface *ifp; - static struct sockaddr_in gate_sa; - struct rt_addrinfo info; - - - mib[0] = CTL_NET; - mib[1] = PF_ROUTE; - mib[2] = 0; /* protocol */ - mib[3] = 0; /* wildcard address family */ - mib[4] = NET_RT_DUMP; - mib[5] = 0; /* no flags */ - if (sysctl(mib, 6, 0, &needed, 0, 0) < 0) { - DBGERR(1,"RT_DUMP-sysctl-estimate"); - return; - } - buf = malloc(needed); - if (sysctl(mib, 6, buf, &needed, 0, 0) < 0) - BADERR(1,"RT_DUMP"); - lim = buf + needed; - for (next = buf; next < lim; next += rtm->rtm_msglen) { - rtm = (struct rt_msghdr *)next; - - rt_xaddrs(&info, - (struct sockaddr *)(rtm+1), - (struct sockaddr *)(next + rtm->rtm_msglen), - rtm->rtm_addrs); - - if (INFO_DST(&info) == 0 - || INFO_DST(&info)->sa_family != AF_INET) - continue; - - /* ignore ARP table entries on systems with a merged route - * and ARP table. - */ - if (rtm->rtm_flags & RTF_LLINFO) - continue; - - if (INFO_GATE(&info) == 0) - continue; - if (INFO_GATE(&info)->sa_family != AF_INET) { - if (INFO_GATE(&info)->sa_family != AF_LINK) - continue; - ifp = ifwithindex(((struct sockaddr_dl *) - INFO_GATE(&info))->sdl_index); - if (ifp == 0) - continue; - if ((ifp->int_if_flags & IFF_POINTOPOINT) - || S_ADDR(INFO_DST(&info)) == ifp->int_addr) - gate_sa.sin_addr.s_addr = ifp->int_addr; - else - gate_sa.sin_addr.s_addr = htonl(ifp->int_net); -#ifdef _HAVE_SA_LEN - gate_sa.sin_len = sizeof(gate_sa); -#endif - gate_sa.sin_family = AF_INET; - INFO_GATE(&info) = (struct sockaddr *)&gate_sa; - } - - /* ignore multicast addresses - */ - if (IN_MULTICAST(ntohl(S_ADDR(INFO_DST(&info))))) - continue; - - /* Note static routes and interface routes, and also - * preload the image of the kernel table so that - * we can later clean it, as well as avoid making - * unneeded changes. Keep the old kernel routes for a - * few seconds to allow a RIP or router-discovery - * response to be heard. - */ - rtm_add(rtm,&info,MIN_WAITTIME); - } - free(buf); -} - - -/* Listen to announcements from the kernel - */ -void -read_rt(void) -{ - long cc; - struct interface *ifp; - naddr mask; - union { - struct { - struct rt_msghdr rtm; - struct sockaddr addrs[RTAX_MAX]; - } r; - struct if_msghdr ifm; - } m; - char str[100], *strp; - struct rt_addrinfo info; - - - for (;;) { - cc = read(rt_sock, &m, sizeof(m)); - if (cc <= 0) { - if (cc < 0 && errno != EWOULDBLOCK) - LOGERR("read(rt_sock)"); - return; - } - - if (m.r.rtm.rtm_version != RTM_VERSION) { - msglog("bogus routing message version %d", - m.r.rtm.rtm_version); - continue; - } - - /* Ignore our own results. - */ - if (m.r.rtm.rtm_type <= RTM_CHANGE - && m.r.rtm.rtm_pid == mypid) { - static int complained = 0; - if (!complained) { - msglog("receiving our own change messages"); - complained = 1; - } - continue; - } - - if (m.r.rtm.rtm_type == RTM_IFINFO - || m.r.rtm.rtm_type == RTM_NEWADDR - || m.r.rtm.rtm_type == RTM_DELADDR) { - ifp = ifwithindex(m.ifm.ifm_index); - if (ifp == 0) - trace_act("note %s with flags %#x" - " for index #%d\n", - rtm_type_name(m.r.rtm.rtm_type), - m.ifm.ifm_flags, - m.ifm.ifm_index); - else - trace_act("note %s with flags %#x for %s\n", - rtm_type_name(m.r.rtm.rtm_type), - m.ifm.ifm_flags, - ifp->int_name); - - /* After being informed of a change to an interface, - * check them all now if the check would otherwise - * be a long time from now, if the interface is - * not known, or if the interface has been turned - * off or on. - */ - if (ifinit_timer.tv_sec-now.tv_sec>=CHECK_BAD_INTERVAL - || ifp == 0 - || ((ifp->int_if_flags ^ m.ifm.ifm_flags) - & IFF_UP_RUNNING) != 0) - ifinit_timer.tv_sec = now.tv_sec; - continue; - } - - strcpy(str, rtm_type_name(m.r.rtm.rtm_type)); - strp = &str[strlen(str)]; - if (m.r.rtm.rtm_type <= RTM_CHANGE) - strp += sprintf(strp," from pid %d",m.r.rtm.rtm_pid); - - rt_xaddrs(&info, m.r.addrs, &m.r.addrs[RTAX_MAX], - m.r.rtm.rtm_addrs); - - if (INFO_DST(&info) == 0) { - trace_act("ignore %s without dst\n", str); - continue; - } - - if (INFO_DST(&info)->sa_family != AF_INET) { - trace_act("ignore %s for AF %d\n", str, - INFO_DST(&info)->sa_family); - continue; - } - - mask = ((INFO_MASK(&info) != 0) - ? ntohl(S_ADDR(INFO_MASK(&info))) - : (m.r.rtm.rtm_flags & RTF_HOST) - ? HOST_MASK - : std_mask(S_ADDR(INFO_DST(&info)))); - - strp += sprintf(strp, ": %s", - addrname(S_ADDR(INFO_DST(&info)), mask, 0)); - - if (IN_MULTICAST(ntohl(S_ADDR(INFO_DST(&info))))) { - trace_act("ignore multicast %s\n", str); - continue; - } - - if (INFO_GATE(&info) != 0 - && INFO_GATE(&info)->sa_family == AF_INET) - strp += sprintf(strp, " --> %s", - saddr_ntoa(INFO_GATE(&info))); - - if (INFO_AUTHOR(&info) != 0) - strp += sprintf(strp, " by authority of %s", - saddr_ntoa(INFO_AUTHOR(&info))); - - switch (m.r.rtm.rtm_type) { - case RTM_ADD: - case RTM_CHANGE: - case RTM_REDIRECT: - if (m.r.rtm.rtm_errno != 0) { - trace_act("ignore %s with \"%s\" error\n", - str, strerror(m.r.rtm.rtm_errno)); - } else { - trace_act("%s\n", str); - rtm_add(&m.r.rtm,&info,0); - } - break; - - case RTM_DELETE: - if (m.r.rtm.rtm_errno != 0) { - trace_act("ignore %s with \"%s\" error\n", - str, strerror(m.r.rtm.rtm_errno)); - } else { - trace_act("%s\n", str); - del_static(S_ADDR(INFO_DST(&info)), mask, 1); - } - break; - - case RTM_LOSING: - trace_act("%s\n", str); - rtm_lose(&m.r.rtm,&info); - break; - - default: - trace_act("ignore %s\n", str); - break; - } - } -} - - -/* after aggregating, note routes that belong in the kernel - */ -static void -kern_out(struct ag_info *ag) -{ - struct khash *k; - - - /* Do not install bad routes if they are not already present. - * This includes routes that had RS_NET_SYN for interfaces that - * recently died. - */ - if (ag->ag_metric == HOPCNT_INFINITY) { - k = kern_find(htonl(ag->ag_dst_h), ag->ag_mask, 0); - if (k == 0) - return; - } else { - k = kern_add(htonl(ag->ag_dst_h), ag->ag_mask); - } - - if (k->k_state & KS_NEW) { - /* will need to add new entry to the kernel table */ - k->k_state = KS_ADD; - if (ag->ag_state & AGS_GATEWAY) - k->k_state |= KS_GATEWAY; - k->k_gate = ag->ag_gate; - k->k_metric = ag->ag_metric; - return; - } - - if (k->k_state & KS_STATIC) - return; - - /* modify existing kernel entry if necessary */ - if (k->k_gate != ag->ag_gate - || k->k_metric != ag->ag_metric) { - k->k_gate = ag->ag_gate; - k->k_metric = ag->ag_metric; - k->k_state |= KS_CHANGE; - } - - if (k->k_state & KS_DYNAMIC) { - k->k_state &= ~KS_DYNAMIC; - k->k_state |= (KS_ADD | KS_DEL_ADD); - } - - if ((k->k_state & KS_GATEWAY) - && !(ag->ag_state & AGS_GATEWAY)) { - k->k_state &= ~KS_GATEWAY; - k->k_state |= (KS_ADD | KS_DEL_ADD); - } else if (!(k->k_state & KS_GATEWAY) - && (ag->ag_state & AGS_GATEWAY)) { - k->k_state |= KS_GATEWAY; - k->k_state |= (KS_ADD | KS_DEL_ADD); - } - - /* Deleting-and-adding is necessary to change aspects of a route. - * Just delete instead of deleting and then adding a bad route. - * Otherwise, we want to keep the route in the kernel. - */ - if (k->k_metric == HOPCNT_INFINITY - && (k->k_state & KS_DEL_ADD)) - k->k_state |= KS_DELETE; - else - k->k_state &= ~KS_DELETE; -#undef RT -} - - -/* ARGSUSED */ -static int -walk_kern(struct radix_node *rn, - struct walkarg *w) -{ -#define RT ((struct rt_entry *)rn) - char metric, pref; - u_int ags = 0; - - - /* Do not install synthetic routes */ - if (RT->rt_state & RS_NET_SYN) - return 0; - - if (!(RT->rt_state & RS_IF)) { - ags |= (AGS_GATEWAY | AGS_SUPPRESS | AGS_PROMOTE); - - } else { - /* Do not install routes for "external" remote interfaces. - */ - if (RT->rt_ifp != 0 && (RT->rt_ifp->int_state & IS_EXTERNAL)) - return 0; - - ags |= AGS_IF; - - /* If it is not an interface, or an alias for an interface, - * it must be a "gateway." - * - * If it is a "remote" interface, it is also a "gateway" to - * the kernel if is not a alias. - */ - if (RT->rt_ifp == 0 - || ((RT->rt_ifp->int_state & IS_REMOTE) - && RT->rt_ifp->int_metric == 0)) - ags |= (AGS_GATEWAY | AGS_SUPPRESS | AGS_PROMOTE); - } - - if (RT->rt_state & RS_RDISC) - ags |= AGS_CORS_GATE; - - /* aggregate good routes without regard to their metric */ - pref = 1; - metric = RT->rt_metric; - if (metric == HOPCNT_INFINITY) { - /* if the route is dead, so try hard to aggregate. */ - pref = HOPCNT_INFINITY; - ags |= (AGS_FINE_GATE | AGS_SUPPRESS); - } - - ag_check(RT->rt_dst, RT->rt_mask, RT->rt_gate, 0, - metric,pref, 0, 0, ags, kern_out); - return 0; -#undef RT -} - - -/* Update the kernel table to match the daemon table. - */ -static void -fix_kern(void) -{ - int i, flags; - struct khash *k, **pk; - - - need_kern = age_timer; - - /* Walk daemon table, updating the copy of the kernel table. - */ - (void)rn_walktree(rhead, walk_kern, 0); - ag_flush(0,0,kern_out); - - for (i = 0; i < KHASH_SIZE; i++) { - for (pk = &khash_bins[i]; (k = *pk) != 0; ) { - /* Do not touch static routes */ - if (k->k_state & KS_STATIC) { - kern_check_static(k,0); - pk = &k->k_next; - continue; - } - - /* check hold on routes deleted by the operator */ - if (k->k_keep > now.tv_sec) { - LIM_SEC(need_kern, k->k_keep); - k->k_state |= KS_DELETE; - pk = &k->k_next; - continue; - } - - if ((k->k_state & (KS_DELETE | KS_DYNAMIC)) - == KS_DELETE) { - if (!(k->k_state & KS_DELETED)) - rtioctl(RTM_DELETE, - k->k_dst, k->k_gate, k->k_mask, - 0, 0); - *pk = k->k_next; - free(k); - continue; - } - - if (0 != (k->k_state&(KS_ADD|KS_CHANGE|KS_DEL_ADD))) { - if (k->k_state & KS_DEL_ADD) { - rtioctl(RTM_DELETE, - k->k_dst,k->k_gate,k->k_mask, - 0, 0); - k->k_state &= ~KS_DYNAMIC; - } - - flags = 0; - if (0 != (k->k_state&(KS_GATEWAY|KS_DYNAMIC))) - flags |= RTF_GATEWAY; - - if (k->k_state & KS_ADD) { - rtioctl(RTM_ADD, - k->k_dst, k->k_gate, k->k_mask, - k->k_metric, flags); - } else if (k->k_state & KS_CHANGE) { - rtioctl(RTM_CHANGE, - k->k_dst,k->k_gate,k->k_mask, - k->k_metric, flags); - } - k->k_state &= ~(KS_ADD|KS_CHANGE|KS_DEL_ADD); - } - - /* Mark this route to be deleted in the next cycle. - * This deletes routes that disappear from the - * daemon table, since the normal aging code - * will clear the bit for routes that have not - * disappeared from the daemon table. - */ - k->k_state |= KS_DELETE; - pk = &k->k_next; - } - } -} - - -/* Delete a static route in the image of the kernel table. - */ -void -del_static(naddr dst, - naddr mask, - int gone) -{ - struct khash *k; - struct rt_entry *rt; - - /* Just mark it in the table to be deleted next time the kernel - * table is updated. - * If it has already been deleted, mark it as such, and set its - * keep-timer so that it will not be deleted again for a while. - * This lets the operator delete a route added by the daemon - * and add a replacement. - */ - k = kern_find(dst, mask, 0); - if (k != 0) { - k->k_state &= ~(KS_STATIC | KS_DYNAMIC); - k->k_state |= KS_DELETE; - if (gone) { - k->k_state |= KS_DELETED; - k->k_keep = now.tv_sec + K_KEEP_LIM; - } - } - - rt = rtget(dst, mask); - if (rt != 0 && (rt->rt_state & RS_STATIC)) - rtbad(rt); -} - - -/* Delete all routes generated from ICMP Redirects that use a given gateway, - * as well as old redirected routes. - */ -void -del_redirects(naddr bad_gate, - time_t old) -{ - int i; - struct khash *k; - - - for (i = 0; i < KHASH_SIZE; i++) { - for (k = khash_bins[i]; k != 0; k = k->k_next) { - if (!(k->k_state & KS_DYNAMIC) - || (k->k_state & KS_STATIC)) - continue; - - if (k->k_gate != bad_gate - && k->k_redirect_time > old - && !supplier) - continue; - - k->k_state |= KS_DELETE; - k->k_state &= ~KS_DYNAMIC; - need_kern.tv_sec = now.tv_sec; - trace_act("mark redirected %s --> %s for deletion\n", - addrname(k->k_dst, k->k_mask, 0), - naddr_ntoa(k->k_gate)); - } - } -} - - -/* Start the daemon tables. - */ -void -rtinit(void) -{ - extern int max_keylen; - int i; - struct ag_info *ag; - - /* Initialize the radix trees */ - max_keylen = sizeof(struct sockaddr_in); - rn_init(); - rn_inithead((void**)&rhead, 32); - - /* mark all of the slots in the table free */ - ag_avail = ag_slots; - for (ag = ag_slots, i = 1; i < NUM_AG_SLOTS; i++) { - ag->ag_fine = ag+1; - ag++; - } -} - - -#ifdef _HAVE_SIN_LEN -static struct sockaddr_in dst_sock = {sizeof(dst_sock), AF_INET}; -static struct sockaddr_in mask_sock = {sizeof(mask_sock), AF_INET}; -#else -static struct sockaddr_in_new dst_sock = {_SIN_ADDR_SIZE, AF_INET}; -static struct sockaddr_in_new mask_sock = {_SIN_ADDR_SIZE, AF_INET}; -#endif - - -void -set_need_flash(void) -{ - if (!need_flash) { - need_flash = 1; - /* Do not send the flash update immediately. Wait a little - * while to hear from other routers. - */ - no_flash.tv_sec = now.tv_sec + MIN_WAITTIME; - } -} - - -/* Get a particular routing table entry - */ -struct rt_entry * -rtget(naddr dst, naddr mask) -{ - struct rt_entry *rt; - - dst_sock.sin_addr.s_addr = dst; - mask_sock.sin_addr.s_addr = mask; - masktrim(&mask_sock); - rt = (struct rt_entry *)rhead->rnh_lookup(&dst_sock,&mask_sock,rhead); - if (!rt - || rt->rt_dst != dst - || rt->rt_mask != mask) - return 0; - - return rt; -} - - -/* Find a route to dst as the kernel would. - */ -struct rt_entry * -rtfind(naddr dst) -{ - dst_sock.sin_addr.s_addr = dst; - return (struct rt_entry *)rhead->rnh_matchaddr(&dst_sock, rhead); -} - - -/* add a route to the table - */ -void -rtadd(naddr dst, - naddr mask, - naddr gate, /* forward packets here */ - naddr router, /* on the authority of this router */ - int metric, - u_short tag, - u_int state, /* rs_state for the entry */ - struct interface *ifp) -{ - struct rt_entry *rt; - naddr smask; - int i; - struct rt_spare *rts; - - rt = (struct rt_entry *)rtmalloc(sizeof (*rt), "rtadd"); - bzero(rt, sizeof(*rt)); - for (rts = rt->rt_spares, i = NUM_SPARES; i != 0; i--, rts++) - rts->rts_metric = HOPCNT_INFINITY; - - rt->rt_nodes->rn_key = (caddr_t)&rt->rt_dst_sock; - rt->rt_dst = dst; - rt->rt_dst_sock.sin_family = AF_INET; -#ifdef _HAVE_SIN_LEN - rt->rt_dst_sock.sin_len = dst_sock.sin_len; -#endif - if (mask != HOST_MASK) { - smask = std_mask(dst); - if ((smask & ~mask) == 0 && mask > smask) - state |= RS_SUBNET; - } - mask_sock.sin_addr.s_addr = mask; - masktrim(&mask_sock); - rt->rt_mask = mask; - rt->rt_state = state; - rt->rt_gate = gate; - rt->rt_router = router; - rt->rt_time = now.tv_sec; - rt->rt_metric = metric; - rt->rt_poison_metric = HOPCNT_INFINITY; - rt->rt_tag = tag; - rt->rt_ifp = ifp; - rt->rt_seqno = update_seqno; - - if (++total_routes == MAX_ROUTES) - msglog("have maximum (%d) routes", total_routes); - if (TRACEACTIONS) - trace_add_del("Add", rt); - - need_kern.tv_sec = now.tv_sec; - set_need_flash(); - - if (0 == rhead->rnh_addaddr(&rt->rt_dst_sock, &mask_sock, - rhead, rt->rt_nodes)) { - msglog("rnh_addaddr() failed for %s mask=%#x", - naddr_ntoa(dst), mask); - } -} - - -/* notice a changed route - */ -void -rtchange(struct rt_entry *rt, - u_int state, /* new state bits */ - naddr gate, /* now forward packets here */ - naddr router, /* on the authority of this router */ - int metric, /* new metric */ - u_short tag, - struct interface *ifp, - time_t new_time, - char *label) -{ - if (rt->rt_metric != metric) { - /* Fix the kernel immediately if it seems the route - * has gone bad, since there may be a working route that - * aggregates this route. - */ - if (metric == HOPCNT_INFINITY) - need_kern.tv_sec = now.tv_sec; - rt->rt_seqno = update_seqno; - set_need_flash(); - } - - if (rt->rt_gate != gate) { - need_kern.tv_sec = now.tv_sec; - rt->rt_seqno = update_seqno; - set_need_flash(); - } - - state |= (rt->rt_state & RS_SUBNET); - - if (TRACEACTIONS) - trace_change(rt, state, gate, router, metric, tag, ifp, - new_time, - label ? label : "Chg "); - - rt->rt_state = state; - rt->rt_gate = gate; - rt->rt_router = router; - rt->rt_metric = metric; - rt->rt_tag = tag; - rt->rt_ifp = ifp; - rt->rt_time = new_time; -} - - -/* check for a better route among the spares - */ -static struct rt_spare * -rts_better(struct rt_entry *rt) -{ - struct rt_spare *rts, *rts1; - int i; - - /* find the best alternative among the spares */ - rts = rt->rt_spares+1; - for (i = NUM_SPARES, rts1 = rts+1; i > 2; i--, rts1++) { - if (BETTER_LINK(rt,rts1,rts)) - rts = rts1; - } - - return rts; -} - - -/* switch to a backup route - */ -void -rtswitch(struct rt_entry *rt, - struct rt_spare *rts) -{ - struct rt_spare swap; - char label[10]; - - - /* Do not change permanent routes */ - if (0 != (rt->rt_state & RS_PERMANENT)) - return; - - /* Do not discard synthetic routes until they go bad */ - if ((rt->rt_state & RS_NET_SYN) - && rt->rt_metric < HOPCNT_INFINITY) - return; - - /* find the best alternative among the spares */ - if (rts == 0) - rts = rts_better(rt); - - /* Do not bother if it is not worthwhile. - */ - if (!BETTER_LINK(rt, rts, rt->rt_spares)) - return; - - swap = rt->rt_spares[0]; - (void)sprintf(label, "Use #%d", rts - rt->rt_spares); - rtchange(rt, rt->rt_state & ~(RS_NET_SYN | RS_RDISC), - rts->rts_gate, rts->rts_router, rts->rts_metric, - rts->rts_tag, rts->rts_ifp, rts->rts_time, label); - *rts = swap; -} - - -void -rtdelete(struct rt_entry *rt) -{ - struct khash *k; - - - if (TRACEACTIONS) - trace_add_del("Del", rt); - - k = kern_find(rt->rt_dst, rt->rt_mask, 0); - if (k != 0) { - k->k_state |= KS_DELETE; - need_kern.tv_sec = now.tv_sec; - } - - dst_sock.sin_addr.s_addr = rt->rt_dst; - mask_sock.sin_addr.s_addr = rt->rt_mask; - masktrim(&mask_sock); - if (rt != (struct rt_entry *)rhead->rnh_deladdr(&dst_sock, &mask_sock, - rhead)) { - msglog("rnh_deladdr() failed"); - } else { - free(rt); - total_routes--; - } -} - - -/* Get rid of a bad route, and try to switch to a replacement. - */ -void -rtbad(struct rt_entry *rt) -{ - /* Poison the route */ - rtchange(rt, rt->rt_state & ~(RS_IF | RS_LOCAL | RS_STATIC), - rt->rt_gate, rt->rt_router, HOPCNT_INFINITY, rt->rt_tag, - 0, rt->rt_time, 0); - - rtswitch(rt, 0); -} - - -/* Junk a RS_NET_SYN or RS_LOCAL route, - * unless it is needed by another interface. - */ -void -rtbad_sub(struct rt_entry *rt) -{ - struct interface *ifp, *ifp1; - struct intnet *intnetp; - u_int state; - - - ifp1 = 0; - state = 0; - - if (rt->rt_state & RS_LOCAL) { - /* Is this the route through loopback for the interface? - * If so, see if it is used by any other interfaces, such - * as a point-to-point interface with the same local address. - */ - for (ifp = ifnet; ifp != 0; ifp = ifp->int_next) { - /* Retain it if another interface needs it. - */ - if (ifp->int_addr == rt->rt_ifp->int_addr) { - state |= RS_LOCAL; - ifp1 = ifp; - break; - } - } - - } - - if (!(state & RS_LOCAL)) { - /* Retain RIPv1 logical network route if there is another - * interface that justifies it. - */ - if (rt->rt_state & RS_NET_SYN) { - for (ifp = ifnet; ifp != 0; ifp = ifp->int_next) { - if ((ifp->int_state & IS_NEED_NET_SYN) - && rt->rt_mask == ifp->int_std_mask - && rt->rt_dst == ifp->int_std_addr) { - state |= RS_NET_SYN; - ifp1 = ifp; - break; - } - } - } - - /* or if there is an authority route that needs it. */ - for (intnetp = intnets; - intnetp != 0; - intnetp = intnetp->intnet_next) { - if (intnetp->intnet_addr == rt->rt_dst - && intnetp->intnet_mask == rt->rt_mask) { - state |= (RS_NET_SYN | RS_NET_INT); - break; - } - } - } - - if (ifp1 != 0 || (state & RS_NET_SYN)) { - rtchange(rt, ((rt->rt_state & ~(RS_NET_SYN | RS_LOCAL)) - | state), - rt->rt_gate, rt->rt_router, rt->rt_metric, - rt->rt_tag, ifp1, rt->rt_time, 0); - } else { - rtbad(rt); - } -} - - -/* Called while walking the table looking for sick interfaces - * or after a time change. - */ -/* ARGSUSED */ -int -walk_bad(struct radix_node *rn, - struct walkarg *w) -{ -#define RT ((struct rt_entry *)rn) - struct rt_spare *rts; - int i; - time_t new_time; - - - /* fix any spare routes through the interface - */ - rts = RT->rt_spares; - for (i = NUM_SPARES; i != 1; i--) { - rts++; - - if (rts->rts_ifp != 0 - && (rts->rts_ifp->int_state & IS_BROKE)) { - new_time = rts->rts_time; - if (new_time >= now_garbage) - new_time = now_garbage-1; - trace_upslot(RT, rts, rts->rts_gate, - rts->rts_router, 0, - HOPCNT_INFINITY, rts->rts_tag, - new_time); - rts->rts_ifp = 0; - rts->rts_metric = HOPCNT_INFINITY; - rts->rts_time = new_time; - } - } - - /* Deal with the main route - */ - /* finished if it has been handled before or if its interface is ok - */ - if (RT->rt_ifp == 0 || !(RT->rt_ifp->int_state & IS_BROKE)) - return 0; - - /* Bad routes for other than interfaces are easy. - */ - if (0 == (RT->rt_state & (RS_IF | RS_NET_SYN | RS_LOCAL))) { - rtbad(RT); - return 0; - } - - rtbad_sub(RT); - return 0; -#undef RT -} - - -/* Check the age of an individual route. - */ -/* ARGSUSED */ -static int -walk_age(struct radix_node *rn, - struct walkarg *w) -{ -#define RT ((struct rt_entry *)rn) - struct interface *ifp; - struct rt_spare *rts; - int i; - - - /* age all of the spare routes, including the primary route - * currently in use - */ - rts = RT->rt_spares; - for (i = NUM_SPARES; i != 0; i--, rts++) { - - ifp = rts->rts_ifp; - if (i == NUM_SPARES) { - if (!AGE_RT(RT, ifp)) { - /* Keep various things from deciding ageless - * routes are stale */ - rts->rts_time = now.tv_sec; - continue; - } - - /* forget RIP routes after RIP has been turned off. - */ - if (rip_sock < 0) { - rtdelete(RT); - return 0; - } - } - - /* age failing routes - */ - if (age_bad_gate == rts->rts_gate - && rts->rts_time >= now_stale) { - rts->rts_time -= SUPPLY_INTERVAL; - } - - /* trash the spare routes when they go bad */ - if (rts->rts_metric < HOPCNT_INFINITY - && now_garbage > rts->rts_time) { - trace_upslot(RT, rts, rts->rts_gate, - rts->rts_router, rts->rts_ifp, - HOPCNT_INFINITY, rts->rts_tag, - rts->rts_time); - rts->rts_metric = HOPCNT_INFINITY; - } - } - - - /* finished if the active route is still fresh */ - if (now_stale <= RT->rt_time) - return 0; - - /* try to switch to an alternative */ - rtswitch(RT, 0); - - /* Delete a dead route after it has been publically mourned. */ - if (now_garbage > RT->rt_time) { - rtdelete(RT); - return 0; - } - - /* Start poisoning a bad route before deleting it. */ - if (now.tv_sec - RT->rt_time > EXPIRE_TIME) - rtchange(RT, RT->rt_state, RT->rt_gate, RT->rt_router, - HOPCNT_INFINITY, RT->rt_tag, RT->rt_ifp, - RT->rt_time, 0); - return 0; -} - - -/* Watch for dead routes and interfaces. - */ -void -age(naddr bad_gate) -{ - struct interface *ifp; - - - age_timer.tv_sec = now.tv_sec + (rip_sock < 0 - ? NEVER - : SUPPLY_INTERVAL); - - for (ifp = ifnet; ifp; ifp = ifp->int_next) { - /* Check for dead IS_REMOTE interfaces by timing their - * transmissions. - */ - if ((ifp->int_state & IS_REMOTE) - && !(ifp->int_state & IS_PASSIVE) - && (ifp->int_state & IS_ACTIVE)) { - LIM_SEC(age_timer, now.tv_sec+SUPPLY_INTERVAL); - - if (now.tv_sec - ifp->int_act_time > EXPIRE_TIME - && !(ifp->int_state & IS_BROKE)) { - msglog("remote interface %s to %s timed out" - "--turned off", - ifp->int_name, - naddr_ntoa(ifp->int_addr)); - if_bad(ifp); - } - } - } - - /* Age routes. */ - age_bad_gate = bad_gate; - (void)rn_walktree(rhead, walk_age, 0); - - /* Update the kernel routing table. */ - fix_kern(); -} diff --git a/usr.sbin/routed/trace.c b/usr.sbin/routed/trace.c deleted file mode 100644 index e9c1e03f05aa..000000000000 --- a/usr.sbin/routed/trace.c +++ /dev/null @@ -1,800 +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. - */ - -#if !defined(lint) && !defined(sgi) && !defined(__NetBSD__) -static char sccsid[] = "@(#)trace.c 8.1 (Berkeley) 6/5/93"; -#elif defined(__NetBSD__) -static char rcsid[] = "$NetBSD$"; -#endif -#ident "$Revision: 1.11 $" - -#define RIPCMDS -#include "defs.h" -#include "pathnames.h" -#include -#include -#include - - -#ifdef sgi -/* use *stat64 for files on large filesystems */ -#define stat stat64 -#endif - -#define NRECORDS 50 /* size of circular trace buffer */ - -u_int tracelevel, new_tracelevel; -FILE *ftrace = stdout; /* output trace file */ -static char *tracelevel_pat = "%s\n"; - -char savetracename[MAXPATHLEN+1]; - - -/* convert IP address to a string, but not into a single buffer - */ -char * -naddr_ntoa(naddr a) -{ -#define NUM_BUFS 4 - static int bufno; - static struct { - char str[16]; /* xxx.xxx.xxx.xxx\0 */ - } bufs[NUM_BUFS]; - char *s; - struct in_addr addr; - - addr.s_addr = a; - s = strcpy(bufs[bufno].str, inet_ntoa(addr)); - bufno = (bufno+1) % NUM_BUFS; - return s; -#undef NUM_BUFS -} - - -char * -saddr_ntoa(struct sockaddr *sa) -{ - return (sa == 0) ? "?" : naddr_ntoa(S_ADDR(sa)); -} - - -static char * -ts(time_t secs) { - static char s[20]; - - secs += epoch.tv_sec; -#ifdef sgi - (void)cftime(s, "%T", &secs); -#else - bcopy(ctime(&secs)+11, s, 8); - s[8] = '\0'; -#endif - return s; -} - - -/* On each event, display a time stamp. - * This assumes that 'now' is update once for each event, and - * that at least now.tv_usec changes. - */ -void -lastlog(void) -{ - static struct timeval last; - - if (last.tv_sec != now.tv_sec - || last.tv_usec != now.tv_usec) { - (void)fprintf(ftrace, "-- %s --\n", ts(now.tv_sec)); - last = now; - } -} - - -static void -tmsg(char *p, ...) -{ - va_list args; - - if (ftrace != 0) { - lastlog(); - va_start(args, p); - vfprintf(ftrace, p, args); - fflush(ftrace); - } -} - - -static void -trace_close(void) -{ - int fd; - - - fflush(stdout); - fflush(stderr); - - if (ftrace != 0 - && savetracename[0] != '\0') { - fd = open(_PATH_DEVNULL, O_RDWR); - (void)dup2(fd, STDOUT_FILENO); - (void)dup2(fd, STDERR_FILENO); - (void)close(fd); - fclose(ftrace); - ftrace = 0; - } -} - - -void -trace_flush(void) -{ - if (ftrace != 0) { - fflush(ftrace); - if (ferror(ftrace)) - trace_off("tracing off: ", strerror(ferror(ftrace))); - } -} - - -void -trace_off(char *p, ...) -{ - va_list args; - - - if (ftrace != 0) { - lastlog(); - va_start(args, p); - vfprintf(ftrace, p, args); - fflush(ftrace); - } - trace_close(); - - new_tracelevel = tracelevel = 0; -} - - -void -trace_on(char *filename, - int trusted) -{ - struct stat stbuf; - FILE *n_ftrace; - - - /* Given a null filename when tracing is already on, increase the - * debugging level and re-open the file in case it has been unlinked. - */ - if (filename[0] == '\0') { - if (tracelevel != 0) { - new_tracelevel++; - tracelevel_pat = "trace command: %s\n"; - } else if (savetracename[0] == '\0') { - msglog("missing trace file name"); - return; - } - filename = savetracename; - - } else if (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", - filename, strerror(errno)); - return; - } - - tmsg("switch to trace file %s\n", filename); - trace_close(); - if (filename != savetracename) - strncpy(savetracename, filename, sizeof(savetracename)-1); - ftrace = n_ftrace; - - fflush(stdout); - fflush(stderr); - dup2(fileno(ftrace), STDOUT_FILENO); - dup2(fileno(ftrace), STDERR_FILENO); - - if (new_tracelevel == 0) - new_tracelevel = 1; - set_tracelevel(); -} - - -/* ARGSUSED */ -void -sigtrace_on(int s) -{ - new_tracelevel++; - tracelevel_pat = "SIGUSR1: %s\n"; -} - - -/* ARGSUSED */ -void -sigtrace_off(int s) -{ - new_tracelevel--; - tracelevel_pat = "SIGUSR2: %s\n"; -} - - -/* Move to next higher level of tracing when -t option processed or - * SIGUSR1 is received. Successive levels are: - * actions - * actions + packets - * actions + packets + contents - */ -void -set_tracelevel(void) -{ - static char *off_msgs[MAX_TRACELEVEL] = { - "Tracing actions stopped", - "Tracing packets stopped", - "Tracing packet contents stopped", - }; - 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; - if (new_tracelevel == tracelevel) { - tmsg(tracelevel_pat, on_msgs[tracelevel-1]); - return; - } - } - while (new_tracelevel != tracelevel) { - if (new_tracelevel < tracelevel) { - if (--tracelevel == 0) - trace_off(tracelevel_pat, off_msgs[0]); - else - tmsg(tracelevel_pat, off_msgs[tracelevel]); - } else { - if (ftrace == 0) { - if (savetracename[0] != '\0') - trace_on(savetracename, 1); - else - ftrace = stdout; - } - tmsg(tracelevel_pat, on_msgs[tracelevel++]); - } - } - tracelevel_pat = "%s\n"; -} - - -/* display an address - */ -char * -addrname(naddr addr, /* in network byte order */ - naddr mask, - int force) /* 0=show mask if nonstandard, */ -{ /* 1=always show mask, 2=never */ -#define NUM_BUFS 4 - static int bufno; - static struct { - char str[15+20]; - } bufs[NUM_BUFS]; - char *s, *sp; - naddr dmask; - int i; - - s = strcpy(bufs[bufno].str, naddr_ntoa(addr)); - bufno = (bufno+1) % NUM_BUFS; - - if (force == 1 || (force == 0 && mask != std_mask(addr))) { - sp = &s[strlen(s)]; - - dmask = mask & -mask; - if (mask + dmask == 0) { - for (i = 0; i != 32 && ((1<bits_mask) != 0) { - if ((b & field) == b) { - if (tbl->bits_name[0] != '\0') { - if (c) - (void)putc(c, ftrace); - (void)fprintf(ftrace, "%s", tbl->bits_name); - c = '|'; - } - if (0 == (field &= ~(b | tbl->bits_clear))) - break; - } - tbl++; - } - if (field != 0 && tbl->bits_name != 0) { - if (c) - (void)putc(c, ftrace); - (void)fprintf(ftrace, tbl->bits_name, field); - c = '|'; - } - - if (c != '<' || force) - (void)fputs("> ", ftrace); -} - - -static char * -trace_pair(naddr dst, - naddr mask, - char *gate) -{ - static char buf[3*4+3+1+2+3 /* "xxx.xxx.xxx.xxx/xx-->" */ - +3*4+3+1]; /* "xxx.xxx.xxx.xxx" */ - int i; - - i = sprintf(buf, "%-16s-->", addrname(dst, mask, 0)); - (void)sprintf(&buf[i], "%-*s", 15+20-MAX(20,i), gate); - return buf; -} - - -void -trace_if(char *act, - struct interface *ifp) -{ - if (!TRACEACTIONS || ftrace == 0) - return; - - lastlog(); - (void)fprintf(ftrace, "%s interface %-4s ", act, ifp->int_name); - (void)fprintf(ftrace, "%-15s-->%-15s ", - naddr_ntoa(ifp->int_addr), - addrname(htonl((ifp->int_if_flags & IFF_POINTOPOINT) - ? ifp->int_dstaddr - : ifp->int_net), - ifp->int_mask, 1)); - if (ifp->int_metric != 0) - (void)fprintf(ftrace, "metric=%d ", ifp->int_metric); - trace_bits(if_bits, ifp->int_if_flags, 0); - trace_bits(is_bits, ifp->int_state, 0); - (void)fputc('\n',ftrace); -} - - -void -trace_upslot(struct rt_entry *rt, - struct rt_spare *rts, - naddr gate, - naddr router, - struct interface *ifp, - int metric, - u_short tag, - time_t new_time) -{ - if (!TRACEACTIONS || ftrace == 0) - return; - if (rts->rts_gate == gate - && rts->rts_router == router - && rts->rts_metric == metric - && rts->rts_tag == tag) - return; - - lastlog(); - if (rts->rts_gate != RIP_DEFAULT) { - (void)fprintf(ftrace, "Chg #%d %-35s ", - rts - rt->rt_spares, - trace_pair(rt->rt_dst, rt->rt_mask, - naddr_ntoa(rts->rts_gate))); - if (rts->rts_gate != rts->rts_gate) - (void)fprintf(ftrace, "router=%s ", - naddr_ntoa(rts->rts_gate)); - if (rts->rts_tag != 0) - (void)fprintf(ftrace, "tag=%#x ", ntohs(rts->rts_tag)); - (void)fprintf(ftrace, "metric=%-2d ", rts->rts_metric); - if (rts->rts_ifp != 0) - (void)fprintf(ftrace, "%s ", - rts->rts_ifp->int_name); - (void)fprintf(ftrace, "%s\n", ts(rts->rts_time)); - - (void)fprintf(ftrace, " %19s%-16s ", - "", - gate != rts->rts_gate ? naddr_ntoa(gate) : ""); - if (gate != router) - (void)fprintf(ftrace,"router=%s ",naddr_ntoa(router)); - if (tag != rts->rts_tag) - (void)fprintf(ftrace, "tag=%#x ", ntohs(tag)); - if (metric != rts->rts_metric) - (void)fprintf(ftrace, "metric=%-2d ", metric); - if (ifp != rts->rts_ifp && ifp != 0 ) - (void)fprintf(ftrace, "%s ", ifp->int_name); - (void)fprintf(ftrace, "%s\n", - new_time != rts->rts_time ? ts(new_time) : ""); - - } else { - (void)fprintf(ftrace, "Add #%d %-35s ", - rts - rt->rt_spares, - trace_pair(rt->rt_dst, rt->rt_mask, - naddr_ntoa(gate))); - if (gate != router) - (void)fprintf(ftrace, "router=%s ", naddr_ntoa(gate)); - if (tag != 0) - (void)fprintf(ftrace, "tag=%#x ", ntohs(tag)); - (void)fprintf(ftrace, "metric=%-2d ", metric); - if (ifp != 0) - (void)fprintf(ftrace, "%s ", ifp->int_name); - (void)fprintf(ftrace, "%s\n", ts(new_time)); - } -} - - -/* display a message if tracing actions - */ -void -trace_act(char *p, ...) -{ - va_list args; - - if (!TRACEACTIONS || ftrace == 0) - return; - - lastlog(); - va_start(args, p); - vfprintf(ftrace, p, args); -} - - -/* display a message if tracing packets - */ -void -trace_pkt(char *p, ...) -{ - va_list args; - - if (!TRACEPACKETS || ftrace == 0) - return; - - lastlog(); - va_start(args, p); - vfprintf(ftrace, p, args); -} - - -void -trace_change(struct rt_entry *rt, - u_int state, - naddr gate, /* forward packets here */ - naddr router, /* on the authority of this router */ - int metric, - u_short tag, - struct interface *ifp, - time_t new_time, - char *label) -{ - if (ftrace == 0) - return; - - if (rt->rt_metric == metric - && rt->rt_gate == gate - && rt->rt_router == router - && rt->rt_state == state - && rt->rt_tag == tag) - return; - - lastlog(); - (void)fprintf(ftrace, "%s %-35s metric=%-2d ", - label, - trace_pair(rt->rt_dst, rt->rt_mask, - naddr_ntoa(rt->rt_gate)), - rt->rt_metric); - if (rt->rt_router != rt->rt_gate) - (void)fprintf(ftrace, "router=%s ", - naddr_ntoa(rt->rt_router)); - if (rt->rt_tag != 0) - (void)fprintf(ftrace, "tag=%#x ", ntohs(rt->rt_tag)); - trace_bits(rs_bits, rt->rt_state, rt->rt_state != state); - (void)fprintf(ftrace, "%s ", - rt->rt_ifp == 0 ? "?" : rt->rt_ifp->int_name); - (void)fprintf(ftrace, "%s\n", - AGE_RT(rt, rt->rt_ifp) ? ts(rt->rt_time) : ""); - - (void)fprintf(ftrace, "%*s %19s%-16s ", - strlen(label), "", "", - rt->rt_gate != gate ? naddr_ntoa(gate) : ""); - if (rt->rt_metric != metric) - (void)fprintf(ftrace, "metric=%-2d ", metric); - if (router != gate) - (void)fprintf(ftrace, "router=%s ", naddr_ntoa(router)); - if (rt->rt_tag != tag) - (void)fprintf(ftrace, "tag=%#x ", ntohs(tag)); - if (rt->rt_state != state) - trace_bits(rs_bits, state, 1); - if (rt->rt_ifp != ifp) - (void)fprintf(ftrace, "%s ", - ifp != 0 ? ifp->int_name : "?"); - (void)fprintf(ftrace, "%s\n", - ((rt->rt_time == new_time || !AGE_RT(rt, ifp)) - ? "" : ts(new_time))); -} - - -void -trace_add_del(char * action, struct rt_entry *rt) -{ - u_int state = rt->rt_state; - - if (ftrace == 0) - return; - - lastlog(); - (void)fprintf(ftrace, "%s %-35s metric=%-2d ", - action, - trace_pair(rt->rt_dst, rt->rt_mask, - naddr_ntoa(rt->rt_gate)), - rt->rt_metric); - if (rt->rt_router != rt->rt_gate) - (void)fprintf(ftrace, "router=%s ", - naddr_ntoa(rt->rt_router)); - if (rt->rt_tag != 0) - (void)fprintf(ftrace, "tag=%#x ", ntohs(rt->rt_tag)); - trace_bits(rs_bits, state, 0); - (void)fprintf(ftrace, "%s ", - rt->rt_ifp != 0 ? rt->rt_ifp->int_name : "?"); - (void)fprintf(ftrace, "%s\n", ts(rt->rt_time)); -} - - -void -trace_rip(char *dir1, char *dir2, - struct sockaddr_in *who, - struct interface *ifp, - struct rip *msg, - int size) /* total size of message */ -{ - struct netinfo *n, *lim; - struct netauth *a; - int i; - - if (!TRACEPACKETS || ftrace == 0) - return; - - lastlog(); - if (msg->rip_cmd >= RIPCMD_MAX - || msg->rip_vers == 0) { - (void)fprintf(ftrace, "%s bad RIPv%d cmd=%d %s" - " %s.%d size=%d\n", - dir1, msg->rip_vers, msg->rip_cmd, dir2, - naddr_ntoa(who->sin_addr.s_addr), - ntohs(who->sin_port), - size); - return; - } - - (void)fprintf(ftrace, "%s RIPv%d %s %s %s.%d%s%s\n", - dir1, msg->rip_vers, ripcmds[msg->rip_cmd], dir2, - naddr_ntoa(who->sin_addr.s_addr), ntohs(who->sin_port), - ifp ? " via " : "", ifp ? ifp->int_name : ""); - if (!TRACECONTENTS) - return; - - switch (msg->rip_cmd) { - case RIPCMD_REQUEST: - case RIPCMD_RESPONSE: - n = msg->rip_nets; - lim = (struct netinfo *)((char*)msg + size); - for (; n < lim; n++) { - if (n->n_family == RIP_AF_UNSPEC - && ntohl(n->n_metric) == HOPCNT_INFINITY - && n+1 == lim - && n == msg->rip_nets - && msg->rip_cmd == RIPCMD_REQUEST) { - (void)fputs("\tQUERY ", ftrace); - if (n->n_dst != 0) - (void)fprintf(ftrace, "%s ", - naddr_ntoa(n->n_dst)); - if (n->n_mask != 0) - (void)fprintf(ftrace, "mask=%#x ", - (u_int)ntohl(n->n_mask)); - if (n->n_nhop != 0) - (void)fprintf(ftrace, " nhop=%s ", - naddr_ntoa(n->n_nhop)); - if (n->n_tag != 0) - (void)fprintf(ftrace, "tag=%#x", - ntohs(n->n_tag)); - (void)fputc('\n',ftrace); - continue; - } - - if (n->n_family == RIP_AF_AUTH) { - a = (struct netauth*)n; - (void)fprintf(ftrace, - "\tAuthentication type %d: ", - ntohs(a->a_type)); - for (i = 0; - i < sizeof(a->au.au_pw); - i++) - (void)fprintf(ftrace, "%02x ", - a->au.au_pw[i]); - (void)fputc('\n',ftrace); - continue; - } - - if (n->n_family != RIP_AF_INET) { - (void)fprintf(ftrace, - "\t(af %d) %-18s mask=%#x", - ntohs(n->n_family), - naddr_ntoa(n->n_dst), - (u_int)ntohl(n->n_mask)); - } else if (msg->rip_vers == RIPv1) { - (void)fprintf(ftrace, "\t%-18s ", - addrname(n->n_dst, - ntohl(n->n_mask), - n->n_mask==0 ? 2 : 1)); - } else { - (void)fprintf(ftrace, "\t%-18s ", - addrname(n->n_dst, - ntohl(n->n_mask), - n->n_mask==0 ? 2 : 0)); - } - (void)fprintf(ftrace, "metric=%-2d ", - (u_int)ntohl(n->n_metric)); - if (n->n_nhop != 0) - (void)fprintf(ftrace, " nhop=%s ", - naddr_ntoa(n->n_nhop)); - if (n->n_tag != 0) - (void)fprintf(ftrace, "tag=%#x", - ntohs(n->n_tag)); - (void)fputc('\n',ftrace); - } - if (size != (char *)n - (char *)msg) - (void)fprintf(ftrace, "truncated record, len %d\n", - size); - break; - - case RIPCMD_TRACEON: - fprintf(ftrace, "\tfile=%*s\n", size-4, msg->rip_tracefile); - break; - - case RIPCMD_TRACEOFF: - break; - } -} -- cgit v1.3