SIGCOMM 2019 Tutorial Edits (#272)

* Updated the utils/run_exercise.py to allow exercises to customize
host configuration from the topology.json file.

Now hosts and `ping` each other in the basic exercise. Other Linux
utilities should work as well (e.g. iperf).

```
mininet> h1 ping h2
PING 10.0.2.2 (10.0.2.2) 56(84) bytes of data.
64 bytes from 10.0.2.2: icmp_seq=1 ttl=62 time=3.11 ms
64 bytes from 10.0.2.2: icmp_seq=2 ttl=62 time=2.34 ms
64 bytes from 10.0.2.2: icmp_seq=3 ttl=62 time=2.15 ms
^C
--- 10.0.2.2 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2003ms
rtt min/avg/max/mdev = 2.153/2.540/3.118/0.416 ms
mininet> pingall
*** Ping: testing ping reachability
h1 -> h2 h3
h2 -> h1 h3
h3 -> h1 h2
*** Results: 0% dropped (6/6 received)
```

Only updated basic exercise, still need to update other exercises.

Also, updated the root-bootstrap.sh because I was running into issues
with latest version of vagrant.

* Accidentially added the solution to the basic exercise in the previous
commit. Undoing that here ...

* Updated the topology.json file and table entries for the basic_tunnel
exercise.

* Updated P4Runtime exercise with new topology and table entries.

* Fixed MAC addresses in P4Runtime exercise. It is working now.

* Fixed MAC addresses in P4Runtime exercise starter code

* Updated ECN exercise to use new topology.json file. Updated the
table entries / MAC addresses as well.

* Updated the topology.json file and table entries for the MRI exercise.

* Updated source_routing exercise with new topology file and verified
correct functionality.

* Updated load_balance exercise with new topology.

* Moved basic exercise triangle topology into a separate folder

* Added new topology for the basic exercise: a single pod of a fat-tree.

* Updated Makefiles and run_exercise.py to allow exercises to configure
each switch with a different P4 program. This is mainly for the
firewall exercise.

* Updated Makefiles of project to work with new utils/Makefile

* Updated load_balance and p4runtime exercise Makefiles

* Initial commit of the firewall exercise, which is a simple stateful
firewall that uses a bloom filter. Need to update README files

* Initial commit of the path_monitor exercise. It is working but still
need to update the README and figure out what we want the tutorial
attendees to implement.

* Updated README file in firewall exercise. Also removed the bits
from the starter code that we want the tutorial attendees to
implement

* Renamed path_monitor exercise to link_monitor

* Updated the README in the link_monitor exercise and removed the
bits from the starter code that we want the tutorial attendees
to implement.

* Updated README for the firewall exercise

* Adding pod-topo.png image to basic exercise

* Added firewall-topo.png image to firewall exercise

* Added link-monitor-topo.png to link_monitor exercise

* Updated README files to point to topology images

* Updated top-level README to point to new exercises.

* Fixed link for VM dependencies script in README

* Updated bmv2/pi/p4c commits

* Updated README files for exercises to fix some typos and added
a note about the V1Model architecture.

* Added a note about food for thought in the link_monitor README

* Updated the firewall.p4 program to use two register arrays rather
than a single one. This is to make the design more portable to
high line rate devices which can only support a single access
to each register array.

* Minor fix to firewall exercise to get rid of compiler warning.

* Updated comment in firewall exercise.

* Minor (typo) fixes in the firewall ReadMe

* More info in firewall exercise ReadMe step 2

* Updated firewall.p4 to reuse direction variable

* More testing steps, small fixes in firewall exercise Readme

* Added food for thought to firewall Readme

* Cosmetic fixes to firewall ReadMe

* Made a few updates to the basic exercise README and added more
details to the link_monitor exercise README.

Also added a command to install grip when provisioning the VM.
This could be useful for rendering the markdown README files offline.

* Updated top level README so it can be merged into the master branch.

* Moved cmd to install grip from root-bootstrap to user-bootstrap
This commit is contained in:
sibanez12
2019-08-14 03:39:06 -07:00
committed by Nate Foster
parent b5c82700b8
commit 76a9067dea
69 changed files with 3015 additions and 246 deletions

View File

@@ -2,18 +2,26 @@ BUILD_DIR = build
PCAP_DIR = pcaps
LOG_DIR = logs
TOPO = topology.json
P4C = p4c-bm2-ss
P4C_ARGS += --p4runtime-files $(BUILD_DIR)/$(basename $@).p4.p4info.txt
RUN_SCRIPT = ../../utils/run_exercise.py
source := $(wildcard *.p4)
outfile := $(source:.p4=.json)
ifndef TOPO
TOPO = topology.json
endif
compiled_json := $(BUILD_DIR)/$(outfile)
source = $(wildcard *.p4)
compiled_json := $(source:.p4=.json)
ifndef DEFAULT_PROG
DEFAULT_PROG = $(wildcard *.p4)
endif
DEFAULT_JSON = $(BUILD_DIR)/$(DEFAULT_PROG:.p4=.json)
# Define NO_P4 to start BMv2 without a program
ifndef NO_P4
run_args += -j $(compiled_json)
run_args += -j $(DEFAULT_JSON)
endif
# Set BMV2_SWITCH_EXE to override the BMv2 target
@@ -31,8 +39,8 @@ stop:
build: dirs $(compiled_json)
$(BUILD_DIR)/%.json: %.p4
$(P4C) --p4v 16 $(P4C_ARGS) -o $@ $<
%.json: %.p4
$(P4C) --p4v 16 $(P4C_ARGS) -o $(BUILD_DIR)/$@ $<
dirs:
mkdir -p $(BUILD_DIR) $(PCAP_DIR) $(LOG_DIR)

View File

@@ -48,7 +48,7 @@ class P4RuntimeSwitch(P4Switch):
if json_path is not None:
# make sure that the provided JSON file exists
if not os.path.isfile(json_path):
error("Invalid JSON file.\n")
error("Invalid JSON file: {}\n".format(json_path))
exit(1)
self.json_path = json_path
else:

View File

@@ -66,65 +66,57 @@ def configureP4Switch(**switch_args):
class ExerciseTopo(Topo):
""" The mininet topology class for the P4 tutorial exercises.
A custom class is used because the exercises make a few topology
assumptions, mostly about the IP and MAC addresses.
"""
def __init__(self, hosts, switches, links, log_dir, **opts):
def __init__(self, hosts, switches, links, log_dir, bmv2_exe, pcap_dir, **opts):
Topo.__init__(self, **opts)
host_links = []
switch_links = []
self.sw_port_mapping = {}
# assumes host always comes first for host<-->switch links
for link in links:
if link['node1'][0] == 'h':
host_links.append(link)
else:
switch_links.append(link)
link_sort_key = lambda x: x['node1'] + x['node2']
# Links must be added in a sorted order so bmv2 port numbers are predictable
host_links.sort(key=link_sort_key)
switch_links.sort(key=link_sort_key)
for sw in switches:
self.addSwitch(sw, log_file="%s/%s.log" %(log_dir, sw))
for sw, params in switches.iteritems():
if "program" in params:
switchClass = configureP4Switch(
sw_path=bmv2_exe,
json_path=params["program"],
log_console=True,
pcap_dump=pcap_dir)
else:
# add default switch
switchClass = None
self.addSwitch(sw, log_file="%s/%s.log" %(log_dir, sw), cls=switchClass)
for link in host_links:
host_name = link['node1']
host_sw = link['node2']
host_num = int(host_name[1:])
sw_num = int(host_sw[1:])
host_ip = "10.0.%d.%d" % (sw_num, host_num)
host_mac = '00:00:00:00:%02x:%02x' % (sw_num, host_num)
# Each host IP should be /24, so all exercise traffic will use the
# default gateway (the switch) without sending ARP requests.
self.addHost(host_name, ip=host_ip+'/24', mac=host_mac)
self.addLink(host_name, host_sw,
sw_name, sw_port = self.parse_switch_node(link['node2'])
host_ip = hosts[host_name]['ip']
host_mac = hosts[host_name]['mac']
self.addHost(host_name, ip=host_ip, mac=host_mac)
self.addLink(host_name, sw_name,
delay=link['latency'], bw=link['bandwidth'],
addr1=host_mac, addr2=host_mac)
self.addSwitchPort(host_sw, host_name)
port2=sw_port)
for link in switch_links:
self.addLink(link['node1'], link['node2'],
sw1_name, sw1_port = self.parse_switch_node(link['node1'])
sw2_name, sw2_port = self.parse_switch_node(link['node2'])
self.addLink(sw1_name, sw2_name,
port1=sw1_port, port2=sw2_port,
delay=link['latency'], bw=link['bandwidth'])
self.addSwitchPort(link['node1'], link['node2'])
self.addSwitchPort(link['node2'], link['node1'])
self.printPortMapping()
def addSwitchPort(self, sw, node2):
if sw not in self.sw_port_mapping:
self.sw_port_mapping[sw] = []
portno = len(self.sw_port_mapping[sw])+1
self.sw_port_mapping[sw].append((portno, node2))
def printPortMapping(self):
print "Switch port mapping:"
for sw in sorted(self.sw_port_mapping.keys()):
print "%s: " % sw,
for portno, node2 in self.sw_port_mapping[sw]:
print "%d:%s\t" % (portno, node2),
print
def parse_switch_node(self, node):
assert(len(node.split('-')) == 2)
sw_name, sw_port = node.split('-')
try:
sw_port = int(sw_port[1])
except:
raise Exception('Invalid switch node in topology file: {}'.format(node))
return sw_name, sw_port
class ExerciseRunner:
@@ -134,8 +126,8 @@ class ExerciseRunner:
pcap_dir : string // directory for mininet switch pcap files
quiet : bool // determines if we print logger messages
hosts : list<string> // list of mininet host names
switches : dict<string, dict> // mininet host names and their associated properties
hosts : dict<string, dict> // mininet host names and their associated properties
switches : dict<string, dict> // mininet switch names and their associated properties
links : list<dict> // list of mininet link properties
switch_json : string // json of the compiled p4 example
@@ -149,7 +141,7 @@ class ExerciseRunner:
if not self.quiet:
print(' '.join(items))
def formatLatency(self, l):
def format_latency(self, l):
""" Helper method for parsing link latencies from the topology json. """
if isinstance(l, (str, unicode)):
return l
@@ -232,7 +224,7 @@ class ExerciseRunner:
'bandwidth':None
}
if len(link) > 2:
link_dict['latency'] = self.formatLatency(link[2])
link_dict['latency'] = self.format_latency(link[2])
if len(link) > 3:
link_dict['bandwidth'] = link[3]
@@ -251,18 +243,18 @@ class ExerciseRunner:
"""
self.logger("Building mininet topology.")
self.topo = ExerciseTopo(self.hosts, self.switches.keys(), self.links, self.log_dir)
defaultSwitchClass = configureP4Switch(
sw_path=self.bmv2_exe,
json_path=self.switch_json,
log_console=True,
pcap_dump=self.pcap_dir)
switchClass = configureP4Switch(
sw_path=self.bmv2_exe,
json_path=self.switch_json,
log_console=True,
pcap_dump=self.pcap_dir)
self.topo = ExerciseTopo(self.hosts, self.switches, self.links, self.log_dir, self.bmv2_exe, self.pcap_dir)
self.net = Mininet(topo = self.topo,
link = TCLink,
host = P4Host,
switch = switchClass,
switch = defaultSwitchClass,
controller = None)
def program_switch_p4runtime(self, sw_name, sw_dict):
@@ -312,30 +304,13 @@ class ExerciseRunner:
self.program_switch_p4runtime(sw_name, sw_dict)
def program_hosts(self):
""" Adds static ARP entries and default routes to each mininet host.
Assumes:
- A mininet instance is stored as self.net and self.net.start() has
been called.
""" Execute any commands provided in the topology.json file on each Mininet host
"""
for host_name in self.topo.hosts():
for host_name, host_info in self.hosts.items():
h = self.net.get(host_name)
h_iface = h.intfs.values()[0]
link = h_iface.link
sw_iface = link.intf1 if link.intf1 != h_iface else link.intf2
# phony IP to lie to the host about
host_id = int(host_name[1:])
sw_ip = '10.0.%d.254' % host_id
# Ensure each host's interface name is unique, or else
# mininet cannot shutdown gracefully
h.defaultIntf().rename('%s-eth0' % host_name)
# static arp entries and default routes
h.cmd('arp -i %s -s %s %s' % (h_iface.name, sw_ip, sw_iface.mac))
h.cmd('ethtool --offload %s rx off tx off' % h_iface.name)
h.cmd('ip route add %s dev %s' % (sw_ip, h_iface.name))
h.setDefaultRoute("via %s" % sw_ip)
if "commands" in host_info:
for cmd in host_info["commands"]:
h.cmd(cmd)
def do_net_cli(self):