Skip to content

Instantly share code, notes, and snippets.

@kisel
Last active January 11, 2018 18:11
Show Gist options
  • Save kisel/48b5083abcd90bfc64ee7d548d7fa598 to your computer and use it in GitHub Desktop.
Save kisel/48b5083abcd90bfc64ee7d548d7fa598 to your computer and use it in GitHub Desktop.
Tap interface test

Testing ARP on tap interfaces forwarding

Running

make run

This will create ns_tap0, ns_tap1 namespaces and tap0, tap1 adapters

sudo ip netns exec ns_tap0 ip a s
tap0: 
    inet 10.200.0.2/24 scope global tap0
sudo ip netns exec ns_tap1 ip a s
tap0: 
    inet 10.200.0.3/24 scope global tap0

arping works as expected, no static ARP required:

sudo ip netns exec ns_tap0 arping -I tap0 10.200.0.3
ARPING 10.200.0.3
42 bytes from 42:8e:ba:0d:ab:de (10.200.0.3): index=0 time=11.571 msec

root@vagrant:/home/vagrant# tcpdump -i tap1 -n -l
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on tap1, link-type EN10MB (Ethernet), capture size 262144 bytes
13:08:49.209282 ARP, Request who-has 10.200.0.3 tell 10.200.0.2, length 28
13:08:49.209300 ARP, Reply 10.200.0.3 is-at 42:8e:ba:0d:ab:de, length 28

# sudo ip netns exec ns_tap1 python -mSimpleHTTPServer 8000
Serving HTTP on 0.0.0.0 port 8000 ...
10.200.0.2 - - [11/Jan/2018 13:11:01] "GET / HTTP/1.1" 200 -
10.200.0.2 - - [11/Jan/2018 13:12:35] "GET / HTTP/1.1" 200 -
10.200.0.2 - - [11/Jan/2018 13:12:39] "GET / HTTP/1.1" 200 -

sudo ip netns exec ns_tap0 curl -i 10.200.0.3:8000

Testing in the global namespace

make run-no-ns

In this case system doesn't respond to ARP requests even if we change mac addr and

sudo arping -I tap0 -s de:ad:be:ef:de:ad -S 10.200.0.55 10.200.0.3
root@vagrant:/home/vagrant# tcpdump -i tap1 -n -e
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on tap1, link-type EN10MB (Ethernet), capture size 262144 bytes
13:42:15.758624 de:ad:be:ef:de:ad > ff:ff:ff:ff:ff:ff, ethertype ARP (0x0806), length 42: Request who-has 10.200.0.3 tell 10.200.0.55, length 28
13:42:16.760068 de:ad:be:ef:de:ad > ff:ff:ff:ff:ff:ff, ethertype ARP (0x0806), length 42: Request who-has 10.200.0.3 tell 10.200.0.55, length 28
13:42:17.762684 de:ad:be:ef:de:ad > ff:ff:ff:ff:ff:ff, ethertype ARP (0x0806), length 42: Request who-has 10.200.0.3 tell 10.200.0.55, length 28
13:42:18.764038 de:ad:be:ef:de:ad > ff:ff:ff:ff:ff:ff, ethertype ARP (0x0806), length 42: Request who-has 10.200.0.3 tell 10.200.0.55, length 28

Once we remove IP overlapping - ARP starts working again

# change  or even flush an IP from tap0
sudo ifconfig tap0 1.2.3.4/24
#sudo ip a f tap0 # this works as well
# inject some ARP request from random ip/mac
sudo arping -I tap0 -s de:ad:be:ef:de:ad -S 10.200.0.55 10.200.0.3


tcpdump -i tap1 -n -e arp
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on tap1, link-type EN10MB (Ethernet), capture size 262144 bytes
13:47:52.387049 de:ad:be:ef:de:ad > ff:ff:ff:ff:ff:ff, ethertype ARP (0x0806), length 42: Request who-has 10.200.0.3 tell 10.200.0.55, length 28
13:47:52.387068 ba:b4:f0:77:53:68 > de:ad:be:ef:de:ad, ethertype ARP (0x0806), length 42: Reply 10.200.0.3 is-at ba:b4:f0:77:53:68, length 28

