Skip site navigation (1)Skip section navigation (2)
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>