diff options
author | Garrett Wollman <wollman@FreeBSD.org> | 1996-11-19 20:23:47 +0000 |
---|---|---|
committer | Garrett Wollman <wollman@FreeBSD.org> | 1996-11-19 20:23:47 +0000 |
commit | 47fe01ae9cbf1a4d7d57005d1a4147253abfecc6 (patch) | |
tree | 52e6c2a9e5816062d9d356285665895d535048fb | |
parent | d095837342390345759fc2bf522ee22f26b326be (diff) | |
parent | d5b718b3ee1a3d309b44921e01bba0ca4511ff00 (diff) | |
download | src-47fe01ae9cbf1a4d7d57005d1a4147253abfecc6.tar.gz src-47fe01ae9cbf1a4d7d57005d1a4147253abfecc6.zip |
This commit was generated by cvs2svn to compensate for changes in r19880,
which included commits to RCS files with non-trunk default branches.
Notes
Notes:
svn path=/head/; revision=19881
-rw-r--r-- | sbin/routed/if.c | 518 | ||||
-rw-r--r-- | sbin/routed/input.c | 615 | ||||
-rw-r--r-- | sbin/routed/output.c | 360 | ||||
-rw-r--r-- | sbin/routed/parms.c | 583 | ||||
-rw-r--r-- | sbin/routed/rdisc.c | 148 | ||||
-rw-r--r-- | sbin/routed/routed.h | 174 | ||||
-rw-r--r-- | sbin/routed/rtquery/md5.c | 325 | ||||
-rw-r--r-- | sbin/routed/rtquery/rtquery.8 | 12 | ||||
-rw-r--r-- | sbin/routed/rtquery/rtquery.c | 232 | ||||
-rw-r--r-- | sbin/routed/trace.c | 158 |
10 files changed, 2295 insertions, 830 deletions
diff --git a/sbin/routed/if.c b/sbin/routed/if.c index 539b07323e3a..3eea2df322c6 100644 --- a/sbin/routed/if.c +++ b/sbin/routed/if.c @@ -36,12 +36,35 @@ static char sccsid[] = "@(#)if.c 8.1 (Berkeley) 6/5/93"; #elif defined(__NetBSD__) static char rcsid[] = "$NetBSD$"; #endif -#ident "$Revision: 1.17 $" +#ident "$Revision: 1.18 $" #include "defs.h" #include "pathnames.h" -struct interface *ifnet; /* all interfaces */ +struct interface *ifnet; /* all interfaces */ + +/* hash table for all interfaces, big enough to tolerate ridiculous + * numbers of IP aliases. Crazy numbers of aliases such as 7000 + * still will not do well, but not just in looking up interfaces + * by name or address. + */ +#define AHASH_LEN 211 /* must be prime */ +#define AHASH(a) &ahash[(a)%AHASH_LEN] +struct interface *ahash[AHASH_LEN]; + +#define BHASH_LEN 211 /* must be prime */ +#define BHASH(a) &bhash[(a)%BHASH_LEN] +struct interface *bhash[BHASH_LEN]; + +struct interface *remote_if; /* remote interfaces */ + +/* hash for physical interface names. + * Assume there are never more 100 or 200 real interfaces, and that + * aliases put on the end of the hash chains. + */ +#define NHASH_LEN 97 +struct interface *nhash[NHASH_LEN]; + int tot_interfaces; /* # of remote and local interfaces */ int rip_interfaces; /* # of interfaces doing RIP */ int foundloopback; /* valid flag for loopaddr */ @@ -53,6 +76,58 @@ int have_ripv1_out; /* have a RIPv1 interface */ int have_ripv1_in; +void +if_link(struct interface *ifp) +{ + int i; + char *p; + struct interface **hifp; + + ifp->int_prev = &ifnet; + ifp->int_next = ifnet; + if (ifnet != 0) + ifnet->int_prev = &ifp->int_next; + ifnet = ifp; + + hifp = AHASH(ifp->int_addr); + ifp->int_ahash_prev = hifp; + ifp->int_ahash = *hifp; + if ((ifp->int_ahash = *hifp) != 0) + (*hifp)->int_ahash_prev = &ifp->int_ahash; + *hifp = ifp; + + if (ifp->int_if_flags & IFF_BROADCAST) { + hifp = BHASH(ifp->int_brdaddr); + ifp->int_bhash = *hifp; + ifp->int_bhash_prev = hifp; + if ((ifp->int_bhash = *hifp) != 0) + (*hifp)->int_bhash_prev = &ifp->int_bhash; + *hifp = ifp; + } + + if (ifp->int_state & IS_REMOTE) { + ifp->int_rlink_prev = &remote_if; + ifp->int_rlink = remote_if; + if (remote_if != 0) + remote_if->int_rlink_prev = &ifp->int_rlink; + remote_if = ifp; + } + + for (i = 0, p = ifp->int_name; *p != '\0'; p++) + i += *p; + hifp = &nhash[i % NHASH_LEN]; + if (ifp->int_state & IS_ALIAS) { + while (*hifp != 0) + hifp = &(*hifp)->int_nhash; + } + ifp->int_nhash = *hifp; + ifp->int_nhash_prev = hifp; + if ((ifp->int_nhash = *hifp) != 0) + (*hifp)->int_nhash_prev = &ifp->int_nhash; + *hifp = ifp; +} + + /* Find the interface with an address */ struct interface * @@ -62,20 +137,29 @@ ifwithaddr(naddr addr, { struct interface *ifp, *possible = 0; - for (ifp = ifnet; ifp; ifp = ifp->int_next) { - if (ifp->int_addr == addr - || ((ifp->int_if_flags & IFF_BROADCAST) - && ifp->int_brdaddr == addr - && bcast)) { - if ((ifp->int_state & IS_REMOTE) && !remote) - continue; + remote = (remote == 0) ? IS_REMOTE : 0; - if (!(ifp->int_state & IS_BROKE) - && !(ifp->int_state & IS_PASSIVE)) - return ifp; + for (ifp = *AHASH(addr); ifp; ifp = ifp->int_ahash) { + if (ifp->int_addr != addr) + continue; + if ((ifp->int_state & remote) != 0) + continue; + if ((ifp->int_state & (IS_BROKE | IS_PASSIVE)) == 0) + return ifp; + possible = ifp; + } - possible = ifp; - } + if (possible || !bcast) + return possible; + + for (ifp = *BHASH(addr); ifp; ifp = ifp->int_bhash) { + if (ifp->int_brdaddr != addr) + continue; + if ((ifp->int_state & remote) != 0) + continue; + if ((ifp->int_state & (IS_BROKE | IS_PASSIVE)) == 0) + return ifp; + possible = ifp; } return possible; @@ -89,12 +173,19 @@ ifwithname(char *name, /* "ec0" or whatever */ naddr addr) /* 0 or network address */ { struct interface *ifp; - - - for (ifp = ifnet; 0 != ifp; ifp = ifp->int_next) { + int i; + char *p; + + for (i = 0, p = name; *p != '\0'; p++) + i += *p; + for (ifp = nhash[i % NHASH_LEN]; ifp != 0; ifp = ifp->int_nhash) { + /* If the network address is not specified, + * ignore any alias interfaces. Otherwise, look + * for the interface with the target name and address. + */ if (!strcmp(ifp->int_name, name) - && (ifp->int_addr == addr - || (addr == 0 && !(ifp->int_state & IS_ALIAS)))) + && ((addr == 0 && !(ifp->int_state & IS_ALIAS)) + || (ifp->int_addr == addr))) return ifp; } return 0; @@ -127,17 +218,14 @@ iflookup(naddr addr) maybe = 0; for (ifp = ifnet; ifp; ifp = ifp->int_next) { if (ifp->int_if_flags & IFF_POINTOPOINT) { + /* finished with a match */ if (ifp->int_dstaddr == addr) - /* finished with a match */ return ifp; } else { /* finished with an exact match */ if (ifp->int_addr == addr) return ifp; - if ((ifp->int_if_flags & IFF_BROADCAST) - && ifp->int_brdaddr == addr) - return ifp; /* Look for the longest approximate match. */ @@ -247,6 +335,68 @@ check_dst(naddr addr) } +/* See a new interface duplicates an existing interface. + */ +struct interface * +check_dup(naddr addr, + naddr dstaddr, + naddr mask, + int if_flags) +{ + struct interface *ifp; + + for (ifp = ifnet; 0 != ifp; ifp = ifp->int_next) { + if (ifp->int_mask != mask) + continue; + + if (!iff_alive(ifp->int_if_flags)) + continue; + + /* The local address can only be shared with a point-to- + * point link. + */ + if (ifp->int_addr == addr + && (((if_flags|ifp->int_if_flags) & IFF_POINTOPOINT) == 0)) + return ifp; + + if (on_net(ifp->int_dstaddr, ntohl(dstaddr),mask)) + return ifp; + } + return 0; +} + + +/* See that a remote gateway is reachable. + * Note that the answer can change as real interfaces come and go. + */ +int /* 0=bad */ +check_remote(struct interface *ifp) +{ + struct rt_entry *rt; + + /* do not worry about other kinds */ + if (!(ifp->int_state & IS_REMOTE)) + return 1; + + rt = rtfind(ifp->int_addr); + if (rt != 0 + && rt->rt_ifp != 0 + &&on_net(ifp->int_addr, + rt->rt_ifp->int_net, rt->rt_ifp->int_mask)) + return 1; + + /* the gateway cannot be reached directly from one of our + * interfaces + */ + if (!(ifp->int_state & IS_BROKE)) { + msglog("unreachable gateway %s in "_PATH_GATEWAYS, + naddr_ntoa(ifp->int_addr)); + if_bad(ifp); + } + return 0; +} + + /* Delete an interface. */ static void @@ -262,17 +412,25 @@ ifdel(struct interface *ifp) /* unlink the interface */ - if (rip_sock_mcast == ifp) - rip_sock_mcast = 0; + *ifp->int_prev = ifp->int_next; if (ifp->int_next != 0) ifp->int_next->int_prev = ifp->int_prev; - if (ifp->int_prev != 0) - ifp->int_prev->int_next = ifp->int_next; - else - ifnet = ifp->int_next; + *ifp->int_ahash_prev = ifp->int_ahash; + if (ifp->int_ahash != 0) + ifp->int_ahash->int_ahash_prev = ifp->int_ahash_prev; + if (ifp->int_if_flags & IFF_BROADCAST) { + *ifp->int_bhash_prev = ifp->int_bhash; + if (ifp->int_bhash != 0) + ifp->int_bhash->int_bhash_prev = ifp->int_bhash_prev; + } + if (ifp->int_state & IS_REMOTE) { + *ifp->int_rlink_prev = ifp->int_rlink; + if (ifp->int_rlink != 0) + ifp->int_rlink->int_rlink_prev = ifp->int_rlink_prev; + } if (!(ifp->int_state & IS_ALIAS)) { - /* delete aliases + /* delete aliases when the main interface dies */ for (ifp1 = ifnet; 0 != ifp1; ifp1 = ifp1->int_next) { if (ifp1 != ifp @@ -295,6 +453,8 @@ ifdel(struct interface *ifp) && errno != EADDRNOTAVAIL && !TRACEACTIONS) LOGERR("setsockopt(IP_DROP_MEMBERSHIP RIP)"); + if (rip_sock_mcast == ifp) + rip_sock_mcast = 0; } if (ifp->int_rip_sock >= 0) { (void)close(ifp->int_rip_sock); @@ -327,6 +487,7 @@ if_sick(struct interface *ifp) { if (0 == (ifp->int_state & (IS_SICK | IS_BROKE))) { ifp->int_state |= IS_SICK; + ifp->int_act_time = NEVER; trace_if("Chg", ifp); LIM_SEC(ifinit_timer, now.tv_sec+CHECK_BAD_INTERVAL); @@ -348,7 +509,8 @@ if_bad(struct interface *ifp) LIM_SEC(ifinit_timer, now.tv_sec+CHECK_BAD_INTERVAL); ifp->int_state |= (IS_BROKE | IS_SICK); - ifp->int_state &= ~(IS_RIP_QUERIED | IS_ACTIVE); + ifp->int_act_time = NEVER; + ifp->int_query_time = NEVER; ifp->int_data.ts = 0; trace_if("Chg", ifp); @@ -376,16 +538,16 @@ if_ok(struct interface *ifp, if (!(ifp->int_state & IS_BROKE)) { if (ifp->int_state & IS_SICK) { - trace_act("%sinterface %s to %s working better\n", + trace_act("%sinterface %s to %s working better", type, - ifp->int_name, naddr_ntoa(ifp->int_addr)); + ifp->int_name, naddr_ntoa(ifp->int_dstaddr)); ifp->int_state &= ~IS_SICK; } return 0; } msglog("%sinterface %s to %s restored", - type, ifp->int_name, naddr_ntoa(ifp->int_addr)); + type, ifp->int_name, naddr_ntoa(ifp->int_dstaddr)); ifp->int_state &= ~(IS_BROKE | IS_SICK); ifp->int_data.ts = 0; @@ -397,6 +559,11 @@ if_ok(struct interface *ifp, } if_ok_rdisc(ifp); } + + if (ifp->int_state & IS_REMOTE) { + if (!addrouteforif(ifp)) + return 0; + } return 1; } @@ -452,15 +619,14 @@ ifinit(void) uint complaints = 0; static u_int prev_complaints = 0; # define COMP_NOT_INET 0x001 -# define COMP_WIERD 0x002 -# define COMP_NOADDR 0x004 -# define COMP_BADADDR 0x008 -# define COMP_NODST 0x010 -# define COMP_NOBADR 0x020 -# define COMP_NOMASK 0x040 -# define COMP_DUP 0x080 -# define COMP_BAD_METRIC 0x100 -# define COMP_NETMASK 0x200 +# define COMP_NOADDR 0x002 +# define COMP_BADADDR 0x004 +# define COMP_NODST 0x008 +# define COMP_NOBADR 0x010 +# define COMP_NOMASK 0x020 +# define COMP_DUP 0x040 +# define COMP_BAD_METRIC 0x080 +# define COMP_NETMASK 0x100 struct interface ifs, ifs0, *ifp, *ifp1; struct rt_entry *rt; @@ -468,7 +634,6 @@ ifinit(void) int mib[6]; struct if_msghdr *ifm; struct ifa_msghdr *ifam, *ifam_lim, *ifam2; - struct sockaddr_dl *sdl; int in, ierr, out, oerr; struct intnet *intnetp; struct rt_addrinfo info; @@ -516,6 +681,8 @@ ifinit(void) ifam2 = (struct ifa_msghdr*)((char*)ifam + ifam->ifam_msglen); if (ifam->ifam_type == RTM_IFINFO) { + struct sockaddr_dl *sdl; + ifm = (struct if_msghdr *)ifam; /* make prototype structure for the IP aliases */ @@ -535,22 +702,30 @@ ifinit(void) #endif sdl = (struct sockaddr_dl *)(ifm + 1); sdl->sdl_data[sdl->sdl_nlen] = 0; + strncpy(ifs0.int_name, sdl->sdl_data, + MIN(sizeof(ifs0.int_name), sdl->sdl_nlen)); continue; } if (ifam->ifam_type != RTM_NEWADDR) { logbad(1,"ifinit: out of sync"); continue; } - rt_xaddrs(&info, (struct sockaddr *)(ifam+1), (struct sockaddr *)ifam2, ifam->ifam_addrs); + /* Prepare for the next address of this interface, which + * will be an alias. + * Do not output RIP or Router-Discovery packets via aliases. + */ + bcopy(&ifs0, &ifs, sizeof(ifs)); + ifs0.int_state |= (IS_ALIAS | IS_NO_RIP | IS_NO_RDISC); + if (INFO_IFA(&info) == 0) { if (iff_alive(ifs.int_if_flags)) { if (!(prev_complaints & COMP_NOADDR)) msglog("%s has no address", - sdl->sdl_data); + ifs.int_name); complaints |= COMP_NOADDR; } continue; @@ -558,16 +733,13 @@ ifinit(void) if (INFO_IFA(&info)->sa_family != AF_INET) { if (iff_alive(ifs.int_if_flags)) { if (!(prev_complaints & COMP_NOT_INET)) - trace_act("%s: not AF_INET\n", - sdl->sdl_data); + trace_act("%s: not AF_INET", + ifs.int_name); complaints |= COMP_NOT_INET; } continue; } - bcopy(&ifs0, &ifs, sizeof(ifs0)); - ifs0.int_state |= IS_ALIAS; /* next will be an alias */ - ifs.int_addr = S_ADDR(INFO_IFA(&info)); if (ntohl(ifs.int_addr)>>24 == 0 @@ -575,41 +747,23 @@ ifinit(void) if (iff_alive(ifs.int_if_flags)) { if (!(prev_complaints & COMP_BADADDR)) msglog("%s has a bad address", - sdl->sdl_data); + ifs.int_name); complaints |= COMP_BADADDR; } continue; } - if (ifs.int_if_flags & IFF_BROADCAST) { - if (INFO_MASK(&info) == 0) { - if (iff_alive(ifs.int_if_flags)) { - if (!(prev_complaints & COMP_NOMASK)) - msglog("%s has no netmask", - sdl->sdl_data); - complaints |= COMP_NOMASK; - } - continue; - } + if (ifs.int_if_flags & IFF_LOOPBACK) { + ifs.int_state |= IS_PASSIVE | IS_NO_RIP | IS_NO_RDISC; ifs.int_dstaddr = ifs.int_addr; - ifs.int_mask = ntohl(S_ADDR(INFO_MASK(&info))); - ifs.int_ripv1_mask = ifs.int_mask; - ifs.int_net = ntohl(ifs.int_addr) & ifs.int_mask; - ifs.int_std_mask = std_mask(ifs.int_addr); - if (ifs.int_mask != ifs.int_std_mask) - ifs.int_state |= IS_SUBNET; - - if (INFO_BRD(&info) == 0) { - if (iff_alive(ifs.int_if_flags)) { - if (!(prev_complaints & COMP_NOBADR)) - msglog("%s has no" - " broadcast address", - sdl->sdl_data); - complaints |= COMP_NOBADR; - } - continue; + ifs.int_mask = HOST_MASK; + ifs.int_ripv1_mask = HOST_MASK; + ifs.int_std_mask = std_mask(ifs.int_dstaddr); + ifs.int_net = ntohl(ifs.int_dstaddr); + if (!foundloopback) { + foundloopback = 1; + loopaddr = ifs.int_addr; } - ifs.int_brdaddr = S_ADDR(INFO_BRD(&info)); } else if (ifs.int_if_flags & IFF_POINTOPOINT) { if (INFO_BRD(&info) == 0 @@ -618,7 +772,7 @@ ifinit(void) if (!(prev_complaints & COMP_NODST)) msglog("%s has a bad" " destination address", - sdl->sdl_data); + ifs.int_name); complaints |= COMP_NODST; } continue; @@ -630,35 +784,48 @@ ifinit(void) if (!(prev_complaints & COMP_NODST)) msglog("%s has a bad" " destination address", - sdl->sdl_data); + ifs.int_name); complaints |= COMP_NODST; } continue; } ifs.int_mask = HOST_MASK; ifs.int_ripv1_mask = ntohl(S_ADDR(INFO_MASK(&info))); - ifs.int_net = ntohl(ifs.int_dstaddr); ifs.int_std_mask = std_mask(ifs.int_dstaddr); - - } else if (ifs.int_if_flags & IFF_LOOPBACK) { - ifs.int_state |= IS_PASSIVE | IS_NO_RIP; - ifs.int_dstaddr = ifs.int_addr; - ifs.int_mask = HOST_MASK; - ifs.int_ripv1_mask = HOST_MASK; ifs.int_net = ntohl(ifs.int_dstaddr); - ifs.int_std_mask = std_mask(ifs.int_dstaddr); - if (!foundloopback) { - foundloopback = 1; - loopaddr = ifs.int_addr; + + } else { + if (INFO_MASK(&info) == 0) { + if (iff_alive(ifs.int_if_flags)) { + if (!(prev_complaints & COMP_NOMASK)) + msglog("%s has no netmask", + ifs.int_name); + complaints |= COMP_NOMASK; + } + continue; } + ifs.int_dstaddr = ifs.int_addr; + ifs.int_mask = ntohl(S_ADDR(INFO_MASK(&info))); + ifs.int_ripv1_mask = ifs.int_mask; + ifs.int_std_mask = std_mask(ifs.int_addr); + ifs.int_net = ntohl(ifs.int_addr) & ifs.int_mask; + if (ifs.int_mask != ifs.int_std_mask) + ifs.int_state |= IS_SUBNET; - } else { - if (!(prev_complaints & COMP_WIERD)) - trace_act("%s is neither broadcast" - " nor point-to-point nor loopback", - sdl->sdl_data); - complaints |= COMP_WIERD; - continue; + if (ifs.int_if_flags & IFF_BROADCAST) { + if (INFO_BRD(&info) == 0) { + if (iff_alive(ifs.int_if_flags)) { + if (!(prev_complaints + & COMP_NOBADR)) + msglog("%s has" + "no broadcast address", + ifs.int_name); + complaints |= COMP_NOBADR; + } + continue; + } + ifs.int_brdaddr = S_ADDR(INFO_BRD(&info)); + } } ifs.int_std_net = ifs.int_net & ifs.int_std_mask; ifs.int_std_addr = htonl(ifs.int_std_net); @@ -671,7 +838,7 @@ ifinit(void) * SIOCSIFMETRIC ioctl. */ #ifdef SIOCGIFMETRIC - strncpy(ifr.ifr_name, sdl->sdl_data, sizeof(ifr.ifr_name)); + strncpy(ifr.ifr_name, ifs.int_name, sizeof(ifr.ifr_name)); if (ioctl(rt_sock, SIOCGIFMETRIC, &ifr) < 0) { DBGERR(1, "ioctl(SIOCGIFMETRIC)"); ifs.int_metric = 0; @@ -687,7 +854,7 @@ ifinit(void) && iff_alive(ifs.int_if_flags)) { complaints |= COMP_BAD_METRIC; msglog("%s has a metric of %d", - sdl->sdl_data, ifs.int_metric); + ifs.int_name, ifs.int_metric); } } @@ -696,9 +863,9 @@ ifinit(void) * Start it over if it now is to somewhere else, as happens * frequently with PPP and SLIP. */ - ifp = ifwithname(sdl->sdl_data, ((ifs.int_state & IS_ALIAS) - ? ifs.int_addr - : 0)); + ifp = ifwithname(ifs.int_name, ((ifs.int_state & IS_ALIAS) + ? ifs.int_addr + : 0)); if (ifp != 0) { ifp->int_state |= IS_CHECKED; @@ -717,7 +884,7 @@ ifinit(void) /* Forget old information about * a changed interface. */ - trace_act("interface %s has changed\n", + trace_act("interface %s has changed", ifp->int_name); ifdel(ifp); ifp = 0; @@ -737,7 +904,7 @@ ifinit(void) if (iff_alive(ifp->int_if_flags)) { msglog("interface %s to %s turned off", ifp->int_name, - naddr_ntoa(ifp->int_addr)); + naddr_ntoa(ifp->int_dstaddr)); if_bad(ifp); ifp->int_if_flags &= ~IFF_UP_RUNNING; } @@ -799,18 +966,18 @@ ifinit(void) if (!(ifp->int_state & IS_SICK)) { trace_act("interface %s to %s" " sick: in=%d ierr=%d" - " out=%d oerr=%d\n", + " out=%d oerr=%d", ifp->int_name, - naddr_ntoa(ifp->int_addr), + naddr_ntoa(ifp->int_dstaddr), in, ierr, out, oerr); if_sick(ifp); continue; } if (!(ifp->int_state & IS_BROKE)) { - msglog("interface %s to %s bad:" + msglog("interface %s to %s broken:" " in=%d ierr=%d out=%d oerr=%d", ifp->int_name, - naddr_ntoa(ifp->int_addr), + naddr_ntoa(ifp->int_dstaddr), in, ierr, out, oerr); if_bad(ifp); } @@ -830,73 +997,60 @@ ifinit(void) if (!iff_alive(ifs.int_if_flags)) continue; - /* See if it duplicates an existing interface. + /* If it duplicates an existing interface, + * complain about it, mark the other one + * duplicated, and forget this one. */ - for (ifp = ifnet; 0 != ifp; ifp = ifp->int_next) { - if (ifp->int_mask != ifs.int_mask) - continue; - if (((ifp->int_addr != ifs.int_addr - && ifs.int_mask != HOST_MASK) - || (ifp->int_dstaddr != ifs.int_dstaddr - && ifs.int_mask == HOST_MASK))) - continue; - if (!iff_alive(ifp->int_if_flags)) - continue; - /* Let one of our real interfaces be marked - * passive. - */ - if ((ifp->int_state & IS_PASSIVE) - && !(ifp->int_state & IS_EXTERNAL)) - continue; - - /* It does duplicate an existing interface, - * so complain about it, mark the other one - * duplicated, and for get this one. - */ + ifp = check_dup(ifs.int_addr,ifs.int_dstaddr,ifs.int_mask, + ifs.int_if_flags); + if (ifp != 0) { if (!(prev_complaints & COMP_DUP)) { complaints |= COMP_DUP; - msglog("%s is duplicated by %s at %s", - sdl->sdl_data, ifp->int_name, - naddr_ntoa(ifp->int_addr)); + msglog("%s is duplicated by %s at %s to %s", + ifs.int_name, ifp->int_name, + naddr_ntoa(ifp->int_addr), + naddr_ntoa(ifp->int_dstaddr)); } ifp->int_state |= IS_DUP; - break; - } - if (ifp != 0) continue; + } + + if (0 == (ifs.int_if_flags & (IFF_POINTOPOINT | IFF_BROADCAST)) + && !(ifs.int_state & IS_PASSIVE)) { + trace_act("%s is neither broadcast, point-to-point," + " nor loopback", + ifs.int_name); + if (!(ifs.int_state & IFF_MULTICAST)) + ifs.int_state |= IS_NO_RDISC; + } - /* It is new and ok. So make it real - */ - strncpy(ifs.int_name, sdl->sdl_data, - MIN(sizeof(ifs.int_name)-1, sdl->sdl_nlen)); - get_parms(&ifs); - /* Add it to the list of interfaces + /* It is new and ok. Add it to the list of interfaces */ ifp = (struct interface *)rtmalloc(sizeof(*ifp), "ifinit"); bcopy(&ifs, ifp, sizeof(*ifp)); - if (ifnet != 0) { - ifp->int_next = ifnet; - ifnet->int_prev = ifp; - } - ifnet = ifp; + get_parms(ifp); + if_link(ifp); trace_if("Add", ifp); /* Notice likely bad netmask. */ if (!(prev_complaints & COMP_NETMASK) - && !(ifp->int_if_flags & IFF_POINTOPOINT)) { + && !(ifp->int_if_flags & IFF_POINTOPOINT) + && ifp->int_addr != RIP_DEFAULT) { for (ifp1 = ifnet; 0 != ifp1; ifp1 = ifp1->int_next) { if (ifp1->int_mask == ifp->int_mask) continue; if (ifp1->int_if_flags & IFF_POINTOPOINT) continue; - if (on_net(ifp->int_addr, + if (ifp1->int_dstaddr == RIP_DEFAULT) + continue; + if (on_net(ifp->int_dstaddr, ifp1->int_net, ifp1->int_mask) - || on_net(ifp1->int_addr, + || on_net(ifp1->int_dstaddr, ifp->int_net, ifp->int_mask)) { msglog("possible netmask problem" - " betwen %s:%s and %s:%s", + " between %s:%s and %s:%s", ifp->int_name, addrname(htonl(ifp->int_net), ifp->int_mask, 1), @@ -908,20 +1062,21 @@ ifinit(void) } } - /* Count the # of directly connected networks. - */ if (!(ifp->int_state & IS_ALIAS)) { + /* Count the # of directly connected networks. + */ if (!(ifp->int_if_flags & IFF_LOOPBACK)) tot_interfaces++; if (!IS_RIP_OFF(ifp->int_state)) rip_interfaces++; - } - if_ok_rdisc(ifp); - rip_on(ifp); + /* turn on router discovery and RIP If needed */ + if_ok_rdisc(ifp); + rip_on(ifp); + } } - /* If we are multi-homed and have at least one interface + /* If we are multi-homed and have at least two interfaces * listening to RIP, then output by default. */ if (!supplier_set && rip_interfaces > 1) @@ -959,7 +1114,7 @@ ifinit(void) /* Forget any interfaces that have disappeared. */ if (!(ifp->int_state & (IS_CHECKED | IS_REMOTE))) { - trace_act("interface %s has disappeared\n", + trace_act("interface %s has disappeared", ifp->int_name); ifdel(ifp); continue; @@ -983,7 +1138,8 @@ ifinit(void) * after any dead interfaces have been deleted, which * might affect routes for point-to-point links. */ - addrouteforif(ifp); + if (!addrouteforif(ifp)) + continue; /* Add routes to the local end of point-to-point interfaces * using loopback. @@ -1080,7 +1236,7 @@ check_net_syn(struct interface *ifp) * Create route to other end if a point-to-point link, * otherwise a route to this (sub)network. */ -void +int /* 0=bad interface */ addrouteforif(struct interface *ifp) { struct rt_entry *rt; @@ -1090,7 +1246,7 @@ addrouteforif(struct interface *ifp) /* skip sick interfaces */ if (ifp->int_state & IS_BROKE) - return; + return 0; /* If the interface on a subnet, then install a RIPv1 route to * the network as well (unless it is sick). @@ -1098,28 +1254,18 @@ addrouteforif(struct interface *ifp) if (ifp->int_state & IS_SUBNET) check_net_syn(ifp); - if (ifp->int_state & IS_REMOTE) { - dst = ifp->int_addr; - gate = ifp->int_dstaddr; - /* If we are going to send packets to the gateway, - * it must be reachable using our physical interfaces - */ - if (!(ifp->int_state && IS_EXTERNAL) - && !rtfind(ifp->int_dstaddr) - && ifp->int_transitions == 0) { - msglog("unreachable gateway %s in " - _PATH_GATEWAYS" entry %s", - naddr_ntoa(gate), ifp->int_name); - return; - } + gate = ifp->int_addr; + dst = (0 != (ifp->int_if_flags & (IFF_POINTOPOINT | IFF_LOOPBACK)) + ? ifp->int_dstaddr + : htonl(ifp->int_net)); - } else { - dst = (0 != (ifp->int_if_flags & (IFF_POINTOPOINT - | IFF_LOOPBACK)) - ? ifp->int_dstaddr - : htonl(ifp->int_net)); - gate = ifp->int_addr; - } + /* If we are going to send packets to the gateway, + * it must be reachable using our physical interfaces + */ + if ((ifp->int_state & IS_REMOTE) + && !(ifp->int_state && IS_EXTERNAL) + && !check_remote(ifp)) + return 0; /* We are finished if the correct main interface route exists. * The right route must be for the right interface, not synthesized @@ -1144,10 +1290,12 @@ addrouteforif(struct interface *ifp) } if (rt == 0) { if (ifp->int_transitions++ > 0) - trace_act("re-install interface %s\n", + trace_act("re-install interface %s", ifp->int_name); rtadd(dst, ifp->int_mask, gate, gate, ifp->int_metric, 0, RS_IF, ifp); } + + return 1; } diff --git a/sbin/routed/input.c b/sbin/routed/input.c index a854c41914e4..24a02b9e79c4 100644 --- a/sbin/routed/input.c +++ b/sbin/routed/input.c @@ -36,24 +36,34 @@ static char sccsid[] = "@(#)input.c 8.1 (Berkeley) 6/5/93"; #elif defined(__NetBSD__) static char rcsid[] = "$NetBSD$"; #endif -#ident "$Revision: 1.16 $" +#ident "$Revision: 1.17 $" #include "defs.h" -static void input(struct sockaddr_in *, struct interface*, struct rip *, int); +static void input(struct sockaddr_in *, struct interface *, struct interface *, + struct rip *, int); static void input_route(struct interface *, naddr, naddr, naddr, naddr, 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 *ifp) + struct interface *sifp) { + static struct msg_limit bad_name; struct sockaddr_in from; + struct interface *aifp; int fromlen, cc; - union pkt_buf inbuf; + struct { +#ifdef USE_PASSIFNAME + char ifname[IFNAMSIZ]; +#endif + union pkt_buf pbuf; + } inbuf; for (;;) { @@ -69,7 +79,54 @@ read_rip(int sock, logbad(1,"impossible recvfrom(rip) fromlen=%d", fromlen); - input(&from, ifp, &inbuf.rip, cc); + /* 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 */ + for (aifp = remote_if; aifp; aifp = aifp->int_rlink) { + if (aifp->int_addr == from.sin_addr.s_addr) + break; + } + if (aifp == 0) { + aifp = ifwithname(inbuf.ifname, 0); + if (aifp == 0) { + /* maybe it is a new interface */ + ifinit(); + aifp = ifwithname(inbuf.ifname, 0); + if (aifp == 0) { + msglim(&bad_name, from.sin_addr.s_addr, + "impossible interface name" + " %.*s", IFNAMSIZ, + inbuf.ifname); + } + } + + /* If it came via the wrong interface, do not + * trust it. + */ + 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))) + aifp = 0; + } +#else + aifp = iflookup(from.sin_addr.s_addr); +#endif + if (sifp == 0) + sifp = aifp; + + input(&from, sifp, aifp, &inbuf.pbuf.rip, cc); } } @@ -78,58 +135,53 @@ read_rip(int sock, */ static void input(struct sockaddr_in *from, /* received from this IP address */ - struct interface *sifp, /* interface by which it arrived */ + struct interface *sifp, /* interface of incoming socket */ + struct interface *aifp, /* "authenticated" interface */ struct rip *rip, - int size) + int cc) { # define FROM_NADDR from->sin_addr.s_addr - static naddr use_auth, bad_len, bad_mask; - static naddr unk_router, bad_router, bad_nhop; + static struct msg_limit use_auth, bad_len, bad_mask; + static struct msg_limit unk_router, bad_router, bad_nhop; - struct interface *aifp; /* interface if via 1 hop */ struct rt_entry *rt; struct netinfo *n, *lim; struct interface *ifp1; naddr gate, mask, v1_mask, dst, ddst_h; + struct auth_key *ap; int i; - aifp = iflookup(from->sin_addr.s_addr); - if (sifp == 0) - sifp = aifp; - - if (sifp != 0) - sifp->int_state |= IS_ACTIVE; + /* 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, size); + trace_rip("Recv", "from", from, sifp, rip, cc); if (rip->rip_vers == 0) { - if (from->sin_addr.s_addr != bad_router) - msglog("RIP version 0, cmd %d, packet received" - " from %s", - rip->rip_cmd, naddr_ntoa(FROM_NADDR)); - bad_router = from->sin_addr.s_addr; + 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 (size > MAXPACKETSIZE) { - if (from->sin_addr.s_addr != bad_router) - msglog("packet at least %d bytes too long received" - " from %s", - size-MAXPACKETSIZE, naddr_ntoa(FROM_NADDR)); - bad_router = from->sin_addr.s_addr; + if (cc > 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 + size); + 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, since snooping on the wire makes - * its simple passwords evident. Also, why authenticate queries? + * RIPv2 authentication is lame. Why authenticate queries? * Why should a RIPv2 implementation with authentication disabled * not be able to listen to RIPv2 packets with authenication, while * RIPv1 systems will listen? Crazy! @@ -137,54 +189,93 @@ input(struct sockaddr_in *from, /* received from this IP address */ if (!auth_ok && rip->rip_vers == RIPv2 && n < lim && n->n_family == RIP_AF_AUTH) { - if (from->sin_addr.s_addr != use_auth) - msglog("RIPv2 message with authentication" - " from %s discarded", - naddr_ntoa(FROM_NADDR)); - use_auth = from->sin_addr.s_addr; - trace_pkt("discard authenticated RIPv2 message\n"); + msglim(&use_auth, FROM_NADDR, + "RIPv2 message with authentication from %s discarded", + naddr_ntoa(FROM_NADDR)); return; } switch (rip->rip_cmd) { case RIPCMD_REQUEST: - /* did the request come from a router? + /* For mere requests, be a little sloppy about the source */ - if (from->sin_port == htons(RIP_PORT)) { - /* yes, ignore it if RIP is off so that it does not - * depend on us. - */ - if (rip_sock < 0) { - trace_pkt("ignore request while RIP off\n"); + 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; } + } - /* Ignore the request if we talking to ourself - * (and not a remote gateway). + /* 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 (ifwithaddr(FROM_NADDR, 0, 0) != 0) { - trace_pkt("discard our own RIP request\n"); + 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 unathenticated * queries. That is too silly to bother with. Sheesh! - * Are forwarding tables supposed to be secret? When - * a bad guy can infer them with test traffic? + * 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 - || size%sizeof(*n) != sizeof(struct rip)%sizeof(*n)) { - if (from->sin_addr.s_addr != bad_len) - msglog("request of bad length (%d) from %s", - size, naddr_ntoa(FROM_NADDR)); - bad_len = from->sin_addr.s_addr; + 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. + */ + if (aifp != 0 + && aifp->int_auth.type == RIP_AUTH_PW + && !ck_passwd(aifp,rip,lim,FROM_NADDR,&use_auth)) + ap = 0; + else + ap = find_auth(aifp); + } else { + v12buf.buf->rip_vers = RIPv1; + ap = 0; } - for (; n < lim; n++) { - n->n_metric = ntohl(n->n_metric); + clr_ws_buf(&v12buf, ap, aifp); + + do { + NTOHL(n->n_metric); /* A single entry with family RIP_AF_UNSPEC and * metric HOPCNT_INFINITY means "all routes". @@ -193,17 +284,16 @@ input(struct sockaddr_in *from, /* received from this IP address */ * (i.e. a query). */ if (n->n_family == RIP_AF_UNSPEC - && n->n_metric == HOPCNT_INFINITY - && n == rip->rip_nets - && n+1 == lim) { + && n->n_metric == HOPCNT_INFINITY) { if (from->sin_port != htons(RIP_PORT)) { /* Answer a query from a utility * program with all we know. */ - supply(from, sifp, OUT_QUERY, 0, - rip->rip_vers); + supply(from, aifp, OUT_QUERY, 0, + rip->rip_vers, ap != 0); return; } + /* A router trying to prime its tables. * Filter the answer in the about same way * broadcasts are filtered. @@ -216,91 +306,115 @@ input(struct sockaddr_in *from, /* received from this IP address */ * the remote router from getting the wrong * initial idea of the routes we send. */ + if (aifp == 0) { + trace_pkt("ignore distant router"); + return; + } if (!supplier - || aifp == 0 - || (aifp->int_state & IS_PASSIVE) - || (aifp->int_state & IS_ALIAS) - || ((aifp->int_state & IS_NO_RIPV1_OUT) - && (aifp->int_state&IS_NO_RIPV2_OUT))) + || IS_RIP_OFF(aifp->int_state)) { + trace_pkt("ignore; not supplying"); return; + } supply(from, aifp, OUT_UNICAST, 0, (aifp->int_state&IS_NO_RIPV1_OUT) - ? RIPv2 : RIPv1); + ? RIPv2 : RIPv1, + ap != 0); return; } + /* Ignore authentication */ + if (n->n_family == RIP_AF_AUTH) + continue; + if (n->n_family != RIP_AF_INET) { - if (from->sin_addr.s_addr != bad_router) - msglog("request from %s" - " for unsupported (af %d) %s", - naddr_ntoa(FROM_NADDR), - ntohs(n->n_family), - naddr_ntoa(n->n_dst)); - bad_router = from->sin_addr.s_addr; + 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)) { - if (from->sin_addr.s_addr != bad_router) - msglog("bad queried destination" - " %s from %s", - naddr_ntoa(dst), - naddr_ntoa(FROM_NADDR)); - bad_router = from->sin_addr.s_addr; + 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,sifp); + 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); - n->n_tag = 0; - n->n_nhop = 0; - if (rip->rip_vers == RIPv1) { - n->n_mask = 0; - } else { - n->n_mask = mask; - } + if (v12buf.buf->rip_vers != RIPv1) + v12buf.n->n_mask = mask; if (rt == 0) { - n->n_metric = HOPCNT_INFINITY; + /* we do not have the answer */ + v12buf.n->n_metric = HOPCNT_INFINITY; } else { - n->n_metric = rt->rt_metric+1; - n->n_metric += (sifp!=0)?sifp->int_metric : 1; - if (n->n_metric > HOPCNT_INFINITY) - n->n_metric = HOPCNT_INFINITY; - if (rip->rip_vers != RIPv1) { - n->n_tag = rt->rt_tag; - if (sifp != 0 + /* we have the answer, so compute the + * right metric and next hop. + */ + v12buf.n->n_family = RIP_AF_INET; + v12buf.n->n_dst = dst; + v12buf.n->n_metric = (rt->rt_metric+1 + + ((aifp!=0) + ? aifp->int_metric + : 1)); + if (v12buf.n->n_metric > HOPCNT_INFINITY) + 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, - sifp->int_net, - sifp->int_mask) - && rt->rt_gate != sifp->int_addr) - n->n_nhop = rt->rt_gate; + aifp->int_net, + aifp->int_mask) + && rt->rt_gate != aifp->int_addr) + v12buf.n->n_nhop = rt->rt_gate; } } - HTONL(n->n_metric); - } - /* Answer about specific routes. - * Only answer a router if we are a supplier - * to keep an unwary host that is just starting - * from picking us an a router. + 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. */ - rip->rip_cmd = RIPCMD_RESPONSE; - rip->rip_res1 = 0; - if (rip->rip_vers != RIPv1) - rip->rip_vers = RIPv2; + if (ap != 0 && aifp->int_auth.type == RIP_AUTH_MD5) + end_md5_auth(&v12buf, ap); + if (from->sin_port != htons(RIP_PORT)) { /* query */ - (void)output(OUT_QUERY, from, sifp, rip, size); + (void)output(OUT_QUERY, from, aifp, + v12buf.buf, + ((char *)v12buf.n - (char*)v12buf.buf)); } else if (supplier) { - (void)output(OUT_UNICAST, from, sifp, rip, size); + (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; @@ -318,7 +432,7 @@ input(struct sockaddr_in *from, /* received from this IP address */ return; } if (rip->rip_cmd == RIPCMD_TRACEON) { - rip->rip_tracefile[size-4] = '\0'; + rip->rip_tracefile[cc-4] = '\0'; trace_on((char*)rip->rip_tracefile, 0); } else { trace_off("tracing turned off by %s\n", @@ -327,21 +441,22 @@ input(struct sockaddr_in *from, /* received from this IP address */ return; case RIPCMD_RESPONSE: - if (size%sizeof(*n) != sizeof(struct rip)%sizeof(*n)) { - if (from->sin_addr.s_addr != bad_len) - msglog("response of bad length (%d) from %s", - size, naddr_ntoa(FROM_NADDR)); - bad_len = from->sin_addr.s_addr; + 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)) { - trace_pkt("discard RIP response from unknown port\n"); + msglim(&bad_router, FROM_NADDR, + " discard RIP response from unknown port" + " %d", from->sin_port); return; } if (rip_sock < 0) { - trace_pkt("discard response while RIP off\n"); + trace_pkt(" discard response while RIP off"); return; } @@ -350,50 +465,47 @@ input(struct sockaddr_in *from, /* received from this IP address */ ifp1 = ifwithaddr(FROM_NADDR, 0, 1); if (ifp1) { if (ifp1->int_state & IS_REMOTE) { - if (ifp1->int_state & IS_PASSIVE) { - msglog("bogus input from %s on" - " supposedly passive %s", - naddr_ntoa(FROM_NADDR), - ifp1->int_name); - } else { - ifp1->int_act_time = now.tv_sec; - if (if_ok(ifp1, "remote ")) - addrouteforif(ifp1); + /* 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\n"); + trace_pkt(" discard our own RIP response"); + return; } - return; } - /* Check the router from which message originated. We accept - * routing packets from routers directly connected via - * broadcast or point-to-point networks, and from + /* Accept routing packets from routers directly connected + * via broadcast or point-to-point networks, and from * those listed in /etc/gateways. */ - if (!aifp) { - if (from->sin_addr.s_addr != unk_router) - msglog("discard packet from unknown router %s" - " or via unidentified interface", - naddr_ntoa(FROM_NADDR)); - unk_router = from->sin_addr.s_addr; + if (aifp == 0) { + msglim(&unk_router, FROM_NADDR, + " discard response from %s" + " via unexpected interface", + naddr_ntoa(FROM_NADDR)); return; } - if (aifp->int_state & IS_PASSIVE) { - trace_act("discard packet from %s" - " via passive interface %s\n", - naddr_ntoa(FROM_NADDR), - aifp->int_name); + 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; } - /* Check required version - */ if (((aifp->int_state & IS_NO_RIPV1_IN) && rip->rip_vers == RIPv1) || ((aifp->int_state & IS_NO_RIPV2_IN) && rip->rip_vers != RIPv1)) { - trace_pkt("discard RIPv%d response\n", + trace_pkt(" discard RIPv%d response", rip->rip_vers); return; } @@ -401,35 +513,39 @@ input(struct sockaddr_in *from, /* received from this IP address */ /* Ignore routes via dead interface. */ if (aifp->int_state & IS_BROKE) { - trace_pkt("discard response via broken interface %s\n", + trace_pkt("%sdiscard response via broken interface %s", aifp->int_name); return; } - /* Authenticate the packet if we have a secret. + /* If the interface cares, ignore bad routers. + * Trace but do not log this problem because when it + * happens it happens a lot. */ - if (aifp->int_passwd[0] != '\0') { - if (n >= lim - || n->n_family != RIP_AF_AUTH - || ((struct netauth*)n)->a_type != RIP_AUTH_PW) { - if (from->sin_addr.s_addr != use_auth) - msglog("missing password from %s", - naddr_ntoa(FROM_NADDR)); - use_auth = from->sin_addr.s_addr; - return; + if (aifp->int_state & IS_DISTRUST) { + struct tgate *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; + } + } + } - } else if (0 != bcmp(((struct netauth*)n)->au.au_pw, - aifp->int_passwd, - sizeof(aifp->int_passwd))) { - if (from->sin_addr.s_addr != use_auth) - msglog("bad password from %s", - naddr_ntoa(FROM_NADDR)); - use_auth = from->sin_addr.s_addr; + /* Authenticate the packet if we have a secret. + * If we do not, ignore the silliness in RFC 1723 + * and accept it regardless. + */ + if (aifp->int_auth.type != RIP_AUTH_NONE + && rip->rip_vers != RIPv1) { + if (!ck_passwd(aifp,rip,lim,FROM_NADDR,&use_auth)) return; - } } - for (; n < lim; n++) { + do { if (n->n_family == RIP_AF_AUTH) continue; @@ -438,39 +554,35 @@ input(struct sockaddr_in *from, /* received from this IP address */ if (n->n_family != RIP_AF_INET && (n->n_family != RIP_AF_UNSPEC || dst != RIP_DEFAULT)) { - if (from->sin_addr.s_addr != bad_router) - msglog("route from %s to unsupported" - " address family %d," - " destination %s", - naddr_ntoa(FROM_NADDR), - n->n_family, - naddr_ntoa(dst)); - bad_router = from->sin_addr.s_addr; + 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)) { - if (from->sin_addr.s_addr != bad_router) - msglog("bad destination %s from %s", - naddr_ntoa(dst), - naddr_ntoa(FROM_NADDR)); - bad_router = from->sin_addr.s_addr; + 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) { - if (from->sin_addr.s_addr != bad_router) - msglog("bad metric %d from %s" - " for destination %s", - n->n_metric, - naddr_ntoa(FROM_NADDR), - naddr_ntoa(dst)); - bad_router = from->sin_addr.s_addr; + 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->sin_addr.s_addr; + gate = FROM_NADDR; if (n->n_nhop != 0) { if (rip->rip_vers == RIPv2) { n->n_nhop = 0; @@ -481,14 +593,13 @@ input(struct sockaddr_in *from, /* received from this IP address */ && check_dst(n->n_nhop)) { gate = n->n_nhop; } else { - if (bad_nhop != from->sin_addr.s_addr) - msglog("router %s to %s has" - " bad next hop %s", - naddr_ntoa(FROM_NADDR), - naddr_ntoa(dst), - naddr_ntoa(n->n_nhop)); - bad_nhop = from->sin_addr.s_addr; - n->n_nhop = 0; + 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; } } } @@ -497,14 +608,12 @@ input(struct sockaddr_in *from, /* received from this IP address */ || 0 == (mask = ntohl(n->n_mask))) { mask = ripv1_mask_host(dst,aifp); } else if ((ntohl(dst) & ~mask) != 0) { - if (bad_mask != from->sin_addr.s_addr) { - msglog("router %s sent bad netmask" - " %#x with %s", - naddr_ntoa(FROM_NADDR), - mask, - naddr_ntoa(dst)); - bad_mask = from->sin_addr.s_addr; - } + msglim(&bad_mask, FROM_NADDR, + "router %s sent bad netmask" + " %#x with %s", + naddr_ntoa(FROM_NADDR), + mask, + naddr_ntoa(dst)); continue; } if (rip->rip_vers == RIPv1) @@ -548,9 +657,9 @@ input(struct sockaddr_in *from, /* received from this IP address */ * of the defense against RS_NET_SYN. */ if (have_ripv1_out - && (v1_mask = ripv1_mask_net(dst,0)) > mask && (((rt = rtget(dst,mask)) == 0 - || !(rt->rt_state & RS_NET_SYN)))) { + || !(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) { @@ -579,9 +688,10 @@ input(struct sockaddr_in *from, /* received from this IP address */ break; dst = htonl(ntohl(dst) + ddst_h); } - } + } while (++n < lim); break; } +#undef FROM_NADDR } @@ -610,7 +720,8 @@ input_route(struct interface *ifp, */ ifp1 = ifwithaddr(dst, 1, 1); if (ifp1 != 0 - && !(ifp1->int_state & IS_BROKE)) + && (!(ifp1->int_state & IS_BROKE) + || (ifp1->int_state & IS_PASSIVE))) return; /* Look for the route in our table. @@ -739,3 +850,85 @@ input_route(struct interface *ifp, /* 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) +# define DAY (24*60*60) + struct netauth *na2; + struct auth_key *akp = aifp->int_auth.keys; + MD5_CTX md5_ctx; + u_char hash[RIP_AUTH_PW_LEN]; + int i; + + + if ((void *)NA >= lim || NA->a_family != RIP_AF_AUTH) { + msglim(use_authp, from, "missing password from %s", + naddr_ntoa(from)); + return 0; + } + + if (NA->a_type != aifp->int_auth.type) { + msglim(use_authp, from, "wrong type of password from %s", + naddr_ntoa(from)); + return 0; + } + + if (NA->a_type == RIP_AUTH_PW) { + /* accept any current cleartext password + */ + for (i = 0; i < MAX_AUTH_KEYS; i++, akp++) { + if ((u_long)akp->start-DAY > (u_long)clk.tv_sec + || (u_long)akp->end+DAY < (u_long)clk.tv_sec) + continue; + + if (!bcmp(NA->au.au_pw, akp->key, RIP_AUTH_PW_LEN)) + return 1; + } + + } else { + /* accept any current MD5 secret with the right key ID + */ + for (i = 0; i < MAX_AUTH_KEYS; i++, akp++) { + if (NA->au.a_md5.md5_keyid == akp->keyid + && (u_long)akp->start-DAY <= (u_long)clk.tv_sec + && (u_long)akp->end+DAY >= (u_long)clk.tv_sec) + break; + } + + if (i < MAX_AUTH_KEYS) { + na2 = (struct netauth *)((char *)(NA+1) + + NA->au.a_md5.md5_pkt_len); + if (NA->au.a_md5.md5_pkt_len % sizeof(*NA) != 0 + || lim < (void *)(na2+1)) { + msglim(use_authp, from, + "bad MD5 RIP-II pkt length %d from %s", + NA->au.a_md5.md5_pkt_len, + naddr_ntoa(from)); + return 0; + } + MD5Init(&md5_ctx); + MD5Update(&md5_ctx, (u_char *)NA, + (char *)na2->au.au_pw - (char *)NA); + MD5Update(&md5_ctx, + (u_char *)akp->key, sizeof(akp->key)); + MD5Final(hash, &md5_ctx); + if (na2->a_family == RIP_AF_AUTH + && na2->a_type == 1 + && NA->au.a_md5.md5_auth_len == RIP_AUTH_PW_LEN + && !bcmp(hash, na2->au.au_pw, sizeof(hash))) + return 1; + } + } + + msglim(use_authp, from, "bad password from %s", + naddr_ntoa(from)); + return 0; +#undef NA +} diff --git a/sbin/routed/output.c b/sbin/routed/output.c index eafcf31e7864..69f7da9fa78d 100644 --- a/sbin/routed/output.c +++ b/sbin/routed/output.c @@ -36,7 +36,7 @@ static char sccsid[] = "@(#)output.c 8.1 (Berkeley) 6/5/93"; #elif defined(__NetBSD__) static char rcsid[] = "$NetBSD$"; #endif -#ident "$Revision: 1.17 $" +#ident "$Revision: 1.18 $" #include "defs.h" @@ -53,37 +53,45 @@ struct { naddr to_std_mask; naddr to_std_net; struct interface *ifp; /* usually output interface */ - struct ws_buf { /* info for each buffer */ - struct rip *buf; - struct netinfo *n; - struct netinfo *base; - struct netinfo *lim; - enum output_type type; - } v12, v2; + struct auth_key *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_SAFE 0x002 /* send RIPv2 safe for RIPv1 */ -#define WS_ST_RIP2_ALL 0x004 /* send full featured RIPv2 */ -#define WS_ST_AG 0x008 /* ok to aggregate subnets */ -#define WS_ST_SUPER_AG 0x010 /* ok to aggregate networks */ -#define WS_ST_SUB_AG 0x020 /* aggregate subnets in odd case */ -#define WS_ST_QUERY 0x040 /* responding to a query */ -#define WS_ST_TO_ON_NET 0x080 /* sending onto one of our nets */ -#define WS_ST_DEFAULT 0x100 /* faking a default */ -#define WS_ST_PM_RDISC 0x200 /* poor-man's router discovery */ +#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_SUB_AG 0x010 /* aggregate subnets in odd case */ +#define WS_ST_QUERY 0x020 /* responding to a query */ +#define WS_ST_TO_ON_NET 0x040 /* sending onto one of our nets */ +#define WS_ST_DEFAULT 0x080 /* faking a default */ } ws; /* A buffer for what can be heard by both RIPv1 and RIPv2 listeners */ +struct ws_buf v12buf; union pkt_buf ripv12_buf; /* Another for only RIPv2 listeners */ +struct ws_buf v2buf; 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 */ @@ -137,7 +145,7 @@ output(enum output_type type, msg = "Send pt-to-pt"; } else if (ifp->int_state & IS_DUP) { trace_act("abort multicast output via %s" - " with duplicate address\n", + " with duplicate address", ifp->int_name); return 0; } else { @@ -198,20 +206,86 @@ output(enum output_type type, } -/* install authentication if appropriate +/* Find the first key that has not expired, but settle for + * the last key if they have all expired. + * If no key is ready yet, give up. */ -static void -set_auth(struct ws_buf *w) +struct auth_key * +find_auth(struct interface *ifp) { - if (ws.ifp != 0 - && ws.ifp->int_passwd[0] != '\0' - && (ws.state & WS_ST_RIP2_SAFE)) { - w->n->n_family = RIP_AF_AUTH; - ((struct netauth*)w->n)->a_type = RIP_AUTH_PW; - bcopy(ws.ifp->int_passwd, ((struct netauth*)w->n)->au.au_pw, - sizeof(((struct netauth*)w->n)->au.au_pw)); - w->n++; + struct auth_key *ap, *res; + int i; + + + if (ifp == 0 || ifp->int_auth.type == RIP_AUTH_NONE) + return 0; + + res = 0; + ap = ifp->int_auth.keys; + for (i = 0; i < MAX_AUTH_KEYS; i++, ap++) { + if ((u_long)ap->start <= (u_long)clk.tv_sec) { + if ((u_long)ap->end >= (u_long)clk.tv_sec) + return ap; + res = ap; + } } + return res; +} + + +void +clr_ws_buf(struct ws_buf *wb, + struct auth_key *ap, + struct interface *ifp) +{ + struct netauth *na; + + wb->lim = wb->base + NETS_LEN; + wb->n = wb->base; + bzero(wb->n, NETS_LEN*sizeof(*wb->n)); + + /* install authentication if appropriate + */ + if (ap == 0) + return; + na = (struct netauth*)wb->n; + if (ifp->int_auth.type == RIP_AUTH_PW) { + na->a_family = RIP_AF_AUTH; + na->a_type = RIP_AUTH_PW; + bcopy(ap->key, na->au.au_pw, sizeof(na->au.au_pw)); + wb->n++; + + } else if (ifp->int_auth.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_PW_LEN; + na->au.a_md5.md5_seqno = clk.tv_sec; + wb->n++; + wb->lim--; /* make room for trailer */ + } +} + + +void +end_md5_auth(struct ws_buf *wb, + struct auth_key *ap) +{ + struct netauth *na, *na2; + MD5_CTX md5_ctx; + + + na = (struct netauth*)wb->base; + na2 = (struct netauth*)wb->n; + na2->a_family = RIP_AF_AUTH; + na2->a_type = 1; + bcopy(ap->key, na2->au.au_pw, sizeof(na2->au.au_pw)); + na->au.a_md5.md5_pkt_len = (char *)na2-(char *)(na+1); + MD5Init(&md5_ctx); + MD5Update(&md5_ctx, (u_char *)na, + (char *)(na2+1) - (char *)na); + MD5Final(na2->au.au_pw, &md5_ctx); + wb->n++; } @@ -226,12 +300,14 @@ supply_write(struct ws_buf *wb) */ switch (wb->type) { case NO_OUT_MULTICAST: - trace_pkt("skip multicast to %s because impossible\n", + 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.ifp->int_auth.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) @@ -240,9 +316,7 @@ supply_write(struct ws_buf *wb) break; } - bzero(wb->n = wb->base, sizeof(*wb->n)*NETS_LEN); - if (wb->buf->rip_vers == RIPv2) - set_auth(wb); + clr_ws_buf(wb,ws.a,ws.ifp); } @@ -252,7 +326,7 @@ static void supply_out(struct ag_info *ag) { int i; - naddr mask, v1_mask, s_mask, dst_h, ddst_h; + naddr mask, v1_mask, dst_h, ddst_h; struct ws_buf *wb; @@ -272,7 +346,6 @@ supply_out(struct ag_info *ag) mask = ag->ag_mask; v1_mask = ripv1_mask_host(htonl(dst_h), (ws.state & WS_ST_TO_ON_NET) ? ws.ifp : 0); - s_mask = std_mask(htonl(dst_h)); i = 0; /* If we are sending RIPv2 packets that cannot (or must not) be @@ -280,19 +353,16 @@ supply_out(struct ag_info *ag) * Subnets (from other networks) can only be sent via multicast. * A pair of subnet routes might have been promoted so that they * are legal to send by RIPv1. - * If RIPv1 is off, use the multicast buffer, unless this is the - * fake default route and it is acting as a poor-man's router- - * discovery mechanism. + * If RIPv1 is off, use the multicast buffer. */ - if (((ws.state & WS_ST_RIP2_ALL) - && (dst_h != RIP_DEFAULT || !(ws.state & WS_ST_PM_RDISC))) + if ((ws.state & WS_ST_RIP2_ALL) || ((ag->ag_state & AGS_RIPV2) && v1_mask != mask)) { /* use the RIPv2-only buffer */ - wb = &ws.v2; + wb = &v2buf; } else { /* use the RIPv1-or-RIPv2 buffer */ - wb = &ws.v12; + wb = &v12buf; /* Convert supernet route into corresponding set of network * routes for RIPv1, but leave non-contiguous netmasks @@ -333,18 +403,20 @@ supply_out(struct ag_info *ag) ? HOPCNT_INFINITY : ag->ag_metric); HTONL(wb->n->n_metric); - if (wb->buf->rip_vers == RIPv2) { + /* 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_RIP2_SAFE) && ((ws.state & WS_ST_QUERY) || (ag->ag_nhop != ws.ifp->int_addr && on_net(ag->ag_nhop, ws.ifp->int_net, ws.ifp->int_mask)))) wb->n->n_nhop = ag->ag_nhop; - if ((ws.state & WS_ST_RIP2_ALL) - || mask != s_mask) - wb->n->n_mask = htonl(mask); + wb->n->n_mask = htonl(mask); wb->n->n_tag = ag->ag_tag; } dst_h += ddst_h; @@ -368,20 +440,20 @@ walk_supply(struct radix_node *rn, naddr dst, nhop; - /* Do not advertise the loopback interface - * or external remote interfaces + /* Do not advertise external remote interfaces or passive interfaces. */ if ((RT->rt_state & RS_IF) && RT->rt_ifp != 0 - && ((RT->rt_ifp->int_if_flags & IFF_LOOPBACK) - || (RT->rt_ifp->int_state & IS_EXTERNAL)) + && (RT->rt_ifp->int_if_flags & 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. + * do not say anything unless responding to a query, + * except about our main interface. */ - if (!supplier && !(ws.state & WS_ST_QUERY)) + if (!supplier && !(ws.state & WS_ST_QUERY) + && !(RT->rt_state & RS_MHOME)) return 0; dst = RT->rt_dst; @@ -528,14 +600,15 @@ walk_supply(struct radix_node *rn, } else { /* Do not advertise stable routes that will be ignored, - * unless they are being held down and poisoned. If the - * route recently was advertised with a metric that would - * have been less than infinity through this interface, we - * need to continue to advertise it in order to poison it. + * 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. */ pref = RT->rt_poison_metric + ws.metric; - if (pref >= HOPCNT_INFINITY - || RT->rt_poison_time < now_garbage ) + if (!(ws.state & WS_ST_QUERY) + && (pref >= HOPCNT_INFINITY + || RT->rt_poison_time < now_garbage)) return 0; metric = HOPCNT_INFINITY; @@ -556,10 +629,11 @@ supply(struct sockaddr_in *dst, struct interface *ifp, /* output interface */ enum output_type type, int flash, /* 1=flash update */ - int vers) /* RIP version */ + int vers, /* RIP version */ + int passwd_ok) /* OK to include cleartext password */ { - static int init = 1; struct rt_entry *rt; + int def_metric; ws.state = 0; @@ -598,94 +672,79 @@ supply(struct sockaddr_in *dst, ws.metric = ifp->int_metric+1; } - if (init) { - init = 0; - - bzero(&ripv12_buf, sizeof(ripv12_buf)); - ripv12_buf.rip.rip_cmd = RIPCMD_RESPONSE; - ws.v12.buf = &ripv12_buf.rip; - ws.v12.base = &ws.v12.buf->rip_nets[0]; - ws.v12.lim = ws.v12.base + NETS_LEN; - - bzero(&rip_v2_buf, sizeof(rip_v2_buf)); - rip_v2_buf.rip.rip_cmd = RIPCMD_RESPONSE; - rip_v2_buf.rip.rip_vers = RIPv2; - ws.v2.buf = &rip_v2_buf.rip; - ws.v2.base = &ws.v2.buf->rip_nets[0]; - ws.v2.lim = ws.v2.base + NETS_LEN; - } ripv12_buf.rip.rip_vers = vers; - ws.v12.n = ws.v12.base; - set_auth(&ws.v12); - ws.v2.n = ws.v2.base; - set_auth(&ws.v2); - switch (type) { case OUT_BROADCAST: - ws.v2.type = ((ws.ifp != 0 - && (ws.ifp->int_if_flags & IFF_MULTICAST)) + v2buf.type = ((ifp != 0 && (ifp->int_if_flags & IFF_MULTICAST)) ? OUT_MULTICAST : NO_OUT_MULTICAST); - ws.v12.type = OUT_BROADCAST; + v12buf.type = OUT_BROADCAST; break; case OUT_MULTICAST: - ws.v2.type = ((ws.ifp != 0 - && (ws.ifp->int_if_flags & IFF_MULTICAST)) + v2buf.type = ((ifp != 0 && (ifp->int_if_flags & IFF_MULTICAST)) ? OUT_MULTICAST : NO_OUT_MULTICAST); - ws.v12.type = OUT_BROADCAST; + v12buf.type = OUT_BROADCAST; break; case OUT_UNICAST: case OUT_QUERY: - ws.v2.type = (vers == RIPv2) ? type : NO_OUT_RIPV2; - ws.v12.type = type; + v2buf.type = (vers == RIPv2) ? type : NO_OUT_RIPV2; + v12buf.type = type; break; default: - ws.v2.type = type; - ws.v12.type = type; + v2buf.type = type; + v12buf.type = type; break; } if (vers == RIPv2) { - /* if asked to send RIPv2, send at least that which can - * be safely heard by RIPv1 listeners. - */ - ws.state |= WS_ST_RIP2_SAFE; - /* full RIPv2 only if cannot be heard by RIPv1 listeners */ if (type != OUT_BROADCAST) ws.state |= WS_ST_RIP2_ALL; if (!(ws.state & WS_ST_TO_ON_NET)) { ws.state |= (WS_ST_AG | WS_ST_SUPER_AG); - } else if (ws.ifp == 0 || !(ws.ifp->int_state & IS_NO_AG)) { + } else if (ifp == 0 || !(ifp->int_state & IS_NO_AG)) { ws.state |= WS_ST_AG; if (type != OUT_BROADCAST - && (ws.ifp == 0 - || !(ws.ifp->int_state & IS_NO_SUPER_AG))) + && (ifp == 0 || !(ifp->int_state&IS_NO_SUPER_AG))) ws.state |= WS_ST_SUPER_AG; } - } else if (ws.ifp == 0 || !(ws.ifp->int_state & IS_NO_AG)) { + } else if (ifp == 0 || !(ifp->int_state & IS_NO_AG)) { ws.state |= WS_ST_SUB_AG; } - if (supplier) { - /* Fake a default route if asked, and if there is not - * a better, real default route. - */ - if (ifp->int_d_metric != 0 - && (0 == (rt = rtget(RIP_DEFAULT, 0)) - || rt->rt_metric+ws.metric >= ifp->int_d_metric)) { + ws.a = (vers == RIPv2) ? find_auth(ifp) : 0; + if (ws.a != 0 && !passwd_ok && ifp->int_auth.type == RIP_AUTH_PW) + ws.a = 0; + clr_ws_buf(&v12buf,ws.a,ifp); + clr_ws_buf(&v2buf,ws.a,ifp); + + /* 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, - ifp->int_d_metric,ifp->int_d_metric, + 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)) { - ws.state |= WS_ST_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++; } } @@ -695,21 +754,21 @@ supply(struct sockaddr_in *dst, /* Flush the packet buffers, provided they are not empty and * do not contain only the password. */ - if (ws.v12.n != ws.v12.base - && (ws.v12.n > ws.v12.base+1 - || ws.v12.n->n_family != RIP_AF_AUTH)) - supply_write(&ws.v12); - if (ws.v2.n != ws.v2.base - && (ws.v2.n > ws.v2.base+1 - || ws.v2.n->n_family != RIP_AF_AUTH)) - supply_write(&ws.v2); + if (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(&ws.v12); + supply_write(&v12buf); } @@ -737,36 +796,28 @@ rip_bcast(int flash) if (rip_sock < 0) return; - trace_act("send %s and inhibit dynamic updates for %.3f sec\n", + 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); for (ifp = ifnet; ifp != 0; ifp = ifp->int_next) { - /* skip interfaces not doing RIP, those already queried, - * and aliases. Do try broken interfaces to see - * if they have healed. + /* Skip interfaces not doing RIP. + * Do try broken interfaces to see if they have healed. */ - if (0 != (ifp->int_state & (IS_PASSIVE | IS_ALIAS))) + if (IS_RIP_OUT_OFF(ifp->int_state)) continue; /* skip turned off interfaces */ if (!iff_alive(ifp->int_if_flags)) continue; - /* default to RIPv1 output */ - if (ifp->int_state & IS_NO_RIPV1_OUT) { - /* Say nothing if this interface is turned off */ - if (ifp->int_state & IS_NO_RIPV2_OUT) - continue; - vers = RIPv2; - } else { - vers = RIPv1; - } + 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 RIPv1 is not turned off, then broadcast so + + /* If RIPv1 is not turned off, then broadcast so * that RIPv1 listeners can hear. */ if (vers == RIPv2 @@ -781,13 +832,17 @@ rip_bcast(int flash) dst.sin_addr.s_addr = ifp->int_dstaddr; type = OUT_UNICAST; - } else { + } 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); + supply(&dst, ifp, type, flash, vers, 1); } update_seqno++; /* all routes are up to date */ @@ -817,28 +872,21 @@ rip_query(void) bzero(&buf, sizeof(buf)); for (ifp = ifnet; ifp; ifp = ifp->int_next) { - /* skip interfaces not doing RIP, those already queried, - * and aliases. Do try broken interfaces to see - * if they have healed. + /* 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 (0 != (ifp->int_state & (IS_RIP_QUERIED - | IS_PASSIVE | IS_ALIAS))) + if (IS_RIP_IN_OFF(ifp->int_state) + || ifp->int_query_time != NEVER) continue; /* skip turned off interfaces */ if (!iff_alive(ifp->int_if_flags)) continue; - /* default to RIPv1 output */ - if (ifp->int_state & IS_NO_RIPV2_OUT) { - /* Say nothing if this interface is turned off */ - if (ifp->int_state & IS_NO_RIPV1_OUT) - continue; - buf.rip_vers = RIPv1; - } else { - buf.rip_vers = RIPv2; - } - + buf.rip_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); @@ -861,13 +909,17 @@ rip_query(void) dst.sin_addr.s_addr = ifp->int_dstaddr; type = OUT_UNICAST; - } else { + } 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_state |= IS_RIP_QUERIED; + ifp->int_query_time = now.tv_sec+SUPPLY_INTERVAL; if (output(type, &dst, ifp, &buf, sizeof(buf)) < 0) if_sick(ifp); } diff --git a/sbin/routed/parms.c b/sbin/routed/parms.c index 0d178c3698bc..ab5c75e65c35 100644 --- a/sbin/routed/parms.c +++ b/sbin/routed/parms.c @@ -36,7 +36,7 @@ static char sccsid[] = "@(#)if.c 8.1 (Berkeley) 6/5/93"; #elif defined(__NetBSD__) static char rcsid[] = "$NetBSD$"; #endif -#ident "$Revision: 1.9 $" +#ident "$Revision: 1.10 $" #include "defs.h" #include "pathnames.h" @@ -44,6 +44,7 @@ static char rcsid[] = "$NetBSD$"; struct parm *parms; struct intnet *intnets; +struct tgate *tgates; /* use configured parameters @@ -51,49 +52,49 @@ struct intnet *intnets; void get_parms(struct interface *ifp) { + static warned_auth_in, warned_auth_out; struct parm *parmp; /* get all relevant parameters */ for (parmp = parms; parmp != 0; parmp = parmp->parm_next) { - if ((parmp->parm_name[0] == '\0' - && on_net(ifp->int_addr, - parmp->parm_addr_h, parmp->parm_mask)) - || (parmp->parm_name[0] != '\0' - && !strcmp(ifp->int_name, parmp->parm_name))) { - /* this group of parameters is relevant, + if (parmp->parm_name[0] == '\0' + || !strcmp(ifp->int_name, parmp->parm_name) + || (parmp->parm_name[0] == '\n' + && on_net(ifp->int_addr, + parmp->parm_net, parmp->parm_mask))) { + + /* This group of parameters is relevant, * so get its settings */ ifp->int_state |= parmp->parm_int_state; - if (parmp->parm_passwd[0] != '\0') - bcopy(parmp->parm_passwd, ifp->int_passwd, - sizeof(ifp->int_passwd)); + if (parmp->parm_auth.type != RIP_AUTH_NONE) + bcopy(&parmp->parm_auth, &ifp->int_auth, + sizeof(ifp->int_auth)); if (parmp->parm_rdisc_pref != 0) ifp->int_rdisc_pref = parmp->parm_rdisc_pref; if (parmp->parm_rdisc_int != 0) ifp->int_rdisc_int = parmp->parm_rdisc_int; if (parmp->parm_d_metric != 0) ifp->int_d_metric = parmp->parm_d_metric; - } + } } - /* default poor-man's router discovery to a metric that will - * be heard by old versions of routed. + + /* Set general defaults. + * + * Default poor-man's router discovery to a metric that will + * be heard by old versions of `routed`. They ignored received + * routes with metric 15. */ if ((ifp->int_state & IS_PM_RDISC) && ifp->int_d_metric == 0) - ifp->int_d_metric = HOPCNT_INFINITY-2; - - if (IS_RIP_IN_OFF(ifp->int_state)) - ifp->int_state |= IS_NO_RIP_OUT; + ifp->int_d_metric = FAKE_METRIC; if (ifp->int_rdisc_int == 0) ifp->int_rdisc_int = DefMaxAdvertiseInterval; if (!(ifp->int_if_flags & IFF_MULTICAST) - && !(ifp->int_if_flags & IFF_POINTOPOINT)) - ifp->int_state |= IS_NO_RIPV2_OUT; - - if (!(ifp->int_if_flags & IFF_MULTICAST)) + && !(ifp->int_state & IS_REMOTE)) ifp->int_state |= IS_BCAST_RDISC; if (ifp->int_if_flags & IFF_POINTOPOINT) { @@ -110,10 +111,27 @@ get_parms(struct interface *ifp) if (0 != (ifp->int_state & (IS_PASSIVE | IS_REMOTE))) ifp->int_state |= IS_NO_RDISC; if (ifp->int_state & IS_PASSIVE) - ifp->int_state |= (IS_NO_RIP | IS_NO_RDISC); - if ((ifp->int_state & (IS_NO_RIP | IS_NO_RDISC)) - == (IS_NO_RIP|IS_NO_RDISC)) - ifp->int_state |= IS_PASSIVE; + ifp->int_state |= IS_NO_RIP; + + if (!IS_RIP_IN_OFF(ifp->int_state) + && ifp->int_auth.type != RIP_AUTH_NONE + && !(ifp->int_state & IS_NO_RIPV1_IN) + && !warned_auth_in) { + msglog("Warning: RIPv1 input via %s" + " will be accepted without authentication", + ifp->int_name); + warned_auth_in = 1; + } + if (!IS_RIP_OUT_OFF(ifp->int_state) + && ifp->int_auth.type != RIP_AUTH_NONE + && !(ifp->int_state & IS_NO_RIPV1_OUT) + && !warned_auth_out) { + msglog("Warning: RIPv1 output via %s" + " will be sent without authentication", + ifp->int_name); + warned_auth_out = 1; + ifp->int_auth.type = RIP_AUTH_NONE; + } } @@ -144,7 +162,6 @@ gwkludge(void) int metric, n; u_int state; char *type; - struct parm *parmp; fp = fopen(_PATH_GATEWAYS, "r"); @@ -161,8 +178,7 @@ gwkludge(void) || *lptr == '#') continue; p = lptr+strlen(lptr)-1; - while (*p == '\n' - || *p == ' ') + while (*p == '\n' || *p == ' ') *p-- = '\0'; /* notice newfangled parameter lines @@ -171,9 +187,9 @@ gwkludge(void) && strncasecmp("host", lptr, 4)) { p = parse_parms(lptr); if (p != 0) { - if (strcmp(p,lptr)) - msglog("bad \"%s\" in "_PATH_GATEWAYS - " entry \"%s\"", lptr, p); + if (strcasecmp(p,lptr)) + msglog("bad %s in "_PATH_GATEWAYS + " entry \"%s\"", p, lptr); else msglog("bad \"%s\" in "_PATH_GATEWAYS, lptr); @@ -182,26 +198,28 @@ gwkludge(void) } /* {net | host} XX[/M] XX gateway XX metric DD [passive | external]\n */ + qual[0] = '\0'; n = sscanf(lptr, "%4s %129[^ \t] gateway" - " %64[^ / \t] metric %d %8s\n", + " %64[^ / \t] metric %u %8s\n", net_host, dname, gname, &metric, qual); - if (n != 5) { - msglog("bad "_PATH_GATEWAYS" entry \"%s\"", lptr); + if (n != 4 && n != 5) { + msglog("bad "_PATH_GATEWAYS" entry \"%s\"; %d values", + lptr, n); continue; } - if (metric < 0 || metric >= HOPCNT_INFINITY) { + if (metric >= HOPCNT_INFINITY) { msglog("bad metric in "_PATH_GATEWAYS" entry \"%s\"", lptr); continue; } - if (!strcmp(net_host, "host")) { + if (!strcasecmp(net_host, "host")) { if (!gethost(dname, &dst)) { msglog("bad host \"%s\" in "_PATH_GATEWAYS " entry \"%s\"", dname, lptr); continue; } netmask = HOST_MASK; - } else if (!strcmp(net_host, "net")) { + } else if (!strcasecmp(net_host, "net")) { if (!getnet(dname, &dst, &netmask)) { msglog("bad net \"%s\" in "_PATH_GATEWAYS " entry \"%s\"", dname, lptr); @@ -219,7 +237,7 @@ gwkludge(void) continue; } - if (strcmp(qual, type = "passive") == 0) { + if (!strcasecmp(qual, type = "passive")) { /* Passive entries are not placed in our tables, * only the kernel's, so we don't copy all of the * external routing information within a net. @@ -230,17 +248,19 @@ gwkludge(void) if (metric == 0) metric = 1; - } else if (strcmp(qual, type = "external") == 0) { + } else if (!strcasecmp(qual, type = "external")) { /* External entries are handled by other means * such as EGP, and are placed only in the daemon * tables to prevent overriding them with something * else. */ + strcpy(qual,"external"); state = IS_REMOTE | IS_PASSIVE | IS_EXTERNAL; if (metric == 0) metric = 1; - } else if (qual[0] == '\0') { + } else if (!strcasecmp(qual, "active") + || qual[0] == '\0') { if (metric != 0) { /* Entries that are neither "passive" nor * "external" are "remote" and must behave @@ -253,110 +273,274 @@ gwkludge(void) /* "remote" entries with a metric of 0 * are aliases for our own interfaces */ - state = IS_REMOTE | IS_PASSIVE; + state = IS_REMOTE | IS_PASSIVE | IS_ALIAS; type = "alias"; } } else { - msglog("bad "_PATH_GATEWAYS" entry \"%s\"", lptr); + msglog("bad "_PATH_GATEWAYS" entry \"%s\";" + " unknown type %s", lptr, qual); continue; } - /* Remember to advertise the corresponding logical network. - */ - if (!(state & IS_EXTERNAL) - && netmask != std_mask(dst)) - state |= IS_SUBNET; - if (0 != (state & (IS_PASSIVE | IS_REMOTE))) state |= IS_NO_RDISC; if (state & IS_PASSIVE) - state |= (IS_NO_RIP | IS_NO_RDISC); - if ((state & (IS_NO_RIP | IS_NO_RDISC)) - == (IS_NO_RIP|IS_NO_RDISC)) - state |= IS_PASSIVE; - - parmp = (struct parm*)malloc(sizeof(*parmp)); - bzero(parmp, sizeof(*parmp)); - parmp->parm_next = parms; - parms = parmp; - parmp->parm_addr_h = ntohl(dst); - parmp->parm_mask = -1; - parmp->parm_d_metric = 0; - parmp->parm_int_state = state; - - /* See if this new interface duplicates an existing - * interface. - */ - for (ifp = ifnet; 0 != ifp; ifp = ifp->int_next) { - if (ifp->int_mask == netmask - && ((ifp->int_addr == dst - && netmask != HOST_MASK) - || (ifp->int_dstaddr == dst - && netmask == HOST_MASK))) - break; - } + state |= IS_NO_RIP; + + ifp = check_dup(gate,dst,netmask,0); if (ifp != 0) { - /* Let one of our real interfaces be marked passive. - */ - if ((state & IS_PASSIVE) && !(state & IS_EXTERNAL)) { - ifp->int_state |= state; - } else { - msglog("%s is duplicated in "_PATH_GATEWAYS - " by %s", - ifp->int_name, lptr); - } + msglog("duplicate "_PATH_GATEWAYS" entry \"%s\"",lptr); continue; } - tot_interfaces++; - ifp = (struct interface *)malloc(sizeof(*ifp)); bzero(ifp, sizeof(*ifp)); - if (ifnet != 0) { - ifp->int_next = ifnet; - ifnet->int_prev = ifp; - } - ifnet = ifp; ifp->int_state = state; - ifp->int_net = ntohl(dst) & netmask; - ifp->int_mask = netmask; if (netmask == HOST_MASK) - ifp->int_if_flags |= IFF_POINTOPOINT; - ifp->int_dstaddr = dst; + ifp->int_if_flags = IFF_POINTOPOINT | IFF_UP_RUNNING; + else + ifp->int_if_flags = IFF_UP_RUNNING; + ifp->int_act_time = NEVER; ifp->int_addr = gate; + ifp->int_dstaddr = dst; + ifp->int_mask = netmask; + ifp->int_ripv1_mask = netmask; + ifp->int_std_mask = std_mask(gate); + ifp->int_net = ntohl(dst); + ifp->int_std_net = ifp->int_net & ifp->int_std_mask; + ifp->int_std_addr = htonl(ifp->int_std_net); ifp->int_metric = metric; - (void)sprintf(ifp->int_name, "%s-%s", type, naddr_ntoa(dst)); + if (!(state & IS_EXTERNAL) + && ifp->int_mask != ifp->int_std_mask) + ifp->int_state |= IS_SUBNET; + (void)sprintf(ifp->int_name, "%s(%s)", type, gname); ifp->int_index = -1; + if_link(ifp); + } + + /* After all of the parameter lines have been read, + * apply them to any remote interfaces. + */ + for (ifp = ifnet; 0 != ifp; ifp = ifp->int_next) { get_parms(ifp); + tot_interfaces++; + if (!IS_RIP_OFF(ifp->int_state)) + rip_interfaces++; + trace_if("Add", ifp); } } -/* parse a set of parameters for an interface +/* strtok(), but honoring backslash + */ +static int /* -1=bad */ +parse_quote(char **linep, + char *delims, + char *delimp, + char *buf, + int lim) +{ + char c, *pc, *p; + + + pc = *linep; + if (*pc == '\0') + return -1; + + for (;;) { + if (lim == 0) + return -1; + c = *pc++; + if (c == '\0') + break; + + if (c == '\\' && pc != '\0') { + if ((c = *pc++) == 'n') { + c = '\n'; + } else if (c == 'r') { + c = '\r'; + } else if (c == 't') { + c = '\t'; + } else if (c == 'b') { + c = '\b'; + } else if (c >= '0' && c <= '7') { + c -= '0'; + if (*pc >= '0' && *pc <= '7') { + c = (c<<3)+(*pc++ - '0'); + if (*pc >= '0' && *pc <= '7') + c = (c<<3)+(*pc++ - '0'); + } + } + + } else { + for (p = delims; *p != '\0'; ++p) { + if (*p == c) + goto exit; + } + } + + *buf++ = c; + --lim; + } +exit: + if (delimp != 0) + *delimp = c; + *linep = pc-1; + if (lim != 0) + *buf = '\0'; + return 0; +} + + +/* Parse password timestamp + */ +static char * +parse_ts(time_t *tp, + char **valp, + char *val0, + char *delimp, + char *buf, + u_int bufsize) +{ + struct tm tm; + + if (0 > parse_quote(valp, "| ,\n\r", delimp, + buf,bufsize) + || buf[bufsize-1] != '\0' + || buf[bufsize-2] != '\0') { + sprintf(buf,"timestamp %.25s", val0); + return buf; + } + strcat(buf,"\n"); + bzero(&tm, sizeof(tm)); + if (5 != sscanf(buf, "%u/%u/%u@%u:%u\n", + &tm.tm_year, &tm.tm_mon, &tm.tm_mday, + &tm.tm_hour, &tm.tm_min)) { + sprintf(buf,"timestamp %.25s", val0); + return buf; + } + if (tm.tm_year <= 37) + tm.tm_year += 100; + + if ((*tp = mktime(&tm)) == -1) { + sprintf(buf,"timestamp %.25s", val0); + return buf; + } + + return 0; +} + + +/* Get one or more password, key ID's, and expiration dates in + * the format + * passwd|keyID|year/mon/day@hour:min|year/mon/day@hour:min|passwd|... + */ +static char * /* 0 or error message */ +get_passwds(char *tgt, + char *val, + struct parm *parmp, + u_char type) +{ + static char buf[80]; + char *val0, *p, delim; + struct auth_key *akp, *akp2; + int i; + u_long l; + + + if (parmp->parm_auth.type != RIP_AUTH_NONE) + return "duplicate authentication"; + parmp->parm_auth.type = type; + + bzero(buf, sizeof(buf)); + + akp = parmp->parm_auth.keys; + for (i = 0; i < MAX_AUTH_KEYS; i++, val++, akp++) { + if ((delim = *val) == '\0') + break; + val0 = val; + if (0 > parse_quote(&val, "| ,\n\r", &delim, + (char *)akp->key, sizeof(akp->key))) + return tgt; + + akp->end = -1; + + if (delim != '|') { + if (type == RIP_AUTH_MD5) + return "missing Keyid"; + break; + } + val0 = ++val; + if (0 > parse_quote(&val, "| ,\n\r", &delim, buf,sizeof(buf)) + || buf[sizeof(buf)-1] != '\0' + || (l = strtoul(buf,&p,0)) > 255 + || *p != '\0') { + sprintf(buf,"KeyID \"%.20s\"", val0); + return buf; + } + for (akp2 = parmp->parm_auth.keys; akp2 < akp; akp2++) { + if (akp2->keyid == l) { + *val = '\0'; + sprintf(buf,"duplicate KeyID \"%.20s\"", val0); + return buf; + } + } + akp->keyid = (int)l; + + if (delim != '|') + break; + + val0 = ++val; + if (0 != (p = parse_ts(&akp->start,&val,val0,&delim, + buf,sizeof(buf)))) + return p; + if (delim != '|') + return "missing second timestamp"; + val0 = ++val; + if (0 != (p = parse_ts(&akp->end,&val,val0,&delim, + buf,sizeof(buf)))) + return p; + if ((u_long)akp->start > (u_long)akp->end) { + sprintf(buf,"out of order timestamp %.30s",val0); + return buf; + } + + if (delim != '|') + break; + } + + return (delim != '\0') ? tgt : 0; +} + + +/* Parse a set of parameters for an interface. */ char * /* 0 or error message */ parse_parms(char *line) { -#define PARS(str) (0 == (tgt = str, strcasecmp(tok, tgt))) -#define PARSE(str) (0 == (tgt = str, strncasecmp(tok, str "=", sizeof(str)))) +#define PARS(str) (!strcasecmp(tgt, str)) +#define PARSEQ(str) (!strncasecmp(tgt, str"=", sizeof(str))) #define CKF(g,b) {if (0 != (parm.parm_int_state & ((g) & ~(b)))) break; \ parm.parm_int_state |= (b);} -#define DELIMS " ,\t\n" struct parm parm; struct intnet *intnetp; - char *tok, *tgt, *p; + struct tgate *tg; + naddr addr, mask; + char delim, *val0, *tgt, *val, *p; + char buf[64]; - /* "subnet=x.y.z.u/mask" must be alone on the line */ - if (!strncasecmp("subnet=",line,7)) { + /* "subnet=x.y.z.u/mask,metric" must be alone on the line */ + if (!strncasecmp(line, "subnet=", sizeof("subnet=")-1) + && *(val = &line[sizeof("subnet=")]) != '\0') { intnetp = (struct intnet*)malloc(sizeof(*intnetp)); intnetp->intnet_metric = 1; - if ((p = strrchr(line,','))) { + if ((p = strrchr(val,','))) { *p++ = '\0'; intnetp->intnet_metric = (int)strtol(p,&p,0); if (*p != '\0' @@ -364,8 +548,7 @@ parse_parms(char *line) || intnetp->intnet_metric >= HOPCNT_INFINITY) return line; } - if (!getnet(&line[7], &intnetp->intnet_addr, - &intnetp->intnet_mask) + if (!getnet(val, &intnetp->intnet_addr, &intnetp->intnet_mask) || intnetp->intnet_mask == HOST_MASK || intnetp->intnet_addr == RIP_DEFAULT) { free(intnetp); @@ -379,21 +562,51 @@ parse_parms(char *line) bzero(&parm, sizeof(parm)); tgt = "null"; - for (tok = strtok(line, DELIMS); - tok != 0 && tok[0] != '\0'; - tgt = 0, tok = strtok(0,DELIMS)) { - if (PARSE("if")) { - if (parm.parm_name[0] != '\0' - || tok[3] == '\0' - || strlen(tok) > IFNAMSIZ+3) - break; - strcpy(parm.parm_name, tok+3); + for (;;) { + tgt = line + strspn(line, " ,\n\r"); + if (*tgt == '\0') + break; + + line += strcspn(tgt, "= ,\n\r"); + delim = *line; + if (delim == '=') { + val0 = ++line; + if (0 > parse_quote(&line," ,\n\r",&delim, + buf,sizeof(buf))) + return tgt; + } + if (delim != '\0') + *line++ = '\0'; - } else if (PARSE("passwd")) { - if (tok[7] == '\0' - || strlen(tok) > RIP_AUTH_PW_LEN+7) - break; - strcpy(parm.parm_passwd, tok+7); + if (PARSEQ("if")) { + if (parm.parm_name[0] != '\0' + || strlen(buf) > IFNAMSIZ) + return tgt; + strcpy(parm.parm_name, buf); + + } else if (PARSEQ("addr")) { + /* This is a bad idea, because the address based + * sets of parameters cannot be checked for + * consistency with the interface name parameters. + * The parm_net stuff is needed to allow several + * -F settings. + */ + if (!getnet(val, &addr, &mask) + || parm.parm_name[0] != '\0') + return tgt; + parm.parm_net = addr; + parm.parm_mask = mask; + parm.parm_name[0] = '\n'; + + } else if (PARSEQ("passwd")) { + tgt = get_passwds(tgt, val0, &parm, RIP_AUTH_PW); + if (tgt) + return tgt; + + } else if (PARSEQ("md5_passwd")) { + tgt = get_passwds(tgt, val0, &parm, RIP_AUTH_MD5); + if (tgt) + return tgt; } else if (PARS("no_ag")) { parm.parm_int_state |= (IS_NO_AG | IS_NO_SUPER_AG); @@ -409,11 +622,18 @@ parse_parms(char *line) } else if (PARS("ripv2_out")) { if (parm.parm_int_state & IS_NO_RIPV2_OUT) - break; + return tgt; parm.parm_int_state |= IS_NO_RIPV1_OUT; + } else if (PARS("ripv2")) { + if ((parm.parm_int_state & IS_NO_RIPV2_OUT) + || (parm.parm_int_state & IS_NO_RIPV2_IN)) + return tgt; + parm.parm_int_state |= (IS_NO_RIPV1_IN + | IS_NO_RIPV1_OUT); + } else if (PARS("no_rip")) { - parm.parm_int_state |= IS_NO_RIP; + CKF(IS_PM_RDISC, IS_NO_RIP); } else if (PARS("no_rdisc")) { CKF((GROUP_IS_SOL|GROUP_IS_ADV), IS_NO_RDISC); @@ -437,47 +657,50 @@ parse_parms(char *line) CKF((GROUP_IS_SOL|GROUP_IS_ADV), IS_NO_RDISC); parm.parm_int_state |= IS_NO_RIP; - } else if (PARSE("rdisc_pref")) { + } else if (PARSEQ("rdisc_pref")) { if (parm.parm_rdisc_pref != 0 - || tok[11] == '\0' - || (parm.parm_rdisc_pref = (int)strtol(&tok[11], - &p,0), + || (parm.parm_rdisc_pref = (int)strtoul(buf, &p,0), *p != '\0')) - break; + return tgt; } else if (PARS("pm_rdisc")) { + if (IS_RIP_OUT_OFF(parm.parm_int_state)) + return tgt; parm.parm_int_state |= IS_PM_RDISC; - } else if (PARSE("rdisc_interval")) { + } else if (PARSEQ("rdisc_interval")) { if (parm.parm_rdisc_int != 0 - || tok[15] == '\0' - || (parm.parm_rdisc_int = (int)strtol(&tok[15], - &p,0), + || (parm.parm_rdisc_int = (int)strtoul(buf,&p,0), *p != '\0') || parm.parm_rdisc_int < MinMaxAdvertiseInterval || parm.parm_rdisc_int > MaxMaxAdvertiseInterval) - break; + return tgt; - } else if (PARSE("fake_default")) { + } else if (PARSEQ("fake_default")) { if (parm.parm_d_metric != 0 - || tok[13] == '\0' - || (parm.parm_d_metric=(int)strtol(&tok[13],&p,0), + || IS_RIP_OUT_OFF(parm.parm_int_state) + || (parm.parm_d_metric = (int)strtoul(buf,&p,0), *p != '\0') || parm.parm_d_metric > HOPCNT_INFINITY-1) - break; + return tgt; + + } else if (PARSEQ("trust_gateway")) { + if (!gethost(buf,&addr)) + return tgt; + tg = (struct tgate *)malloc(sizeof(*tg)); + tg->tgate_next = tgates; + tg->tgate_addr = addr; + tgates = tg; + parm.parm_int_state |= IS_DISTRUST; } else { - tgt = tok; - break; + return tgt; /* error */ } } - if (tgt != 0) - return tgt; return check_parms(&parm); -#undef DELIMS #undef PARS -#undef PARSE +#undef PARSEQ } @@ -487,36 +710,33 @@ check_parms(struct parm *new) { struct parm *parmp; - /* set implicit values */ - if (!supplier && supplier_set) - new->parm_int_state |= (IS_NO_RIPV1_OUT - | IS_NO_RIPV2_OUT - | IS_NO_ADV_OUT); if (new->parm_int_state & IS_NO_ADV_IN) new->parm_int_state |= IS_NO_SOL_OUT; - if ((new->parm_int_state & (IS_NO_RIP | IS_NO_RDISC)) - == (IS_NO_RIP | IS_NO_RDISC)) - new->parm_int_state |= IS_PASSIVE; - /* compare with existing sets of parameters */ for (parmp = parms; parmp != 0; parmp = parmp->parm_next) { if (strcmp(new->parm_name, parmp->parm_name)) continue; - if (!on_net(htonl(parmp->parm_addr_h), - new->parm_addr_h, new->parm_mask) - && !on_net(htonl(new->parm_addr_h), - parmp->parm_addr_h, parmp->parm_mask)) + if (!on_net(htonl(parmp->parm_net), + new->parm_net, new->parm_mask) + && !on_net(htonl(new->parm_net), + parmp->parm_net, parmp->parm_mask)) continue; - if (strcmp(parmp->parm_passwd, new->parm_passwd) - || (0 != (new->parm_int_state & GROUP_IS_SOL) - && 0 != (parmp->parm_int_state & GROUP_IS_SOL) - && 0 != ((new->parm_int_state ^ parmp->parm_int_state) - && GROUP_IS_SOL)) + if (parmp->parm_auth.type != RIP_AUTH_NONE + && new->parm_auth.type != RIP_AUTH_NONE + && bcmp(&parmp->parm_auth, &new->parm_auth, + sizeof(parmp->parm_auth))) { + return "conflicting, duplicate authentication"; + } + + if ((0 != (new->parm_int_state & GROUP_IS_SOL) + && 0 != (parmp->parm_int_state & GROUP_IS_SOL) + && 0 != ((new->parm_int_state ^ parmp->parm_int_state) + && GROUP_IS_SOL)) || (0 != (new->parm_int_state & GROUP_IS_ADV) && 0 != (parmp->parm_int_state & GROUP_IS_ADV) && 0 != ((new->parm_int_state ^ parmp->parm_int_state) @@ -526,11 +746,18 @@ check_parms(struct parm *new) && new->parm_rdisc_pref != parmp->parm_rdisc_pref) || (new->parm_rdisc_int != 0 && parmp->parm_rdisc_int != 0 - && new->parm_rdisc_int != parmp->parm_rdisc_int) - || (new->parm_d_metric != 0 - && parmp->parm_d_metric != 0 - && new->parm_d_metric != parmp->parm_d_metric)) - return "duplicate"; + && new->parm_rdisc_int != parmp->parm_rdisc_int)) { + return ("conflicting, duplicate router discovery" + " parameters"); + + } + + if (new->parm_d_metric != 0 + && parmp->parm_d_metric != 0 + && new->parm_d_metric != parmp->parm_d_metric) { + return ("conflicting, duplicate poor man's router" + " discovery or fake default metric"); + } } parmp = (struct parm*)malloc(sizeof(*parmp)); @@ -547,7 +774,7 @@ check_parms(struct parm *new) */ int /* 0=bad */ getnet(char *name, - naddr *addrp, /* host byte order */ + naddr *netp, /* host byte order */ naddr *maskp) { int i; @@ -574,17 +801,19 @@ getnet(char *name, if (np != 0) { in.s_addr = (naddr)np->n_net; } else if (inet_aton(name, &in) == 1) { - HTONL(in.s_addr); + NTOHL(in.s_addr); + } else if (!mname && !strcasecmp(name,"default")) { + in.s_addr = RIP_DEFAULT; } else { return 0; } - if (mname == 0) { + if (!mname) { /* we cannot use the interfaces here because we have not * looked at them yet. */ mask = std_mask(in.s_addr); - if ((~mask & ntohl(in.s_addr)) != 0) + if ((~mask & in.s_addr) != 0) mask = HOST_MASK; } else { mask = (naddr)strtoul(mname, &p, 0); @@ -592,12 +821,22 @@ getnet(char *name, return 0; mask = HOST_MASK << (32-mask); } + + /* must have mask of 0 with default */ if (mask != 0 && in.s_addr == RIP_DEFAULT) return 0; - if ((~mask & ntohl(in.s_addr)) != 0) + /* no host bits allowed in a network number */ + if ((~mask & in.s_addr) != 0) + return 0; + /* require non-zero network number */ + if ((mask & in.s_addr) == 0 && in.s_addr != RIP_DEFAULT) + return 0; + if (in.s_addr>>24 == 0 && in.s_addr != RIP_DEFAULT) + return 0; + if (in.s_addr>>24 == 0xff) return 0; - *addrp = in.s_addr; + *netp = in.s_addr; *maskp = mask; return 1; } @@ -616,6 +855,12 @@ gethost(char *name, * might be sick because routing is. */ if (inet_aton(name, &in) == 1) { + /* get a good number, but check that it it makes some + * sense. + */ + if (ntohl(in.s_addr)>>24 == 0 + || ntohl(in.s_addr)>>24 == 0xff) + return 0; *addrp = in.s_addr; return 1; } diff --git a/sbin/routed/rdisc.c b/sbin/routed/rdisc.c index da1784c793e4..7433b792e6fe 100644 --- a/sbin/routed/rdisc.c +++ b/sbin/routed/rdisc.c @@ -36,7 +36,7 @@ static char sccsid[] = "@(#)rdisc.c 8.1 (Berkeley) x/y/95"; #elif defined(__NetBSD__) static char rcsid[] = "$NetBSD$"; #endif -#ident "$Revision: 1.16 $" +#ident "$Revision: 1.17 $" #include "defs.h" #include <netinet/in_systm.h> @@ -134,8 +134,7 @@ trace_rdisc(char *act, (void)fputc('\n',ftrace); } else { - trace_act("%s Router Solic. from %s to %s via %s" - " value=%#x\n", + trace_act("%s Router Solic. from %s to %s via %s value=%#x", act, naddr_ntoa(from), naddr_ntoa(to), ifp ? ifp->int_name : "?", ntohl(p->so.icmp_so_rsvd)); @@ -161,7 +160,8 @@ get_rdisc_sock(void) */ void set_rdisc_mg(struct interface *ifp, - int on) { /* 0=turn it off */ + int on) /* 0=turn it off */ +{ struct ip_mreq m; if (rdisc_sock < 0) { @@ -174,8 +174,7 @@ set_rdisc_mg(struct interface *ifp, get_rdisc_sock(); } - if (!(ifp->int_if_flags & IFF_MULTICAST) - || (ifp->int_state & IS_ALIAS)) { + if (!(ifp->int_if_flags & IFF_MULTICAST)) { ifp->int_state &= ~(IS_ALL_HOSTS | IS_ALL_ROUTERS); return; } @@ -253,7 +252,7 @@ set_supplier(void) if (supplier_set) return; - trace_act("start suppying routes\n"); + trace_act("start suppying routes"); /* Forget discovered routes. */ @@ -327,8 +326,7 @@ rdisc_age(naddr bad_gate) sec = (now.tv_sec - drp->dr_life + SUPPLY_INTERVAL); if (drp->dr_ts > sec) { - trace_act("age 0.0.0.0 --> %s" - " via %s\n", + trace_act("age 0.0.0.0 --> %s via %s", naddr_ntoa(drp->dr_gate), drp->dr_ifp->int_name); drp->dr_ts = sec; @@ -414,8 +412,8 @@ del_rdisc(struct dr *drp) */ if (i == 0 && ifp->int_rdisc_cnt >= MAX_SOLICITATIONS) { - trace_act("discovered route is bad" - "--re-solicit routers via %s\n", ifp->int_name); + trace_act("discovered route is bad--re-solicit routers via %s", + ifp->int_name); ifp->int_rdisc_cnt = 0; ifp->int_rdisc_timer.tv_sec = 0; rdisc_sol(); @@ -486,7 +484,7 @@ rdisc_sort(void) /* Stop using discovered routes if they are all bad */ if (new_drp == 0) { - trace_act("turn off Router Discovery client\n"); + trace_act("turn off Router Discovery client"); rdisc_ok = 0; if (rt != 0 @@ -504,7 +502,7 @@ rdisc_sort(void) } else { if (cur_drp == 0) { trace_act("turn on Router Discovery client" - " using %s via %s\n", + " using %s via %s", naddr_ntoa(new_drp->dr_gate), new_drp->dr_ifp->int_name); @@ -512,7 +510,7 @@ rdisc_sort(void) } else { trace_act("switch Router Discovery from" - " %s via %s to %s via %s\n", + " %s via %s to %s via %s", naddr_ntoa(cur_drp->dr_gate), cur_drp->dr_ifp->int_name, naddr_ntoa(new_drp->dr_gate), @@ -527,7 +525,8 @@ rdisc_sort(void) } else { rtadd(RIP_DEFAULT, 0, new_drp->dr_gate, new_drp->dr_gate, - 0, 0, RS_RDISC, new_drp->dr_ifp); + HOPCNT_INFINITY-1, 0, + RS_RDISC, new_drp->dr_ifp); } /* Now turn off RIP and delete RIP routes, @@ -551,30 +550,27 @@ parse_ad(naddr from, u_short life, struct interface *ifp) { - static naddr bad_gate; + static struct msg_limit bad_gate; struct dr *drp, *new_drp; if (gate == RIP_DEFAULT || !check_dst(gate)) { - if (bad_gate != from) { - msglog("router %s advertising bad gateway %s", - naddr_ntoa(from), - naddr_ntoa(gate)); - bad_gate = from; - } + msglim(&bad_gate, from,"router %s advertising bad gateway %s", + naddr_ntoa(from), + naddr_ntoa(gate)); return; } /* ignore pointers to ourself and routes via unreachable networks */ if (ifwithaddr(gate, 1, 0) != 0) { - trace_pkt("\tdiscard Router Discovery Ad pointing at us\n"); + trace_pkt(" discard Router Discovery Ad pointing at us"); return; } if (!on_net(gate, ifp->int_net, ifp->int_mask)) { - trace_pkt("\tdiscard Router Discovery Ad" - " toward unreachable net\n"); + trace_pkt(" discard Router Discovery Ad" + " toward unreachable net"); return; } @@ -710,7 +706,7 @@ send_rdisc(union ad_u *p, msg = "Send multicast"; if (ifp->int_state & IS_DUP) { trace_act("abort multicast output via %s" - " with duplicate address\n", + " with duplicate address", ifp->int_name); return; } @@ -800,14 +796,13 @@ rdisc_adv(void) { struct interface *ifp; + if (!supplier) + return; rdisc_timer.tv_sec = now.tv_sec + NEVER; for (ifp = ifnet; ifp; ifp = ifp->int_next) { - if (0 != (ifp->int_state & (IS_NO_ADV_OUT - | IS_PASSIVE - | IS_ALIAS - | IS_BROKE))) + if (0 != (ifp->int_state & (IS_NO_ADV_OUT | IS_BROKE))) continue; if (!timercmp(&ifp->int_rdisc_timer, &now, >) @@ -843,13 +838,13 @@ rdisc_sol(void) union ad_u u; + if (supplier) + return; + rdisc_timer.tv_sec = now.tv_sec + NEVER; for (ifp = ifnet; ifp; ifp = ifp->int_next) { - if (0 != (ifp->int_state & (IS_NO_SOL_OUT - | IS_PASSIVE - | IS_ALIAS - | IS_BROKE)) + if (0 != (ifp->int_state & (IS_NO_SOL_OUT | IS_BROKE)) || ifp->int_rdisc_cnt >= MAX_SOLICITATIONS) continue; @@ -880,20 +875,14 @@ rdisc_sol(void) static struct interface * /* 0 if bad */ ck_icmp(char *act, naddr from, + struct interface *ifp, naddr to, union ad_u *p, u_int len) { - struct interface *ifp; char *type; - /* If we could tell the interface on which a packet from address 0 - * arrived, we could deal with such solicitations. - */ - - ifp = ((from == 0) ? 0 : iflookup(from)); - if (p->icmp.icmp_type == ICMP_ROUTERADVERT) { type = "advertisement"; } else if (p->icmp.icmp_type == ICMP_ROUTERSOLICIT) { @@ -903,8 +892,7 @@ ck_icmp(char *act, } if (p->icmp.icmp_code != 0) { - trace_pkt("unrecognized ICMP Router" - " %s code=%d from %s to %s\n", + trace_pkt("unrecognized ICMP Router %s code=%d from %s to %s", type, p->icmp.icmp_code, naddr_ntoa(from), naddr_ntoa(to)); return 0; @@ -926,14 +914,20 @@ ck_icmp(char *act, void read_d(void) { - static naddr bad_asize, bad_len; + static struct msg_limit bad_asize, bad_len; struct sockaddr_in from; int n, fromlen, cc, hlen; - union { - struct ip ip; - u_short s[512/2]; - u_char b[512]; - } pkt; + struct { +#undef USE_PASSIFNAME /* it is too bad it does not work on raw sockets */ +#ifdef USE_PASSIFNAME + char ifname[IFNAMSIZ]; +#endif + union { + struct ip ip; + u_short s[512/2]; + u_char b[512]; + } pkt; + } buf; union ad_u *p; n_long *wp; struct interface *ifp; @@ -941,7 +935,7 @@ read_d(void) for (;;) { fromlen = sizeof(from); - cc = recvfrom(rdisc_sock, &pkt, sizeof(pkt), 0, + cc = recvfrom(rdisc_sock, &buf, sizeof(buf), 0, (struct sockaddr*)&from, &fromlen); if (cc <= 0) { @@ -952,20 +946,46 @@ read_d(void) if (fromlen != sizeof(struct sockaddr_in)) logbad(1,"impossible recvfrom(rdisc_sock) fromlen=%d", fromlen); +#ifdef USE_PASSIFNAME + if ((cc -= sizeof(buf.ifname)) < 0) + logbad(0,"missing USE_PASSIFNAME; only %d bytes", + cc+sizeof(buf.ifname)); +#endif - hlen = pkt.ip.ip_hl << 2; + hlen = buf.pkt.ip.ip_hl << 2; if (cc < hlen + ICMP_MINLEN) continue; - p = (union ad_u *)&pkt.b[hlen]; + p = (union ad_u *)&buf.pkt.b[hlen]; cc -= hlen; +#ifdef USE_PASSIFNAME + ifp = ifwithname(buf.ifname, 0); + if (ifp == 0) { + /* maybe it is a new interface */ + ifinit(); + ifp = ifwithname(buf.ifname, 0); + if (ifp == 0) { + msglim(&bad_name, from.sin_addr.s_addr, + "impossible rdisc if_ name %.*s", + IFNAMSIZ, buf.ifname); + } + } +#else + /* If we could tell the interface on which a packet from + * address 0 arrived, we could deal with such solicitations. + */ + ifp = ((from.sin_addr.s_addr == 0) + ? 0 : iflookup(from.sin_addr.s_addr)); +#endif ifp = ck_icmp("Recv", - from.sin_addr.s_addr, pkt.ip.ip_dst.s_addr, + from.sin_addr.s_addr, ifp, + buf.pkt.ip.ip_dst.s_addr, p, cc); if (ifp == 0) continue; if (ifwithaddr(from.sin_addr.s_addr, 0, 0)) { - trace_pkt("\tdiscard our own Router Discovery msg\n"); + trace_pkt(" discard our own Router Discovery" + " message"); continue; } @@ -973,27 +993,21 @@ read_d(void) case ICMP_ROUTERADVERT: if (p->ad.icmp_ad_asize*4 < sizeof(p->ad.icmp_ad_info[0])) { - if (bad_asize != from.sin_addr.s_addr) { - msglog("intolerable rdisc address" - " size=%d", - p->ad.icmp_ad_asize); - bad_asize = from.sin_addr.s_addr; - } + msglim(&bad_asize, from.sin_addr.s_addr, + "intolerable rdisc address size=%d", + p->ad.icmp_ad_asize); continue; } if (p->ad.icmp_ad_num == 0) { - trace_pkt("\tempty?\n"); + trace_pkt(" empty?"); continue; } if (cc != (sizeof(p->ad) - sizeof(p->ad.icmp_ad_info) + (p->ad.icmp_ad_num * sizeof(p->ad.icmp_ad_info[0])))) { - if (bad_len != from.sin_addr.s_addr) { - msglog("rdisc length %d does not" - " match ad_num %d", - cc, p->ad.icmp_ad_num); - bad_len = from.sin_addr.s_addr; - } + msglim(&bad_len, from.sin_addr.s_addr, + "rdisc length %d does not match ad_num" + " %d", cc, p->ad.icmp_ad_num); continue; } if (supplier) diff --git a/sbin/routed/routed.h b/sbin/routed/routed.h new file mode 100644 index 000000000000..3981d99887db --- /dev/null +++ b/sbin/routed/routed.h @@ -0,0 +1,174 @@ +/*- + * Copyright (c) 1983, 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)routed.h 8.1 (Berkeley) 6/2/93 + * + * $NetBSD$ + */ + +#ifndef _ROUTED_H_ +#define _ROUTED_H_ +#ifdef __cplusplus +extern "C" { +#endif +#ident "$Revision: 1.10 $" + +/* + * Routing Information Protocol + * + * Derived from Xerox NS Routing Information Protocol + * by changing 32-bit net numbers to sockaddr's and + * padding stuff to 32-bit boundaries. + */ + +#define RIPv1 1 +#define RIPv2 2 +#ifndef RIPVERSION +#define RIPVERSION RIPv1 +#endif + +#define RIP_PORT 520 + +#if RIPVERSION == 1 +/* Note that this so called sockaddr has a 2-byte sa_family and no sa_len. + * It is not a UNIX sockaddr, but the shape of an address as defined + * in RIPv1. It is still defined to allow old versions of programs + * such as `gated` to use this file to define RIPv1. + */ +struct netinfo { + struct sockaddr rip_dst; /* destination net/host */ + u_int32_t rip_metric; /* cost of route */ +}; +#else +struct netinfo { + u_int16_t n_family; +#define RIP_AF_INET htons(AF_INET) +#define RIP_AF_UNSPEC 0 +#define RIP_AF_AUTH 0xffff + u_int16_t n_tag; /* optional in RIPv2 */ + u_int32_t n_dst; /* destination net or host */ +#define RIP_DEFAULT 0 + u_int32_t n_mask; /* netmask in RIPv2 */ + u_int32_t n_nhop; /* optional next hop in RIPv2 */ + u_int32_t n_metric; /* cost of route */ +}; +#endif + +/* RIPv2 authentication */ +struct netauth { + u_int16_t a_family; /* always RIP_AF_AUTH */ + u_int16_t a_type; +#define RIP_AUTH_NONE 0 +#define RIP_AUTH_PW htons(2) /* password type */ +#define RIP_AUTH_MD5 htons(3) /* Keyed MD5 */ + union { +#define RIP_AUTH_PW_LEN 16 + u_int8_t au_pw[RIP_AUTH_PW_LEN]; + struct a_md5 { + int16_t md5_pkt_len; /* RIP-II packet length */ + int8_t md5_keyid; /* key ID and auth data len */ + int8_t md5_auth_len; /* 16 */ + u_int32_t md5_seqno; /* sequence number */ + u_int32_t rsvd[2]; /* must be 0 */ +#define RIP_AUTH_MD5_LEN RIP_AUTH_PW_LEN + } a_md5; + } au; +}; + +struct rip { + u_int8_t rip_cmd; /* request/response */ + u_int8_t rip_vers; /* protocol version # */ + u_int16_t rip_res1; /* pad to 32-bit boundary */ + union { /* variable length... */ + struct netinfo ru_nets[1]; + int8_t ru_tracefile[1]; + struct netauth ru_auth[1]; + } ripun; +#define rip_nets ripun.ru_nets +#define rip_auths ripun.ru_auth +#define rip_tracefile ripun.ru_tracefile +}; + +/* Packet types. + */ +#define RIPCMD_REQUEST 1 /* want info */ +#define RIPCMD_RESPONSE 2 /* responding to request */ +#define RIPCMD_TRACEON 3 /* turn tracing on */ +#define RIPCMD_TRACEOFF 4 /* turn it off */ + +/* Gated extended RIP to include a "poll" command instead of using + * RIPCMD_REQUEST with (RIP_AF_UNSPEC, RIP_DEFAULT). RFC 1058 says + * command 5 is used by Sun Microsystems for its own purposes. + */ +#define RIPCMD_POLL 5 + +#define RIPCMD_MAX 6 + +#ifdef RIPCMDS +char *ripcmds[RIPCMD_MAX] = { + "#0", "REQUEST", "RESPONSE", "TRACEON", "TRACEOFF" +}; +#endif + +#define HOPCNT_INFINITY 16 +#define MAXPACKETSIZE 512 /* max broadcast size */ +#define NETS_LEN ((MAXPACKETSIZE-sizeof(struct rip)) \ + / sizeof(struct netinfo) +1) + +#define INADDR_RIP_GROUP (u_int32_t)0xe0000009 /* 224.0.0.9 */ + + +/* Timer values used in managing the routing table. + * + * Complete tables are broadcast every SUPPLY_INTERVAL seconds. + * If changes occur between updates, dynamic updates containing only changes + * may be sent. When these are sent, a timer is set for a random value + * between MIN_WAITTIME and MAX_WAITTIME, and no additional dynamic updates + * are sent until the timer expires. + * + * Every update of a routing entry forces an entry's timer to be reset. + * After EXPIRE_TIME without updates, the entry is marked invalid, + * but held onto until GARBAGE_TIME so that others may see it, to + * "poison" the bad route. + */ +#define SUPPLY_INTERVAL 30 /* time to supply tables */ +#define MIN_WAITTIME 2 /* min sec until next flash updates */ +#define MAX_WAITTIME 5 /* max sec until flash update */ + +#define STALE_TIME 90 /* switch to a new gateway */ +#define EXPIRE_TIME 180 /* time to mark entry invalid */ +#define GARBAGE_TIME 240 /* time to garbage collect */ + +#ifdef __cplusplus +} +#endif +#endif /* !_ROUTED_H_ */ diff --git a/sbin/routed/rtquery/md5.c b/sbin/routed/rtquery/md5.c new file mode 100644 index 000000000000..a6fcf16a4821 --- /dev/null +++ b/sbin/routed/rtquery/md5.c @@ -0,0 +1,325 @@ +/* This code could be made a lot faster for PPP */ + +/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All + * rights reserved. + * + * License to copy and use this software is granted provided that it + * is identified as the "RSA Data Security, Inc. MD5 Message-Digest + * Algorithm" in all material mentioning or referencing this software + * or this function. + * + * License is also granted to make and use derivative works provided + * that such works are identified as "derived from the RSA Data + * Security, Inc. MD5 Message-Digest Algorithm" in all material + * mentioning or referencing the derived work. + * + * RSA Data Security, Inc. makes no representations concerning either + * the merchantability of this software or the suitability of this + * software for any particular purpose. It is provided "as is" + * without express or implied warranty of any kind. + * + * These notices must be retained in any copies of any part of this + * documentation and/or software. + */ + +#ident "$Revision: 1.2 $" + +#ifdef sgi +#include <strings.h> +#include <bstring.h> +#endif +#include <sys/types.h> + +#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; +extern void MD5Init(MD5_CTX*); +extern void MD5Update(MD5_CTX*, u_char*, u_int); +extern void MD5Final(u_char[MD5_DIGEST_LEN], MD5_CTX*); + +/* UINT4 defines a four byte word */ +#define UINT4 u_int32_t + + +#define MD5_memcpy(d,s,l) bcopy(s,d,l) + +/* Constants for MD5Transform routine. + */ +#define S11 7 +#define S12 12 +#define S13 17 +#define S14 22 +#define S21 5 +#define S22 9 +#define S23 14 +#define S24 20 +#define S31 4 +#define S32 11 +#define S33 16 +#define S34 23 +#define S41 6 +#define S42 10 +#define S43 15 +#define S44 21 + +static void MD5Transform(UINT4[4], unsigned char [64]); +static void Encode(unsigned char *, UINT4 *, unsigned int); +static void Decode(UINT4 *, unsigned char *, unsigned int); + +static unsigned char PADDING[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + + +/* F, G, H and I are basic MD5 functions. + */ +#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) +#define G(x, y, z) (((x) & (z)) | ((y) & (~z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define I(x, y, z) ((y) ^ ((x) | (~z))) + +/* ROTATE_LEFT rotates x left n bits. + */ +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) + +/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. + * Rotation is separate from addition to prevent recomputation. + */ +#define FF(a, b, c, d, x, s, ac) { \ + (a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ +} +#define GG(a, b, c, d, x, s, ac) { \ + (a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ +} +#define HH(a, b, c, d, x, s, ac) { \ + (a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ +} +#define II(a, b, c, d, x, s, ac) { \ + (a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ +} + +/* MD5 initialization. Begins an MD5 operation, writing a new context. + */ +void +MD5Init(MD5_CTX *context) +{ + context->count[0] = context->count[1] = 0; + /* Load magic initialization constants. + */ + context->state[0] = 0x67452301; + context->state[1] = 0xefcdab89; + context->state[2] = 0x98badcfe; + context->state[3] = 0x10325476; +} + +/* MD5 block update operation. Continues an MD5 message-digest + * operation, processing another message block, and updating the + * context. + */ +void +MD5Update(MD5_CTX *context, /* context */ + unsigned char *input, /* input block */ + unsigned int inputLen) /* length of input block */ +{ + unsigned int i, indx, partLen; + + /* Compute number of bytes mod 64 */ + indx = ((context->count[0] >> 3) & 0x3F); + + /* Update number of bits */ + if ((context->count[0] += ((UINT4)inputLen << 3)) + < ((UINT4)inputLen << 3)) + context->count[1]++; + context->count[1] += ((UINT4)inputLen >> 29); + + partLen = 64 - indx; + + /* Transform as many times as possible. + */ + if (inputLen >= partLen) { + bcopy(input, &context->buffer[indx], partLen); + MD5Transform (context->state, context->buffer); + + for (i = partLen; i + 63 < inputLen; i += 64) + MD5Transform (context->state, &input[i]); + + indx = 0; + } else { + i = 0; + } + + /* Buffer remaining input */ + bcopy(&input[i], &context->buffer[indx], inputLen-i); +} + + +/* MD5 finalization. Ends an MD5 message-digest operation, writing the + the message digest and zeroizing the context. + */ +void +MD5Final(unsigned char digest[MD5_DIGEST_LEN], /* message digest */ + MD5_CTX *context) /* context */ +{ + unsigned char bits[8]; + unsigned int indx, padLen; + + /* Save number of bits */ + Encode (bits, context->count, 8); + + /* Pad out to 56 mod 64. + */ + indx = (unsigned int)((context->count[0] >> 3) & 0x3f); + padLen = (indx < 56) ? (56 - indx) : (120 - indx); + MD5Update(context, PADDING, padLen); + + /* Append length (before padding) */ + MD5Update(context, bits, 8); + + /* Store state in digest */ + Encode(digest, context->state, MD5_DIGEST_LEN); + + /* Zeroize sensitive information. + */ + bzero(context, sizeof(*context)); +} + + +/* MD5 basic transformation. Transforms state based on block. + */ +static void +MD5Transform(UINT4 state[4], + unsigned char block[64]) +{ + UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16]; + + Decode (x, block, 64); + + /* Round 1 */ + FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */ + FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */ + FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */ + FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */ + FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */ + FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */ + FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */ + FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */ + FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */ + FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */ + FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ + FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ + FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ + FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ + FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ + FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ + + /* Round 2 */ + GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */ + GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */ + GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ + GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */ + GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */ + GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */ + GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ + GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */ + GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */ + GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ + GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */ + GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */ + GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ + GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */ + GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */ + GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ + + /* Round 3 */ + HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */ + HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */ + HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ + HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ + HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */ + HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */ + HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */ + HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ + HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ + HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */ + HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */ + HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */ + HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */ + HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ + HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ + HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */ + + /* Round 4 */ + II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */ + II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */ + II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ + II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */ + II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ + II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */ + II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ + II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */ + II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */ + II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ + II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */ + II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ + II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */ + II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ + II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */ + II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */ + + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + + /* Zeroize sensitive information. + */ + bzero(x, sizeof(x)); +} + + +/* Encodes input (UINT4) into output (unsigned char). Assumes len is + * a multiple of 4. + */ +static void +Encode(unsigned char *output, + UINT4 *input, + unsigned int len) +{ + unsigned int i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) { + output[j] = (unsigned char)(input[i] & 0xff); + output[j+1] = (unsigned char)((input[i] >> 8) & 0xff); + output[j+2] = (unsigned char)((input[i] >> 16) & 0xff); + output[j+3] = (unsigned char)((input[i] >> 24) & 0xff); + } +} + + +/* Decodes input (unsigned char) into output (UINT4). Assumes len is + * a multiple of 4. + */ +static void +Decode (UINT4 *output, + unsigned char *input, + unsigned int len) +{ + unsigned int i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) + output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) | + (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24); +} diff --git a/sbin/routed/rtquery/rtquery.8 b/sbin/routed/rtquery/rtquery.8 index d77ca30b9c8f..7227bc41157f 100644 --- a/sbin/routed/rtquery/rtquery.8 +++ b/sbin/routed/rtquery/rtquery.8 @@ -9,10 +9,15 @@ .Op Fl np1 .Op Fl w Ar timeout .Op Fl r Ar addr +.Op Fl a Ar secret +.Ar host ... + +.Nm +.Op Fl t Ar op .Ar host ... .Sh DESCRIPTION .Nm Rtquery -is used to query a network routing daemon, +is used to query a RIP network routing daemon, .Xr routed 8 or .Xr gated 8 , @@ -74,12 +79,17 @@ By default, each host is given 15 seconds to respond. .It Fl r Ar addr ask about the route to destination .Em addr . +.It Fl a Ar passwd=XXX +.It Fl a Ar md5_passwd=XXX|KeyID +cause the query to be sent with the indicated cleartext or MD5 password. .It Fl t Ar op change tracing, where .Em op is one of the following. Requests from processes not running with UID 0 or on distant networks are generally ignored by the daemon except for a message in the system log. +.Xr gated 8 +is likely to ignore these debugging requests. .El .Bl -tag -width Ds -offset indent-two .It Em on=tracefile diff --git a/sbin/routed/rtquery/rtquery.c b/sbin/routed/rtquery/rtquery.c index 7d6913a048d7..0579141b667b 100644 --- a/sbin/routed/rtquery/rtquery.c +++ b/sbin/routed/rtquery/rtquery.c @@ -65,6 +65,17 @@ static char rcsid[] = "$NetBSD$"; #define _HAVE_SIN_LEN #endif +#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; +extern void MD5Init(MD5_CTX*); +extern void MD5Update(MD5_CTX*, u_char*, u_int); +extern void MD5Final(u_char[MD5_DIGEST_LEN], MD5_CTX*); + + #define WTIME 15 /* Time to wait for all responses */ #define STIME (250*1000) /* usec to wait for another response */ @@ -90,8 +101,10 @@ int pflag; /* play the `gated` game */ int ripv2 = 1; /* use RIP version 2 */ int wtime = WTIME; int rflag; /* 1=ask about a particular route */ -int trace; -int not_trace; +int trace, not_trace; /* send trace command or not */ +int auth_type = RIP_AUTH_NONE; +char passwd[RIP_AUTH_PW_LEN]; +u_long keyid; struct timeval sent; /* when query sent */ @@ -101,6 +114,7 @@ static void trace_loop(char *argv[]); static void query_loop(char *argv[], int); static int getnet(char *, struct netinfo *); static u_int std_mask(u_int); +static int parse_quote(char **, char *, char *, char *, int); int @@ -108,14 +122,14 @@ main(int argc, char *argv[]) { int ch, bsize; - char *p, *options, *value; + char *p, *options, *value, delim; OMSG.rip_nets[0].n_dst = RIP_DEFAULT; OMSG.rip_nets[0].n_family = RIP_AF_UNSPEC; OMSG.rip_nets[0].n_metric = htonl(HOPCNT_INFINITY); pgmname = argv[0]; - while ((ch = getopt(argc, argv, "np1w:r:t:")) != EOF) + while ((ch = getopt(argc, argv, "np1w:r:t:a:")) != EOF) switch (ch) { case 'n': not_trace = 1; @@ -208,6 +222,31 @@ main(int argc, } break; + case 'a': + not_trace = 1; + p = strchr(optarg,'='); + if (!p) + goto usage; + *p++ = '\0'; + if (!strcasecmp("passwd",optarg)) + auth_type = RIP_AUTH_PW; + else if (!strcasecmp("md5_passwd",optarg)) + auth_type = RIP_AUTH_MD5; + else + goto usage; + if (0 > parse_quote(&p,"|",&delim, + passwd,sizeof(passwd))) + goto usage; + if (auth_type == RIP_AUTH_MD5 + && delim == '|') { + keyid = strtoul(p+1,&p,0); + if (keyid > 255 || *p != '\0') + goto usage; + } else if (delim != '\0') { + goto usage; + } + break; + default: goto usage; } @@ -215,8 +254,9 @@ main(int argc, argc -= optind; if ((not_trace && trace) || argc == 0) { usage: fprintf(stderr, "%s: [-np1v] [-r tgt_rt] [-w wtime]" - " host1 [host2 ...]\n" - "or\t-t {on=filename|more|off} host1 host2 ...\n", + " [-a type=passwd] host1 [host2 ...]\n" + "or\t-t {on=filename|more|off|on=dump/../table}" + " host1 [host2 ...]\n", pgmname); exit(1); } @@ -294,6 +334,8 @@ trace_loop(char *argv[]) static void query_loop(char *argv[], int argc) { +# define NA0 (OMSG.rip_auths[0]) +# define NA2 (OMSG.rip_auths[2]) struct seen { struct seen *next; struct in_addr addr; @@ -304,11 +346,38 @@ query_loop(char *argv[], int argc) struct timeval now, delay; struct sockaddr_in from; int fromlen; + MD5_CTX md5_ctx; OMSG.rip_cmd = (pflag) ? RIPCMD_POLL : RIPCMD_REQUEST; if (ripv2) { OMSG.rip_vers = RIPv2; + if (auth_type == RIP_AUTH_PW) { + OMSG.rip_nets[1] = OMSG.rip_nets[0]; + NA0.a_family = RIP_AF_AUTH; + NA0.a_type = RIP_AUTH_PW; + bcopy(passwd, NA0.au.au_pw, + RIP_AUTH_PW_LEN); + omsg_len += sizeof(OMSG.rip_nets[0]); + + } else if (auth_type == RIP_AUTH_MD5) { + OMSG.rip_nets[1] = OMSG.rip_nets[0]; + NA0.a_family = RIP_AF_AUTH; + NA0.a_type = RIP_AUTH_MD5; + NA0.au.a_md5.md5_keyid = (int8_t)keyid; + NA0.au.a_md5.md5_auth_len = RIP_AUTH_PW_LEN; + NA0.au.a_md5.md5_seqno = 0; + NA0.au.a_md5.md5_pkt_len = sizeof(OMSG.rip_nets[1]); + NA2.a_family = RIP_AF_AUTH; + NA2.a_type = 1; + bcopy(passwd, NA2.au.au_pw, sizeof(NA2.au.au_pw)); + MD5Init(&md5_ctx); + MD5Update(&md5_ctx, (u_char *)&NA0, + (char *)(&NA2+1) - (char *)&NA0); + MD5Final(NA2.au.au_pw, &md5_ctx); + omsg_len += 2*sizeof(OMSG.rip_nets[0]); + } + } else { OMSG.rip_vers = RIPv1; OMSG.rip_nets[0].n_mask = 0; @@ -393,7 +462,7 @@ query_loop(char *argv[], int argc) } -/* sent do one host +/* send to one host */ static int out(char *host) @@ -432,6 +501,60 @@ out(char *host) /* + * Convert string to printable characters + */ +static char * +qstring(u_char *s, int len) +{ + static char buf[8*20+1]; + char *p; + u_char *s2, c; + + + for (p = buf; len != 0 && p < &buf[sizeof(buf)-1]; len--) { + c = *s++; + if (c == '\0') { + for (s2 = s+1; s2 < &s[len]; s2++) { + if (*s2 != '\0') + break; + } + if (s2 >= &s[len]) + goto exit; + } + + if (c >= ' ' && c < 0x7f && c != '\\') { + *p++ = c; + continue; + } + *p++ = '\\'; + switch (c) { + case '\\': + *p++ = '\\'; + break; + case '\n': + *p++= 'n'; + break; + case '\r': + *p++= 'r'; + break; + case '\t': + *p++ = 't'; + break; + case '\b': + *p++ = 'b'; + break; + default: + p += sprintf(p,"%o",c); + break; + } + } +exit: + *p = '\0'; + return buf; +} + + +/* * Handle an incoming RIP packet. */ static void @@ -447,7 +570,7 @@ rip_input(struct sockaddr_in *from, int i; struct hostent *hp; struct netent *np; - struct netauth *a; + struct netauth *na; if (nflag) { @@ -544,11 +667,33 @@ rip_input(struct sockaddr_in *from, } } else if (n->n_family == RIP_AF_AUTH) { - a = (struct netauth*)n; - (void)printf(" authentication type %d: ", - a->a_type); - for (i = 0; i < sizeof(a->au.au_pw); i++) - (void)printf("%02x ", a->au.au_pw[i]); + na = (struct netauth*)n; + if (na->a_type == RIP_AUTH_PW + && n == IMSG.rip_nets) { + (void)printf(" Password Authentication:" + " \"%s\"\n", + qstring(na->au.au_pw, + RIP_AUTH_PW_LEN)); + continue; + } + + if (na->a_type == RIP_AUTH_MD5 + && n == IMSG.rip_nets) { + (void)printf(" MD5 Authentication" + " len=%d KeyID=%d" + " seqno=%d" + " rsvd=%#x,%#x\n", + na->au.a_md5.md5_pkt_len, + na->au.a_md5.md5_keyid, + na->au.a_md5.md5_seqno, + na->au.a_md5.rsvd[0], + na->au.a_md5.rsvd[1]); + continue; + } + (void)printf(" Authentication type %d: ", + ntohs(na->a_type)); + for (i = 0; i < sizeof(na->au.au_pw); i++) + (void)printf("%02x ", na->au.au_pw[i]); putc('\n', stdout); continue; @@ -652,3 +797,64 @@ getnet(char *name, rt->n_mask = htonl(mask); return 1; } + + +/* strtok(), but honoring backslash + */ +static int /* -1=bad */ +parse_quote(char **linep, + char *delims, + char *delimp, + char *buf, + int lim) +{ + char c, *pc, *p; + + + pc = *linep; + if (*pc == '\0') + return -1; + + for (;;) { + if (lim == 0) + return -1; + c = *pc++; + if (c == '\0') + break; + + if (c == '\\' && pc != '\0') { + if ((c = *pc++) == 'n') { + c = '\n'; + } else if (c == 'r') { + c = '\r'; + } else if (c == 't') { + c = '\t'; + } else if (c == 'b') { + c = '\b'; + } else if (c >= '0' && c <= '7') { + c -= '0'; + if (*pc >= '0' && *pc <= '7') { + c = (c<<3)+(*pc++ - '0'); + if (*pc >= '0' && *pc <= '7') + c = (c<<3)+(*pc++ - '0'); + } + } + + } else { + for (p = delims; *p != '\0'; ++p) { + if (*p == c) + goto exit; + } + } + + *buf++ = c; + --lim; + } +exit: + if (delimp != 0) + *delimp = c; + *linep = pc-1; + if (lim != 0) + *buf = '\0'; + return 0; +} diff --git a/sbin/routed/trace.c b/sbin/routed/trace.c index 8a0b59ccb023..dabf4d146768 100644 --- a/sbin/routed/trace.c +++ b/sbin/routed/trace.c @@ -36,7 +36,7 @@ static char sccsid[] = "@(#)trace.c 8.1 (Berkeley) 6/5/93"; #elif defined(__NetBSD__) static char rcsid[] = "$NetBSD$"; #endif -#ident "$Revision: 1.13 $" +#ident "$Revision: 1.14 $" #define RIPCMDS #include "defs.h" @@ -62,6 +62,59 @@ char savetracename[MAXPATHLEN+1]; static void trace_dump(void); +/* convert string to printable characters + */ +static char * +qstring(u_char *s, int len) +{ + static char buf[8*20+1]; + char *p; + u_char *s2, c; + + + for (p = buf; len != 0 && p < &buf[sizeof(buf)-1]; len--) { + c = *s++; + if (c == '\0') { + for (s2 = s+1; s2 < &s[len]; s2++) { + if (*s2 != '\0') + break; + } + if (s2 >= &s[len]) + goto exit; + } + + if (c >= ' ' && c < 0x7f && c != '\\') { + *p++ = c; + continue; + } + *p++ = '\\'; + switch (c) { + case '\\': + *p++ = '\\'; + break; + case '\n': + *p++= 'n'; + break; + case '\r': + *p++= 'r'; + break; + case '\t': + *p++ = 't'; + break; + case '\b': + *p++ = 'b'; + break; + default: + p += sprintf(p,"%o",c); + break; + } + } +exit: + *p = '\0'; + return buf; +} + + /* convert IP address to a string, but not into a single buffer */ char * @@ -188,10 +241,11 @@ trace_off(char *p, ...) void trace_on(char *filename, - int trusted) + int initial) /* 1=setting from command line */ { struct stat stbuf; FILE *n_ftrace; + u_int old_tracelevel; /* Given a null filename when tracing is already on, increase the @@ -219,7 +273,7 @@ trace_on(char *filename, return; } - if (!trusted + if (!initial #ifdef _PATH_TRACE && (strncmp(filename, _PATH_TRACE, sizeof(_PATH_TRACE)-1) || strstr(filename,"../") @@ -252,7 +306,11 @@ trace_on(char *filename, if (new_tracelevel == 0) new_tracelevel = 1; - set_tracelevel(); + old_tracelevel = tracelevel; + set_tracelevel(initial); + + if (!initial && old_tracelevel == 0) + trace_dump(); } @@ -281,7 +339,7 @@ sigtrace_off(int s) * actions + packets + contents */ void -set_tracelevel(void) +set_tracelevel(int initial) { static char *off_msgs[MAX_TRACELEVEL] = { "Tracing actions stopped", @@ -304,7 +362,8 @@ set_tracelevel(void) return; } } - while (new_tracelevel != tracelevel) { + + for (; new_tracelevel != tracelevel; tracelevel++) { if (new_tracelevel < tracelevel) { if (--tracelevel == 0) trace_off(tracelevel_pat, off_msgs[0]); @@ -317,7 +376,8 @@ set_tracelevel(void) else ftrace = stdout; } - tmsg(tracelevel_pat, on_msgs[tracelevel++]); + if (!initial || tracelevel+1 == new_tracelevel) + tmsg(tracelevel_pat, on_msgs[tracelevel]); } } tracelevel_pat = "%s\n"; @@ -377,10 +437,11 @@ static struct bits if_bits[] = { }; static struct bits is_bits[] = { + { IS_ALIAS, 0, "ALIAS" }, { IS_SUBNET, 0, "" }, - { IS_REMOTE, 0, "REMOTE" }, + { IS_REMOTE, (IS_NO_RDISC + | IS_BCAST_RDISC), "REMOTE" }, { IS_PASSIVE, (IS_NO_RDISC - | IS_BCAST_RDISC | IS_NO_RIP | IS_NO_SUPER_AG | IS_PM_RDISC @@ -389,10 +450,10 @@ static struct bits is_bits[] = { { IS_CHECKED, 0, "" }, { IS_ALL_HOSTS, 0, "" }, { IS_ALL_ROUTERS, 0, "" }, - { IS_RIP_QUERIED, 0, "" }, + { IS_DISTRUST, 0, "DISTRUST" }, { IS_BROKE, IS_SICK, "BROKEN" }, { IS_SICK, 0, "SICK" }, - { IS_ACTIVE, 0, "ACTIVE" }, + { IS_DUP, 0, "DUPLICATE" }, { IS_NEED_NET_SYN, 0, "" }, { IS_NO_AG, IS_NO_SUPER_AG, "NO_AG" }, { IS_NO_SUPER_AG, 0, "NO_SUPER_AG" }, @@ -414,7 +475,7 @@ static struct bits is_bits[] = { { IS_NO_ADV_OUT, IS_BCAST_RDISC, "NO_RDISC_ADV" }, { IS_ADV_OUT, 0, "RDISC_ADV" }, { IS_BCAST_RDISC, 0, "BCAST_RDISC" }, - { IS_PM_RDISC, 0, "PM_RDISC" }, + { IS_PM_RDISC, 0, "" }, { 0, 0, "%#x"} }; @@ -495,15 +556,18 @@ trace_if(char *act, return; lastlog(); - (void)fprintf(ftrace, "%s interface %-4s ", act, ifp->int_name); + (void)fprintf(ftrace, "%-3s interface %-4s ", act, ifp->int_name); (void)fprintf(ftrace, "%-15s-->%-15s ", naddr_ntoa(ifp->int_addr), - addrname(htonl((ifp->int_if_flags & IFF_POINTOPOINT) - ? ifp->int_dstaddr - : ifp->int_net), + addrname(((ifp->int_if_flags & IFF_POINTOPOINT) + ? ifp->int_dstaddr + : htonl(ifp->int_net)), ifp->int_mask, 1)); if (ifp->int_metric != 0) (void)fprintf(ftrace, "metric=%d ", ifp->int_metric); + if (!IS_RIP_OUT_OFF(ifp->int_state) + && ifp->int_d_metric != 0) + (void)fprintf(ftrace, "fake_default=%d ", ifp->int_d_metric); trace_bits(if_bits, ifp->int_if_flags, 0); trace_bits(is_bits, ifp->int_state, 0); (void)fputc('\n',ftrace); @@ -605,6 +669,7 @@ trace_act(char *p, ...) lastlog(); va_start(args, p); vfprintf(ftrace, p, args); + (void)fputc('\n',ftrace); } @@ -621,6 +686,7 @@ trace_pkt(char *p, ...) lastlog(); va_start(args, p); vfprintf(ftrace, p, args); + (void)fputc('\n',ftrace); } @@ -762,10 +828,14 @@ walk_trace(struct radix_node *rn, static void trace_dump(void) { + struct interface *ifp; + if (ftrace == 0) return; lastlog(); + for (ifp = ifnet; ifp != 0; ifp = ifp->int_next) + trace_if("", ifp); (void)rn_walktree(rhead, walk_trace, 0); } @@ -778,8 +848,8 @@ trace_rip(char *dir1, char *dir2, int size) /* total size of message */ { struct netinfo *n, *lim; - struct netauth *a; - int i; +# define NA (msg->rip_auths) + int i, seen_route; if (!TRACEPACKETS || ftrace == 0) return; @@ -803,17 +873,20 @@ trace_rip(char *dir1, char *dir2, if (!TRACECONTENTS) return; + seen_route = 0; switch (msg->rip_cmd) { case RIPCMD_REQUEST: case RIPCMD_RESPONSE: n = msg->rip_nets; lim = (struct netinfo *)((char*)msg + size); for (; n < lim; n++) { - if (n->n_family == RIP_AF_UNSPEC + if (!seen_route + && n->n_family == RIP_AF_UNSPEC && ntohl(n->n_metric) == HOPCNT_INFINITY - && n+1 == lim - && n == msg->rip_nets - && msg->rip_cmd == RIPCMD_REQUEST) { + && msg->rip_cmd == RIPCMD_REQUEST + && (n+1 == lim + || (n+2 == lim + && (n+1)->n_family == RIP_AF_AUTH))) { (void)fputs("\tQUERY ", ftrace); if (n->n_dst != 0) (void)fprintf(ftrace, "%s ", @@ -822,32 +895,57 @@ trace_rip(char *dir1, char *dir2, (void)fprintf(ftrace, "mask=%#x ", (u_int)ntohl(n->n_mask)); if (n->n_nhop != 0) - (void)fprintf(ftrace, " nhop=%s ", + (void)fprintf(ftrace, "nhop=%s ", naddr_ntoa(n->n_nhop)); if (n->n_tag != 0) - (void)fprintf(ftrace, "tag=%#x", + (void)fprintf(ftrace, "tag=%#x ", ntohs(n->n_tag)); (void)fputc('\n',ftrace); continue; } if (n->n_family == RIP_AF_AUTH) { - a = (struct netauth*)n; + if (NA->a_type == RIP_AUTH_PW + && n == msg->rip_nets) { + (void)fprintf(ftrace, "\tPassword" + " Authentication:" + " \"%s\"\n", + qstring(NA->au.au_pw, + RIP_AUTH_PW_LEN)); + continue; + } + + if (NA->a_type == RIP_AUTH_MD5 + && n == msg->rip_nets) { + (void)fprintf(ftrace, + "\tMD5 Authentication" + " len=%d KeyID=%u" + " seqno=%u" + " rsvd=%#x,%#x\n", + NA->au.a_md5.md5_pkt_len, + NA->au.a_md5.md5_keyid, + NA->au.a_md5.md5_seqno, + NA->au.a_md5.rsvd[0], + NA->au.a_md5.rsvd[1]); + continue; + } (void)fprintf(ftrace, - "\tAuthentication type %d: ", - ntohs(a->a_type)); + "\tAuthentication" + " type %d: ", + ntohs(NA->a_type)); for (i = 0; - i < sizeof(a->au.au_pw); + i < sizeof(NA->au.au_pw); i++) (void)fprintf(ftrace, "%02x ", - a->au.au_pw[i]); + NA->au.au_pw[i]); (void)fputc('\n',ftrace); continue; } + seen_route = 1; if (n->n_family != RIP_AF_INET) { (void)fprintf(ftrace, - "\t(af %d) %-18s mask=%#x", + "\t(af %d) %-18s mask=%#x ", ntohs(n->n_family), naddr_ntoa(n->n_dst), (u_int)ntohl(n->n_mask)); |