build:
cc -g -o tun -pthread tun.c
run: build;
sudo ./tun
run-no-ns: build;
sudo /usr/bin/env NO_NETNS=1 ./tun
#!/bin/sh
# This script is supposed to apply source-based routing and fix ARP configuration
# when tap0 and tap1 are located in the same namespace
# The setup has 2 connected tap devices - BEEF and F00D
# tap0 - 10.200.0.2/24 -- hwaddr be:ef:be:ef:be:ef
# tap1 - 10.200.0.3/24 -- hwaddr f0:0d:f0:0d:f0:0d
set -x
del_rule_for_ip() {
while ip rule del from $1 2>/dev/null; do continue; done
}
# Source-based routing rules
del_rule_for_ip 10.200.0.2
del_rule_for_ip 10.200.0.3
ip rule add from 10.200.0.2 table 2002 pref 0
ip rule add from 10.200.0.3 table 2003 pref 0
ip route add 10.200.0.0/24 dev tap0 table 2002
ip route add 10.200.0.0/24 dev tap1 table 2003
cfg() {
# this is supposed to fix ARP resolution for overlapping IPs
# rp_filter - INTEGER
# 0 - No source validation.
# 1 - Strict mode as defined in RFC3704 Strict Reverse Path
# Each incoming packet is tested against the FIB and if the interface
# is not the best reverse path the packet check will fail.
# By default failed packets are discarded.
# 2 - Loose mode as defined in RFC3704 Loose Reverse Path
# Each incoming packet's source address is also tested against the FIB
# and if the source address is not reachable via any interface
# the packet check will fail.
#
# Current recommended practice in RFC3704 is to enable strict mode
# to prevent IP spoofing from DDos attacks. If using asymmetric routing
# or other complicated routing, then loose mode is recommended.
#
# The max value from conf/{all,interface}/rp_filter is used
# when doing source validation on the {interface}.
#
# Default value is 0. Note that some distributions enable it
# in startup scripts.
sysctl -w net.ipv4.conf.$1.rp_filter=0
# arp_ignore - INTEGER
# Define different modes for sending replies in response to
# received ARP requests that resolve local target IP addresses:
# 0 - (default): reply for any local target IP address, configured
# on any interface
# 1 - reply only if the target IP address is local address
# configured on the incoming interface
# 2 - reply only if the target IP address is local address
# configured on the incoming interface and both with the
# sender's IP address are part from same subnet on this interface
# 3 - do not reply for local addresses configured with scope host,
# only resolutions for global and link addresses are replied
# 4-7 - reserved
# 8 - do not reply for all local addresses
#
# The max value from conf/{all,interface}/arp_ignore is used
# when ARP request is received on the {interface}
#
sysctl -w net.ipv4.conf.$1.arp_ignore=0
#arp_accept - BOOLEAN
# 0 - don't create new entries in the ARP table
# 1 - create new entries in the ARP table
sysctl -w net.ipv4.conf.$1.arp_accept=1
sysctl -w net.ipv4.conf.$1.arp_announce=2
# arp_filter - BOOLEAN
# 1 - Allows you to have multiple network interfaces on the same
# subnet, and have the ARPs for each interface be answered
# based on whether or not the kernel would route a packet from
# the ARP'd IP out that interface (therefore you must use source
# based routing for this to work). In other words it allows control
# of which cards (usually 1) will respond to an arp request.
#
# 0 - (default) The kernel can respond to arp requests with addresses
# from other interfaces. This may seem wrong but it usually makes
# sense, because it increases the chance of successful communication.
# IP addresses are owned by the complete host on Linux, not by
# particular interfaces. Only for more complex setups like load-
# balancing, does this behaviour cause problems.
#
# arp_filter for the interface will be enabled if at least one of
# conf/{all,interface}/arp_filter is set to TRUE,
# it will be disabled otherwise
sysctl -w net.ipv4.conf.$1.arp_filter=1
#
sysctl -w net.ipv4.conf.$1.arp_notify=0
sysctl -w net.ipv4.conf.$1.proxy_arp=0
sysctl -w net.ipv4.conf.$1.proxy_arp_pvlan=0
}
sysctl -w net.ipv4.conf.all.arp_ignore=1
cfg tap0
cfg tap1
cfg all
sysctl -w net.ipv4.conf.tap0.forwarding=0
sysctl -w net.ipv4.conf.tap1.forwarding=0
for i in /proc/sys/net/ipv4/conf/*/rp_filter ; do
echo 2 > $i
done
# I didn't get dynamic ARP working yet, so adding static records for now
ip neigh flush dev tap0
ip neigh flush dev tap1
ip neigh replace 10.200.0.3 lladdr f0:0d:f0:0d:f0:0d dev tap0 nud perm
ip neigh replace 10.200.0.2 lladdr be:ef:be:ef:be:ef dev tap1 nud perm
#include <fcntl.h> /* O_RDWR */
#include <string.h> /* memset(), memcpy() */
#include <stdio.h> /* perror(), printf(), fprintf() */
#include <stdlib.h> /* exit(), malloc(), free() */
#include <sys/ioctl.h> /* ioctl() */
#include <ev.h>
#include <unistd.h>
#include <stdlib.h>
/* includes for struct ifreq, etc */
#include <sys/types.h>
#include <sys/socket.h>
#include <linux/if.h>
#include <linux/if_tun.h>
#include <pthread.h>
// should be more than MSS, otherwise won't work
#define BUFSZ 65536
int tap_alloc(char *dev, int flags) {
struct ifreq ifr;
int fd, err;
char *clonedev = "/dev/net/tun";
if( (fd = open(clonedev, O_RDWR)) < 0 ) {
return fd;
}
/* preparation of the struct ifr, of type "struct ifreq" */
memset(&ifr, 0, sizeof(ifr));
ifr.ifr_flags = flags; /* IFF_TUN or IFF_TAP, plus maybe IFF_NO_PI */
if (*dev) {
strncpy(ifr.ifr_name, dev, IFNAMSIZ);
}
/* try to create the device */
if( (err = ioctl(fd, TUNSETIFF, (void *) &ifr)) < 0 ) {
close(fd);
return err;
}
printf("created dev %s fd=%d ifr_name=%s\n", dev, fd, ifr.ifr_name);
return fd;
}
void print_data(int sz, char *buf) {
int i;
for (i = 0; i < sz; i++)
{
if (i > 0) printf(":");
printf("%02X", buf[i]);
}
}
struct forward_ctx {
char *desc;
int fd_from;
int fd_to;
};
void forward_tun_pkts(void * arg)
{
struct forward_ctx *ctx = arg;
while(1) {
char buf[BUFSZ];
int nbytes = read(ctx->fd_from, buf, sizeof(buf));
write(ctx->fd_to, buf, nbytes);
printf("Forwarded %d bytes - %s\n", nbytes, ctx->desc);
//print_data(nbytes, buf);
}
}
void cmd(char *command) {
printf("#%s\n", command);
system(command);
}
int main(int argc, char *argv[])
{
int tap0 = tap_alloc("tap0", IFF_TAP | IFF_NO_PI);
int tap1 = tap_alloc("tap1", IFF_TAP | IFF_NO_PI);
if (tap0 < 0 || tap1 < 0) {
printf("failed to create tap devices");
return 1;
}
if (!getenv("NO_NETNS")) {
cmd("ip netns add ns_tap0");
cmd("ip link set tap0 netns ns_tap0 address be:ef:be:ef:be:ef up");
cmd("ip netns exec ns_tap0 ip a a 10.200.0.2/24 dev tap0");
cmd("ip netns add ns_tap1");
cmd("ip link set tap1 netns ns_tap1 address f0:0d:f0:0d:f0:0d up");
cmd("ip netns exec ns_tap1 ip a a 10.200.0.3/24 dev tap1");
} else {
cmd("ip link set tap0 address be:ef:be:ef:be:ef up");
cmd("ip a a 10.200.0.2/24 dev tap0");
cmd("ip link set tap1 address f0:0d:f0:0d:f0:0d up");
cmd("ip a a 10.200.0.3/24 dev tap1");
// in this case custom routing is required
system("sh ./no-ns-routing-cfg.sh");
}
pthread_t t1, t2;
pthread_attr_t attr;
pthread_attr_init(&attr);
struct forward_ctx f1;
f1.fd_from = tap0;
f1.fd_to = tap1;
f1.desc = "tap0 -> tap1";
pthread_create(&t1, &attr, forward_tun_pkts, &f1);
struct forward_ctx f2;
f2.fd_from = tap1;
f2.fd_to = tap0;
f2.desc = "tap1 -> tap0";
pthread_create(&t2, &attr, forward_tun_pkts, &f2);
printf("Devices tap0 & tap1 connected\n");
pthread_join(t1, 0);
pthread_join(t2, 0);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment