Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/share/examples/rump/virtual_ip_router Add an example for a r...



details:   https://anonhg.NetBSD.org/src/rev/1bdf25f5ee37
branches:  trunk
changeset: 753437:1bdf25f5ee37
user:      pooka <pooka%NetBSD.org@localhost>
date:      Mon Mar 29 02:01:47 2010 +0000

description:
Add an example for a rump router cluster setup along with a README.

some contributions to the code from Martti Kuparinen

diffstat:

 share/examples/rump/virtual_ip_router/README.txt   |  107 ++++++++++
 share/examples/rump/virtual_ip_router/rumprouter.c |  207 +++++++++++++++++++++
 2 files changed, 314 insertions(+), 0 deletions(-)

diffs (truncated from 322 to 300 lines):

diff -r d206076e5864 -r 1bdf25f5ee37 share/examples/rump/virtual_ip_router/README.txt
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/share/examples/rump/virtual_ip_router/README.txt  Mon Mar 29 02:01:47 2010 +0000
@@ -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
diff -r d206076e5864 -r 1bdf25f5ee37 share/examples/rump/virtual_ip_router/rumprouter.c
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/share/examples/rump/virtual_ip_router/rumprouter.c        Mon Mar 29 02:01:47 2010 +0000
@@ -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");



Home | Main Index | Thread Index | Old Index