Module Name: src Committed By: pooka Date: Mon Mar 29 02:01:47 UTC 2010
Added Files: src/share/examples/rump/virtual_ip_router: README.txt rumprouter.c Log Message: Add an example for a rump router cluster setup along with a README. some contributions to the code from Martti Kuparinen To generate a diff of this commit: cvs rdiff -u -r0 -r1.1 src/share/examples/rump/virtual_ip_router/README.txt \ src/share/examples/rump/virtual_ip_router/rumprouter.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Added files: Index: src/share/examples/rump/virtual_ip_router/README.txt diff -u /dev/null src/share/examples/rump/virtual_ip_router/README.txt:1.1 --- /dev/null Mon Mar 29 02:01:47 2010 +++ src/share/examples/rump/virtual_ip_router/README.txt Mon Mar 29 02:01:47 2010 @@ -0,0 +1,107 @@ + $NetBSD: README.txt,v 1.1 2010/03/29 02:01:47 pooka Exp $ + +Using rump it is possible to build a router test setup consisting +of thousands of NetBSD IP stacks within a single host OS, one +networking stack per application process. Each IP stack instance +has its own set of interfaces, addresses and routing tables. These +instances may or may not share the same code, i.e. it is possible +to do compatibility testing of new features. The advantage over +using full-fledged virtual OS setups (qemu, Xen, etc.) is scalability: +the rump IP router base runtime takes less than 500kB of memory +per instance. + +The code is _ONLY AN EXAMPLE_ as opposed a fully featured test kit. +Some code tweaking is probably required to make this do what you +want. Usage examples follow. + +To use one single rump networking stack instance with access to +two real networks, you need tap and bridge on the host system (yes, +this involves some memory copies. the resulting router setup can +still saturate a GigE, though. it should not be difficult to bring +performance to be ~the same as an in-kernel stack, but haven't +managed to implement that yet). + +Anyway, the following can be done with the current code: + +/* + * Usage: + * + * # ifconfig yourrealif0 up + * # ifconfig tap0 create + * # ifconfig tap0 up + * # ifconfig bridge0 create + * # brconfig bridge0 add tap0 add yourrealif0 + * # brconfig bridge0 up + * # + * # ifconfig yourrealif1 up + * # ifconfig tap1 create + * # ifconfig tap1 up + * # ifconfig bridge1 create + * # brconfig bridge1 add tap1 add yourrealif1 + * # brconfig bridge1 up + * # + * # ./router virt0 192.168.1.1 255.255.255.0 192.168.1.255 \ + * # virt1 192.168.2.1 255.255.255.0 192.168.2.255 + * + * This will bind virtN to tapN and act as a router. + */ + +As brilliant ascii art, it would look something like this: + + network network + ^ ^ + | | + /----v-------------\ /------------v----\ + kernel | realif0 <-> tap0 | | tap1 -> realif1 | + \---------------^--/ \---^-------------/ +-------------------------|-------------------|-------------------- + /----v-------------------v----\ + user | virt0 <-> rump IP <-> virt1 | + \-----------------------------/ + +(ok, no more drawing) + +The addresses configured to the rump virt0 and virt1 interfaces +will be visible on the physical network, and their traffic can be +examined with e.g. wireshark. You can also use wireshark on +tap0/tap1. + +The alternate approach is to use purely internal simulation. The +shmif rump driver uses a memory-mapped file as an ethernet "bus" +between multiple rump networking stack instances. Just use +rump_pub_shmif_create() in the code. This can also of course be +combined with the tap setup, and you can have setups where border +nodes talk to an internal mesh of shmif's. Semi-drawn, it looks +like this: + +net1 <-> virt0, shm0 <-> shm1, shm2 <-> .... <-> shmN, virt1 <-> net1 + (rump0) (rump1) .... (rumpN) + +Linear setups (where router n talks to exactly router n-1 and n+1) +can be easily autogenerated. Here's a snippet of executed commands +I used to start a few hundred routers (NOTE! the usage of the +example code is different!): + +./a.out 10.0.0.1 10.0.0.255 /tmp/rumpshm_0 0 10.0.1.2 10.0.1.255 /tmp/rumpshm_1 10.0.1.1 +./a.out 10.0.1.1 10.0.1.255 /tmp/rumpshm_1 10.0.1.2 10.0.2.2 10.0.2.255 /tmp/rumpshm_2 10.0.2.1 +./a.out 10.0.2.1 10.0.2.255 /tmp/rumpshm_2 10.0.2.2 10.0.3.2 10.0.3.255 /tmp/rumpshm_3 10.0.3.1 +./a.out 10.0.3.1 10.0.3.255 /tmp/rumpshm_3 10.0.3.2 10.0.4.2 10.0.4.255 /tmp/rumpshm_4 10.0.4.1 +.... +./a.out 10.0.252.1 10.0.252.255 /tmp/rumpshm_252 10.0.252.2 10.0.253.2 10.0.253. +255 /tmp/rumpshm_253 10.0.253.1 +./a.out 10.0.253.1 10.0.253.255 /tmp/rumpshm_253 10.0.253.2 10.0.255.1 10.0.255. +255 /tmp/rumpshm_255 0 + +Unfortunately I lost script used to produce that, but the algorithm +is quickly obvious. + +Easy but slightly more interesting setups, such as a M^N matrix +(hyper-matrix?) are also possible, but left as an exercise to the +reader. + +Compiling the router depends a little on what networking domain +and what interface you want to use for testing. The very basic +setup with IP+virtif will get you quite far: + +cc rumprouter.c -lrumpnet_virtif -lrumpnet_netinet -lrumpnet_net -lrumpnet \ + -lrump -lrumpuser -lpthread Index: src/share/examples/rump/virtual_ip_router/rumprouter.c diff -u /dev/null src/share/examples/rump/virtual_ip_router/rumprouter.c:1.1 --- /dev/null Mon Mar 29 02:01:47 2010 +++ src/share/examples/rump/virtual_ip_router/rumprouter.c Mon Mar 29 02:01:47 2010 @@ -0,0 +1,207 @@ +/* $NetBSD: rumprouter.c,v 1.1 2010/03/29 02:01:47 pooka Exp $ */ + +/* + * Copyright (c) 2008 Antti Kantee. All Rights Reserved. + * + * Development of this software was supported by then + * Finnish Cultural Foundation. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 <sys/types.h> +#include <sys/socket.h> +#include <sys/time.h> + +#include <arpa/inet.h> + +#include <netinet/in.h> +#include <net/route.h> + +#include <rump/rump.h> +#include <rump/rump_syscalls.h> + +#include <err.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <sys/sockio.h> +#include <arpa/inet.h> +#include <net/if.h> +#include <net/route.h> + +#undef DEBUG + +#ifdef DEBUG +#define DP if (1) printf +#else +#define DP if (0) printf +#endif + +static void +configure_interface(const char *ifname, const char *addr, const char *mask, + const char *bcast) +{ + struct ifaliasreq ia; + struct sockaddr_in *sin; + int s, rv; + + DP("Entering %s\n", __FUNCTION__); + + DP("Create an interface(%s)\n", ifname); + s = atoi(ifname + strlen(ifname) - 1); /* XXX FIXME XXX */ + if ((s = rump_pub_virtif_create(s)) != 0) { + err(1, "rump_pub_virtif_create(%d)", s); + } + + DP("Get a socket for configuring the interface\n"); + if ((s = rump_sys_socket(PF_INET, SOCK_DGRAM, 0)) < 0) { + err(1, "rump_sys_socket"); + } + + /* Address */ + memset(&ia, 0, sizeof(ia)); + strcpy(ia.ifra_name, ifname); + sin = (struct sockaddr_in *)&ia.ifra_addr; + sin->sin_family = AF_INET; + sin->sin_len = sizeof(struct sockaddr_in); + sin->sin_addr.s_addr = inet_addr(addr); + + /* Netmask */ + sin = (struct sockaddr_in *)&ia.ifra_mask; + sin->sin_family = AF_INET; + sin->sin_len = sizeof(struct sockaddr_in); + sin->sin_addr.s_addr = inet_addr(mask); + + /* Broadcast address */ + sin = (struct sockaddr_in *)&ia.ifra_broadaddr; + sin->sin_family = AF_INET; + sin->sin_len = sizeof(struct sockaddr_in); + sin->sin_addr.s_addr = inet_addr(bcast); + + DP("Set the addresses\n"); + rv = rump_sys_ioctl(s, SIOCAIFADDR, &ia); + if (rv) { + err(1, "SIOCAIFADDR"); + } + rump_sys_close(s); + DP("Done with %s\n", __FUNCTION__); +} + +static void +configure_routing(const char *dst, const char *mask, const char *gw) +{ + size_t len; + struct { + struct rt_msghdr m_rtm; + uint8_t m_space; + } m_rtmsg; +#define rtm m_rtmsg.m_rtm + uint8_t *bp = &m_rtmsg.m_space; + struct sockaddr_in sinstore; + int s, rv; + + DP("Entering %s\n", __FUNCTION__); + + DP("Open a routing socket\n"); + s = rump_sys_socket(PF_INET, SOCK_DGRAM, 0); + if (s == -1) { + err(1, "rump_sys_socket"); + } + + memset(&m_rtmsg, 0, sizeof(m_rtmsg)); + rtm.rtm_type = RTM_ADD; + rtm.rtm_flags = RTF_UP | RTF_GATEWAY | RTF_STATIC; + rtm.rtm_version = RTM_VERSION; + rtm.rtm_seq = 2; + rtm.rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK; + + /* dst */ + memset(&sinstore, 0, sizeof(sinstore)); + sinstore.sin_family = AF_INET; + sinstore.sin_len = sizeof(sinstore); + sinstore.sin_addr.s_addr = inet_addr(dst); + memcpy(bp, &sinstore, sizeof(sinstore)); + bp += sizeof(sinstore); + + /* gw */ + memset(&sinstore, 0, sizeof(sinstore)); + sinstore.sin_family = AF_INET; + sinstore.sin_len = sizeof(sinstore); + sinstore.sin_addr.s_addr = inet_addr(gw); + memcpy(bp, &sinstore, sizeof(sinstore)); + bp += sizeof(sinstore); + + /* netmask */ + memset(&sinstore, 0, sizeof(sinstore)); + sinstore.sin_family = AF_INET; + sinstore.sin_len = sizeof(sinstore); + sinstore.sin_addr.s_addr = inet_addr(mask); + memcpy(bp, &sinstore, sizeof(sinstore)); + bp += sizeof(sinstore); + + len = bp - (uint8_t *)&m_rtmsg; + rtm.rtm_msglen = len; + + DP("Set the route\n"); + rv = rump_sys_write(s, &m_rtmsg, len); + if (rv != (int)len) { + err(1, "rump_sys_write"); + } + rump_sys_close(s); + DP("Done with %s\n", __FUNCTION__); +} + +static void +usage(const char *argv0) +{ + printf("Usage: %s if1 if2 [route]\n", argv0); + printf("\n"); + printf("where both \"if1\" and \"if2\" are\n"); + printf("\n"); + printf("ifname address netmask broadcast\n"); + printf("\n"); + printf("and \"route\" is an optional default route\n"); + exit(1); +} + +int +main(int argc, char *argv[]) +{ + if (argc < 9 || argc > 10) { + usage(argv[0]); + } + + rump_init(); + configure_interface(argv[1], argv[2], argv[3], argv[4]); + configure_interface(argv[5], argv[6], argv[7], argv[8]); + if (argc == 10) { + configure_routing("192.168.3.0", "255.255.255.0", "192.168.1.2"); + } + printf("Press Ctrl+C to quit..."); + pause(); + + return 0; +}