Index: stable/10/sbin/routed/defs.h =================================================================== --- stable/10/sbin/routed/defs.h (revision 272871) +++ stable/10/sbin/routed/defs.h (revision 272872) @@ -1,647 +1,648 @@ /* * 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. * 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 * * $FreeBSD$ */ #ifdef sgi #ident "$FreeBSD$" #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 vestigial 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 #include #ifdef sgi #include #include #endif #include #include #include #include #include #include #include #include #include #include #include #ifdef sgi #define _USER_ROUTE_TREE #include #else #include "radix.h" #define UNUSED __attribute__((unused)) #define PATTRIB(f,l) __attribute__((format (printf,f,l))) #endif #include #include #include #include #include #define RIPVERSION RIPv2 #include #ifndef __RCSID #define __RCSID(_s) static const char rcsid[] UNUSED = _s #endif #ifndef __COPYRIGHT #define __COPYRIGHT(_s) static const char copyright[] UNUSED = _s #endif /* 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 u_int32_t #elif defined (__NetBSD__) #define naddr u_int32_t #define _HAVE_SA_LEN #define _HAVE_SIN_LEN #else #define naddr u_long #define _HAVE_SA_LEN #define _HAVE_SIN_LEN #endif #define DAY (24*60*60) #define NEVER DAY /* 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))) /* Metric used for fake default routes. It ought to be 15, but when * processing advertised routes, previous versions of `routed` added * to the received metric and discarded the route if the total was 16 * or larger. */ #define FAKE_METRIC (HOPCNT_INFINITY-2) /* 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 MIN_PreferenceLevel 0x80000000 #define MAX_INITIAL_ADVERT_INTERVAL 16 #define MAX_INITIAL_ADVERTS 3 #define MAX_SOLICITATION_DELAY 1 #define SOLICITATION_INTERVAL 3 #define MAX_SOLICITATIONS 3 /* Bloated packet size for systems that simply add authentication to * full-sized packets */ #define OVER_MAXPACKETSIZE (MAXPACKETSIZE+sizeof(struct netinfo)*2) /* typical packet buffers */ union pkt_buf { char packet[OVER_MAXPACKETSIZE*2]; struct rip rip; }; #define GNAME_LEN 64 /* assumed=64 in parms.c */ /* bigger than IFNAMSIZ, with room for "external()" or "remote()" */ #define IF_NAME_LEN (GNAME_LEN+15) /* No more routes than this, to protect ourself in case something goes * whacko and starts broadcasting 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 */ u_int rts_de_ag; /* de-aggregation level */ #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 rt_de_ag rt_spares[0].rts_de_ag #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 { LIST_ENTRY(interface) int_list; LIST_ENTRY(interface) remote_list; struct interface *int_ahash, **int_ahash_prev; struct interface *int_bhash, **int_bhash_prev; struct interface *int_nhash, **int_nhash_prev; char int_name[IF_NAME_LEN+1]; 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 */ time_t int_query_time; u_short int_transitions; /* times gone up-down */ char int_metric; u_char int_d_metric; /* for faked default route */ u_char int_adj_inmetric; /* adjust advertised metrics */ u_char int_adj_outmetric; /* instead of interface metric */ 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; # define MAX_AUTH_KEYS 5 struct auth { /* authentication info */ u_int16_t type; u_char key[RIP_AUTH_PW_LEN]; u_char keyid; time_t start, end; } int_auth[MAX_AUTH_KEYS]; /* router discovery parameters */ int int_rdisc_pref; /* signed preference to advertise */ 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_DISTRUST 0x0000100 /* ignore untrusted routers */ #define IS_REDIRECT_OK 0x0000200 /* accept ICMP redirects */ #define IS_BROKE 0x0000400 /* seems to be broken */ #define IS_SICK 0x0000800 /* seems to be broken */ #define IS_DUP 0x0001000 /* has a duplicate address */ #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_RIP_MCAST 0x0100000 /* broadcast RIPv2 */ #define IS_NO_ADV_IN 0x0200000 /* do not listen to advertisements */ #define IS_NO_SOL_OUT 0x0400000 /* send no solicitations */ #define IS_SOL_OUT 0x0800000 /* send solicitations */ #define GROUP_IS_SOL_OUT (IS_SOL_OUT | IS_NO_SOL_OUT) #define IS_NO_ADV_OUT 0x1000000 /* do not advertise rdisc */ #define IS_ADV_OUT 0x2000000 /* advertise rdisc */ #define GROUP_IS_ADV_OUT (IS_NO_ADV_OUT | IS_ADV_OUT) #define IS_BCAST_RDISC 0x4000000 /* broadcast instead of multicast */ #define IS_NO_RDISC (IS_NO_ADV_IN | IS_NO_SOL_OUT | IS_NO_ADV_OUT) #define IS_PM_RDISC 0x8000000 /* poor-man's router discovery */ #define iff_up(f) ((f) & IFF_UP) LIST_HEAD(ifhead, interface); /* 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 coarser mask */ #define AGS_AGGREGATE 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 netmasks */ #define AGS_SPLIT_HZ 0x200 /* suppress for split horizon */ /* some bits are set if they are set on either route */ #define AGS_AGGREGATE_EITHER (AGS_RIPV2 | AGS_GATEWAY | \ AGS_SUPPRESS | AGS_CORS_GATE) }; /* parameters for interfaces */ struct parm { struct parm *parm_next; char parm_name[IF_NAME_LEN+1]; naddr parm_net; naddr parm_mask; u_char parm_d_metric; u_char parm_adj_inmetric; char parm_adj_outmetric; u_int parm_int_state; int parm_rdisc_pref; /* signed IRDP preference */ int parm_rdisc_int; /* IRDP advertising interval */ struct auth parm_auth[MAX_AUTH_KEYS]; }; /* authority for internal networks */ extern struct intnet { struct intnet *intnet_next; naddr intnet_addr; /* network byte order */ naddr intnet_mask; char intnet_metric; } *intnets; /* defined RIPv1 netmasks */ extern struct r1net { struct r1net *r1net_next; naddr r1net_net; /* host order */ naddr r1net_match; naddr r1net_mask; } *r1nets; /* trusted routers */ extern struct tgate { struct tgate *tgate_next; naddr tgate_addr; #define MAX_TGATE_NETS 32 struct tgate_net { naddr net; /* host order */ naddr mask; } tgate_nets[MAX_TGATE_NETS]; } *tgates; enum output_type {OUT_QUERY, OUT_UNICAST, OUT_BROADCAST, OUT_MULTICAST, NO_OUT_MULTICAST, NO_OUT_RIPV2}; /* common output buffers */ extern struct ws_buf { struct rip *buf; struct netinfo *n; struct netinfo *base; struct netinfo *lim; enum output_type type; } v12buf; extern pid_t mypid; extern naddr myaddr; /* main address of this system */ extern int stopint; /* !=0 to stop */ extern int rip_sock; /* RIP socket */ extern const 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 supplier; /* process should supply updates */ 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 advertising it */ extern int auth_ok; /* 1=ignore auth if we do not care */ +extern int insecure; /* Reply to special queries or not */ extern struct timeval clk; /* system clock's idea of time */ extern struct timeval epoch; /* system clock 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 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 ifhead ifnet; /* all interfaces */ extern struct ifhead remote_if; /* remote interfaces */ extern int have_ripv1_out; /* have a RIPv1 interface */ extern int need_flash; /* flash update needed */ extern struct timeval need_kern; /* need to update kernel table */ extern u_int update_seqno; /* a route has changed */ extern 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 char inittracename[PATH_MAX]; extern struct radix_node_head *rhead; #ifdef sgi /* Fix conflicts */ #define dup2(x,y) BSDdup2(x,y) #endif /* sgi */ void fix_sock(int, const char *); void fix_select(void); void rip_off(void); void rip_on(struct interface *); void bufinit(void); int output(enum output_type, struct sockaddr_in *, struct interface *, struct rip *, int); void clr_ws_buf(struct ws_buf *, struct auth *); void rip_query(void); void rip_bcast(int); void supply(struct sockaddr_in *, struct interface *, enum output_type, int, int, int); void msglog(const char *, ...) PATTRIB(1,2); struct msg_limit { time_t reuse; struct msg_sub { naddr addr; time_t until; # define MSG_SUBJECT_N 8 } subs[MSG_SUBJECT_N]; }; void msglim(struct msg_limit *, naddr, const char *, ...) PATTRIB(3,4); #define LOGERR(msg) msglog(msg ": %s", strerror(errno)) void logbad(int, const char *, ...) PATTRIB(2,3); #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 char *naddr_ntoa(naddr); const char *saddr_ntoa(struct sockaddr *); void *rtmalloc(size_t, const char *); void timevaladd(struct timeval *, struct timeval *); void intvl_random(struct timeval *, u_long, u_long); int getnet(char *, naddr *, naddr *); int gethost(char *, naddr *); void gwkludge(void); const char *parse_parms(char *, int); const char *check_parms(struct parm *); void get_parms(struct interface *); void lastlog(void); void trace_close(int); void set_tracefile(const char *, const char *, int); void tracelevel_msg(const char *, int); void trace_off(const char*, ...) PATTRIB(1,2); void set_tracelevel(void); void trace_flush(void); void trace_misc(const char *, ...) PATTRIB(1,2); void trace_act(const char *, ...) PATTRIB(1,2); void trace_pkt(const char *, ...) PATTRIB(1,2); void trace_add_del(const char *, struct rt_entry *); void trace_change(struct rt_entry *, u_int, struct rt_spare *, const char *); void trace_if(const char *, struct interface *); void trace_upslot(struct rt_entry *, struct rt_spare *, struct rt_spare *); void trace_rip(const char*, const char*, struct sockaddr_in *, struct interface *, struct rip *, int); char *addrname(naddr, naddr, int); char *rtname(naddr, naddr, naddr); void rdisc_age(naddr); void set_rdisc_mg(struct interface *, int); void set_supplier(void); void if_bad_rdisc(struct interface *); void if_ok_rdisc(struct interface *); void read_rip(int, struct interface *); void read_rt(void); void read_d(void); void rdisc_adv(void); void rdisc_sol(void); void sigtrace_on(int); void sigtrace_off(int); void flush_kern(void); void age(naddr); void ag_flush(naddr, naddr, void (*)(struct ag_info *)); void ag_check(naddr, naddr, naddr, naddr, char, char, u_int, u_short, u_short, void (*)(struct ag_info *)); void del_static(naddr, naddr, naddr, int); void del_redirects(naddr, time_t); struct rt_entry *rtget(naddr, naddr); struct rt_entry *rtfind(naddr); void rtinit(void); void rtadd(naddr, naddr, u_int, struct rt_spare *); void rtchange(struct rt_entry *, u_int, struct rt_spare *, char *); void rtdelete(struct rt_entry *); void rts_delete(struct rt_entry *, struct rt_spare *); void rtbad_sub(struct rt_entry *); void rtswitch(struct rt_entry *, struct rt_spare *); #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_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); naddr std_mask(naddr); naddr ripv1_mask_net(naddr, struct interface *); naddr ripv1_mask_host(naddr,struct interface *); #define on_net(a,net,mask) (((ntohl(a) ^ (net)) & (mask)) == 0) int check_dst(naddr); struct interface *check_dup(naddr, naddr, naddr, int); int check_remote(struct interface *); void ifinit(void); int walk_bad(struct radix_node *, struct walkarg *); int if_ok(struct interface *, const char *); void if_sick(struct interface *); void if_link(struct interface *); struct interface *ifwithaddr(naddr addr, int bcast, int remote); struct interface *ifwithindex(u_short, int); struct interface *iflookup(naddr); struct auth *find_auth(struct interface *); void end_md5_auth(struct ws_buf *, struct auth *); #if defined(__FreeBSD__) || defined(__NetBSD__) #include #else #define MD5_DIGEST_LEN 16 typedef struct { u_int32_t state[4]; /* state (ABCD) */ u_int32_t count[2]; /* # of bits, modulo 2^64 (LSB 1st) */ unsigned char buffer[64]; /* input buffer */ } MD5_CTX; void MD5Init(MD5_CTX*); void MD5Update(MD5_CTX*, u_char*, u_int); void MD5Final(u_char[MD5_DIGEST_LEN], MD5_CTX*); #endif Index: stable/10/sbin/routed/input.c =================================================================== --- stable/10/sbin/routed/input.c (revision 272871) +++ stable/10/sbin/routed/input.c (revision 272872) @@ -1,1013 +1,1024 @@ /* * 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. * 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. * * $FreeBSD$ */ #include "defs.h" #ifdef __NetBSD__ __RCSID("$NetBSD$"); #elif defined(__FreeBSD__) __RCSID("$FreeBSD$"); #else __RCSID("$Revision: 2.26 $"); #ident "$Revision: 2.26 $" #endif static void input(struct sockaddr_in *, struct interface *, struct interface *, struct rip *, int); static void input_route(naddr, naddr, struct rt_spare *, struct netinfo *); static int ck_passwd(struct interface *, struct rip *, void *, naddr, struct msg_limit *); /* process RIP input */ void read_rip(int sock, struct interface *sifp) { struct sockaddr_in from; struct interface *aifp; socklen_t fromlen; int cc; #ifdef USE_PASSIFNAME static struct msg_limit bad_name; struct { char ifname[IFNAMSIZ]; union pkt_buf pbuf; } inbuf; #else struct { union pkt_buf pbuf; } inbuf; #endif 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", (int)fromlen); /* aifp is the "authenticated" interface via which the packet * arrived. In fact, it is only the interface on which * the packet should have arrived based on is source * address. * sifp is interface associated with the socket through which * the packet was received. */ #ifdef USE_PASSIFNAME if ((cc -= sizeof(inbuf.ifname)) < 0) logbad(0,"missing USE_PASSIFNAME; only %d bytes", cc+sizeof(inbuf.ifname)); /* check the remote interfaces first */ LIST_FOREACH(aifp, &remote_if, remote_list) { if (aifp->int_addr == from.sin_addr.s_addr) break; } if (aifp == 0) { aifp = ifwithname(inbuf.ifname, 0); if (aifp == 0) { msglim(&bad_name, from.sin_addr.s_addr, "impossible interface name %.*s", IFNAMSIZ, inbuf.ifname); } else if (((aifp->int_if_flags & IFF_POINTOPOINT) && aifp->int_dstaddr!=from.sin_addr.s_addr) || (!(aifp->int_if_flags & IFF_POINTOPOINT) && !on_net(from.sin_addr.s_addr, aifp->int_net, aifp->int_mask))) { /* If it came via the wrong interface, do not * trust it. */ aifp = 0; } } #else aifp = iflookup(from.sin_addr.s_addr); #endif if (sifp == 0) sifp = aifp; input(&from, sifp, aifp, &inbuf.pbuf.rip, cc); } } /* Process a RIP packet */ static void input(struct sockaddr_in *from, /* received from this IP address */ struct interface *sifp, /* interface of incoming socket */ struct interface *aifp, /* "authenticated" interface */ struct rip *rip, int cc) { # define FROM_NADDR from->sin_addr.s_addr static struct msg_limit use_auth, bad_len, bad_mask; static struct msg_limit unk_router, bad_router, bad_nhop; struct rt_entry *rt; struct rt_spare new; struct netinfo *n, *lim; struct interface *ifp1; naddr gate, mask, v1_mask, dst, ddst_h = 0; struct auth *ap; struct tgate *tg = 0; struct tgate_net *tn; int i, j; /* Notice when we hear from a remote gateway */ if (aifp != 0 && (aifp->int_state & IS_REMOTE)) aifp->int_act_time = now.tv_sec; trace_rip("Recv", "from", from, sifp, rip, cc); if (rip->rip_vers == 0) { msglim(&bad_router, FROM_NADDR, "RIP version 0, cmd %d, packet received from %s", rip->rip_cmd, naddr_ntoa(FROM_NADDR)); return; } else if (rip->rip_vers > RIPv2) { rip->rip_vers = RIPv2; } if (cc > (int)OVER_MAXPACKETSIZE) { msglim(&bad_router, FROM_NADDR, "packet at least %d bytes too long received from %s", cc-MAXPACKETSIZE, naddr_ntoa(FROM_NADDR)); return; } n = rip->rip_nets; lim = (struct netinfo *)((char*)rip + cc); /* 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. Why authenticate queries? * Why should a RIPv2 implementation with authentication disabled * not be able to listen to RIPv2 packets with authentication, while * RIPv1 systems will listen? Crazy! */ if (!auth_ok && rip->rip_vers == RIPv2 && n < lim && n->n_family == RIP_AF_AUTH) { msglim(&use_auth, FROM_NADDR, "RIPv2 message with authentication from %s discarded", naddr_ntoa(FROM_NADDR)); return; } switch (rip->rip_cmd) { case RIPCMD_REQUEST: /* For mere requests, be a little sloppy about the source */ if (aifp == 0) aifp = sifp; /* Are we talking to ourself or a remote gateway? */ ifp1 = ifwithaddr(FROM_NADDR, 0, 1); if (ifp1) { if (ifp1->int_state & IS_REMOTE) { /* remote gateway */ aifp = ifp1; if (check_remote(aifp)) { aifp->int_act_time = now.tv_sec; (void)if_ok(aifp, "remote "); } } else if (from->sin_port == htons(RIP_PORT)) { trace_pkt(" discard our own RIP request"); return; } } /* did the request come from a router? */ if (from->sin_port == htons(RIP_PORT)) { /* yes, ignore the request if RIP is off so that * the router does not depend on us. */ if (rip_sock < 0 || (aifp != 0 && IS_RIP_OUT_OFF(aifp->int_state))) { trace_pkt(" discard request while RIP off"); return; } } /* According to RFC 1723, we should ignore unauthenticated * 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? When RIP * is still the most common router-discovery protocol * and so hosts need to send queries that will be answered? * What about `rtquery`? * Maybe on firewalls you'd care, but not enough to * give up the diagnostic facilities of remote probing. */ if (n >= lim) { msglim(&bad_len, FROM_NADDR, "empty request from %s", naddr_ntoa(FROM_NADDR)); return; } if (cc%sizeof(*n) != sizeof(struct rip)%sizeof(*n)) { msglim(&bad_len, FROM_NADDR, "request of bad length (%d) from %s", cc, naddr_ntoa(FROM_NADDR)); } if (rip->rip_vers == RIPv2 && (aifp == 0 || (aifp->int_state & IS_NO_RIPV1_OUT))) { v12buf.buf->rip_vers = RIPv2; /* If we have a secret but it is a cleartext secret, * do not disclose our secret unless the other guy * already knows it. */ ap = find_auth(aifp); if (ap != 0 && ap->type == RIP_AUTH_PW && n->n_family == RIP_AF_AUTH && !ck_passwd(aifp,rip,lim,FROM_NADDR,&use_auth)) ap = 0; } else { v12buf.buf->rip_vers = RIPv1; ap = 0; } clr_ws_buf(&v12buf, ap); do { 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) { /* Answer a query from a utility program * with all we know. */ if (from->sin_port != htons(RIP_PORT)) { - supply(from, aifp, OUT_QUERY, 0, - rip->rip_vers, ap != 0); + /* + * insecure: query from non-router node + * > 1: allow from distant node + * > 0: allow from neighbor node + * == 0: deny + */ + if ((aifp != NULL && insecure > 0) || + (aifp == NULL && insecure > 1)) + supply(from, aifp, OUT_QUERY, 0, + rip->rip_vers, ap != 0); + else + trace_pkt("Warning: " + "possible attack detected"); 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. */ if (aifp == 0) { trace_pkt("ignore distant router"); return; } if (!supplier || IS_RIP_OFF(aifp->int_state)) { trace_pkt("ignore; not supplying"); return; } /* Do not answer a RIPv1 router if * we are sending RIPv2. But do offer * poor man's router discovery. */ if ((aifp->int_state & IS_NO_RIPV1_OUT) && rip->rip_vers == RIPv1) { if (!(aifp->int_state & IS_PM_RDISC)) { trace_pkt("ignore; sending RIPv2"); return; } v12buf.n->n_family = RIP_AF_INET; v12buf.n->n_dst = RIP_DEFAULT; i = aifp->int_d_metric; if (0 != (rt = rtget(RIP_DEFAULT, 0))) { j = (rt->rt_metric +aifp->int_metric +aifp->int_adj_outmetric +1); if (i > j) i = j; } v12buf.n->n_metric = htonl(i); v12buf.n++; break; } /* 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. */ supply(from, aifp, OUT_UNICAST, 0, (aifp->int_state & IS_NO_RIPV1_OUT) ? RIPv2 : RIPv1, ap != 0); return; } /* Ignore authentication */ if (n->n_family == RIP_AF_AUTH) continue; if (n->n_family != RIP_AF_INET) { msglim(&bad_router, FROM_NADDR, "request from %s for unsupported" " (af %d) %s", naddr_ntoa(FROM_NADDR), ntohs(n->n_family), naddr_ntoa(n->n_dst)); return; } /* We are being asked about a specific destination. */ dst = n->n_dst; if (!check_dst(dst)) { msglim(&bad_router, FROM_NADDR, "bad queried destination %s from %s", naddr_ntoa(dst), naddr_ntoa(FROM_NADDR)); return; } /* decide what mask was intended */ if (rip->rip_vers == RIPv1 || 0 == (mask = ntohl(n->n_mask)) || 0 != (ntohl(dst) & ~mask)) mask = ripv1_mask_host(dst, aifp); /* try to find the answer */ rt = rtget(dst, mask); if (!rt && dst != RIP_DEFAULT) rt = rtfind(n->n_dst); if (v12buf.buf->rip_vers != RIPv1) v12buf.n->n_mask = mask; if (rt == 0) { /* we do not have the answer */ v12buf.n->n_metric = HOPCNT_INFINITY; } else { /* we have the answer, so compute the * right metric and next hop. */ v12buf.n->n_family = RIP_AF_INET; v12buf.n->n_dst = dst; j = rt->rt_metric+1; if (!aifp) ++j; else j += (aifp->int_metric + aifp->int_adj_outmetric); if (j < HOPCNT_INFINITY) v12buf.n->n_metric = j; else v12buf.n->n_metric = HOPCNT_INFINITY; if (v12buf.buf->rip_vers != RIPv1) { v12buf.n->n_tag = rt->rt_tag; v12buf.n->n_mask = mask; if (aifp != 0 && on_net(rt->rt_gate, aifp->int_net, aifp->int_mask) && rt->rt_gate != aifp->int_addr) v12buf.n->n_nhop = rt->rt_gate; } } v12buf.n->n_metric = htonl(v12buf.n->n_metric); /* Stop paying attention if we fill the output buffer. */ if (++v12buf.n >= v12buf.lim) break; } while (++n < lim); /* Send the answer about specific routes. */ if (ap != 0 && ap->type == RIP_AUTH_MD5) end_md5_auth(&v12buf, ap); if (from->sin_port != htons(RIP_PORT)) { /* query */ (void)output(OUT_QUERY, from, aifp, v12buf.buf, ((char *)v12buf.n - (char*)v12buf.buf)); } else if (supplier) { (void)output(OUT_UNICAST, from, aifp, v12buf.buf, ((char *)v12buf.n - (char*)v12buf.buf)); } else { /* Only answer a router if we are a supplier * to keep an unwary host that is just starting * from picking us an a router. */ ; } return; case RIPCMD_TRACEON: case RIPCMD_TRACEOFF: /* Notice that trace messages are turned off for all possible * abuse if _PATH_TRACE is undefined in pathnames.h. * Notice also that because of the way the trace file is * handled in trace.c, no abuse is plausible even if * _PATH_TRACE_ is defined. * * First 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[cc-4] = '\0'; set_tracefile((char*)rip->rip_tracefile, "trace command: %s\n", 0); } else { trace_off("tracing turned off by %s", naddr_ntoa(FROM_NADDR)); } return; case RIPCMD_RESPONSE: if (cc%sizeof(*n) != sizeof(struct rip)%sizeof(*n)) { msglim(&bad_len, FROM_NADDR, "response of bad length (%d) from %s", cc, naddr_ntoa(FROM_NADDR)); } /* verify message came from a router */ if (from->sin_port != ntohs(RIP_PORT)) { msglim(&bad_router, FROM_NADDR, " discard RIP response from unknown port" " %d on %s", ntohs(from->sin_port), naddr_ntoa(FROM_NADDR)); return; } if (rip_sock < 0) { trace_pkt(" discard response while RIP off"); return; } /* Are we talking to ourself or a remote gateway? */ ifp1 = ifwithaddr(FROM_NADDR, 0, 1); if (ifp1) { if (ifp1->int_state & IS_REMOTE) { /* remote gateway */ aifp = ifp1; if (check_remote(aifp)) { aifp->int_act_time = now.tv_sec; (void)if_ok(aifp, "remote "); } } else { trace_pkt(" discard our own RIP response"); return; } } /* Accept routing packets from routers directly connected * via broadcast or point-to-point networks, and from * those listed in /etc/gateways. */ if (aifp == 0) { msglim(&unk_router, FROM_NADDR, " discard response from %s" " via unexpected interface", naddr_ntoa(FROM_NADDR)); return; } if (IS_RIP_IN_OFF(aifp->int_state)) { trace_pkt(" discard RIPv%d response" " via disabled interface %s", rip->rip_vers, aifp->int_name); return; } if (n >= lim) { msglim(&bad_len, FROM_NADDR, "empty response from %s", naddr_ntoa(FROM_NADDR)); return; } 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", rip->rip_vers); return; } /* Ignore routes via dead interface. */ if (aifp->int_state & IS_BROKE) { trace_pkt("discard response via broken interface %s", aifp->int_name); return; } /* If the interface cares, ignore bad routers. * Trace but do not log this problem, because where it * happens, it happens frequently. */ if (aifp->int_state & IS_DISTRUST) { tg = tgates; while (tg->tgate_addr != FROM_NADDR) { tg = tg->tgate_next; if (tg == 0) { trace_pkt(" discard RIP response" " from untrusted router %s", naddr_ntoa(FROM_NADDR)); return; } } } /* Authenticate the packet if we have a secret. * If we do not have any secrets, ignore the error in * RFC 1723 and accept it regardless. */ if (aifp->int_auth[0].type != RIP_AUTH_NONE && rip->rip_vers != RIPv1 && !ck_passwd(aifp,rip,lim,FROM_NADDR,&use_auth)) return; do { if (n->n_family == RIP_AF_AUTH) continue; n->n_metric = ntohl(n->n_metric); dst = n->n_dst; if (n->n_family != RIP_AF_INET && (n->n_family != RIP_AF_UNSPEC || dst != RIP_DEFAULT)) { msglim(&bad_router, FROM_NADDR, "route from %s to unsupported" " address family=%d destination=%s", naddr_ntoa(FROM_NADDR), n->n_family, naddr_ntoa(dst)); continue; } if (!check_dst(dst)) { msglim(&bad_router, FROM_NADDR, "bad destination %s from %s", naddr_ntoa(dst), naddr_ntoa(FROM_NADDR)); return; } if (n->n_metric == 0 || n->n_metric > HOPCNT_INFINITY) { msglim(&bad_router, FROM_NADDR, "bad metric %d from %s" " for destination %s", n->n_metric, naddr_ntoa(FROM_NADDR), naddr_ntoa(dst)); return; } /* Notice the next-hop. */ gate = FROM_NADDR; if (n->n_nhop != 0) { if (rip->rip_vers == RIPv1) { 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 { msglim(&bad_nhop, FROM_NADDR, "router %s to %s" " has bad next hop %s", naddr_ntoa(FROM_NADDR), naddr_ntoa(dst), naddr_ntoa(n->n_nhop)); 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) { msglim(&bad_mask, FROM_NADDR, "router %s sent bad netmask" " %#lx with %s", naddr_ntoa(FROM_NADDR), (u_long)mask, naddr_ntoa(dst)); continue; } if (rip->rip_vers == RIPv1) n->n_tag = 0; /* Adjust metric according to incoming interface.. */ n->n_metric += (aifp->int_metric + aifp->int_adj_inmetric); if (n->n_metric > HOPCNT_INFINITY) n->n_metric = HOPCNT_INFINITY; /* Should we trust this route from this router? */ if (tg && (tn = tg->tgate_nets)->mask != 0) { for (i = 0; i < MAX_TGATE_NETS; i++, tn++) { if (on_net(dst, tn->net, tn->mask) && tn->mask <= mask) break; } if (i >= MAX_TGATE_NETS || tn->mask == 0) { trace_pkt(" ignored unauthorized %s", addrname(dst,mask,0)); continue; } } /* 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 && (int)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 && (((rt = rtget(dst,mask)) == 0 || !(rt->rt_state & RS_NET_SYN))) && (v1_mask = ripv1_mask_net(dst,0)) > mask) { 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. */ if (TRACECONTENTS) trace_misc("accept %s-->%s as 1" " instead of %d routes", addrname(dst,mask,0), naddr_ntoa(FROM_NADDR), i+1); i = 0; } else { mask = v1_mask; } } else { i = 0; } new.rts_gate = gate; new.rts_router = FROM_NADDR; new.rts_metric = n->n_metric; new.rts_tag = n->n_tag; new.rts_time = now.tv_sec; new.rts_ifp = aifp; new.rts_de_ag = i; j = 0; for (;;) { input_route(dst, mask, &new, n); if (++j > i) break; dst = htonl(ntohl(dst) + ddst_h); } } while (++n < lim); break; } #undef FROM_NADDR } /* Process a single input route. */ static void input_route(naddr dst, /* network order */ naddr mask, struct rt_spare *new, struct netinfo *n) { int i; struct rt_entry *rt; struct rt_spare *rts, *rts0; struct interface *ifp1; /* 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) || (ifp1->int_state & IS_PASSIVE))) 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 (new->rts_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, 0, new); 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 == new->rts_router) 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 a route from the router already in the table. */ /* If the new route is a route broken down from an * aggregated route, and if the previous route is either * not a broken down route or was broken down from a finer * netmask, and if the previous route is current, * then forget this one. */ if (new->rts_de_ag > rts->rts_de_ag && now_stale <= rts->rts_time) return; /* Keep poisoned routes around only long enough to pass * the poison on. Use a new timestamp for good routes. */ if (rts->rts_metric == HOPCNT_INFINITY && new->rts_metric == HOPCNT_INFINITY) new->rts_time = rts->rts_time; /* If this is an update for the router we currently prefer, * then note it. */ if (i == NUM_SPARES) { rtchange(rt, rt->rt_state, new, 0); /* If the route got worse, check for something better. */ if (new->rts_metric > rts->rts_metric) rtswitch(rt, 0); return; } /* This is an update for a spare route. * Finished if the route is unchanged. */ if (rts->rts_gate == new->rts_gate && rts->rts_metric == new->rts_metric && rts->rts_tag == new->rts_tag) { trace_upslot(rt, rts, new); *rts = *new; return; } /* Forget it if it has gone bad. */ if (new->rts_metric == HOPCNT_INFINITY) { rts_delete(rt, rts); 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; /* the loop above set rts0=worst spare */ 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 (new->rts_metric >= rts->rts_metric) return; } trace_upslot(rt, rts, new); *rts = *new; /* try to switch to a better route */ rtswitch(rt, rts); } static int /* 0 if bad */ ck_passwd(struct interface *aifp, struct rip *rip, void *lim, naddr from, struct msg_limit *use_authp) { # define NA (rip->rip_auths) struct netauth *na2; struct auth *ap; MD5_CTX md5_ctx; u_char hash[RIP_AUTH_PW_LEN]; int i, len; assert(aifp != NULL); if ((void *)NA >= lim || NA->a_family != RIP_AF_AUTH) { msglim(use_authp, from, "missing password from %s", naddr_ntoa(from)); return 0; } /* accept any current (+/- 24 hours) password */ for (ap = aifp->int_auth, i = 0; i < MAX_AUTH_KEYS; i++, ap++) { if (ap->type != NA->a_type || (u_long)ap->start > (u_long)clk.tv_sec+DAY || (u_long)ap->end+DAY < (u_long)clk.tv_sec) continue; if (NA->a_type == RIP_AUTH_PW) { if (!memcmp(NA->au.au_pw, ap->key, RIP_AUTH_PW_LEN)) return 1; } else { /* accept MD5 secret with the right key ID */ if (NA->au.a_md5.md5_keyid != ap->keyid) continue; len = ntohs(NA->au.a_md5.md5_pkt_len); if ((len-sizeof(*rip)) % sizeof(*NA) != 0 || len != (char *)lim-(char*)rip-(int)sizeof(*NA)) { msglim(use_authp, from, "wrong MD5 RIPv2 packet length of %d" " instead of %d from %s", len, (int)((char *)lim-(char *)rip -sizeof(*NA)), naddr_ntoa(from)); return 0; } na2 = (struct netauth *)((char *)rip+len); /* Given a good hash value, these are not security * problems so be generous and accept the routes, * after complaining. */ if (TRACEPACKETS) { if (NA->au.a_md5.md5_auth_len != RIP_AUTH_MD5_HASH_LEN) msglim(use_authp, from, "unknown MD5 RIPv2 auth len %#x" " instead of %#x from %s", NA->au.a_md5.md5_auth_len, (unsigned)RIP_AUTH_MD5_HASH_LEN, naddr_ntoa(from)); if (na2->a_family != RIP_AF_AUTH) msglim(use_authp, from, "unknown MD5 RIPv2 family %#x" " instead of %#x from %s", na2->a_family, RIP_AF_AUTH, naddr_ntoa(from)); if (na2->a_type != ntohs(1)) msglim(use_authp, from, "MD5 RIPv2 hash has %#x" " instead of %#x from %s", na2->a_type, ntohs(1), naddr_ntoa(from)); } MD5Init(&md5_ctx); MD5Update(&md5_ctx, (u_char *)rip, len + RIP_AUTH_MD5_HASH_XTRA); MD5Update(&md5_ctx, ap->key, RIP_AUTH_MD5_KEY_LEN); MD5Final(hash, &md5_ctx); if (!memcmp(hash, na2->au.au_pw, sizeof(hash))) return 1; } } msglim(use_authp, from, "bad password from %s", naddr_ntoa(from)); return 0; #undef NA } Index: stable/10/sbin/routed/main.c =================================================================== --- stable/10/sbin/routed/main.c (revision 272871) +++ stable/10/sbin/routed/main.c (revision 272872) @@ -1,965 +1,969 @@ /* * 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. * 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. * * $FreeBSD$ */ #include "defs.h" #include "pathnames.h" #ifdef sgi #include "math.h" #endif #include #include #include __COPYRIGHT("@(#) Copyright (c) 1983, 1988, 1993 " "The Regents of the University of California." " All rights reserved."); #ifdef __NetBSD__ __RCSID("$NetBSD$"); #include #elif defined(__FreeBSD__) __RCSID("$FreeBSD$"); #else __RCSID("$Revision: 2.31 $"); #ident "$Revision: 2.31 $" #endif pid_t mypid; naddr myaddr; /* system address */ static char myname[MAXHOSTNAMELEN+1]; static int verbose; int supplier; /* supply or broadcast updates */ int supplier_set; static int ipforwarding = 1; /* kernel forwarding on */ static int default_gateway; /* 1=advertise default */ static int background = 1; int ridhosts; /* 1=reduce host routes */ int mhome; /* 1=want multi-homed host route */ int advertise_mhome; /* 1=must continue advertising it */ int auth_ok = 1; /* 1=ignore auth if we do not care */ +int insecure; /* Reply to special queries or not */ struct timeval epoch; /* when started */ struct timeval clk; static struct timeval prev_clk; static int usec_fudge; struct timeval now; /* current idea of time */ time_t now_stale; time_t now_expire; time_t now_garbage; static struct timeval next_bcast; /* next general broadcast */ struct timeval no_flash = { /* inhibit flash update */ EPOCH+SUPPLY_INTERVAL, 0 }; static struct timeval flush_kern_timer; static fd_set fdbits; static int sock_max; int rip_sock = -1; /* RIP socket */ const 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 *); static void sigalrm(int s UNUSED); static void sigterm(int sig); int main(int argc, char *argv[]) { int n, mib[4], off; size_t len; char *p, *q; const char *cp; struct timeval wtime, t2; time_t dt; fd_set ibits; naddr p_net, p_mask; struct interface *ifp; struct parm parm; char *tracename = 0; /* Some shells are badly broken and send SIGHUP to backgrounded * processes. */ signal(SIGHUP, SIG_IGN); openlog("routed", LOG_PID, 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, "sqdghmAtvT:F:P:")) != -1) { + while ((n = getopt(argc, argv, "isqdghmAtvT:F:P:")) != -1) { switch (n) { + case 'i': + insecure++; + break; case 's': supplier = 1; supplier_set = 1; break; case 'q': supplier = 0; supplier_set = 1; break; case 'd': background = 0; break; case 'g': memset(&parm, 0, sizeof(parm)); parm.parm_d_metric = 1; cp = check_parms(&parm); if (cp != 0) msglog("bad -g: %s", cp); 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 = FAKE_METRIC; 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_net, &p_mask)) { msglog("bad network; \"-F %s\"", optarg); break; } memset(&parm, 0, sizeof(parm)); parm.parm_net = p_net; parm.parm_mask = p_mask; parm.parm_d_metric = n; cp = check_parms(&parm); if (cp != 0) msglog("bad -F: %s", cp); break; case 'P': /* handle arbitrary parameters. */ q = strdup(optarg); cp = parse_parms(q, 0); if (cp != 0) msglog("%s in \"-P %s\"", cp, optarg); free(q); break; case 'v': /* display version */ verbose++; msglog("version 2.31"); break; default: goto usage; } } argc -= optind; argv += optind; if (tracename == 0 && argc >= 1) { tracename = *argv++; argc--; } if (tracename != 0 && tracename[0] == '\0') goto usage; if (argc != 0) { usage: logbad(0, "usage: routed [-sqdghmAtv] [-T tracefile]" " [-F net[,metric]] [-P parms]"); } if (geteuid() != 0) { if (verbose) exit(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 */ signal(SIGTERM, sigterm); signal(SIGINT, sigterm); signal(SIGUSR1, sigtrace_on); signal(SIGUSR2, sigtrace_off); /* get into the background */ #ifdef sgi if (0 > _daemonize(background ? 0 : (_DF_NOCHDIR|_DF_NOFORK), STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO)) BADERR(0, "_daemonize()"); #else if (background && daemon(0, 1) < 0) BADERR(0,"daemon()"); #endif #if defined(__NetBSD__) pidfile(0); #endif mypid = getpid(); #ifdef __FreeBSD__ srandomdev(); #else srandom((int)(clk.tv_sec ^ clk.tv_usec ^ mypid)); #endif /* 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 (tracename != 0) { strncpy(inittracename, tracename, sizeof(inittracename)-1); set_tracefile(inittracename, "%s", -1); } else { tracelevel_msg("%s", -1); /* turn on tracing to stdio */ } bufinit(); /* 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(); /* Ask for routes */ rip_query(); rdisc_sol(); /* Now turn off stdio if not tracing */ if (new_tracelevel == 0) trace_close(background); /* Loop forever, listening and broadcasting. */ for (;;) { prev_clk = clk; gettimeofday(&clk, 0); if (prev_clk.tv_sec == clk.tv_sec && prev_clk.tv_usec == clk.tv_usec+usec_fudge) { /* Much of `routed` depends on time always advancing. * On systems that do not guarantee that gettimeofday() * produces unique timestamps even if called within * a single tick, use trickery like that in classic * BSD kernels. */ clk.tv_usec += ++usec_fudge; } else { usec_fudge = 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", (int)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 signals that should affect tracing */ set_tracelevel(); if (stopint != 0) { rip_bcast(0); rdisc_adv(); trace_off("exiting with signal %d", 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; } /* Check the kernel table occasionally for mysteriously * evaporated routes */ timevalsub(&t2, &flush_kern_timer, &now); if (t2.tv_sec <= 0) { flush_kern(); flush_kern_timer.tv_sec = (now.tv_sec + CHECK_QUIET_INTERVAL); continue; } if (timercmp(&t2, &wtime, <)) wtime = t2; /* 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 * scheduled, 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 anniversary 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 in the correct 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--; } LIST_FOREACH(ifp, &ifnet, int_list) { if (n <= 0) break; if (ifp->int_rip_sock >= 0 && FD_ISSET(ifp->int_rip_sock, &ibits)) { read_rip(ifp->int_rip_sock, ifp); n--; } } } } /* ARGSUSED */ static void sigalrm(int s UNUSED) { /* Historically, SIGALRM would cause the daemon to check for * new and broken interfaces. */ ifinit_timer.tv_sec = now.tv_sec; trace_act("SIGALRM"); } /* watch for fatal signals */ static 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; } LIST_FOREACH(ifp, &ifnet, int_list) { 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, const 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)); #ifdef USE_PASSIFNAME on = 1; if (setsockopt(sock, SOL_SOCKET, SO_PASSIFNAME, &on,sizeof(on)) < 0) msglog("setsockopt(%s,SO_PASSIFNAME): %s", name, strerror(errno)); #endif 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", 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 rsin; unsigned char ttl; int s; if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) BADERR(1,"rip_sock = socket()"); memset(&rsin, 0, sizeof(rsin)); #ifdef _HAVE_SIN_LEN rsin.sin_len = sizeof(rsin); #endif rsin.sin_family = AF_INET; rsin.sin_port = htons(RIP_PORT); rsin.sin_addr.s_addr = addr; if (bind(s, (struct sockaddr *)&rsin, sizeof(rsin)) < 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; naddr addr; if (rip_sock >= 0 && !mhome) { trace_act("turn off RIP"); (void)close(rip_sock); rip_sock = -1; /* get non-broadcast sockets to listen to queries. */ LIST_FOREACH(ifp, &ifnet, int_list) { if (ifp->int_state & IS_REMOTE) continue; if (ifp->int_rip_sock < 0) { 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 group_req gr; struct sockaddr_in *sin; if (!IS_RIP_IN_OFF(ifp->int_state) && (ifp->int_if_flags & IFF_MULTICAST) && !(ifp->int_state & IS_ALIAS)) { memset(&gr, 0, sizeof(gr)); gr.gr_interface = ifp->int_index; sin = (struct sockaddr_in *)&gr.gr_group; sin->sin_family = AF_INET; #ifdef _HAVE_SIN_LEN sin->sin_len = sizeof(struct sockaddr_in); #endif sin->sin_addr.s_addr = htonl(INADDR_RIP_GROUP); if (setsockopt(rip_sock, IPPROTO_IP, MCAST_JOIN_GROUP, &gr, sizeof(gr)) < 0) LOGERR("setsockopt(MCAST_JOIN_GROUP 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, * then turn it on for all of the interfaces. * It makes sense if either router discovery is off, or if * router discover is on and at most one interface is doing RIP. */ if (rip_interfaces > 0 && (!rdisc_ok || rip_interfaces > 1)) { trace_act("turn on RIP"); /* 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. */ LIST_FOREACH(ifp, &ifnet, int_list) { 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; LIST_FOREACH(ifp, &ifnet, int_list) { ifp->int_query_time = NEVER; rip_mcast_on(ifp); } ifinit_timer.tv_sec = now.tv_sec; } else if (ifp != 0 && !(ifp->int_state & IS_REMOTE) && ifp->int_rip_sock < 0) { /* 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, const char *msg) { void *p = malloc(size); if (p == 0) logbad(1,"malloc(%lu) failed in %s", (u_long)size, 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; } } /* put a message into the system log */ void msglog(const char *p, ...) { va_list args; trace_flush(); va_start(args, p); vsyslog(LOG_ERR, p, args); va_end(args); if (ftrace != 0) { if (ftrace == stdout) (void)fputs("routed: ", ftrace); va_start(args, p); (void)vfprintf(ftrace, p, args); va_end(args); (void)fputc('\n', ftrace); } } /* Put a message about a bad system into the system log if * we have not complained about it recently. * * It is desirable to complain about all bad systems, but not too often. * In the worst case, it is not practical to keep track of all bad systems. * For example, there can be many systems with the wrong password. */ void msglim(struct msg_limit *lim, naddr addr, const char *p, ...) { va_list args; int i; struct msg_sub *ms1, *ms; const char *p1; /* look for the oldest slot in the table * or the slot for the bad router. */ ms = ms1 = lim->subs; for (i = MSG_SUBJECT_N; ; i--, ms1++) { if (i == 0) { /* Reuse a slot at most once every 10 minutes. */ if (lim->reuse > now.tv_sec) { ms = 0; } else { ms = ms1; lim->reuse = now.tv_sec + 10*60; } break; } if (ms->addr == addr) { /* Repeat a complaint about a given system at * most once an hour. */ if (ms->until > now.tv_sec) ms = 0; break; } if (ms->until < ms1->until) ms = ms1; } if (ms != 0) { ms->addr = addr; ms->until = now.tv_sec + 60*60; /* 60 minutes */ trace_flush(); for (p1 = p; *p1 == ' '; p1++) continue; va_start(args, p); vsyslog(LOG_ERR, p1, args); va_end(args); } /* always display the message if tracing */ if (ftrace != 0) { va_start(args, p); (void)vfprintf(ftrace, p, args); va_end(args); (void)fputc('\n', ftrace); } } void logbad(int dump, const char *p, ...) { va_list args; trace_flush(); va_start(args, p); vsyslog(LOG_ERR, p, args); va_end(args); (void)fputs("routed: ", stderr); va_start(args, p); (void)vfprintf(stderr, p, args); va_end(args); (void)fputs("; giving up\n",stderr); (void)fflush(stderr); if (dump) abort(); exit(1); } Index: stable/10/sbin/routed/output.c =================================================================== --- stable/10/sbin/routed/output.c (revision 272871) +++ stable/10/sbin/routed/output.c (revision 272872) @@ -1,976 +1,974 @@ /* * 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. * 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. * * $FreeBSD$ */ #include "defs.h" #ifdef __NetBSD__ __RCSID("$NetBSD$"); #elif defined(__FreeBSD__) __RCSID("$FreeBSD$"); #else __RCSID("$Revision: 2.27 $"); #ident "$Revision: 2.27 $" #endif u_int update_seqno; /* walk the tree of routes with this for output */ static 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 auth *a; 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_ALL 0x002 /* send full featured RIPv2 */ #define WS_ST_AG 0x004 /* ok to aggregate subnets */ #define WS_ST_SUPER_AG 0x008 /* ok to aggregate networks */ #define WS_ST_QUERY 0x010 /* responding to a query */ #define WS_ST_TO_ON_NET 0x020 /* sending onto one of our nets */ #define WS_ST_DEFAULT 0x040 /* faking a default */ } ws; /* A buffer for what can be heard by both RIPv1 and RIPv2 listeners */ struct ws_buf v12buf; static union pkt_buf ripv12_buf; /* Another for only RIPv2 listeners */ static struct ws_buf v2buf; static union pkt_buf rip_v2_buf; void bufinit(void) { ripv12_buf.rip.rip_cmd = RIPCMD_RESPONSE; v12buf.buf = &ripv12_buf.rip; v12buf.base = &v12buf.buf->rip_nets[0]; rip_v2_buf.rip.rip_cmd = RIPCMD_RESPONSE; rip_v2_buf.rip.rip_vers = RIPv2; v2buf.buf = &rip_v2_buf.rip; v2buf.base = &v2buf.buf->rip_nets[0]; } /* 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 osin; int flags; const char *msg; int res; int soc; int serrno; assert(ifp != NULL); osin = *dst; if (osin.sin_port == 0) osin.sin_port = htons(RIP_PORT); #ifdef _HAVE_SIN_LEN if (osin.sin_len == 0) osin.sin_len = sizeof(osin); #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|IFF_MULTICAST)) == IFF_POINTOPOINT) { msg = "Send pt-to-pt"; } else if (ifp->int_state & IS_DUP) { trace_act("abort multicast output via %s" " with duplicate address", ifp->int_name); return 0; } else { msg = "Send mcast"; if (rip_sock_mcast != ifp) { struct ip_mreqn mreqn; memset(&mreqn, 0, sizeof(struct ip_mreqn)); mreqn.imr_ifindex = ifp->int_index; if (0 > setsockopt(rip_sock, IPPROTO_IP, IP_MULTICAST_IF, &mreqn, sizeof(mreqn))) { serrno = errno; LOGERR("setsockopt(rip_sock, " "IP_MULTICAST_IF)"); errno = serrno; ifp = 0; return -1; } rip_sock_mcast = ifp; } osin.sin_addr.s_addr = htonl(INADDR_RIP_GROUP); } break; case NO_OUT_MULTICAST: case NO_OUT_RIPV2: default: #ifdef DEBUG abort(); #endif return -1; } trace_rip(msg, "to", &osin, ifp, buf, size); res = sendto(soc, buf, size, flags, (struct sockaddr *)&osin, sizeof(osin)); 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(osin.sin_addr), ntohs(osin.sin_port), strerror(errno)); errno = serrno; } return res; } /* Find the first key for a packet to send. * Try for a key that is eligible and has not expired, but settle for * the last key if they have all expired. * If no key is ready yet, give up. */ struct auth * find_auth(struct interface *ifp) { struct auth *ap, *res; int i; if (ifp == 0) return 0; res = 0; ap = ifp->int_auth; for (i = 0; i < MAX_AUTH_KEYS; i++, ap++) { /* stop looking after the last key */ if (ap->type == RIP_AUTH_NONE) break; /* ignore keys that are not ready yet */ if ((u_long)ap->start > (u_long)clk.tv_sec) continue; if ((u_long)ap->end < (u_long)clk.tv_sec) { /* note best expired password as a fall-back */ if (res == 0 || (u_long)ap->end > (u_long)res->end) res = ap; continue; } /* note key with the best future */ if (res == 0 || (u_long)res->end < (u_long)ap->end) res = ap; } return res; } void clr_ws_buf(struct ws_buf *wb, struct auth *ap) { struct netauth *na; wb->lim = wb->base + NETS_LEN; wb->n = wb->base; memset(wb->n, 0, NETS_LEN*sizeof(*wb->n)); /* (start to) install authentication if appropriate */ if (ap == 0) return; na = (struct netauth*)wb->n; if (ap->type == RIP_AUTH_PW) { na->a_family = RIP_AF_AUTH; na->a_type = RIP_AUTH_PW; memcpy(na->au.au_pw, ap->key, sizeof(na->au.au_pw)); wb->n++; } else if (ap->type == RIP_AUTH_MD5) { na->a_family = RIP_AF_AUTH; na->a_type = RIP_AUTH_MD5; na->au.a_md5.md5_keyid = ap->keyid; na->au.a_md5.md5_auth_len = RIP_AUTH_MD5_KEY_LEN; na->au.a_md5.md5_seqno = htonl(clk.tv_sec); wb->n++; wb->lim--; /* make room for trailer */ } } void end_md5_auth(struct ws_buf *wb, struct auth *ap) { struct netauth *na, *na2; MD5_CTX md5_ctx; int len; na = (struct netauth*)wb->base; na2 = (struct netauth*)wb->n; len = (char *)na2-(char *)wb->buf; na2->a_family = RIP_AF_AUTH; na2->a_type = htons(1); na->au.a_md5.md5_pkt_len = htons(len); MD5Init(&md5_ctx); MD5Update(&md5_ctx, (u_char *)wb->buf, len + RIP_AUTH_MD5_HASH_XTRA); MD5Update(&md5_ctx, ap->key, RIP_AUTH_MD5_KEY_LEN); MD5Final(na2->au.au_pw, &md5_ctx); wb->n++; } /* Send the buffer */ static void supply_write(struct ws_buf *wb) { /* Output multicast only if legal. * If we would multicast and it would be illegal, then discard the * packet. */ switch (wb->type) { case NO_OUT_MULTICAST: trace_pkt("skip multicast to %s because impossible", naddr_ntoa(ws.to.sin_addr.s_addr)); break; case NO_OUT_RIPV2: break; default: if (ws.a != 0 && ws.a->type == RIP_AUTH_MD5) end_md5_auth(wb,ws.a); 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; } clr_ws_buf(wb,ws.a); } /* put an entry into the packet */ static void supply_out(struct ag_info *ag) { int i; naddr mask, v1_mask, dst_h, ddst_h = 0; 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; 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); 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. */ if ((ws.state & WS_ST_RIP2_ALL) || ((ag->ag_state & AGS_RIPV2) && v1_mask != mask)) { /* use the RIPv2-only buffer */ wb = &v2buf; } else { /* use the RIPv1-or-RIPv2 buffer */ wb = &v12buf; /* 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. */ if (TRACECONTENTS) trace_misc("sending %s-->%s as 1" " instead of %d routes", addrname(htonl(dst_h), mask, 1), naddr_ntoa(ws.to.sin_addr .s_addr), i+1); 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); wb->n->n_metric = htonl(wb->n->n_metric); /* Any non-zero bits in the supposedly unused RIPv1 fields * cause the old `routed` to ignore the route. * That means the mask and so forth cannot be sent * in the hybrid RIPv1/RIPv2 mode. */ if (ws.state & WS_ST_RIP2_ALL) { if (ag->ag_nhop != 0 && ((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; 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 *argp UNUSED) { #define RT ((struct rt_entry *)rn) u_short ags; char metric, pref; naddr dst, nhop; struct rt_spare *rts; int i; /* Do not advertise external remote interfaces or passive interfaces. */ if ((RT->rt_state & RS_IF) && RT->rt_ifp != 0 && (RT->rt_ifp->int_state & IS_PASSIVE) && !(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, * except about our main interface. */ if (!supplier && !(ws.state & WS_ST_QUERY) && !(RT->rt_state & RS_MHOME)) 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 because 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. * The final determination is made by supply_out(). */ 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 suppress (into existing network routes) * 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 || on_net(dst, ws.to_net, ws.to_mask)) ags |= AGS_SUPPRESS; /* Aggregate stray host routes into network routes if allowed. * We cannot aggregate host routes into small network routes * without confusing RIPv1 listeners into thinking the * network routes are host routes. */ if ((ws.state & WS_ST_AG) && (ws.state & WS_ST_RIP2_ALL)) ags |= AGS_AGGREGATE; } else { /* Always suppress network routes into other, existing * network routes */ 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 ((ws.state & WS_ST_AG) && ((RT->rt_state & RS_SUBNET) || (ws.state & WS_ST_SUPER_AG))) ags |= AGS_AGGREGATE; } /* 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_AGGREGATE; /* 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 * suppress the fragmented routes and then itself be forgotten. * * Include the routes for both ends of point-to-point interfaces * among those suppressed by split-horizon, since the other side * should knows them as well as we do. * * Notice spare routes with the same metric that we are about to * advertise, to split the horizon on redundant, inactive paths. * * Do not suppress advertisements of interface-related addresses on * non-point-to-point interfaces. This ensures that we have something * to say every 30 seconds to help detect broken Ethernets or * other interfaces where one packet every 30 seconds costs nothing. */ if (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)) { for (rts = RT->rt_spares, i = NUM_SPARES; i != 0; i--, rts++) { if (rts->rts_metric > metric || rts->rts_ifp != ws.ifp) continue; /* If we do not mark the route with AGS_SPLIT_HZ here, * it will be poisoned-reverse, or advertised back * toward its source with an infinite metric. * If we have recently advertised the route with a * better metric than we now have, then we should * poison-reverse the route before suppressing it for * split-horizon. * * In almost all cases, if there is no spare for the * route then it is either old and dead or a brand * new route. If it is brand new, there is no need * for poison-reverse. If it is old and dead, it * is already poisoned. */ if (RT->rt_poison_time < now_expire || RT->rt_poison_metric >= metric || RT->rt_spares[1].rts_gate == 0) { ags |= AGS_SPLIT_HZ; ags &= ~AGS_SUPPRESS; } metric = HOPCNT_INFINITY; break; } } /* 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; } /* Adjust the outgoing metric by the cost of the link. * Avoid aggregation when a route is counting to infinity. */ pref = RT->rt_poison_metric + ws.metric; metric += ws.metric; /* Do not advertise stable routes that will be ignored, * unless we are answering a query. * 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. */ if (metric >= HOPCNT_INFINITY) { if (!(ws.state & WS_ST_QUERY) && (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 */ int passwd_ok) /* OK to include cleartext password */ { struct rt_entry *rt; int def_metric; - assert(ifp != NULL); - 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 ((ws.ifp = ifp) == 0) { ws.metric = 1; } else { /* Adjust the advertised metric by the outgoing interface * metric. */ ws.metric = ifp->int_metric + 1 + ifp->int_adj_outmetric; } ripv12_buf.rip.rip_vers = vers; switch (type) { case OUT_MULTICAST: if (ifp->int_if_flags & IFF_MULTICAST) v2buf.type = OUT_MULTICAST; else v2buf.type = NO_OUT_MULTICAST; v12buf.type = OUT_BROADCAST; break; case OUT_QUERY: ws.state |= WS_ST_QUERY; /* FALLTHROUGH */ case OUT_BROADCAST: case OUT_UNICAST: v2buf.type = (vers == RIPv2) ? type : NO_OUT_RIPV2; v12buf.type = type; break; case NO_OUT_MULTICAST: case NO_OUT_RIPV2: break; /* no output */ } if (vers == RIPv2) { /* 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_QUERY) || !(ws.state & WS_ST_TO_ON_NET)) { ws.state |= (WS_ST_AG | WS_ST_SUPER_AG); } else if (ifp == 0 || !(ifp->int_state & IS_NO_AG)) { ws.state |= WS_ST_AG; if (type != OUT_BROADCAST && (ifp == 0 || !(ifp->int_state & IS_NO_SUPER_AG))) ws.state |= WS_ST_SUPER_AG; } } ws.a = (vers == RIPv2) ? find_auth(ifp) : 0; if (!passwd_ok && ws.a != 0 && ws.a->type == RIP_AUTH_PW) ws.a = 0; clr_ws_buf(&v12buf,ws.a); clr_ws_buf(&v2buf,ws.a); /* Fake a default route if asked and if there is not already * a better, real default route. */ if (supplier && (def_metric = ifp->int_d_metric) != 0) { if (0 == (rt = rtget(RIP_DEFAULT, 0)) || rt->rt_metric+ws.metric >= def_metric) { ws.state |= WS_ST_DEFAULT; ag_check(0, 0, 0, 0, def_metric, def_metric, 0, 0, 0, supply_out); } else { def_metric = rt->rt_metric+ws.metric; } /* If both RIPv2 and the poor-man's router discovery * kludge are on, arrange to advertise an extra * default route via RIPv1. */ if ((ws.state & WS_ST_RIP2_ALL) && (ifp->int_state & IS_PM_RDISC)) { ripv12_buf.rip.rip_vers = RIPv1; v12buf.n->n_family = RIP_AF_INET; v12buf.n->n_dst = htonl(RIP_DEFAULT); v12buf.n->n_metric = htonl(def_metric); v12buf.n++; } } (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 (v12buf.n != v12buf.base && (v12buf.n > v12buf.base+1 || v12buf.base->n_family != RIP_AF_AUTH)) supply_write(&v12buf); if (v2buf.n != v2buf.base && (v2buf.n > v2buf.base+1 || v2buf.base->n_family != RIP_AF_AUTH)) supply_write(&v2buf); /* 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(&v12buf); } /* 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, 0, {0}, {0}}; #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", flash ? "dynamic update" : "all routes", rtime.tv_sec + ((float)rtime.tv_usec)/1000000.0); LIST_FOREACH(ifp, &ifnet, int_list) { /* Skip interfaces not doing RIP. * Do try broken interfaces to see if they have healed. */ if (IS_RIP_OUT_OFF(ifp->int_state)) continue; /* skip turned off interfaces */ if (!iff_up(ifp->int_if_flags)) continue; vers = (ifp->int_state & IS_NO_RIPV1_OUT) ? RIPv2 : RIPv1; if (ifp->int_if_flags & IFF_BROADCAST) { /* ordinary, hardware interface */ dst.sin_addr.s_addr = ifp->int_brdaddr; if (vers == RIPv2 && !(ifp->int_state & IS_NO_RIP_MCAST)) { 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; if (vers == RIPv2 && ifp->int_if_flags & IFF_MULTICAST && !(ifp->int_state & IS_NO_RIP_MCAST)) { type = OUT_MULTICAST; } else { type = OUT_UNICAST; } } else if (ifp->int_state & IS_REMOTE) { /* remote interface */ dst.sin_addr.s_addr = ifp->int_addr; type = OUT_UNICAST; } else { /* ATM, HIPPI, etc. */ continue; } supply(&dst, ifp, type, flash, vers, 1); } 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, 0, {0}, {0}}; #else static struct sockaddr_in dst = {AF_INET}; #endif struct interface *ifp; struct rip buf; enum output_type type; if (rip_sock < 0) return; memset(&buf, 0, sizeof(buf)); LIST_FOREACH(ifp, &ifnet, int_list) { /* Skip interfaces those already queried. * Do not ask via interfaces through which we don't * accept input. Do not ask via interfaces that cannot * send RIP packets. * Do try broken interfaces to see if they have healed. */ if (IS_RIP_IN_OFF(ifp->int_state) || ifp->int_query_time != NEVER) continue; /* skip turned off interfaces */ if (!iff_up(ifp->int_if_flags)) continue; buf.rip_vers = (ifp->int_state&IS_NO_RIPV1_OUT) ? RIPv2:RIPv1; buf.rip_cmd = RIPCMD_REQUEST; buf.rip_nets[0].n_family = RIP_AF_UNSPEC; buf.rip_nets[0].n_metric = htonl(HOPCNT_INFINITY); /* Send a RIPv1 query only if allowed and if we will * listen to RIPv1 routers. */ if ((ifp->int_state & IS_NO_RIPV1_OUT) || (ifp->int_state & IS_NO_RIPV1_IN)) { buf.rip_vers = RIPv2; } else { buf.rip_vers = RIPv1; } if (ifp->int_if_flags & IFF_BROADCAST) { /* ordinary, hardware interface */ dst.sin_addr.s_addr = ifp->int_brdaddr; /* Broadcast RIPv1 queries and RIPv2 queries * when the hardware cannot multicast. */ if (buf.rip_vers == RIPv2 && (ifp->int_if_flags & IFF_MULTICAST) && !(ifp->int_state & IS_NO_RIP_MCAST)) { 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 if (ifp->int_state & IS_REMOTE) { /* remote interface */ dst.sin_addr.s_addr = ifp->int_addr; type = OUT_UNICAST; } else { /* ATM, HIPPI, etc. */ continue; } ifp->int_query_time = now.tv_sec+SUPPLY_INTERVAL; if (output(type, &dst, ifp, &buf, sizeof(buf)) < 0) if_sick(ifp); } } Index: stable/10/sbin/routed/routed.8 =================================================================== --- stable/10/sbin/routed/routed.8 (revision 272871) +++ stable/10/sbin/routed/routed.8 (revision 272872) @@ -1,731 +1,745 @@ .\" $Revision: 2.26 $ .\" .\" 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. .\" 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 .\" $FreeBSD$ .\" -.Dd June 1, 1996 +.Dd August 26, 2014 .Dt ROUTED 8 .Os .Sh NAME .Nm routed , .Nm rdisc .Nd network RIP and router discovery routing daemon .Sh SYNOPSIS .Nm -.Op Fl sqdghmpAtv +.Op Fl isqdghmpAtv .Op Fl T Ar tracefile .Oo .Fl F .Ar net Ns Op /mask Ns Op ,metric .Oc .Op Fl P Ar parms .Sh DESCRIPTION The .Nm utility is a daemon 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 .Bx 4.3 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 advertises 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 -hopcount (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 Discovery 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"). The advertised metric for a route reflects the metrics associated with interfaces (see .Xr ifconfig 8 ) though which it is received and sent, so setting the metric on an interface is an effective way to steer traffic. See also .Cm adj_inmetric and .Cm adj_outmetric parameters below. .Pp Responses do not include 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 table is 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 ensure 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 , 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 1 ) , 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 listening to other RIP routers, it sends Solicitations and listens for Advertisements. If it receives a good Advertisement and it is not multi-homed, 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. It continues listening to RIP while using Router Discovery if multi-homed to ensure all interfaces are used. .Pp The Router Discovery 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. On a host with more than one network interface, this default route will be via only one of the interfaces. Thus, multi-homed hosts running with .Fl q might need .Cm no_rdisc described below. .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 solicitations are sent over point to point links (e.g.\& PPP). The netmask associated with point-to-point links (such as SLIP or PPP, with the IFF_POINTOPOINT flag) is used by .Nm to infer the netmask used by the remote system when RIPv1 is used. .Pp The following options are available: .Bl -tag -width indent +.It Fl i +allow +.Nm +to accept a RIP request from non-router node. +When specified once, +.Nm +replies to a route information query from neighbor nodes. +When specified twice, +it replies to a query from remote nodes in addition. +.Xr rtquery 8 +utility can be used to send a request. +.Pp +This feature is disabled by default because of a risk of reflection attack +though it useful for debugging purpose, .It Fl s force .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. This is the default when only one interface is present. With this explicit option, the daemon is always in "quiet-mode" for RIP and does not supply routing information to other computers. .It Fl d do not run in the background. This option is meant for interactive use. .It Fl g 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 accidentally used to create chaos with a routing loop than to solve problems. .It Fl h cause 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 cause 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 increase 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 .Xr rtquery 8 command. .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 routinely with tracing directed to a file. .It Fl v display and logs the version of daemon. .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 The .Nm utility 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 HIPPI or ATM network in .Pa /etc/gateways with a series of "host" lines. Note that it is usually desirable to use RIPv2 in such situations to avoid generating lists of inferred host routes. .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 necessary, and that other routes to that destination should not be installed by .Nm . Such entries are only required when both routers may learn of routes to the same destination. .Pp The .Pa /etc/gateways file is comprised of a series of lines, each in one of the following two formats or consist of parameters described later. Blank lines and lines starting with '#' are comments. .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 .Nm . ) .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. .Pp .Cm Host Ar hname is equivalent to .Cm net Ar 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 As can be seen when debugging is turned on with .Fl t , such lines create pseudo-interfaces. To set parameters for remote or external interfaces, a line starting with .Cm if=alias(Hname) , .Cm if=remote(Hname) , etc.\& should be used. .Ss Parameters 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 indent .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 Ns Oo / Ns Ar mask Oc Ns Op , Ns Ar 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. The network number must specify a full, 32-bit value, as in 192.0.2.0 instead of 192.0.2. .Pp Do not use this feature unless necessary. It is dangerous. .It Cm ripv1_mask Ns = Ns Ar nname Ns / Ns Ar mask1 , Ns Ar mask2 specifies that netmask of the network of which .Ar nname Ns / Ns Ar mask1 is a subnet should be .Ar mask2 . For example, .Dq Li ripv1_mask=192.0.2.16/28,27 marks 192.0.2.16/28 as a subnet of 192.0.2.0/27 instead of 192.0.2.0/24. It is better to turn on RIPv2 instead of using this facility, for example with .Cm ripv2_out . .It Cm passwd Ns = Ns Ar XXX[|KeyID[start|stop]] specifies a RIPv2 cleartext password that will be included on all RIPv2 responses sent, and checked on all RIPv2 responses received. Any blanks, tab characters, commas, or '#', '|', or NULL characters in the password must be escaped with a backslash (\\). The common escape sequences \\n, \\r, \\t, \\b, and \\xxx have their usual meanings. The .Cm KeyID must be unique but is ignored for cleartext passwords. If present, .Cm start and .Cm stop are timestamps in the form year/month/day@hour:minute. They specify when the password is valid. The valid password with the most future is used on output packets, unless all passwords have expired, in which case the password that expired most recently is used, or unless no passwords are valid yet, in which case no password is output. Incoming packets can carry any password that is valid, will be valid within the next 24 hours, or that was valid within the preceding 24 hours. To protect the secrets, the passwd settings are valid only in the .Pa /etc/gateways file and only when that file is readable only by UID 0. .It Cm md5_passwd Ns \&= Ns Ar XXX|KeyID[start|stop] specifies a RIPv2 MD5 password. Except that a .Cm KeyID is required, this keyword is similar to .Cm passwd . .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 marks the interface to not be advertised in updates sent via other interfaces, and turns off all RIP and router discovery through the interface. .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. .Pp Note that turning off RIP without explicitly turning on router discovery advertisements with .Cm rdisc_adv or .Fl s causes .Nm to act as a client router discovery daemon, not advertising. .It Cm no_rip_mcast causes RIPv2 packets to be broadcast instead of multicast. .It Cm no_rip_out causes no RIP updates to be sent. .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 on RIPv2 output and causes RIPv2 advertisements to be multicast when possible. .It Cm ripv2 is equivalent to .Cm no_ripv1_in and .Cm no_ripv1_out . This enables RIPv2. .It Cm no_rdisc disables the Internet Router Discovery Protocol. .It Cm no_solicit disables the transmission 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 optionally signed integer .Ar N . The default preference is 0. Default routes with smaller or more negative preferences are preferred by clients. .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 specified 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. .It Cm adj_inmetric Ns \&= Ns Ar delta adjusts the hop count or metric of received RIP routes by .Ar delta . The metric of every received RIP route is increased by the sum of two values associated with the interface. One is the adj_inmetric value and the other is the interface metric set with .Xr ifconfig 8 . .It Cm adj_outmetric Ns \&= Ns Ar delta adjusts the hop count or metric of advertised RIP routes by .Ar delta . The metric of every received RIP route is increased by the metric associated with the interface by which it was received, or by 1 if the interface does not have a non-zero metric. The metric of the received route is then increased by the adj_outmetric associated with the interface. Every advertised route is increased by a total of four values, the metric set for the interface by which it was received with .Xr ifconfig 8 , the .Cm adj_inmetric Ar delta of the receiving interface, the metric set for the interface by which it is transmitted with .Xr ifconfig 8 , and the .Cm adj_outmetric Ar delta of the transmitting interface. .It Cm trust_gateway Ns \&= Ns Ar rname[|net1/mask1|net2/mask2|...] causes RIP packets from router .Ar rname and other routers named in other .Cm trust_gateway keywords to be accepted, and packets from other routers to be ignored. If networks are specified, then routes to other networks will be ignored from that router. .It Cm redirect_ok allows the kernel to listen ICMP Redirect messages when the system is acting as a router and forwarding packets. Otherwise, ICMP Redirect messages are overridden and deleted when the system is acting as a router. .El .Sh FILES .Bl -tag -width /etc/gateways -compact .It Pa /etc/gateways for distant gateways .El .Sh SEE ALSO .Xr icmp 4 , .Xr udp 4 , .Xr rtquery 8 .Rs .%T Internet Transport Protocols .%R XSIS 028112 .%Q Xerox System Integration Standard .Re .Sh HISTORY The .Nm utility appeared in .Bx 4.2 . .\" LocalWords: loopback ICMP rtquery ifconfig multicasting Solicitations RIPv .\" LocalWords: netstat rdisc .Sh BUGS It does not always detect unidirectional failures in network interfaces, for example, when the output side fails. Index: stable/10 =================================================================== --- stable/10 (revision 272871) +++ stable/10 (revision 272872) Property changes on: stable/10 ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head:r271919