From owner-freebsd-hackers@FreeBSD.ORG Sat Apr 10 01:44:30 2004 Return-Path: Delivered-To: freebsd-hackers@freebsd.org Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125]) by hub.freebsd.org (Postfix) with ESMTP id E838616A4CE for ; Sat, 10 Apr 2004 01:44:29 -0700 (PDT) Received: from arginine.spc.org (arginine.spc.org [195.206.69.236]) by mx1.FreeBSD.org (Postfix) with ESMTP id 1DCB243D45 for ; Sat, 10 Apr 2004 01:44:29 -0700 (PDT) (envelope-from bms@spc.org) Received: from localhost (localhost [127.0.0.1]) by arginine.spc.org (Postfix) with ESMTP id 194096542F; Sat, 10 Apr 2004 09:44:28 +0100 (BST) Received: from arginine.spc.org ([127.0.0.1]) by localhost (arginine.spc.org [127.0.0.1]) (amavisd-new, port 10024) with LMTP id 86773-01; Sat, 10 Apr 2004 09:44:27 +0100 (BST) Received: from empiric.dek.spc.org (82-147-17-88.dsl.uk.rapidplay.com [82.147.17.88]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (No client certificate requested) by arginine.spc.org (Postfix) with ESMTP id 771DF653DF; Sat, 10 Apr 2004 09:44:26 +0100 (BST) Received: by empiric.dek.spc.org (Postfix, from userid 1001) id C464660EE; Sat, 10 Apr 2004 09:44:25 +0100 (BST) Date: Sat, 10 Apr 2004 09:44:25 +0100 From: Bruce M Simpson To: Vlad GALU Message-ID: <20040410084425.GT710@empiric.dek.spc.org> References: <20040409195541.I31321@qvnfcne.eqfarg.eb> Mime-Version: 1.0 Content-Type: multipart/signed; micalg=pgp-sha1; protocol="application/pgp-signature"; boundary="7vAdt9JsdkkzRPKN" Content-Disposition: inline In-Reply-To: <20040409195541.I31321@qvnfcne.eqfarg.eb> cc: hackers@freebsd.org Subject: Re: passing messages to routing sockets X-BeenThere: freebsd-hackers@freebsd.org X-Mailman-Version: 2.1.1 Precedence: list List-Id: Technical Discussions relating to FreeBSD List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sat, 10 Apr 2004 08:44:30 -0000 --7vAdt9JsdkkzRPKN Content-Type: multipart/mixed; boundary="OOq1TgGhe8eTwFBO" Content-Disposition: inline --OOq1TgGhe8eTwFBO Content-Type: text/plain; charset=us-ascii Content-Disposition: inline On Fri, Apr 09, 2004 at 08:02:30PM +0300, Vlad GALU wrote: > I understand that in order to add a new route to the routing > table, one must fill a buffer with an ifa_msghdr header, followed by three > socked adddress structures, representing the destination net/ip, the > netmask and the gateway. [routing table suckiness snipped] > Could anyone provide a quick/small example on how to fill this > member ? I don't really understand what I should write into it. It's a sucky one and I had to stare at src/sbin/route/* for a while til the penny dropped. The netmasks in particular are packed in in a format which isn't immediately obvious. Have a look at the attached code, in particular, reply_rtmsg_resolve(). Also have a look at Section 19.11 of TCP/IP Illustrated Volume 2, and Section 18.9 of UNIX Network Programming 3e Vol 1. The API sucks, and it's something we'd like to change in future. Regards, BMS --OOq1TgGhe8eTwFBO Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="rtmhack.c" /* $FreeBSD$ */ /* * This is a hack to demonstrate the concept of hooking for the * RTM_RESOLVE message being sent from the FreeBSD routing code, * as a means of looking up routes on demand using a routing protocol * such as AODV. * This code will probably be vastly cleaned up and tested more thoroughly * before being used as the basis for a user-space BSD AODV implementation. */ /* * Copyright (c) 2003 Bruce M. Simpson * 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 Bruce M. Simpson. * 4. Neither the name of Bruce M. Simpson nor the names of co- * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bruce M. Simpson 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 Bruce M. Simpson 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include union sockunion { struct sockaddr sa; struct sockaddr_in sin; struct sockaddr_dl sdl; struct sockaddr_storage ss; }; typedef union sockunion sockunion_t; 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 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); /* * We check for the existence of ifname. */ #if 1 #define _IFNAME "disc1" #else #define _IFNAME "lo0" #endif int rtsock = -1; int created = 0; char *ifname = _IFNAME; void sighand_term(int sig) { /* * Destroying an interface is sufficient to delete the routes * pointing to it. */ if (created) destroy_if(ifname); exit(EXIT_SUCCESS); } void setup_signals(void) { struct sigaction sa, osa; sa.sa_handler = sighand_term; sa.sa_flags = 0; sigemptyset(&sa.sa_mask); sigaction(SIGTERM, &sa, &osa); sigaction(SIGINT, &sa, &osa); sigaction(SIGQUIT, &sa, &osa); sigaction(SIGKILL, &sa, &osa); } int main(int argc, char *argv[]) { int n; int bits; char msg[2048]; 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); /* Parse network argument */ if ((argc != 2) || (inet_cidr_aton(argv[1], &sin.sin_addr, &bits) != 1)) usage(); setup_signals(); /* Open routing socket */ rtsock = socket(PF_ROUTE, SOCK_RAW, 0); if (rtsock == -1) err(EX_OSERR, "socket"); /* Check that the target interface exists; create it if it doesn't. */ if (if_nametoindex(ifname) == 0) { warnx("interface %s does not exist, creating.", ifname); create_if(ifname); created = 1; add_xresolve_route(ifname, &sin, bits); } /* Routing event loop */ for (;;) { n = read(rtsock, msg, sizeof(msg)); handle_rtmsg((struct rt_msghdr *)msg, n); } if (rtsock != -1) close(rtsock); exit (EXIT_SUCCESS); } void usage(void) { fprintf(stderr, "usage: rtmhack \n" " 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)); } /* * 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); } /* * Given the prefix length of an IPv4 CIDR network address, * fill out a sockaddr_in structure accordingly for use with * BSD routing code. * * Return the value of the sin_len member as a hint. */ int inet_makenetmask(int bits, struct sockaddr_in *so_mask) { char *cp; unsigned long mask; int len; const int maxbits = 32; memset(so_mask, 0, sizeof(struct sockaddr_in)); mask = 0xFFFFFFFF << (maxbits - bits); so_mask->sin_addr.s_addr = htonl(mask); /* count number of bytes in mask containing set bits */ cp = (char *)(&so_mask->sin_addr + 1); while (*--cp == 0 && cp > (char *)&so_mask) ; so_mask->sin_len = len = 1 + cp - (char *)&so_mask; return (len); } /* * Bind an cloning XRESOLVE route, for the given network/host, * to a named interface. * * The packing of the rtm message is all important. The kernel * expects it in a certain way. This routine seems to work but * 'route monitor' is reporting some junk at the end of the netmask. * * Return 0 if successful, or -1 if an error occurred. */ int add_xresolve_route(char *ifname, struct sockaddr_in *so_dst, int bits) { #define ROUNDUP(a) \ ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) #define NEXTADDR(cp, l, u) \ do { \ l = (u)->sa.sa_len; \ (l) = ROUNDUP(l); \ memmove((cp), (u), (l)); \ (cp) += (l); \ } while (0) /* */ struct { struct rt_msghdr rtm; sockunion_t addrs[RTAX_MAX]; } r; sockunion_t so_iface; sockunion_t so_mask; int rlen, len, masklen; char *cp; cp = (char *)&r.addrs[0]; masklen = rlen = len = 0; memset(&r, 0, sizeof(r)); memset(&so_iface, 0, sizeof(so_iface)); memset(&so_mask, 0, sizeof(so_mask)); 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 | RTA_NETMASK; if2sockaddr(ifname, &so_iface.sdl); masklen = inet_makenetmask(bits, &so_mask.sin); NEXTADDR(cp, len, (sockunion_t *)so_dst); NEXTADDR(cp, len, &so_iface); NEXTADDR(cp, len, &so_mask); r.rtm.rtm_msglen = len = cp - (char *)&r; rlen = write(rtsock, &r, len); if (rlen < 0) warn("write"); return ((rlen > 0) ? 0 : -1); #undef NEXTADDR #undef ROUNDUP } /* * 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) { struct sockaddr_in *sin; struct sockaddr *sa; void *sp; /* * 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) { struct { struct rt_msghdr rtm; struct sockaddr addrs[RTAX_MAX]; } r; struct sockaddr_dl sdl; int len; 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); } --OOq1TgGhe8eTwFBO-- --7vAdt9JsdkkzRPKN Content-Type: application/pgp-signature Content-Disposition: inline -----BEGIN PGP SIGNATURE----- Comment: '' iD8DBQFAd7PpueUpAYYNtTsRAvYjAJ9Yr+IRLqOTkpkTyQl4CY047WRY+gCgnUiO kMMDaCrFJwyKefYUOObxsjM= =np2+ -----END PGP SIGNATURE----- --7vAdt9JsdkkzRPKN--