From ceaf90f307ea6942a964e8e4742d9127d5f3f1da Mon Sep 17 00:00:00 2001 From: Vincenzo Maffione Date: Fri, 14 Sep 2018 12:08:57 +0200 Subject: [PATCH 01/11] add travis support --- .travis.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..7f06565 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,10 @@ +dist: trusty +sudo: required +language: c +script: + - "sudo apt-get -qq update" + - "sudo apt-get install -y libelf-dev linux-headers-$(uname -r)" + - "git clone https://github.com/netmap-unipi/netmap" + - "cd netmap && ./configure --no-drivers && make && sudo make install" + - "cd codelab && make" + - "cd solutions && make" From cdacd31617079a1a270a9d4362b55f76ea02ea0d Mon Sep 17 00:00:00 2001 From: Vincenzo Maffione Date: Fri, 14 Sep 2018 12:17:21 +0200 Subject: [PATCH 02/11] .travis: add missing pushd and popd instructions --- .travis.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.travis.yml b/.travis.yml index 7f06565..9c52e53 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,9 +2,15 @@ dist: trusty sudo: required language: c script: + - "pushd ." - "sudo apt-get -qq update" - "sudo apt-get install -y libelf-dev linux-headers-$(uname -r)" - "git clone https://github.com/netmap-unipi/netmap" - "cd netmap && ./configure --no-drivers && make && sudo make install" + - "popd" + - "pushd ." - "cd codelab && make" + - "popd" + - "pushd ." - "cd solutions && make" + - "popd" From 4d6a6f8e8c2ddc49a9ac1855844d8025d977696f Mon Sep 17 00:00:00 2001 From: Vincenzo Maffione Date: Sun, 27 Jan 2019 15:09:43 +0100 Subject: [PATCH 03/11] add stateless-filter example for FOSDEM 2019 --- fosdem2019/Makefile | 9 + fosdem2019/stateless-filter.c | 403 ++++++++++++++++++++++++++++++++++ 2 files changed, 412 insertions(+) create mode 100644 fosdem2019/Makefile create mode 100644 fosdem2019/stateless-filter.c diff --git a/fosdem2019/Makefile b/fosdem2019/Makefile new file mode 100644 index 0000000..4c97e0e --- /dev/null +++ b/fosdem2019/Makefile @@ -0,0 +1,9 @@ +CFLAGS=-Wall -g -Werror -O2 +PROGS=stateless-filter + +all: $(PROGS) + +stateless-filter: stateless-filter.o + +clean: + -rm -f *.o $(PROGS) diff --git a/fosdem2019/stateless-filter.c b/fosdem2019/stateless-filter.c new file mode 100644 index 0000000..d628975 --- /dev/null +++ b/fosdem2019/stateless-filter.c @@ -0,0 +1,403 @@ +/* + * This program forwards packets between two netmap ports, an external + * port (ext) and an internal port (int). Packets flowing from ext to + * int are filtered according to the rules provided through command + * line. Packets flowing from int to ext bypass the filter, and they + * are always forwarded. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define NETMAP_WITH_LIBS +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int stop = 0; +static unsigned long long fwd = 0; +static unsigned long long tot = 0; + +static void +sigint_handler(int signum) +{ + stop = 1; +} + +struct filtrule { + /* All fields are in network order. */ + uint32_t ip_daddr; + uint32_t ip_mask; + uint16_t dport; + uint8_t ip_proto; + uint8_t pad; +}; + +static inline int +rx_ready(struct nm_desc *nmd) +{ + unsigned int ri; + + for (ri = nmd->first_rx_ring; ri <= nmd->last_rx_ring; ri++) { + struct netmap_ring *ring; + + ring = NETMAP_RXRING(nmd->nifp, ri); + if (nm_ring_space(ring)) { + return 1; /* there is something to read */ + } + } + + return 0; +} + +static inline int +pkt_select(const char *buf, struct filtrule *rules, + int num_rules) +{ + struct ether_header *ethh; + struct udphdr *udph; + struct ip *iph; + int i; + + ethh = (struct ether_header *)buf; + if (ethh->ether_type != htons(ETHERTYPE_IP)) { + /* Filter out non-IP traffic. */ + return 0; + } + iph = (struct ip *)(ethh + 1); + udph = (struct udphdr *)(iph + 1); + + for (i = 0; i < num_rules; i++) { + struct filtrule *rule = rules + i; + + if ((iph->ip_dst.s_addr & rule->ip_mask) + == rule->ip_daddr && + (!rules->ip_proto || rule->ip_proto == iph->ip_p) && + (!rule->dport || rule->dport == udph->uh_dport)) { + return 1; + } + } + + return 0; /* discard */ +} + +static void +forward_pkts(struct nm_desc *src, struct nm_desc *dst, struct filtrule *rules, + int num_rules, int zerocopy) +{ + unsigned int si = src->first_rx_ring; + unsigned int di = dst->first_tx_ring; + + while (si <= src->last_rx_ring && di <= dst->last_tx_ring) { + struct netmap_ring *txring; + struct netmap_ring *rxring; + unsigned int rxhead, txhead; + int nrx, ntx; + + rxring = NETMAP_RXRING(src->nifp, si); + txring = NETMAP_TXRING(dst->nifp, di); + nrx = nm_ring_space(rxring); + ntx = nm_ring_space(txring); + if (nrx == 0) { + si++; + continue; + } + if (ntx == 0) { + di++; + continue; + } + + rxhead = rxring->head; + txhead = txring->head; + for (; nrx > 0 && ntx > 0; + nrx--, rxhead = nm_ring_next(rxring, rxhead), tot++) { + struct netmap_slot *rs = &rxring->slot[rxhead]; + struct netmap_slot *ts = &txring->slot[txhead]; + char *rxbuf = NETMAP_BUF(rxring, rs->buf_idx); + + if (rules && !pkt_select(rxbuf, rules, num_rules)) { + continue; /* discard */ + } + + ts->len = rs->len; + if (zerocopy) { + uint32_t idx = ts->buf_idx; + ts->buf_idx = rs->buf_idx; + rs->buf_idx = idx; + /* report the buffer change. */ + ts->flags |= NS_BUF_CHANGED; + rs->flags |= NS_BUF_CHANGED; + } else { + char *txbuf = NETMAP_BUF(txring, ts->buf_idx); + memcpy(txbuf, rxbuf, ts->len); + } + txhead = nm_ring_next(txring, txhead); + ntx--; + fwd++; + } + /* Update state of netmap ring. */ + rxring->head = rxring->cur = rxhead; + txring->head = txring->cur = txhead; + } +} + +static int +main_loop(const char *ext_port_name, const char *int_port_name, + struct filtrule *rules, int num_rules) +{ + struct nm_desc *ext_port; + struct nm_desc *int_port; + int zerocopy; + + ext_port = nm_open(ext_port_name, NULL, 0, NULL); + if (ext_port == NULL) { + if (!errno) { + printf("Failed to nm_open(%s): not a netmap port\n", + ext_port_name); + } else { + printf("Failed to nm_open(%s): %s\n", ext_port_name, + strerror(errno)); + } + return -1; + } + + int_port = nm_open(int_port_name, NULL, NM_OPEN_NO_MMAP, ext_port); + if (int_port == NULL) { + if (!errno) { + printf("Failed to nm_open(%s): not a netmap port\n", + int_port_name); + } else { + printf("Failed to nm_open(%s): %s\n", int_port_name, + strerror(errno)); + } + return -1; + } + + /* Check if we can do zerocopy. */ + zerocopy = (ext_port->mem == int_port->mem); + printf("zerocopy %sabled\n", zerocopy ? "en" : "dis"); + + while (!stop) { + struct pollfd pfd[2]; + int ret; + + pfd[0].fd = ext_port->fd; + pfd[1].fd = int_port->fd; + pfd[0].events = 0; + pfd[1].events = 0; + if (!rx_ready(ext_port)) { + /* Ran out of input packets on the first port, we need to + * wait for them. */ + pfd[0].events |= POLLIN; + } else { + /* We have input packets on the first port, let's wait for + * TX ring space in the other port. */ + pfd[1].events |= POLLOUT; + } + if (!rx_ready(int_port)) { + /* Ran out of input packets on the second port, we need to + * wait for them. */ + pfd[1].events |= POLLIN; + } else { + /* We have input packets on the second port, let's wait for + * TX ring space in the other port. */ + pfd[0].events |= POLLOUT; + } + + /* We poll with a timeout to have a chance to break the main loop if + * no packets are coming. */ + ret = poll(pfd, 2, 1000); + if (ret < 0) { + perror("poll()"); + } else if (ret == 0) { + /* Timeout */ + continue; + } + + /* Forward in the two directions. */ + forward_pkts(ext_port, int_port, rules, num_rules, zerocopy); + forward_pkts(int_port, ext_port, NULL, 0, zerocopy); + } + + nm_close(ext_port); + nm_close(int_port); + + printf("Total processed packets: %llu\n", tot); + printf("Forwarded packets : %llu\n", fwd); + + return 0; +} + +static void +usage(char **argv) +{ + printf("usage: %s [-h]\n" + " [-p x.y.z.w/mask:proto:dport (pass rule)] [-p ... ]\n" + " [-i INTERNAL_PORT]\n" + " [-e EXTERNAL_PORT]\n\n" + " Zero or more pass rules can be specified. A zero value for" + " mask, proto or dport means 'any'.\n", + argv[0]); + exit(EXIT_SUCCESS); +} + +int +main(int argc, char **argv) +{ +#define MAXRULES 16 + struct filtrule rules[MAXRULES]; + const char *ext_port_name = NULL; + const char *int_port_name = NULL; + struct sigaction sa; + int num_rules = 0; + int opt, ret, i; + + while ((opt = getopt(argc, argv, "hi:e:p:")) != -1) { + switch (opt) { + case 'h': + usage(argv); + return 0; + + case 'i': + int_port_name = optarg; + break; + + case 'e': + ext_port_name = optarg; + break; + + case 'p': { + char *copy = strdup(optarg); + char *ipstr, *maskstr, *protostr, *portstr, *null; + int port, proto, mask; + struct in_addr ip; + int ret; + + assert(copy != NULL); + ipstr = strtok(copy, "/"); + if (!ipstr) { + printf(" invalid -p '%s': no IPv4 found\n", + optarg); + usage(argv); + } + ret = inet_pton(AF_INET, ipstr, &ip); + if (ret <= 0) { + printf(" invalid IPv4 '%s'\n", + ipstr); + usage(argv); + } + + maskstr = strtok(NULL, ":"); + if (!maskstr) { + printf(" invalid -p '%s': no mask found\n", + optarg); + usage(argv); + } + mask = atoi(maskstr); + if (mask < 0 || mask > 31) { + printf(" invalid mask '%s'\n", + maskstr); + usage(argv); + } + + protostr = strtok(NULL, ":"); + if (!protostr) { + printf(" invalid -p '%s': no proto found\n", + optarg); + usage(argv); + } + proto = atoi(protostr); + if (proto < 0 || proto > 255) { + printf(" invalid proto '%s'\n", + protostr); + usage(argv); + } + + portstr = strtok(NULL, ":"); + if (!portstr) { + printf(" invalid -p '%s': no port found\n", + optarg); + usage(argv); + } + port = atoi(portstr); + if (port < 0 || port > (1<<16)-1) { + printf(" invalid port '%s'\n", + portstr); + usage(argv); + } + + null = strtok(NULL, ":"); + if (null) { + printf(" invalid -p '%s': trailing chars\n", + optarg); + usage(argv); + } + free(copy); + printf("<%08x/%d> <%d> <%d>\n", ip.s_addr, mask, proto, port); + + if (num_rules >= MAXRULES) { + printf(" too many rules, bailing out\n"); + exit(EXIT_FAILURE); + } + + rules[num_rules].ip_mask = + (((uint64_t)1ULL << mask) - 1ULL) << (32 - mask); + rules[num_rules].ip_mask = htonl(rules[num_rules].ip_mask); + rules[num_rules].ip_daddr = ip.s_addr & rules[num_rules].ip_mask; + rules[num_rules].ip_proto = proto; + rules[num_rules].dport = htons(port); + num_rules++; + break; + } + + default: + printf(" unrecognized option '-%c'\n", opt); + usage(argv); + return -1; + } + } + + if (ext_port_name == NULL) { + printf(" missing external port\n"); + usage(argv); + } + + if (int_port_name == NULL) { + printf(" missing internal port\n"); + usage(argv); + } + + /* Register Ctrl-C handler. */ + sa.sa_handler = sigint_handler; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_RESTART; + ret = sigaction(SIGINT, &sa, NULL); + if (ret) { + perror("sigaction(SIGINT)"); + exit(EXIT_FAILURE); + } + + printf("External port: %s\n", ext_port_name); + printf("Internal port: %s\n", int_port_name); + for (i = 0; i < num_rules; i++) { + printf(" rule: ip_daddr 0x%08x/0x%08x ip_proto %u " + "dport %u\n", + ntohl(rules[i].ip_daddr), ntohl(rules[i].ip_mask), + rules[i].ip_proto, ntohs(rules[i].dport)); + } + + main_loop(ext_port_name, int_port_name, rules, num_rules); + + return 0; +} From e194c7d22522c2a296336f52432b5bd47cf4c26f Mon Sep 17 00:00:00 2001 From: Vincenzo Maffione Date: Sun, 27 Jan 2019 17:53:31 +0100 Subject: [PATCH 04/11] stateless-filter: add single-ring simplified implementation --- fosdem2019/stateless-filter.c | 83 +++++++++++++++++++++++++++-------- 1 file changed, 65 insertions(+), 18 deletions(-) diff --git a/fosdem2019/stateless-filter.c b/fosdem2019/stateless-filter.c index d628975..121000f 100644 --- a/fosdem2019/stateless-filter.c +++ b/fosdem2019/stateless-filter.c @@ -25,6 +25,10 @@ #include #include +/* You can undef MULTIRING to get the simpler code, which assumes + * that each netmap port has a single RX ring and a single TX ring. */ +#define MULTIRING + static int stop = 0; static unsigned long long fwd = 0; static unsigned long long tot = 0; @@ -44,23 +48,6 @@ struct filtrule { uint8_t pad; }; -static inline int -rx_ready(struct nm_desc *nmd) -{ - unsigned int ri; - - for (ri = nmd->first_rx_ring; ri <= nmd->last_rx_ring; ri++) { - struct netmap_ring *ring; - - ring = NETMAP_RXRING(nmd->nifp, ri); - if (nm_ring_space(ring)) { - return 1; /* there is something to read */ - } - } - - return 0; -} - static inline int pkt_select(const char *buf, struct filtrule *rules, int num_rules) @@ -96,6 +83,7 @@ static void forward_pkts(struct nm_desc *src, struct nm_desc *dst, struct filtrule *rules, int num_rules, int zerocopy) { +#ifdef MULTIRING unsigned int si = src->first_rx_ring; unsigned int di = dst->first_tx_ring; @@ -146,10 +134,69 @@ forward_pkts(struct nm_desc *src, struct nm_desc *dst, struct filtrule *rules, ntx--; fwd++; } - /* Update state of netmap ring. */ + /* Update the pointers in the netmap rings. */ rxring->head = rxring->cur = rxhead; txring->head = txring->cur = txhead; } +#else /* !MULTIRING */ + struct netmap_ring *txring; + struct netmap_ring *rxring; + unsigned int rxhead, txhead; + + rxring = NETMAP_RXRING(src->nifp, 0); + txring = NETMAP_TXRING(dst->nifp, 0); + + for (rxhead = rxring->head, txhead = txring->head; + rxhead != rxring->tail; tot++, + rxhead = nm_ring_next(rxring, rxhead)) { + struct netmap_slot *rs = &rxring->slot[rxhead]; + struct netmap_slot *ts = &txring->slot[txhead]; + char *rxbuf = NETMAP_BUF(rxring, rs->buf_idx); + + if (rules && !pkt_select(rxbuf, rules, num_rules)) { + continue; /* discard */ + } + + ts->len = rs->len; + if (zerocopy) { + uint32_t idx = ts->buf_idx; + ts->buf_idx = rs->buf_idx; + rs->buf_idx = idx; + /* report the buffer change. */ + ts->flags |= NS_BUF_CHANGED; + rs->flags |= NS_BUF_CHANGED; + } else { + char *txbuf = NETMAP_BUF(txring, ts->buf_idx); + memcpy(txbuf, rxbuf, ts->len); + } + txhead = nm_ring_next(txring, txhead); + fwd++; + } + /* Update the pointers in the netmap rings. */ + rxring->head = rxring->cur = rxhead; + txring->head = txring->cur = txhead; +#endif /* !MULTIRING */ +} + +static inline int +rx_ready(struct nm_desc *nmd) +{ +#ifdef MULTIRING + unsigned int ri; + + for (ri = nmd->first_rx_ring; ri <= nmd->last_rx_ring; ri++) { + struct netmap_ring *ring; + + ring = NETMAP_RXRING(nmd->nifp, ri); + if (nm_ring_space(ring)) { + return 1; /* there is something to read */ + } + } + + return 0; +#else /* !MULTIRING */ + return nm_ring_space(NETMAP_RXRING(nmd->nifp, 0)); +#endif /* !MULTIRING */ } static int From a6972e341022b11abccc69634e669f351c6c6950 Mon Sep 17 00:00:00 2001 From: Vincenzo Maffione Date: Mon, 28 Jan 2019 12:08:30 +0100 Subject: [PATCH 05/11] statless-filter: improve output --- fosdem2019/README | 24 ++++++++++++++++++++++++ fosdem2019/stateless-filter.c | 4 ++-- 2 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 fosdem2019/README diff --git a/fosdem2019/README b/fosdem2019/README new file mode 100644 index 0000000..8ac86c4 --- /dev/null +++ b/fosdem2019/README @@ -0,0 +1,24 @@ +#### Netmap talk at FOSDEM 2019 #### + +URL: https://fosdem.org/2019/schedule/event/netmap_vnf_development/ + +This file contains instructions on how to run the example, step by step. + +Requirements: + - Linux (host) machine with KVM enabled + - Linux VM, or FreeBSD VM (with FreeBSD >= 12.x) + +### Step 1: Build and install netmap and qemu on the host machine + +$ mkdir fosdem2019-netmap +$ cd fosdem2019-netmap +$ git clone https://github.com/luigirizzo/netmap +$ cd netmap +$ ./configure --no-drivers # patched drivers not necessary for this example +$ make && sudo make install +$ sudo insmod netmap.ko +$ cd .. +$ git clone https://github.com/netmap-unipi/qemu +$ cd qemu +$ + diff --git a/fosdem2019/stateless-filter.c b/fosdem2019/stateless-filter.c index 121000f..ab64a22 100644 --- a/fosdem2019/stateless-filter.c +++ b/fosdem2019/stateless-filter.c @@ -391,7 +391,6 @@ main(int argc, char **argv) usage(argv); } free(copy); - printf("<%08x/%d> <%d> <%d>\n", ip.s_addr, mask, proto, port); if (num_rules >= MAXRULES) { printf(" too many rules, bailing out\n"); @@ -437,8 +436,9 @@ main(int argc, char **argv) printf("External port: %s\n", ext_port_name); printf("Internal port: %s\n", int_port_name); + printf("Rules:\n"); for (i = 0; i < num_rules; i++) { - printf(" rule: ip_daddr 0x%08x/0x%08x ip_proto %u " + printf(" pass ip_daddr 0x%08x/0x%08x ip_proto %u " "dport %u\n", ntohl(rules[i].ip_daddr), ntohl(rules[i].ip_mask), rules[i].ip_proto, ntohs(rules[i].dport)); From 6bccaa84a5b608162779233ae204fa9ecf3bc09a Mon Sep 17 00:00:00 2001 From: Vincenzo Maffione Date: Mon, 28 Jan 2019 12:42:20 +0100 Subject: [PATCH 06/11] fosdem 2019: add README --- fosdem2019/README | 99 ++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 84 insertions(+), 15 deletions(-) diff --git a/fosdem2019/README b/fosdem2019/README index 8ac86c4..b05d4fa 100644 --- a/fosdem2019/README +++ b/fosdem2019/README @@ -6,19 +6,88 @@ This file contains instructions on how to run the example, step by step. Requirements: - Linux (host) machine with KVM enabled - - Linux VM, or FreeBSD VM (with FreeBSD >= 12.x) - -### Step 1: Build and install netmap and qemu on the host machine - -$ mkdir fosdem2019-netmap -$ cd fosdem2019-netmap -$ git clone https://github.com/luigirizzo/netmap -$ cd netmap -$ ./configure --no-drivers # patched drivers not necessary for this example -$ make && sudo make install -$ sudo insmod netmap.ko -$ cd .. -$ git clone https://github.com/netmap-unipi/qemu -$ cd qemu -$ + - QEMU Virtual Machine, running Linux or FreeBSD >= 12.x +### Step 1: Build, install and load netmap on the host machine + +(host) $ mkdir fosdem2019-netmap +(host) $ cd fosdem2019-netmap +(host) $ git clone https://github.com/luigirizzo/netmap +(host) $ cd netmap +(host) $ ./configure --no-drivers # patched drivers not necessary for this example +(host) $ make && sudo make install +(host) $ sudo insmod netmap.ko +(host) $ cd .. + + +### Step 2: Build and install qemu on the host machine + +(host) $ git clone https://github.com/netmap-unipi/qemu +(host) $ cd qemu +(host) $ ./configure --target-list=x86_64-softmmu --enable-kvm --disable-werror --enable-netmap +(host) $ make && sudo make install +(host) $ cd .. + + +### Step 3: Prepare the VM + +Install netmap in the VM (same as step 1, but inside the VM). +Then, build the example: + +(vm) $ git clone https://github.com/netmap-unipi/netmap-tutorial +(vm) $ cd netmap-tutorial/fosdem2019 +(vm) $ make + + +### Step 4: Close the VM and re-launch it with two netmap pipe ports + +sudo qemu-system-x86_64 /path/to/qemu/vm/image.qcow2 -enable-kvm -smp 2 -m 2G -vga std -device e1000,netdev=mgmt,mac=00:AA:BB:CC:0a:99 -netdev user,id=mgmt,hostfwd=tcp::20010-:22 -device ptnet-pci,netdev=data10,mac=00:AA:BB:CC:0a:0a -netdev netmap,ifname=vale1:10}1,id=data10,passthrough=on -device ptnet-pci,netdev=data11,mac=00:AA:BB:CC:0a:0b -netdev netmap,ifname=vale1:10{2,id=data11,passthrough=on + + + +### Step 5: Access the VM and find the two passed-through interfaces + +First, load the netmap module: +(vm) $ sudo insmod netmap + +On Linux, a list of passed-through netmap ports can be obtained in this way: +(vm) for if in $(ls /sys/class/net); do ethtool -i $if | grep -qq ptnetmap && echo "ptnetmap if: $if"; done + +On FreeBSD, just run "ifconfig" and look for "ptnetX" interfaces. + + +### Step 6: Run the example application + +Run the filter with a rule to pass TCP/UDP traffic towards +10.10.10.0/24, port 7777: + +(vm) $ cd netmap/tutorial/fosdem2019 +(vm) $ sudo ./stateless-filter -e eth1 -i eth2 -p 10.10.10.0/24:0:7777 + + +### Step 7: Inject traffic into the VM + +Send UDP traffic (with matching destination IP and port) into the other +end of the pipe corresponding to the EXT port: + +(host) pkt-gen -i vale1:10{1 -f tx -d 10.10.10.23:7777 + + +### Step 8: Capture traffic forwarded from the VM + +Receive traffic from the other end of the pipe corresponding the INT +port: +(host) pkt-gen -i vale1:10}2 -f rx + + + +### More experiments: + +1. Restart the filter, changing the rule to pass only port 80, and check + that no traffic is forwarded: +(vm) $ sudo ./stateless-filter -e eth1 -i eth2 -p 10.10.10.0/24:0:80 + +2. Try with bidirectional traffic, replacing "-f tx" with "-f ping", + and "-f rx" with "-f pong". + +3. Add more pass rule or use a pass-any rule ("0.0.0.0/0:0:0"). From a96aa0a926cd0ceefd2fdf6e759aeee88580443e Mon Sep 17 00:00:00 2001 From: Vincenzo Maffione Date: Mon, 28 Jan 2019 15:11:47 +0100 Subject: [PATCH 07/11] stateless-filter: add missing check on txring --- fosdem2019/stateless-filter.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fosdem2019/stateless-filter.c b/fosdem2019/stateless-filter.c index ab64a22..63308b3 100644 --- a/fosdem2019/stateless-filter.c +++ b/fosdem2019/stateless-filter.c @@ -147,8 +147,8 @@ forward_pkts(struct nm_desc *src, struct nm_desc *dst, struct filtrule *rules, txring = NETMAP_TXRING(dst->nifp, 0); for (rxhead = rxring->head, txhead = txring->head; - rxhead != rxring->tail; tot++, - rxhead = nm_ring_next(rxring, rxhead)) { + rxhead != rxring->tail && txhead != txring->tail; + tot++, rxhead = nm_ring_next(rxring, rxhead)) { struct netmap_slot *rs = &rxring->slot[rxhead]; struct netmap_slot *ts = &txring->slot[txhead]; char *rxbuf = NETMAP_BUF(rxring, rs->buf_idx); From faea10a174242f5c193dcc64d9829cdfddbec80a Mon Sep 17 00:00:00 2001 From: Vincenzo Maffione Date: Mon, 28 Jan 2019 16:00:06 +0100 Subject: [PATCH 08/11] stateless-filter: add -c option to turn down zerocopy --- fosdem2019/README | 32 ++++++++++++++++++-------------- fosdem2019/stateless-filter.c | 18 +++++++++++++----- 2 files changed, 31 insertions(+), 19 deletions(-) diff --git a/fosdem2019/README b/fosdem2019/README index b05d4fa..dcc8e1b 100644 --- a/fosdem2019/README +++ b/fosdem2019/README @@ -41,7 +41,7 @@ Then, build the example: ### Step 4: Close the VM and re-launch it with two netmap pipe ports -sudo qemu-system-x86_64 /path/to/qemu/vm/image.qcow2 -enable-kvm -smp 2 -m 2G -vga std -device e1000,netdev=mgmt,mac=00:AA:BB:CC:0a:99 -netdev user,id=mgmt,hostfwd=tcp::20010-:22 -device ptnet-pci,netdev=data10,mac=00:AA:BB:CC:0a:0a -netdev netmap,ifname=vale1:10}1,id=data10,passthrough=on -device ptnet-pci,netdev=data11,mac=00:AA:BB:CC:0a:0b -netdev netmap,ifname=vale1:10{2,id=data11,passthrough=on +(host) $ sudo qemu-system-x86_64 /path/to/qemu/vm/image.qcow2 -enable-kvm -smp 2 -m 2G -vga std -device e1000,netdev=mgmt,mac=00:AA:BB:CC:0a:99 -netdev user,id=mgmt,hostfwd=tcp::20010-:22 -device ptnet-pci,netdev=data10,mac=00:AA:BB:CC:0a:0a -netdev netmap,ifname=vale1:10}1,id=data10,passthrough=on -device ptnet-pci,netdev=data11,mac=00:AA:BB:CC:0a:0b -netdev netmap,ifname=vale1:10{2,id=data11,passthrough=on @@ -51,9 +51,14 @@ First, load the netmap module: (vm) $ sudo insmod netmap On Linux, a list of passed-through netmap ports can be obtained in this way: -(vm) for if in $(ls /sys/class/net); do ethtool -i $if | grep -qq ptnetmap && echo "ptnetmap if: $if"; done +(vm) $ for if in $(ls /sys/class/net); do ethtool -i $if | grep -qq ptnetmap && echo "ptnetmap if: $if"; done -On FreeBSD, just run "ifconfig" and look for "ptnetX" interfaces. +Bring the interfaces up, e.g.: +(vm) $ sudo ip link set $if up + +On FreeBSD, just run "ifconfig" and look for "ptnet*" interfaces, e.g.: +(vm) $ sudo ifconfig ptnet0 up +(vm) $ sudo ifconfig ptnet1 up ### Step 6: Run the example application @@ -62,30 +67,29 @@ Run the filter with a rule to pass TCP/UDP traffic towards 10.10.10.0/24, port 7777: (vm) $ cd netmap/tutorial/fosdem2019 -(vm) $ sudo ./stateless-filter -e eth1 -i eth2 -p 10.10.10.0/24:0:7777 +(vm) $ sudo ./stateless-filter netmap:eth1 -i netmap:eth2 -p 10.10.10.0/24:0:7777 -### Step 7: Inject traffic into the VM +### Step 7: Capture traffic forwarded from the VM -Send UDP traffic (with matching destination IP and port) into the other -end of the pipe corresponding to the EXT port: - -(host) pkt-gen -i vale1:10{1 -f tx -d 10.10.10.23:7777 +Receive traffic from the other end of the pipe corresponding the INT +port: +(host) # pkt-gen -i vale1:10}2 -f rx -### Step 8: Capture traffic forwarded from the VM +### Step 8: Inject traffic into the VM -Receive traffic from the other end of the pipe corresponding the INT -port: -(host) pkt-gen -i vale1:10}2 -f rx +Send UDP traffic (with matching destination IP and port) into the other +end of the pipe corresponding to the EXT port: +(host) # pkt-gen -i vale1:10{1 -f tx -d 10.10.10.23:7777 ### More experiments: 1. Restart the filter, changing the rule to pass only port 80, and check that no traffic is forwarded: -(vm) $ sudo ./stateless-filter -e eth1 -i eth2 -p 10.10.10.0/24:0:80 +(vm) $ sudo ./stateless-filter -e netmap:eth1 -i netmap:eth2 -p 10.10.10.0/24:0:80 2. Try with bidirectional traffic, replacing "-f tx" with "-f ping", and "-f rx" with "-f pong". diff --git a/fosdem2019/stateless-filter.c b/fosdem2019/stateless-filter.c index 63308b3..e93ce89 100644 --- a/fosdem2019/stateless-filter.c +++ b/fosdem2019/stateless-filter.c @@ -201,7 +201,7 @@ rx_ready(struct nm_desc *nmd) static int main_loop(const char *ext_port_name, const char *int_port_name, - struct filtrule *rules, int num_rules) + struct filtrule *rules, int num_rules, int force_copy) { struct nm_desc *ext_port; struct nm_desc *int_port; @@ -232,7 +232,7 @@ main_loop(const char *ext_port_name, const char *int_port_name, } /* Check if we can do zerocopy. */ - zerocopy = (ext_port->mem == int_port->mem); + zerocopy = !force_copy && (ext_port->mem == int_port->mem); printf("zerocopy %sabled\n", zerocopy ? "en" : "dis"); while (!stop) { @@ -282,6 +282,7 @@ main_loop(const char *ext_port_name, const char *int_port_name, printf("Total processed packets: %llu\n", tot); printf("Forwarded packets : %llu\n", fwd); + printf("Dropped packets : %llu\n", tot - fwd); return 0; } @@ -292,7 +293,9 @@ usage(char **argv) printf("usage: %s [-h]\n" " [-p x.y.z.w/mask:proto:dport (pass rule)] [-p ... ]\n" " [-i INTERNAL_PORT]\n" - " [-e EXTERNAL_PORT]\n\n" + " [-e EXTERNAL_PORT]\n" + " [-c (disable zerocopy if supported)]\n" + "\n" " Zero or more pass rules can be specified. A zero value for" " mask, proto or dport means 'any'.\n", argv[0]); @@ -307,10 +310,11 @@ main(int argc, char **argv) const char *ext_port_name = NULL; const char *int_port_name = NULL; struct sigaction sa; + int force_copy = 0; int num_rules = 0; int opt, ret, i; - while ((opt = getopt(argc, argv, "hi:e:p:")) != -1) { + while ((opt = getopt(argc, argv, "hi:e:p:c")) != -1) { switch (opt) { case 'h': usage(argv); @@ -407,6 +411,10 @@ main(int argc, char **argv) break; } + case 'c': + force_copy = 1; + break; + default: printf(" unrecognized option '-%c'\n", opt); usage(argv); @@ -444,7 +452,7 @@ main(int argc, char **argv) rules[i].ip_proto, ntohs(rules[i].dport)); } - main_loop(ext_port_name, int_port_name, rules, num_rules); + main_loop(ext_port_name, int_port_name, rules, num_rules, force_copy); return 0; } From 993fa6e90f079b1b64be1f7359eaf6c9d3f22279 Mon Sep 17 00:00:00 2001 From: Vincenzo Maffione Date: Mon, 28 Jan 2019 16:10:43 +0100 Subject: [PATCH 09/11] stateless-filter: minor fix --- fosdem2019/stateless-filter.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/fosdem2019/stateless-filter.c b/fosdem2019/stateless-filter.c index e93ce89..6a17c76 100644 --- a/fosdem2019/stateless-filter.c +++ b/fosdem2019/stateless-filter.c @@ -49,8 +49,7 @@ struct filtrule { }; static inline int -pkt_select(const char *buf, struct filtrule *rules, - int num_rules) +pkt_select(const char *buf, struct filtrule *rules, int num_rules) { struct ether_header *ethh; struct udphdr *udph; @@ -72,7 +71,7 @@ pkt_select(const char *buf, struct filtrule *rules, == rule->ip_daddr && (!rules->ip_proto || rule->ip_proto == iph->ip_p) && (!rule->dport || rule->dport == udph->uh_dport)) { - return 1; + return 1; /* select */ } } From e7ad953d753870187aa05507c55905e7428bbf21 Mon Sep 17 00:00:00 2001 From: Vincenzo Maffione Date: Wed, 30 Jan 2019 12:06:57 +0100 Subject: [PATCH 10/11] fosdem2019: improve instructions --- fosdem2019/README | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/fosdem2019/README b/fosdem2019/README index dcc8e1b..159f1ad 100644 --- a/fosdem2019/README +++ b/fosdem2019/README @@ -2,7 +2,7 @@ URL: https://fosdem.org/2019/schedule/event/netmap_vnf_development/ -This file contains instructions on how to run the example, step by step. +This file contains step-by-step instructions to run the example. Requirements: - Linux (host) machine with KVM enabled @@ -67,14 +67,14 @@ Run the filter with a rule to pass TCP/UDP traffic towards 10.10.10.0/24, port 7777: (vm) $ cd netmap/tutorial/fosdem2019 -(vm) $ sudo ./stateless-filter netmap:eth1 -i netmap:eth2 -p 10.10.10.0/24:0:7777 +(vm) $ sudo ./stateless-filter -e netmap:eth1 -i netmap:eth2 -p 10.10.10.0/24:0:7777 ### Step 7: Capture traffic forwarded from the VM Receive traffic from the other end of the pipe corresponding the INT port: -(host) # pkt-gen -i vale1:10}2 -f rx +(host) $ sudo pkt-gen -i vale1:10}2 -f rx ### Step 8: Inject traffic into the VM @@ -82,7 +82,7 @@ port: Send UDP traffic (with matching destination IP and port) into the other end of the pipe corresponding to the EXT port: -(host) # pkt-gen -i vale1:10{1 -f tx -d 10.10.10.23:7777 +(host) $ sudo pkt-gen -i vale1:10{1 -f tx -d 10.10.10.23:7777-10.10.10.129:7777 ### More experiments: From 86fb59e8c953838e2a5e6962e63116069897c4db Mon Sep 17 00:00:00 2001 From: Vincenzo Maffione Date: Wed, 30 Jan 2019 12:12:16 +0100 Subject: [PATCH 11/11] README: add more command line for pkt-gen transmitter --- fosdem2019/README | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/fosdem2019/README b/fosdem2019/README index 159f1ad..2a3e153 100644 --- a/fosdem2019/README +++ b/fosdem2019/README @@ -82,7 +82,7 @@ port: Send UDP traffic (with matching destination IP and port) into the other end of the pipe corresponding to the EXT port: -(host) $ sudo pkt-gen -i vale1:10{1 -f tx -d 10.10.10.23:7777-10.10.10.129:7777 +(host) $ sudo pkt-gen -i vale1:10{1 -f tx -d 10.10.10.23:7777 ### More experiments: @@ -95,3 +95,6 @@ end of the pipe corresponding to the EXT port: and "-f rx" with "-f pong". 3. Add more pass rule or use a pass-any rule ("0.0.0.0/0:0:0"). + +4. Try with destination address and ports in a range +(host) $ sudo pkt-gen -i vale1:10{1 -f tx -d 10.10.10.23:7777-10.10.10.129:7780