Date: Sat, 16 Aug 2003 09:45:04 +0100 From: Bruce M Simpson <bms@spc.org> To: freebsd-net@freebsd.org Subject: Re: Netmasks and PF_ROUTE rockets Message-ID: <20030816084504.GC20931@spc.org>
next in thread | raw e-mail | index | archive | help
--pWyiEgJYm5f9v55/ Content-Type: text/plain; charset=us-ascii Content-Disposition: inline Oops. Resend. --pWyiEgJYm5f9v55/ Content-Type: message/rfc822 Content-Disposition: inline Date: Sat, 16 Aug 2003 09:40:30 +0100 From: Bruce M Simpson <bms@spc.org> To: Ruslan Ermilov <ru@FreeBSD.org> Cc: freebsd-net@spc.org Subject: Re: Netmasks and PF_ROUTE rockets Message-ID: <20030816084030.GA26787@spc.org> References: <20030814185342.GK1409@spc.org> <20030815135238.GF58477@sunbay.com> Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="h31gzZEtNLTqOjlF" Content-Disposition: inline In-Reply-To: <20030815135238.GF58477@sunbay.com> User-Agent: Mutt/1.4.1i Organization: SPC --h31gzZEtNLTqOjlF Content-Type: text/plain; charset=us-ascii Content-Disposition: inline On Fri, Aug 15, 2003 at 04:52:38PM +0300, Ruslan Ermilov wrote: > > I have actually managed to panic the 5.1 kernel by passing a wrongly > > formatted routing message in. > I'd be interested in the code that panics the kernel. (To fix > the latter.) Attached. DDB reported the panic address as being in arp_rtrequest() which isn't obvious from the prepended gdb backtrace. Note that I've since started filling out the RTM_ADD message in the same way as route(8) does with its NEXTADDR() macro in the original branch of this code; this works and gets the netmask in the routing table correctly. BMS --h31gzZEtNLTqOjlF Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="addr.c.panic" /* * THIS CODE PANICS a 5.1-RELEASE KERNEL * * Very interestingly, too: #12 0xc023ad2e in rtrequest1 (req=1, info=0xd1d31b24, ret_nrt=0xd1d31b10) at ../../../net/route.c:739 #13 0xc023bae6 in route_output (m=0xc0ee3c00, so=0xc2ac7600) at ../../../net/rtsock.c:338 #14 0xc0239c6d in raw_usend (so=0x0, flags=0, m=0x0, nam=0x0, control=0x0, td=0xc2647260) at ../../../net/raw_usrreq.c:257 #15 0xc023b855 in rts_send (so=0x0, flags=0, m=0x0, nam=0x0, control=0x0, td=0x0) at ../../../net/rtsock.c:233 #16 0xc02011ad in sosend (so=0xc2ac7600, addr=0x0, uio=0xd1d31c70, top=0xc0ee3c00, control=0x0, flags=0, td=0xc2647260) at ../../../kern/uipc_socket.c:712 #17 0xc01f06dd in soo_write (fp=0x0, uio=0xd1d31c70, active_cred=0xc2af7380, flags=0, td=0xc2647260) at ../../../kern/sys_socket.c:107 #18 0xc01ea3b8 in dofilewrite (td=0xc2647260, fp=0xc26e6294, fd=0, buf=0xbfbff550, nbyte=0, offset=0, flags=0) at file.h:239 #19 0xc01ea1f9 in write (td=0xc2647260, uap=0xd1d31d10) at ../../../kern/sys_generic.c:328 #20 0xc031da8a in syscall */ #include <sys/param.h> #include <sys/file.h> #include <sys/socket.h> #include <sys/sysctl.h> #include <sys/ioctl.h> #include <sys/sysctl.h> #include <sys/types.h> #include <net/if.h> #include <net/if_var.h> #include <net/if_mib.h> #include <net/if_types.h> #include <net/if_dl.h> #include <net/route.h> #include <netinet/in.h> #include <netinet/if_ether.h> #include <arpa/inet.h> #include <netdb.h> #include <ctype.h> #include <err.h> #include <errno.h> #include <paths.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sysexits.h> #include <unistd.h> #include <ifaddrs.h> #define _IFNAME "lo0" void usage(void); int add_xresolve_route(char *ifname, struct sockaddr_in *sin, int bits); int inet_cidr_aton(char *s, struct in_addr *pin, int *bits); int get_if_index(char *ifname); int create_if(char *ifname); int destroy_if(char *ifname); int if2sockaddr(char *ifname, struct sockaddr_dl *sdl); int handle_rtmsg(struct rt_msghdr *rtm, int msglen); int handle_rtmsg_resolve(struct rt_msghdr *rtm, int msglen); int reply_rtmsg_resolve(struct sockaddr_in *sin); int rtsock; int main(int argc, char *argv[]) { int bits; struct sockaddr_in sin; if (geteuid() != 0) errx(1, "must be root to alter routing table"); memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_len = sizeof(sin); if ((argc != 2) || (inet_cidr_aton(argv[1], &sin.sin_addr, &bits) != 1)) usage(); rtsock = socket(PF_ROUTE, SOCK_RAW, 0); if (rtsock == -1) err(EX_OSERR, "socket"); add_xresolve_route(_IFNAME, &sin, bits); if (rtsock != -1) close(rtsock); exit (EXIT_SUCCESS); } void usage(void) { fprintf(stderr, "usage: rtmhack <testnet>\n" "<testnet> specifies the test network in CIDR notation\n"); exit(EXIT_FAILURE); } /* * Like inet_aton(), but handle an optional CIDR prefix. */ int inet_cidr_aton(char *s, struct in_addr *pin, int *bits) { char *q; q = NULL; *bits = 32; if ((q = strchr(s, '/')) != NULL) { *bits = strtoul(q+1, 0, 0); *q = '\0'; } return (inet_aton(s, pin)); } /* * Return the index of a named interface in the MIB, or -1 if it does * not exist. */ int get_if_index(char *ifname) { int name[6]; int i; size_t len; int maxifno; int indx; struct ifmibdata ifmd; int ifnamelen; ifnamelen = strlen(ifname); indx = -1; name[0] = CTL_NET; name[1] = PF_LINK; name[2] = NETLINK_GENERIC; name[3] = IFMIB_SYSTEM; name[4] = IFMIB_IFCOUNT; len = sizeof(maxifno); if (sysctl(name, 5, &maxifno, &len, 0, 0) < 0) err(1, "sysctl net.link.generic.system.ifcount"); name[3] = IFMIB_IFDATA; name[5] = IFDATA_GENERAL; len = sizeof(ifmd); for (i = 1; i <= maxifno; i++) { name[4] = i; if (sysctl(name, 6, &ifmd, &len, 0, 0) < 0) { if (errno == ENOENT) continue; err(1, "sysctl"); } if (strncmp(ifname, ifmd.ifmd_name, ifnamelen) == 0) { indx = i; break; } } return (indx); } /* * create an instance of a named clonable interface. * Return 0 if successful, or -1 if an error occurred. */ int create_if(char *ifname) { int s, retval; struct ifreq ifr; retval = 0; s = socket(AF_INET, SOCK_DGRAM, 0); if (s == -1) err(1, "socket"); memset(&ifr, 0, sizeof(ifr)); (void) strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); if (ioctl(s, SIOCIFCREATE, &ifr) < 0) { retval = -1; warn("SIOCIFCREATE"); } close(s); return (retval); } /* * destroy an instance of a named clonable interface. * Return 0 if successful, or -1 if an error occurred. */ int destroy_if(char *ifname) { int s, retval; struct ifreq ifr; retval = 0; s = socket(AF_INET, SOCK_DGRAM, 0); if (s == -1) err(1, "socket"); memset(&ifr, 0, sizeof(ifr)); (void) strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); if (ioctl(s, SIOCIFDESTROY, &ifr) < 0) { retval = -1; warn("SIOCIFDESTROY"); } close(s); return (retval); } /* * Copy the sockaddr_dl structure corresponding to the named interface * into the structure pointed to by sdl. * Returns 0 if successful, or -1 if the structure found was not valid. */ int if2sockaddr(char *ifname, struct sockaddr_dl *sdl) { struct ifaddrs *ifap, *ifa; struct sockaddr_dl *isdl; if (getifaddrs(&ifap)) err(1, "getifaddrs"); isdl = NULL; for (ifa = ifap; ifa; ifa = ifa->ifa_next) { if (ifa->ifa_addr->sa_family != AF_LINK) continue; if (strcmp(ifname, ifa->ifa_name)) continue; isdl = (struct sockaddr_dl *)ifa->ifa_addr; } if (isdl) memcpy(sdl, isdl, isdl->sdl_len); return ((isdl != NULL) ? 0 : -1); } /* * Bind an cloning XRESOLVE route, for the given network/host, * to a named interface. * Return 0 if successful, or -1 if an error occurred. * * XXX there is a glaring bug here - the netmask is not set correctly * when adding the route. what could be the problem? this is a real mess. */ int add_xresolve_route(char *ifname, struct sockaddr_in *sin, int bits) { struct { struct rt_msghdr rtm; struct sockaddr_storage addrs[RTAX_MAX]; } r; union { struct sockaddr sa; struct sockaddr_in sin; } so_mask; struct sockaddr_dl sdl; char *cp; unsigned long mask; int len; const int maxbits = 32; /* RTM_ADD: Add Route: len 172, pid: 31485, seq 1, errno 0 flags:<UP,DONE,STATIC> locks: inits: sockaddrs: <DST,GATEWAY,NETMASK> 1.0.0.0 disc0 (0) 0 ff */ memset(&r, 0, sizeof(r)); r.rtm.rtm_version = RTM_VERSION; r.rtm.rtm_type = RTM_ADD; r.rtm.rtm_pid = getpid(); r.rtm.rtm_seq = 0; r.rtm.rtm_flags = RTF_XRESOLVE | RTF_CLONING | RTF_UP; r.rtm.rtm_addrs = RTA_DST | RTA_GATEWAY; #if 1 /* * netmask sockaddrs are very tricky to initialize correctly. * the fix below suggested by Bill Fenner <fenestro@freebsd.org> * ...only this still doesn't work. * actually this encoding is correct and is being reported * correctly when we shuffle our arguments somewhat. * this points towards incorrect padding somewhere. * XXX netmask must come after addr? try it */ r.rtm.rtm_addrs |= RTA_NETMASK; memset(&so_mask, 0, sizeof(so_mask)); so_mask.sa.sa_len = 0; so_mask.sa.sa_family = 0; mask = 0xffffffff << (maxbits - bits); so_mask.sin.sin_addr.s_addr = htonl(mask); /* * Handle variable length netmasks correctly, by counting * bytes set in such a netmask. */ cp = (char *)(&so_mask.sin.sin_addr + 1); while (*--cp == 0 && cp > (char *)&so_mask) ; so_mask.sa.sa_len = 1 + cp - (char *)&so_mask; #endif if2sockaddr(ifname, &sdl); memmove(&r.addrs[0], sin, sin->sin_len); memmove(&r.addrs[1], &sdl, sdl.sdl_len); memmove(&r.addrs[2], &so_mask, sizeof(so_mask)); #if 1 r.rtm.rtm_msglen = sizeof(r.rtm) + sizeof(struct sockaddr_storage)*2; len = write(rtsock, &r, r.rtm.rtm_msglen); #else r.rtm.rtm_msglen = sizeof(r); len = write(rtsock, &r, r.rtm.rtm_msglen); #endif if (len != r.rtm.rtm_msglen) warn("write"); return ((len > 0) ? 0 : -1); } /* * routing socket message dispatcher */ int handle_rtmsg(struct rt_msghdr *rtm, int msglen) { if (rtm->rtm_version != RTM_VERSION) { (void) printf("bad routing message version %d\n", rtm->rtm_version); return (-1); } switch (rtm->rtm_type) { case RTM_RESOLVE: (void) printf("rtm_type %d: RTM_RESOLVE\n", rtm->rtm_type); handle_rtmsg_resolve(rtm, msglen); break; default: (void) printf("rtm_type %d: ignored\n", rtm->rtm_type); } return (0); } /* * Dispatch routine for RTM_RESOLVE routing messages. * Return 0 if successful; otherwise, return -1 if an error occurred. */ int handle_rtmsg_resolve(struct rt_msghdr *rtm, int msglen) { void *sp; struct sockaddr *sa; struct sockaddr_in *sin; /* * ignore messages from ourselves */ if (rtm->rtm_pid == getpid()) { printf("heard own message, ignoring\n"); return (0); } printf("rtm_index: %04x rtm_addrs: %08x\n", rtm->rtm_index, rtm->rtm_addrs); /* * The message must contain the address for which a route is * being requested, otherwise it is invalid. */ if (!(rtm->rtm_addrs & RTA_DST)) { warnx("RTM_RESOLVE message does not contain destination"); return (-1); } sa = sp = (rtm + 1); if (sa->sa_family != AF_INET) { warnx("RTM_RESOLVE contains non-AF_INET destination %d", sa->sa_family); return (-1); } sin = (struct sockaddr_in *)sa; printf("route requested for %s\n", inet_ntoa(sin->sin_addr)); /* * XXX: Should check if the requested destination is within the * network prefix specified on the command line. */ reply_rtmsg_resolve(sin); printf("route resolved for %s\n", inet_ntoa(sin->sin_addr)); return (0); } /* * Modify a given route in response to an RTM_RESOLVE message from the kernel. * Return 0 if successful; otherwise, return -1. */ int reply_rtmsg_resolve(struct sockaddr_in *sin) { int len; struct { struct rt_msghdr rtm; struct sockaddr addrs[RTAX_MAX]; } r; struct sockaddr_dl sdl; memset(&r, 0, sizeof(r)); r.rtm.rtm_version = RTM_VERSION; r.rtm.rtm_type = RTM_CHANGE; r.rtm.rtm_pid = getpid(); r.rtm.rtm_seq = 0; if2sockaddr("lo0", &sdl); memcpy(&r.addrs[RTAX_DST], sin, sin->sin_len); memcpy(&r.addrs[RTAX_GATEWAY], &sdl, sdl.sdl_len); memset(&r.addrs[RTAX_IFP], 0, sizeof(r.addrs[RTAX_IFP])); memset(&r.addrs[RTAX_IFA], 0, sizeof(r.addrs[RTAX_IFA])); r.rtm.rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_IFP | RTA_IFA; r.rtm.rtm_flags = RTF_DONE; r.rtm.rtm_msglen = sizeof(r); len = write(rtsock, &r, r.rtm.rtm_msglen); if (len != r.rtm.rtm_msglen) warn("write"); return ((len > 0) ? 0 : -1); } --h31gzZEtNLTqOjlF-- --pWyiEgJYm5f9v55/--
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20030816084504.GC20931>