Commit ffaee08c authored by Brenden Blanco's avatar Brenden Blanco

Merge pull request #83 from iovisor/yhs_dev

Yhs dev
parents 0fa99f4d 00eb0883
import atexit import subprocess
from pyroute2 import IPRoute, NetNS, IPDB, NSPopen from pyroute2 import IPRoute, NetNS, IPDB, NSPopen
class Simulation(object): class Simulation(object):
...@@ -14,15 +14,22 @@ class Simulation(object): ...@@ -14,15 +14,22 @@ class Simulation(object):
self.processes = [] self.processes = []
self.released = False self.released = False
# helper function to create a namespace and a veth connecting it # helper function to add additional ifc to namespace
def _create_ns(self, name, in_ifc=None, out_ifc=None, ipaddr=None, # if called directly outside Simulation class, "ifc_base_name" should be
macaddr=None, fn=None, cmd=None, action="ok"): # different from "name", the "ifc_base_name" and "name" are the same for
# the first ifc created by namespace
def _ns_add_ifc(self, name, ns_ifc, ifc_base_name, in_ifc=None, out_ifc=None,
ipaddr=None, macaddr=None, fn=None, cmd=None, action="ok",
disable_ipv6=False):
if name in self.ipdbs:
ns_ipdb = self.ipdbs[name]
else:
ns_ipdb = IPDB(nl=NetNS(name)) ns_ipdb = IPDB(nl=NetNS(name))
if in_ifc: if in_ifc:
in_ifname = in_ifc.ifname in_ifname = in_ifc.ifname
else: else:
out_ifc = self.ipdb.create(ifname="%sa" % name, kind="veth", out_ifc = self.ipdb.create(ifname="%sa" % ifc_base_name, kind="veth",
peer="%sb" % name).commit() peer="%sb" % ifc_base_name).commit()
in_ifc = self.ipdb.interfaces[out_ifc.peer] in_ifc = self.ipdb.interfaces[out_ifc.peer]
in_ifname = in_ifc.ifname in_ifname = in_ifc.ifname
with in_ifc as v: with in_ifc as v:
...@@ -31,21 +38,36 @@ class Simulation(object): ...@@ -31,21 +38,36 @@ class Simulation(object):
in_ifc = ns_ipdb.interfaces[in_ifname] in_ifc = ns_ipdb.interfaces[in_ifname]
if out_ifc: out_ifc.up().commit() if out_ifc: out_ifc.up().commit()
with in_ifc as v: with in_ifc as v:
v.ifname = "eth0" v.ifname = ns_ifc
if ipaddr: v.add_ip("%s" % ipaddr) if ipaddr: v.add_ip("%s" % ipaddr)
if macaddr: v.address = macaddr if macaddr: v.address = macaddr
v.up() v.up()
# if required, disable ipv6 before attaching the filter
if disable_ipv6:
subprocess.call(["sysctl", "-q", "-w",
"net.ipv6.conf." + out_ifc.ifname+ ".disable_ipv6=1"])
nsp = NSPopen(ns_ipdb.nl.netns,
["sysctl", "-q", "-w", "net.ipv6.conf." + ns_ifc + ".disable_ipv6=1"])
nsp.wait(); nsp.release()
if fn and out_ifc: if fn and out_ifc:
self.ipdb.nl.tc("add", "ingress", out_ifc["index"], "ffff:") self.ipdb.nl.tc("add", "ingress", out_ifc["index"], "ffff:")
self.ipdb.nl.tc("add-filter", "bpf", out_ifc["index"], ":1", self.ipdb.nl.tc("add-filter", "bpf", out_ifc["index"], ":1",
fd=fn.fd, name=fn.name, parent="ffff:", fd=fn.fd, name=fn.name, parent="ffff:",
action=action, classid=1) action=action, classid=1)
self.ipdbs[ns_ipdb.nl.netns] = ns_ipdb
self.namespaces.append(ns_ipdb.nl)
if cmd: if cmd:
self.processes.append(NSPopen(ns_ipdb.nl.netns, cmd)) self.processes.append(NSPopen(ns_ipdb.nl.netns, cmd))
return (ns_ipdb, out_ifc, in_ifc) return (ns_ipdb, out_ifc, in_ifc)
# helper function to create a namespace and a veth connecting it
def _create_ns(self, name, in_ifc=None, out_ifc=None, ipaddr=None,
macaddr=None, fn=None, cmd=None, action="ok", disable_ipv6=False):
(ns_ipdb, out_ifc, in_ifc) = self._ns_add_ifc(name, "eth0", name, in_ifc, out_ifc,
ipaddr, macaddr, fn, cmd, action,
disable_ipv6)
self.ipdbs[ns_ipdb.nl.netns] = ns_ipdb
self.namespaces.append(ns_ipdb.nl)
return (ns_ipdb, out_ifc, in_ifc)
def release(self): def release(self):
if self.released: return if self.released: return
self.released = True self.released = True
......
../../examples/simulation.py
\ No newline at end of file
...@@ -101,13 +101,12 @@ int pem(struct __sk_buff *skb) { ...@@ -101,13 +101,12 @@ int pem(struct __sk_buff *skb) {
} }
} }
return 0; return 1;
} }
static int br_common(struct __sk_buff *skb, int which_br) __attribute__((always_inline)); static int br_common(struct __sk_buff *skb, int which_br) __attribute__((always_inline));
static int br_common(struct __sk_buff *skb, int which_br) { static int br_common(struct __sk_buff *skb, int which_br) {
u8 *cursor = 0; u8 *cursor = 0;
bpf_metadata_t meta = {};
u16 proto; u16 proto;
u16 arpop; u16 arpop;
eth_addr_t dmac; eth_addr_t dmac;
...@@ -118,20 +117,16 @@ static int br_common(struct __sk_buff *skb, int which_br) { ...@@ -118,20 +117,16 @@ static int br_common(struct __sk_buff *skb, int which_br) {
bpf_dest_t *dest_p; bpf_dest_t *dest_p;
u32 index, *rtrif_p; u32 index, *rtrif_p;
if (skb->tc_index == 0) {
skb->tc_index = 1;
skb->cb[0] = skb->cb[1] = 0;
meta.prog_id = meta.rx_port_id = 0;
} else {
meta.prog_id = skb->cb[0];
meta.rx_port_id = skb->cb[1];
}
struct ethernet_t *ethernet = cursor_advance(cursor, sizeof(*ethernet)); struct ethernet_t *ethernet = cursor_advance(cursor, sizeof(*ethernet));
/* handle ethernet packet header */
{ {
dmac.addr = ethernet->dst; dmac.addr = ethernet->dst;
if (meta.prog_id != 0) { /* skb->tc_index may be preserved accross router namespace if router simply rewrite packet
/* send to the router */ * and send it back.
*/
if (skb->tc_index == 1) {
/* packet from pem, send to the router, set tc_index to 2 */
skb->tc_index = 2;
if (dmac.addr == 0xffffffffffffULL) { if (dmac.addr == 0xffffffffffffULL) {
index = 0; index = 0;
if (which_br == 1) if (which_br == 1)
...@@ -149,9 +144,11 @@ static int br_common(struct __sk_buff *skb, int which_br) { ...@@ -149,9 +144,11 @@ static int br_common(struct __sk_buff *skb, int which_br) {
if (rtrif_p) if (rtrif_p)
bpf_clone_redirect(skb, *rtrif_p, 0); bpf_clone_redirect(skb, *rtrif_p, 0);
} }
return 0; return 1;
} }
/* set the tc_index to 1 so pem knows it is from internal */
skb->tc_index = 1;
switch (ethernet->type) { switch (ethernet->type) {
case ETH_P_IP: goto ip; case ETH_P_IP: goto ip;
case ETH_P_ARP: goto arp; case ETH_P_ARP: goto arp;
...@@ -217,7 +214,7 @@ xmit: ...@@ -217,7 +214,7 @@ xmit:
} }
EOP: EOP:
return 0; return 1;
} }
int br1(struct __sk_buff *skb) { int br1(struct __sk_buff *skb) {
......
...@@ -61,68 +61,31 @@ ...@@ -61,68 +61,31 @@
# 8: # 8:
# 8: OK # 8: OK
from ctypes import c_ubyte, c_ushort, c_uint, c_ulonglong, Structure from ctypes import c_uint
from netaddr import IPAddress, EUI from netaddr import IPAddress, EUI
from bpf import BPF from bpf import BPF
from pyroute2 import IPRoute from pyroute2 import IPRoute, NetNS, IPDB, NSPopen
from socket import socket, AF_INET, SOCK_DGRAM
import sys import sys
from time import sleep from time import sleep
from unittest import main, TestCase from unittest import main, TestCase
import subprocess from simulation import Simulation
import struct
arg1 = sys.argv.pop(1) arg1 = sys.argv.pop(1)
ipr = IPRoute()
ipdb = IPDB(nl=ipr)
sim = Simulation(ipdb)
class TestBPFSocket(TestCase): class TestBPFSocket(TestCase):
def setup_vm_ns(self, ns, veth_in, veth_out):
subprocess.call(["ip", "link", "add", veth_in, "type", "veth", "peer", "name", veth_out])
subprocess.call(["ip", "netns", "add", ns])
subprocess.call(["ip", "link", "set", veth_in, "netns", ns])
subprocess.call(["ip", "netns", "exec", ns, "ip", "link", "set", veth_in, "name", "eth0"])
subprocess.call(["ip", "link", "set", veth_out, "up"])
def config_vm_ns(self, ns, ip_addr, net_mask, ip_gw):
subprocess.call(["ip", "netns", "exec", ns, "ip", "addr", "add", ip_addr + "/24", "dev", "eth0"])
subprocess.call(["ip", "netns", "exec", ns, "ip", "link", "set", "eth0", "up"])
subprocess.call(["ip", "netns", "exec", ns, "ip", "route", "add", net_mask + "/24", "via", ip_gw])
def setup_router_ns(self, ns, veth1_in, veth1_out, veth2_in, veth2_out):
subprocess.call(["ip", "netns", "add", ns])
subprocess.call(["ip", "link", "add", veth1_in, "type", "veth", "peer", "name", veth1_out])
subprocess.call(["ip", "link", "set", veth1_in, "netns", ns])
subprocess.call(["ip", "netns", "exec", ns, "ip", "link", "set", veth1_in, "name", "eth0"])
subprocess.call(["ip", "link", "add", veth2_in, "type", "veth", "peer", "name", veth2_out])
subprocess.call(["ip", "link", "set", veth2_in, "netns", ns])
subprocess.call(["ip", "netns", "exec", ns, "ip", "link", "set", veth2_in, "name", "eth1"])
subprocess.call(["ip", "link", "set", veth1_out, "up"])
subprocess.call(["ip", "link", "set", veth2_out, "up"])
def config_router_ns(self, ns, ip_eth0, ip_eth1):
subprocess.call(["ip", "netns", "exec", ns, "ip", "addr", "add", ip_eth0 + "/24", "dev", "eth0"])
subprocess.call(["ip", "netns", "exec", ns, "ip", "link", "set", "eth0", "up"])
subprocess.call(["ip", "netns", "exec", ns, "ip", "addr", "add", ip_eth1 + "/24", "dev", "eth1"])
subprocess.call(["ip", "netns", "exec", ns, "ip", "link", "set", "eth1", "up"])
def set_default_const(self): def set_default_const(self):
self.ns1 = "ns1" self.ns1 = "ns1"
self.ns1_eth_in = "v1"
self.ns1_eth_out = "v2"
self.ns2 = "ns2" self.ns2 = "ns2"
self.ns2_eth_in = "v3"
self.ns2_eth_out = "v4"
self.ns_router = "ns_router" self.ns_router = "ns_router"
self.nsrtr_eth0_in = "v10"
self.nsrtr_eth0_out = "v11"
self.nsrtr_eth1_in = "v12"
self.nsrtr_eth1_out = "v13"
self.vm1_ip = "100.1.1.1" self.vm1_ip = "100.1.1.1"
self.vm2_ip = "200.1.1.1" self.vm2_ip = "200.1.1.1"
self.vm1_rtr_ip = "100.1.1.254" self.vm1_rtr_ip = "100.1.1.254"
self.vm2_rtr_ip = "200.1.1.254" self.vm2_rtr_ip = "200.1.1.254"
self.vm1_rtr_mask = "100.1.1.0" self.vm1_rtr_mask = "100.1.1.0/24"
self.vm2_rtr_mask = "200.1.1.0" self.vm2_rtr_mask = "200.1.1.0/24"
def get_table(self, b): def get_table(self, b):
self.jump = b.get_table("jump") self.jump = b.get_table("jump")
...@@ -141,29 +104,15 @@ class TestBPFSocket(TestCase): ...@@ -141,29 +104,15 @@ class TestBPFSocket(TestCase):
self.br2_rtr = b.get_table("br2_rtr") self.br2_rtr = b.get_table("br2_rtr")
def connect_ports(self, prog_id_pem, prog_id_br, curr_pem_pid, curr_br_pid, def connect_ports(self, prog_id_pem, prog_id_br, curr_pem_pid, curr_br_pid,
ip, br_dest_map, br_mac_map, br_dest_map, br_mac_map, ifindex, vm_mac, vm_ip):
ns_eth_out, vm_mac, vm_ip):
self.pem_dest[c_uint(curr_pem_pid)] = self.pem_dest.Leaf(prog_id_br, curr_br_pid) self.pem_dest[c_uint(curr_pem_pid)] = self.pem_dest.Leaf(prog_id_br, curr_br_pid)
br_dest_map[c_uint(curr_br_pid)] = br_dest_map.Leaf(prog_id_pem, curr_pem_pid) br_dest_map[c_uint(curr_br_pid)] = br_dest_map.Leaf(prog_id_pem, curr_pem_pid)
ifindex = ip.link_lookup(ifname=ns_eth_out)[0]
self.pem_port[c_uint(curr_pem_pid)] = c_uint(ifindex) self.pem_port[c_uint(curr_pem_pid)] = c_uint(ifindex)
self.pem_ifindex[c_uint(ifindex)] = c_uint(curr_pem_pid) self.pem_ifindex[c_uint(ifindex)] = c_uint(curr_pem_pid)
mac_addr = br_mac_map.Key(int(EUI(vm_mac.decode()))) mac_addr = br_mac_map.Key(int(EUI(vm_mac.decode())))
br_mac_map[mac_addr] = c_uint(curr_br_pid) br_mac_map[mac_addr] = c_uint(curr_br_pid)
def attach_filter(self, ip, ifname, fd, name):
ifindex = ip.link_lookup(ifname=ifname)[0]
ip.tc("add", "ingress", ifindex, "ffff:")
ip.tc("add-filter", "bpf", ifindex, ":1", fd=fd, name=name,
parent="ffff:", action="drop", classid=1)
def config_maps(self): def config_maps(self):
b = BPF(src_file=arg1, debug=0)
pem_fn = b.load_func("pem", BPF.SCHED_CLS)
br1_fn = b.load_func("br1", BPF.SCHED_CLS)
br2_fn = b.load_func("br2", BPF.SCHED_CLS)
ip = IPRoute()
# program id # program id
prog_id_pem = 1 prog_id_pem = 1
prog_id_br1 = 2 prog_id_br1 = 2
...@@ -173,89 +122,97 @@ class TestBPFSocket(TestCase): ...@@ -173,89 +122,97 @@ class TestBPFSocket(TestCase):
curr_pem_pid = 0 curr_pem_pid = 0
curr_br1_pid = 0 curr_br1_pid = 0
curr_br2_pid = 0 curr_br2_pid = 0
self.get_table(b)
# configure jump table # configure jump table
self.jump[c_uint(prog_id_pem)] = c_uint(pem_fn.fd) self.jump[c_uint(prog_id_pem)] = c_uint(self.pem_fn.fd)
self.jump[c_uint(prog_id_br1)] = c_uint(br1_fn.fd) self.jump[c_uint(prog_id_br1)] = c_uint(self.br1_fn.fd)
self.jump[c_uint(prog_id_br2)] = c_uint(br2_fn.fd) self.jump[c_uint(prog_id_br2)] = c_uint(self.br2_fn.fd)
# connect pem and br1 # connect pem and br1
curr_pem_pid = curr_pem_pid + 1 curr_pem_pid = curr_pem_pid + 1
curr_br1_pid = curr_br1_pid + 1 curr_br1_pid = curr_br1_pid + 1
self.connect_ports(prog_id_pem, prog_id_br1, curr_pem_pid, curr_br1_pid, self.connect_ports(prog_id_pem, prog_id_br1, curr_pem_pid, curr_br1_pid,
ip, self.br1_dest, self.br1_mac, self.br1_dest, self.br1_mac,
self.ns1_eth_out, self.vm1_mac, self.vm1_ip) self.ns1_eth_out.index, self.vm1_mac, self.vm1_ip)
# connect pem and br2 # connect pem and br2
curr_pem_pid = curr_pem_pid + 1 curr_pem_pid = curr_pem_pid + 1
curr_br2_pid = curr_br2_pid + 1 curr_br2_pid = curr_br2_pid + 1
self.connect_ports(prog_id_pem, prog_id_br2, curr_pem_pid, curr_br2_pid, self.connect_ports(prog_id_pem, prog_id_br2, curr_pem_pid, curr_br2_pid,
ip, self.br2_dest, self.br2_mac, self.br2_dest, self.br2_mac,
self.ns2_eth_out, self.vm2_mac, self.vm2_ip) self.ns2_eth_out.index, self.vm2_mac, self.vm2_ip)
# connect <br1, rtr> and <br2, rtr> # connect <br1, rtr> and <br2, rtr>
ifindex = ip.link_lookup(ifname=self.nsrtr_eth0_out)[0] self.br1_rtr[c_uint(0)] = c_uint(self.nsrtr_eth0_out.index)
self.br1_rtr[c_uint(0)] = c_uint(ifindex) self.br2_rtr[c_uint(0)] = c_uint(self.nsrtr_eth1_out.index)
ifindex = ip.link_lookup(ifname=self.nsrtr_eth1_out)[0]
self.br2_rtr[c_uint(0)] = c_uint(ifindex)
# tc filter setup with bpf programs attached def test_brb(self):
self.attach_filter(ip, self.ns1_eth_out, pem_fn.fd, pem_fn.name) try:
self.attach_filter(ip, self.ns2_eth_out, pem_fn.fd, pem_fn.name) b = BPF(src_file=arg1, debug=0)
self.attach_filter(ip, self.nsrtr_eth0_out, br1_fn.fd, br1_fn.name) self.pem_fn = b.load_func("pem", BPF.SCHED_CLS)
self.attach_filter(ip, self.nsrtr_eth1_out, br2_fn.fd, br2_fn.name) self.br1_fn = b.load_func("br1", BPF.SCHED_CLS)
self.br2_fn = b.load_func("br2", BPF.SCHED_CLS)
def setUp(self): self.get_table(b)
# set up the environment # set up the topology
self.set_default_const() self.set_default_const()
self.setup_vm_ns(self.ns1, self.ns1_eth_in, self.ns1_eth_out) (ns1_ipdb, self.ns1_eth_out, _) = sim._create_ns(self.ns1, ipaddr=self.vm1_ip+'/24',
self.setup_vm_ns(self.ns2, self.ns2_eth_in, self.ns2_eth_out) fn=self.pem_fn, action='drop',
self.config_vm_ns(self.ns1, self.vm1_ip, self.vm2_rtr_mask, self.vm1_rtr_ip) disable_ipv6=True)
self.config_vm_ns(self.ns2, self.vm2_ip, self.vm1_rtr_mask, self.vm2_rtr_ip) (ns2_ipdb, self.ns2_eth_out, _) = sim._create_ns(self.ns2, ipaddr=self.vm2_ip+'/24',
self.setup_router_ns(self.ns_router, self.nsrtr_eth0_in, self.nsrtr_eth0_out, fn=self.pem_fn, action='drop',
self.nsrtr_eth1_in, self.nsrtr_eth1_out) disable_ipv6=True)
self.config_router_ns(self.ns_router, self.vm1_rtr_ip, self.vm2_rtr_ip) ns1_ipdb.routes.add({'dst': self.vm2_rtr_mask, 'gateway': self.vm1_rtr_ip}).commit()
ns2_ipdb.routes.add({'dst': self.vm1_rtr_mask, 'gateway': self.vm2_rtr_ip}).commit()
# get vm mac address self.vm1_mac = ns1_ipdb.interfaces['eth0'].address
self.vm1_mac = subprocess.check_output(["ip", "netns", "exec", self.ns1, "cat", "/sys/class/net/eth0/address"]) self.vm2_mac = ns2_ipdb.interfaces['eth0'].address
self.vm1_mac = self.vm1_mac.strip()
self.vm2_mac = subprocess.check_output(["ip", "netns", "exec", self.ns2, "cat", "/sys/class/net/eth0/address"]) (_, self.nsrtr_eth0_out, _) = sim._create_ns(self.ns_router, ipaddr=self.vm1_rtr_ip+'/24',
self.vm2_mac = self.vm2_mac.strip() fn=self.br1_fn, action='drop',
disable_ipv6=True)
# load the program and configure maps (rt_ipdb, self.nsrtr_eth1_out, _) = sim._ns_add_ifc(self.ns_router, "eth1", "ns_router2",
ipaddr=self.vm2_rtr_ip+'/24',
fn=self.br2_fn, action='drop',
disable_ipv6=True)
nsp = NSPopen(rt_ipdb.nl.netns, ["sysctl", "-w", "net.ipv4.ip_forward=1"])
nsp.wait(); nsp.release()
# configure maps
self.config_maps() self.config_maps()
def test_brb(self):
# our bridge is not smart enough, so send arping for router learning to prevent router # our bridge is not smart enough, so send arping for router learning to prevent router
# from sending out arp request # from sending out arp request
subprocess.call(["ip", "netns", "exec", self.ns1, "arping", "-w", "1", "-c", "1", "-I", "eth0", nsp = NSPopen(ns1_ipdb.nl.netns,
self.vm1_rtr_ip]) ["arping", "-w", "1", "-c", "1", "-I", "eth0", self.vm1_rtr_ip])
subprocess.call(["ip", "netns", "exec", self.ns2, "arping", "-w", "1", "-c", "1", "-I", "eth0", nsp.wait(); nsp.release()
self.vm2_rtr_ip]) nsp = NSPopen(ns2_ipdb.nl.netns,
["arping", "-w", "1", "-c", "1", "-I", "eth0", self.vm2_rtr_ip])
nsp.wait(); nsp.release()
# ping # ping
subprocess.call(["ip", "netns", "exec", self.ns1, "ping", self.vm2_ip, "-c", "2"]) nsp = NSPopen(ns1_ipdb.nl.netns, ["ping", self.vm2_ip, "-c", "2"])
# minimum one arp reply, 5 icmp reply nsp.wait(); nsp.release()
self.assertGreater(self.pem_stats[c_uint(0)].value, 5) # pem_stats only counts pem->bridge traffic, each VM has 4: arping/arp request/2 icmp request
# total 8 packets should be counted
self.assertEqual(self.pem_stats[c_uint(0)].value, 8)
# iperf, run server on the background nsp_server = NSPopen(ns2_ipdb.nl.netns, ["iperf", "-s", "-xSC"])
subprocess.Popen(["ip", "netns", "exec", self.ns2, "iperf", "-s", "-xSCD"])
sleep(1) sleep(1)
subprocess.call(["ip", "netns", "exec", self.ns1, "iperf", "-c", self.vm2_ip, "-t", "1", "-xSC"]) nsp = NSPopen(ns1_ipdb.nl.netns, ["iperf", "-c", self.vm2_ip, "-t", "1", "-xSC"])
subprocess.call(["ip", "netns", "exec", self.ns2, "killall", "iperf"]) nsp.wait(); nsp.release()
nsp_server.kill(); nsp_server.wait(); nsp_server.release()
# netperf, run server on the background nsp_server = NSPopen(ns2_ipdb.nl.netns, ["netserver", "-D"])
subprocess.Popen(["ip", "netns", "exec", self.ns2, "netserver"])
sleep(1) sleep(1)
subprocess.call(["ip", "netns", "exec", self.ns1, "netperf", "-l", "1", "-H", self.vm2_ip, "--", "-m", "65160"]) nsp = NSPopen(ns1_ipdb.nl.netns, ["netperf", "-l", "1", "-H", self.vm2_ip, "--", "-m", "65160"])
subprocess.call(["ip", "netns", "exec", self.ns1, "netperf", "-l", "1", "-H", self.vm2_ip, "-t", "TCP_RR"]) nsp.wait(); nsp.release()
subprocess.call(["ip", "netns", "exec", self.ns2, "killall", "netserver"]) nsp = NSPopen(ns1_ipdb.nl.netns, ["netperf", "-l", "1", "-H", self.vm2_ip, "-t", "TCP_RR"])
nsp.wait(); nsp.release()
# cleanup, tear down the veths and namespaces nsp_server.kill(); nsp_server.wait(); nsp_server.release()
subprocess.call(["ip", "netns", "del", self.ns1])
subprocess.call(["ip", "netns", "del", self.ns2]) finally:
subprocess.call(["ip", "netns", "del", self.ns_router]) sim.release()
ipdb.release()
if __name__ == "__main__": if __name__ == "__main__":
......
...@@ -24,5 +24,5 @@ int pem(struct __sk_buff *skb) { ...@@ -24,5 +24,5 @@ int pem(struct __sk_buff *skb) {
bpf_clone_redirect(skb, *ifindex_p, 0); bpf_clone_redirect(skb, *ifindex_p, 0);
} }
return 0; return 1;
} }
...@@ -54,72 +54,41 @@ ...@@ -54,72 +54,41 @@
# 9: # 9:
# 9: OK # 9: OK
from ctypes import c_ubyte, c_ushort, c_uint, c_ulonglong, Structure from ctypes import c_uint
from netaddr import IPAddress
from bpf import BPF from bpf import BPF
from pyroute2 import IPRoute from pyroute2 import IPRoute, NetNS, IPDB, NSPopen
from socket import socket, AF_INET, SOCK_DGRAM
import sys import sys
from time import sleep from time import sleep
from unittest import main, TestCase from unittest import main, TestCase
import subprocess import subprocess
from simulation import Simulation
arg1 = sys.argv.pop(1) arg1 = sys.argv.pop(1)
ipr = IPRoute()
ipdb = IPDB(nl=ipr)
sim = Simulation(ipdb)
class TestBPFSocket(TestCase): class TestBPFSocket(TestCase):
def setup_vm_ns(self, ns, veth_in, veth_out): def setup_br(self, br, veth_rt_2_br, veth_pem_2_br, veth_br_2_pem):
subprocess.call(["ip", "link", "add", veth_in, "type", "veth", "peer", "name", veth_out]) # create veth which connecting pem and br
subprocess.call(["ip", "netns", "add", ns]) with ipdb.create(ifname=veth_pem_2_br, kind="veth", peer=veth_br_2_pem) as v:
subprocess.call(["ip", "link", "set", veth_in, "netns", ns]) v.up()
subprocess.call(["ip", "netns", "exec", ns, "ip", "link", "set", veth_in, "name", "eth0"]) ipdb.interfaces[veth_br_2_pem].up().commit()
subprocess.call(["ip", "link", "set", veth_out, "up"]) subprocess.call(["sysctl", "-q", "-w", "net.ipv6.conf." + veth_pem_2_br + ".disable_ipv6=1"])
subprocess.call(["sysctl", "-q", "-w", "net.ipv6.conf." + veth_br_2_pem + ".disable_ipv6=1"])
def config_vm_ns(self, ns, ip_addr, net_mask, ip_gw):
subprocess.call(["ip", "netns", "exec", ns, "ip", "addr", "add", ip_addr + "/24", "dev", "eth0"])
subprocess.call(["ip", "netns", "exec", ns, "ip", "link", "set", "eth0", "up"])
subprocess.call(["ip", "netns", "exec", ns, "route", "add", "-net", net_mask + "/24", "gw", ip_gw])
def setup_router_ns(self, ns, veth1_in, veth1_out, veth2_in, veth2_out):
subprocess.call(["ip", "netns", "add", ns])
subprocess.call(["ip", "link", "add", veth1_in, "type", "veth", "peer", "name", veth1_out])
subprocess.call(["ip", "link", "set", veth1_in, "netns", ns])
subprocess.call(["ip", "netns", "exec", ns, "ip", "link", "set", veth1_in, "name", "eth0"])
subprocess.call(["ip", "link", "add", veth2_in, "type", "veth", "peer", "name", veth2_out])
subprocess.call(["ip", "link", "set", veth2_in, "netns", ns])
subprocess.call(["ip", "netns", "exec", ns, "ip", "link", "set", veth2_in, "name", "eth1"])
subprocess.call(["ip", "link", "set", veth1_out, "up"])
subprocess.call(["ip", "link", "set", veth2_out, "up"])
def config_router_ns(self, ns, ip_eth0, ip_eth1):
subprocess.call(["ip", "netns", "exec", ns, "ip", "addr", "add", ip_eth0 + "/24", "dev", "eth0"])
subprocess.call(["ip", "netns", "exec", ns, "ip", "link", "set", "eth0", "up"])
subprocess.call(["ip", "netns", "exec", ns, "ip", "addr", "add", ip_eth1 + "/24", "dev", "eth1"])
subprocess.call(["ip", "netns", "exec", ns, "ip", "link", "set", "eth1", "up"])
def setup_br(self, br, veth_rt_2_br):
# set up the bridge and add router interface as one of its slaves
subprocess.call(["ip", "link", "add", "name", br, "type", "bridge"])
subprocess.call(["ip", "link", "set", "dev", veth_rt_2_br, "master", br])
subprocess.call(["ip", "link", "set", br, "up"])
def br_add_pem_link(self, br, veth_pem_2_br, veth_br_2_pem): # set up the bridge and add router interface as one of its slaves
subprocess.call(["ip", "link", "add", veth_pem_2_br, "type", "veth", "peer", "name", veth_br_2_pem]) with ipdb.create(ifname=br, kind="bridge") as br1:
subprocess.call(["ip", "link", "set", "dev", veth_pem_2_br, "master", br]) br1.add_port(ipdb.interfaces[veth_pem_2_br])
subprocess.call(["ip", "link", "set", veth_pem_2_br, "up"]) br1.add_port(ipdb.interfaces[veth_rt_2_br])
subprocess.call(["ip", "link", "set", veth_br_2_pem, "up"]) br1.up()
subprocess.call(["sysctl", "-q", "-w", "net.ipv6.conf." + br + ".disable_ipv6=1"])
def set_default_const(self): def set_default_const(self):
self.ns1 = "ns1" self.ns1 = "ns1"
self.ns1_eth_in = "v1"
self.ns1_eth_out = "v2"
self.ns2 = "ns2" self.ns2 = "ns2"
self.ns2_eth_in = "v3"
self.ns2_eth_out = "v4"
self.ns_router = "ns_router" self.ns_router = "ns_router"
self.nsrtr_eth0_in = "v10"
self.nsrtr_eth0_out = "v11"
self.nsrtr_eth1_in = "v12"
self.nsrtr_eth1_out = "v13"
self.br1 = "br1" self.br1 = "br1"
self.veth_pem_2_br1 = "v20" self.veth_pem_2_br1 = "v20"
self.veth_br1_2_pem = "v21" self.veth_br1_2_pem = "v21"
...@@ -131,88 +100,91 @@ class TestBPFSocket(TestCase): ...@@ -131,88 +100,91 @@ class TestBPFSocket(TestCase):
self.vm2_ip = "200.1.1.1" self.vm2_ip = "200.1.1.1"
self.vm1_rtr_ip = "100.1.1.254" self.vm1_rtr_ip = "100.1.1.254"
self.vm2_rtr_ip = "200.1.1.254" self.vm2_rtr_ip = "200.1.1.254"
self.vm1_rtr_mask = "100.1.1.0" self.vm1_rtr_mask = "100.1.1.0/24"
self.vm2_rtr_mask = "200.1.1.0" self.vm2_rtr_mask = "200.1.1.0/24"
def attach_filter(self, ip, ifname, fd, name): def attach_filter(self, ifname, fd, name):
ifindex = ip.link_lookup(ifname=ifname)[0] ifindex = ipdb.interfaces[ifname].index
ip.tc("add", "ingress", ifindex, "ffff:") ipr.tc("add", "ingress", ifindex, "ffff:")
ip.tc("add-filter", "bpf", ifindex, ":1", fd=fd, name=name, ipr.tc("add-filter", "bpf", ifindex, ":1", fd=fd, name=name,
parent="ffff:", action="drop", classid=1) parent="ffff:", action="drop", classid=1)
def config_maps(self): def config_maps(self):
b = BPF(src_file=arg1, debug=0)
pem_fn = b.load_func("pem", BPF.SCHED_CLS)
self.pem_dest= b.get_table("pem_dest")
self.pem_stats = b.get_table("pem_stats")
ip = IPRoute()
# pem just relays packets between VM and its corresponding # pem just relays packets between VM and its corresponding
# slave link in the bridge interface # slave link in the bridge interface
ns1_ifindex = ip.link_lookup(ifname=self.ns1_eth_out)[0] ns1_ifindex = self.ns1_eth_out.index
ns2_ifindex = ip.link_lookup(ifname=self.ns2_eth_out)[0] ns2_ifindex = self.ns2_eth_out.index
br1_ifindex = ip.link_lookup(ifname=self.veth_br1_2_pem)[0] br1_ifindex = ipdb.interfaces[self.veth_br1_2_pem].index
br2_ifindex = ip.link_lookup(ifname=self.veth_br2_2_pem)[0] br2_ifindex = ipdb.interfaces[self.veth_br2_2_pem].index
self.pem_dest[c_uint(ns1_ifindex)] = c_uint(br1_ifindex) self.pem_dest[c_uint(ns1_ifindex)] = c_uint(br1_ifindex)
self.pem_dest[c_uint(br1_ifindex)] = c_uint(ns1_ifindex) self.pem_dest[c_uint(br1_ifindex)] = c_uint(ns1_ifindex)
self.pem_dest[c_uint(ns2_ifindex)] = c_uint(br2_ifindex) self.pem_dest[c_uint(ns2_ifindex)] = c_uint(br2_ifindex)
self.pem_dest[c_uint(br2_ifindex)] = c_uint(ns2_ifindex) self.pem_dest[c_uint(br2_ifindex)] = c_uint(ns2_ifindex)
# tc filter setup with bpf programs attached # tc filter setup with bpf programs attached
self.attach_filter(ip, self.ns1_eth_out, pem_fn.fd, pem_fn.name) self.attach_filter(self.veth_br1_2_pem, self.pem_fn.fd, self.pem_fn.name)
self.attach_filter(ip, self.ns2_eth_out, pem_fn.fd, pem_fn.name) self.attach_filter(self.veth_br2_2_pem, self.pem_fn.fd, self.pem_fn.name)
self.attach_filter(ip, self.veth_br1_2_pem, pem_fn.fd, pem_fn.name)
self.attach_filter(ip, self.veth_br2_2_pem, pem_fn.fd, pem_fn.name)
def setUp(self): def test_brb2(self):
try:
b = BPF(src_file=arg1, debug=0)
self.pem_fn = b.load_func("pem", BPF.SCHED_CLS)
self.pem_dest= b.get_table("pem_dest")
self.pem_stats = b.get_table("pem_stats")
# set up the environment # set up the topology
self.set_default_const() self.set_default_const()
self.setup_vm_ns(self.ns1, self.ns1_eth_in, self.ns1_eth_out) (ns1_ipdb, self.ns1_eth_out, _) = sim._create_ns(self.ns1, ipaddr=self.vm1_ip+'/24',
self.setup_vm_ns(self.ns2, self.ns2_eth_in, self.ns2_eth_out) fn=self.pem_fn, action='drop',
self.config_vm_ns(self.ns1, self.vm1_ip, self.vm2_rtr_mask, self.vm1_rtr_ip) disable_ipv6=True)
self.config_vm_ns(self.ns2, self.vm2_ip, self.vm1_rtr_mask, self.vm2_rtr_ip) (ns2_ipdb, self.ns2_eth_out, _) = sim._create_ns(self.ns2, ipaddr=self.vm2_ip+'/24',
self.setup_router_ns(self.ns_router, self.nsrtr_eth0_in, self.nsrtr_eth0_out, fn=self.pem_fn, action='drop',
self.nsrtr_eth1_in, self.nsrtr_eth1_out) disable_ipv6=True)
self.config_router_ns(self.ns_router, self.vm1_rtr_ip, self.vm2_rtr_ip) ns1_ipdb.routes.add({'dst': self.vm2_rtr_mask, 'gateway': self.vm1_rtr_ip}).commit()
ns2_ipdb.routes.add({'dst': self.vm1_rtr_mask, 'gateway': self.vm2_rtr_ip}).commit()
# for each VM connecting to pem, there will be a corresponding veth
# connecting to the bridge (_, self.nsrtr_eth0_out, _) = sim._create_ns(self.ns_router, ipaddr=self.vm1_rtr_ip+'/24',
self.setup_br(self.br1, self.nsrtr_eth0_out) disable_ipv6=True)
self.br_add_pem_link(self.br1, self.veth_pem_2_br1, self.veth_br1_2_pem) (rt_ipdb, self.nsrtr_eth1_out, _) = sim._ns_add_ifc(self.ns_router, "eth1", "ns_router2",
self.setup_br(self.br2, self.nsrtr_eth1_out) ipaddr=self.vm2_rtr_ip+'/24',
self.br_add_pem_link(self.br2, self.veth_pem_2_br2, self.veth_br2_2_pem) disable_ipv6=True)
# enable ip forwarding in router ns
nsp = NSPopen(rt_ipdb.nl.netns, ["sysctl", "-w", "net.ipv4.ip_forward=1"])
nsp.wait(); nsp.release()
# for each VM connecting to pem, there will be a corresponding veth connecting to the bridge
self.setup_br(self.br1, self.nsrtr_eth0_out.ifname, self.veth_pem_2_br1, self.veth_br1_2_pem)
self.setup_br(self.br2, self.nsrtr_eth1_out.ifname, self.veth_pem_2_br2, self.veth_br2_2_pem)
# load the program and configure maps # load the program and configure maps
self.config_maps() self.config_maps()
def test_brb2(self):
# ping # ping
subprocess.call(["ip", "netns", "exec", self.ns1, "ping", self.vm2_ip, "-c", "2"]) nsp = NSPopen(ns1_ipdb.nl.netns, ["ping", self.vm2_ip, "-c", "2"]); nsp.wait(); nsp.release()
# minimum one arp request/reply, 5 icmp request/reply # one arp request/reply, 2 icmp request/reply per VM, total 6 packets per VM, 12 packets total
self.assertGreater(self.pem_stats[c_uint(0)].value, 11) self.assertEqual(self.pem_stats[c_uint(0)].value, 12)
# iperf, run server on the background nsp_server = NSPopen(ns2_ipdb.nl.netns, ["iperf", "-s", "-xSC"])
subprocess.Popen(["ip", "netns", "exec", self.ns2, "iperf", "-s", "-xSCD"])
sleep(1) sleep(1)
subprocess.call(["ip", "netns", "exec", self.ns1, "iperf", "-c", self.vm2_ip, "-t", "1", "-xSC"]) nsp = NSPopen(ns1_ipdb.nl.netns, ["iperf", "-c", self.vm2_ip, "-t", "1", "-xSC"])
subprocess.call(["ip", "netns", "exec", self.ns2, "killall", "iperf"]) nsp.wait(); nsp.release()
nsp_server.kill(); nsp_server.wait(); nsp_server.release()
# netperf, run server on the background nsp_server = NSPopen(ns2_ipdb.nl.netns, ["netserver", "-D"])
subprocess.Popen(["ip", "netns", "exec", self.ns2, "netserver"])
sleep(1) sleep(1)
subprocess.call(["ip", "netns", "exec", self.ns1, "netperf", "-l", "1", "-H", self.vm2_ip, "--", "-m", "65160"]) nsp = NSPopen(ns1_ipdb.nl.netns, ["netperf", "-l", "1", "-H", self.vm2_ip, "--", "-m", "65160"])
subprocess.call(["ip", "netns", "exec", self.ns1, "netperf", "-l", "1", "-H", self.vm2_ip, "-t", "TCP_RR"]) nsp.wait(); nsp.release()
subprocess.call(["ip", "netns", "exec", self.ns2, "killall", "netserver"]) nsp = NSPopen(ns1_ipdb.nl.netns, ["netperf", "-l", "1", "-H", self.vm2_ip, "-t", "TCP_RR"])
nsp.wait(); nsp.release()
# cleanup, tear down the veths and namespaces nsp_server.kill(); nsp_server.wait(); nsp_server.release()
subprocess.call(["ip", "link", "del", self.veth_br1_2_pem])
subprocess.call(["ip", "link", "del", self.veth_br2_2_pem]) finally:
subprocess.call(["ip", "link", "del", self.br1]) if self.br1 in ipdb.interfaces: ipdb.interfaces[self.br1].remove().commit()
subprocess.call(["ip", "link", "del", self.br2]) if self.br2 in ipdb.interfaces: ipdb.interfaces[self.br2].remove().commit()
subprocess.call(["ip", "netns", "del", self.ns1]) if self.veth_pem_2_br1 in ipdb.interfaces: ipdb.interfaces[self.veth_pem_2_br1].remove().commit()
subprocess.call(["ip", "netns", "del", self.ns2]) if self.veth_pem_2_br2 in ipdb.interfaces: ipdb.interfaces[self.veth_pem_2_br2].remove().commit()
subprocess.call(["ip", "netns", "del", self.ns_router]) sim.release()
ipdb.release()
if __name__ == "__main__": if __name__ == "__main__":
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment