Tutorial 5 - Software-Defined Networking (SDN)

Submission process:

  • Submission deadline is January 16, 14:00 CET (before the lecture) .
  • Commit and push your solution as single notebook file via git as ./tutorial/tutorial5/tutorial5.ipynb. Please take care of the correct subfolder/filename since submission is denied otherwise.
  • During the first lecture after the deadline we will discuss a sample solution in class.
  • Afterwards, you have time until January 23, 14:00 CET (before the lecture) to submit a corrected version of your submission:
    1. Rework your solution according to our discussion in class.
    2. Commit and push the corrected version as single file via git as ./tutorial/tutorial5/tutorial5.ipynb. Please take care of the correct filename since submission is denied otherwise.

Remarks:

  • Grading is done based on both versions of your submission.
  • If the first submission is missing your submission will not be graded.
  • If the second submission contains major flaws after revision not more than half of the credits for this tutorial can be achieved.
  • A sample solution is provided after January 23, 14:00 CET eventually.
  • Please use acn@net.in.tum.de for questions regarding lecture, tutorial, and project of ACN.

Problem 1- OpenFlow [3.5 credits]

For the following exercise we use the network emulator mininet. The emulated network consists of two hosts called 'h1' and 'h2'. Both hosts are connected via the switch 's1'. For the switching solution we use the software switch Open vSwitch. We use the tool ovs-ofctl (see documentation), which is a CLI controller controlling OpenFlow devices manually.

In [1]:
# in case mininet stops working execute this cell
!mn -c
*** Removing excess controllers/ofprotocols/ofdatapaths/pings/noxes
killall controller ofprotocol ofdatapath ping nox_corelt-nox_core ovs-openflowd ovs-controllerovs-testcontroller udpbwtest mnexec ivs ryu-manager 2> /dev/null
killall -9 controller ofprotocol ofdatapath ping nox_corelt-nox_core ovs-openflowd ovs-controllerovs-testcontroller udpbwtest mnexec ivs ryu-manager 2> /dev/null
pkill -9 -f "sudo mnexec"
*** Removing junk from /tmp
rm -f /tmp/vconn* /tmp/vlogs* /tmp/*.out /tmp/*.log
*** Removing old X11 tunnels
*** Removing excess kernel datapaths
ps ax | egrep -o 'dp[0-9]+' | sed 's/dp/nl:/'
***  Removing OVS datapaths
ovs-vsctl --timeout=1 list-br
ovs-vsctl --timeout=1 list-br
*** Removing all links of the pattern foo-ethX
ip link show | egrep -o '([-_.[:alnum:]]+-eth[[:digit:]]+)'
ip link show
*** Killing stale mininet node processes
pkill -9 -f mininet:
*** Shutting down stale tunnels
pkill -9 -f Tunnel=Ethernet
pkill -9 -f .ssh/mn
rm -f ~/.ssh/mn/*
*** Cleanup complete.

a) [0.5 credits] What is the task of the control plane?

Make the decisions for the forwarding plane.

b) [0.5 credits] What is the task of the data plane?

Forwarding packets.

c) [0.25 credits] What is the forwarding plane?

Another word for data plane.

Execute the following cell to test the mininet setup. The setup works if all hosts are reachable using pingAll().

In [2]:
from mininet.net import Mininet
from mininet.topo import Topo
from mininet.link import TCLink
from mininet.node import OVSSwitch
from mininet.log import setLogLevel, info

class simpleTopo(Topo):
    "Four switches connected to 2 hosts as specified in the task."
    def __init__(self, **opts):
        # initialize topology and default options
        Topo.__init__(self, **opts)
        switch1 = self.addSwitch('s1')
        host1 = self.addHost('h1', ip='10.0.0.1')
        host2 = self.addHost('h2', ip='10.0.0.2')
        self.addLink(host1, switch1, bw=1, delay='1ms')
        self.addLink(host2, switch1, bw=1, delay='1ms')
        
def simpleTest(cmds):
    # create and start network
    topo = simpleTopo()
    net = Mininet(topo, link=TCLink, switch=OVSSwitch)
    net.start()
    
    # preparing switch for this exercise
    s1 = net.get('s1')
    info(s1.cmd('echo switch info:'))
    info(s1.cmd('ovs-vsctl show'))          # output switch info
    info(s1.cmd('ovs-ofctl dump-flows s1')) # print flow info
    info(s1.cmd('ovs-ofctl del-flows s1'))  # remove all default flows
    info(s1.cmd('ovs-ofctl dump-flows s1'))
    
    for cmd in cmds:
        info(s1.cmd(cmd))
        
    # tests reachability of all hosts
    net.pingAll()
    
    # shut down network
    net.stop()
    
if __name__ == '__main__':
    # tell mininet to print useful information
    setLogLevel('info')
    
    # enter user specified OpenFlow rules on the switch 
    cmds = [
         'ovs-ofctl add-flow s1 actions=output:flood' # remove this rule to disable switching
    ]
    
    simpleTest(cmds)
*** Creating network
*** Adding controller
*** Adding hosts:
h1 
h2 
*** Adding switches:
s1 
*** Adding links:
(1.00Mbit 1ms delay) 
(1.00Mbit 1ms delay) 
(h1, s1) 
(1.00Mbit 1ms delay) 
(1.00Mbit 1ms delay) 
(h2, s1) 

*** Configuring hosts
h1 
h2 

*** Starting controller
c0 

*** Starting 1 switches
s1 
...
(1.00Mbit 1ms delay) 
(1.00Mbit 1ms delay) 

switch info:
dd2dca19-2df1-43a2-992d-69c2900c9471
    Bridge s1
        Controller "tcp:127.0.0.1:6653"
        fail_mode: secure
        Port s1
            Interface s1
                type: internal
        Port s1-eth2
            Interface s1-eth2
        Port s1-eth1
            Interface s1-eth1
    ovs_version: "3.1.0"
 cookie=0x0, duration=0.069s, table=0, n_packets=0, n_bytes=0, priority=0 actions=CONTROLLER:128
*** Ping: testing ping reachability
h1 -> 
h2 

h2 -> 
h1 

*** Results: 0% dropped (2/2 received)
*** Stopping 1 controllers
c0 

*** Stopping 2 links
.
.

*** Stopping 1 switches
s1 

*** Stopping 2 hosts
h1 
h2 
*** Done

d) [0.5 credits] Execute the code of the network. The output gives the result of the ping (search for "Ping: testing ping reachability" in the output). What does the command "ovs-ofctl add-flow s1 actions=output:flood" do? How does the switch behave when receiving a packet?

Adds an OpenFlow rule (add-flow) on the device called (s1) which performs forwards the packet to all available ports except the receiving port (actions=output:flood). This transforms the switch (OpenFlow device) into a hub.

e) [0.25 credits] After installing the rule, which packets can be seen by the controller?

The controller cannot see any further packets.

f) [1.5 credits] Execute the following cell. Explain why the ping is not successful. Enter OpenFlow rules at the given location to fix this problem.

Note: You can ignore the following error: "*** Error: RTNETLINK answers: No such file or directory". This does not influence the behavior of the given program.

In [3]:
from mininet.net import Mininet
from mininet.topo import Topo
from mininet.link import TCLink
from mininet.node import OVSSwitch
from mininet.log import setLogLevel, info

class simpleTopo(Topo):
    "Four switches connected to 2 hosts as specified in the task."
    def __init__(self, **opts):
        # initialize topology and default options
        Topo.__init__(self, **opts)
        switch1 = self.addSwitch('s1')
        host1 = self.addHost('h1', ip='10.0.0.1')
        host2 = self.addHost('h2', ip='10.0.0.2')
        self.addLink(host1, switch1, bw=1, delay='1ms')
        self.addLink(host2, switch1, bw=1, delay='1ms')
        
def simpleTest(cmds):
    # create and start network
    topo = simpleTopo()
    net = Mininet(topo, link=TCLink, switch=OVSSwitch)
    net.start()
    
    # preparing switch for this exercise
    s1 = net.get('s1')
    info(s1.cmd('echo switch info:'))
    info(s1.cmd('ovs-vsctl show'))          # output switch info
    info(s1.cmd('ovs-ofctl dump-flows s1')) # print flow info
    info(s1.cmd('ovs-ofctl del-flows s1'))  # remove all default flows
    
    for cmd in cmds:
        info(s1.cmd(cmd))
    
    info(s1.cmd('ovs-ofctl dump-flows s1'))

    
    # tests reachability of all hosts
    net.pingAll()
    
    # remove '#' of next line to see statistics after ping
    #info(s1.cmd('ovs-ofctl dump-flows s1'))
    
    # shut down network
    net.stop()
    
if __name__ == '__main__':
    # tell mininet to print useful information
    setLogLevel('info')
    
    # enter user specified OpenFlow rules on the switch 
    cmds = [
        'ovs-ofctl add-flow s1 dl_type=0x0800,nw_dst=10.0.0.2,actions=output:2',
        'ovs-ofctl add-flow s1 dl_type=0x0800,nw_dst=10.0.0.1,actions=output:1',
        # begin insert code
        
        # simple solution
        #'ovs-ofctl add-flow s1 dl_type=0x0806,actions=output:flood'
        
        # more sophisticated solution
        'ovs-ofctl add-flow s1 in_port:1,dl_type=0x0806,actions=output:2',
        'ovs-ofctl add-flow s1 in_port:2,dl_type=0x0806,actions=output:1'
        
        # end insert code
    ]
    simpleTest(cmds)
*** Creating network
*** Adding controller
*** Adding hosts:
h1 
h2 
*** Adding switches:
s1 
*** Adding links:
(1.00Mbit 1ms delay) 
(1.00Mbit 1ms delay) 
(h1, s1) 
(1.00Mbit 1ms delay) 
(1.00Mbit 1ms delay) 
(h2, s1) 

*** Configuring hosts
h1 
h2 

*** Starting controller
c0 

*** Starting 1 switches
s1 
...
(1.00Mbit 1ms delay) 
(1.00Mbit 1ms delay) 

switch info:
dd2dca19-2df1-43a2-992d-69c2900c9471
    Bridge s1
        Controller "tcp:127.0.0.1:6653"
        fail_mode: secure
        Port s1-eth2
            Interface s1-eth2
        Port s1-eth1
            Interface s1-eth1
        Port s1
            Interface s1
                type: internal
    ovs_version: "3.1.0"
 cookie=0x0, duration=0.063s, table=0, n_packets=1, n_bytes=90, priority=0 actions=CONTROLLER:128
 cookie=0x0, duration=0.020s, table=0, n_packets=0, n_bytes=0, ip,nw_dst=10.0.0.2 actions=output:"s1-eth2"
 cookie=0x0, duration=0.014s, table=0, n_packets=0, n_bytes=0, ip,nw_dst=10.0.0.1 actions=output:"s1-eth1"
 cookie=0x0, duration=0.009s, table=0, n_packets=0, n_bytes=0, arp,in_port="s1-eth1" actions=output:"s1-eth2"
 cookie=0x0, duration=0.004s, table=0, n_packets=0, n_bytes=0, arp,in_port="s1-eth2" actions=output:"s1-eth1"
*** Ping: testing ping reachability
h1 -> 
h2 

h2 -> 
h1 

*** Results: 0% dropped (2/2 received)
*** Stopping 1 controllers
c0 

*** Stopping 2 links
.
.

*** Stopping 1 switches
s1 

*** Stopping 2 hosts
h1 
h2 
*** Done

Hosts h1 and h2 do not know the MAC addresses of each other. Both hosts try to get this information via ARP. However, the ARP requests cannot pass the switch because the OpenFlow rules are only valid for IPv4 (0x0800). To fix this problem rules for ARP forwarding, must be installed.

Problem 2- P4 [6 credits]

For the following exercise we use the mininet-based software P4 switch bmv2.

In [4]:
# Execute this cell to install the 'ipaddr' dependency.
# The following command will work if you ar using a virtual environment.
!pip3 install ipaddr

# In case you are using a systemwide installation you can also use the following command.
# Be aware that this may break other dependencies in your environment.
#
#!pip3 install --break-system-packages ipaddr
Requirement already satisfied: ipaddr in /venv/lib/python3.11/site-packages (2.2.0)
In [5]:
# execute this cell once to get P4 switch configuration working
!chmod +x /usr/lib/python3/dist-packages/runtime_CLI.py

The given P4 app turns the bmv2 switch into an IPv4 software router. The following cell contains a P4 program split into 4 different files:

  1. header4: contains the headers and metadata structures used by the software router
  2. parser4: contains the parser for packet header detection
  3. table4: contains the routing table entries
  4. router4: core logic of the router

Execute the following cell to compile the P4 program.

In [6]:
header4 = """
header ethernet_t {                                                                                                     
    bit<48> dstAddr;                                                                                                    
    bit<48> srcAddr;                                                                                                    
    bit<16> etherType;                                                                                                  
}                                                                                                                       
                                                                                                                        
header ipv4_t {                                                                                                         
    bit<4>  version;                                                                                                    
    bit<4>  ihl;                                                                                                        
    bit<8>  diffserv;                                                                                                   
    bit<16> totalLen;                                                                                                   
    bit<16> identification;                                                                                             
    bit<3>  flags;                                                                                                      
    bit<13> fragOffset;                                                                                                 
    bit<8>  ttl;                                                                                                        
    bit<8>  protocol;                                                                                                   
    bit<16> hdrChecksum;                                                                                                
    bit<32> srcAddr;                                                                                                    
    bit<32> dstAddr;                                                                                                    
}                                                                                                                       

struct ingress_metadata_t {                                                                                             
    bit<32> nhop_ipv4;                                                                                                  
}   

struct metadata {   
    @name("ingress_metadata")
    ingress_metadata_t   ingress_metadata;
}                                                                                                                       
                                                                                                                        
struct headers {                                                                                                        
    @name("ethernet")                                                                                                   
    ethernet_t ethernet;                                                                                                
    @name("ipv4")                                                                                                       
    ipv4_t     ipv4;                                                                                                    
}                   
"""

parser4 = """
parser ParserImpl(packet_in packet, out headers hdr, inout metadata meta, inout standard_metadata_t standard_metadata) {
    state parse_ethernet {                                                                                              
        packet.extract(hdr.ethernet);                                                                                   
        transition select(hdr.ethernet.etherType) {                                                                     
            // 16w for a 16 bit integer
            16w0x800: parse_ipv4;                                                                                       
            default: accept;                                                                                            
        }                                                                                                               
    }                                                                                                                   
    state parse_ipv4 {                                                                                                  
        packet.extract(hdr.ipv4);                                                                                       
        transition accept;                                                                                              
    }                                                                                                                   
    state start {                                                                                                       
        transition parse_ethernet;                                                                                      
    }                                                                                                                   
}                                                                                                                       
                                                                                                                        
control DeparserImpl(packet_out packet, in headers hdr) {                                                               
    apply {                                                                                                             
        packet.emit(hdr.ethernet);                                                                                      
        packet.emit(hdr.ipv4);                                                                                          
    }                                                                                                                   
}                                                                                                                       
                                                                                                                        
control verifyChecksum(inout headers hdr, inout metadata meta) {                                                        
    apply { 
        // checksum verification ignored 
    }                                                                                                           
}                                                                                                                       
                                                                                                                        
control computeChecksum(inout headers hdr, inout metadata meta) {                                                       
    apply {                                                                                                             
        update_checksum(                                                                                                
                hdr.ipv4.isValid(),                                                                                     
                { hdr.ipv4.version, hdr.ipv4.ihl, hdr.ipv4.diffserv,                                                    
                hdr.ipv4.totalLen, hdr.ipv4.identification,                                                             
                hdr.ipv4.flags, hdr.ipv4.fragOffset, hdr.ipv4.ttl,                                                      
                hdr.ipv4.protocol, hdr.ipv4.srcAddr, hdr.ipv4.dstAddr },                                                
                hdr.ipv4.hdrChecksum,                                                                                   
                HashAlgorithm.csum16);                                                                                  
    }                                                                                                                   
}                   
"""

table4 = """                                                                                      
table_add send_frame rewrite_mac 1 => 00:aa:bb:00:00:00                                                                 
table_add send_frame rewrite_mac 2 => 00:aa:bb:00:00:01                                                                 
table_add forward set_dmac 10.0.0.10 => 00:04:00:00:00:00                                                               
table_add forward set_dmac 10.0.1.10 => 00:04:00:00:00:01                                                               
table_add ipv4_lpm set_nhop 10.0.0.10/32 => 10.0.0.10 1                                                                 
table_add ipv4_lpm set_nhop 10.0.1.10/32 => 10.0.1.10 2 
"""

router4 = """
#include <core.p4>                                                                                                      
#include <v1model.p4>                                                                                                   
                                                                                                                        
#include "header.p4"                                                                                                    
#include "parser.p4"                                                                                                    
                                                                                                                        
control egress(inout headers hdr, inout metadata meta, inout standard_metadata_t standard_metadata) {                   
    action rewrite_mac(bit<48> smac) {                                                                                  
        hdr.ethernet.srcAddr = smac;                                                                                    
    }                                                                                                                   
    action my_drop() {                                                                                                    
        mark_to_drop(standard_metadata);                                                                                                 
    }                                                                                                                   
    table send_frame {                                                                                                  
        actions = {                                                                                                     
            rewrite_mac;                                                                                                
            my_drop;                                                                                                      
            NoAction;                                                                                                   
        }                                                                                                               
        key = {                                                                                                         
            standard_metadata.egress_port: exact;                                                                       
        }                                                                                                               
        size = 256;                                                                                                     
        default_action = NoAction();                                                                                    
    }                                                                                                                   
    apply {                                                                                                             
        if (hdr.ipv4.isValid()) {                                                                                       
          send_frame.apply();                                                                                           
        }                                                                                                               
    }                                                                                                                   
}                                                                                                                       
                                                                                                                        
control ingress(inout headers hdr, inout metadata meta, inout standard_metadata_t standard_metadata) {                  
    action my_drop() {                                                                                                    
        mark_to_drop(standard_metadata);                                                                                                 
    }                                                                                                                   
    action set_nhop(bit<32> nhop_ipv4, bit<9> port) {                                                                   
        meta.ingress_metadata.nhop_ipv4 = nhop_ipv4;                                                                    
        standard_metadata.egress_spec = port;                                                                           
        hdr.ipv4.ttl = hdr.ipv4.ttl + 8w255;                                                                            
    }                                                                                                                   
    action set_dmac(bit<48> dmac) {                                                                                     
        hdr.ethernet.dstAddr = dmac;                                                                                    
    }                                                                                                                   
    table ipv4_lpm {                                                                                                    
        actions = {                                                                                                     
            my_drop;                                                                                                      
            set_nhop;                                                                                                   
            NoAction;                                                                                                   
        }                                                                                                               
        key = {                                                                                                         
            hdr.ipv4.dstAddr: lpm;                                                                                      
        }                                                                                                               
        size = 1024;                                                                                                    
        default_action = NoAction();                                                                                    
    }                                                                                                                   
    table forward {                                                                                                     
        actions = {                                                                                                     
            set_dmac;                                                                                                   
            my_drop;                                                                                                      
            NoAction;                                                                                                   
        }                                                                                                               
        key = {                                                                                                         
            meta.ingress_metadata.nhop_ipv4: exact;                                                                     
        }                                                                                                               
        size = 512;                                                                                                     
        default_action = NoAction();                                                                                    
    }                                                                                                                   
    apply {                                                                                                             
        if (hdr.ipv4.isValid()) {                                                                                       
          ipv4_lpm.apply();                                                                                         
          forward.apply();                                                                                              
        }                                                                                                               
    }                                                                                                                   
}                                                                                                                       
                                                                                                                        
V1Switch(ParserImpl(), verifyChecksum(), ingress(), egress(), computeChecksum(), DeparserImpl()) main;
"""

def compileP4(app_path, header, parser, table, router):
    header_path = app_path + '/header.p4'
    parser_path = app_path + '/parser.p4'
    config_path = app_path + '/simple_router.config'
    application_path = app_path + '/simple_router.p4'
    with open(header_path, "w") as header_file:
        header_file.write(header)  
 
    with open(parser_path, "w") as parser_file:
        parser_file.write(parser)
    
    with open(config_path, "w") as config_file:
        config_file.write(table)

    with open(application_path, "w") as app_file:
        app_file.write(router)

    # compile p4 code
    ! p4c-bm2-ss --target bmv2 --arch v1model $app_path/simple_router.p4 -o $app_path/simple_router.json
    print('Compilation ended: ' + app_path)
    
compileP4('/root/p4', header4, parser4, table4, router4)
Compilation ended: /root/p4

If compilation was successful, execute the following cell to test the simple router program. Both pings should be successful.

In [7]:
from mininet.net import Mininet                                                 
from mininet.topo import Topo                                                   
from mininet.log import setLogLevel, info                                       
from mininet.cli import CLI                                                     

import sys
sys.path.append('/usr/lib/python3/dist-packages')
from p4_mininet import P4Switch, P4Host                                         
                                                                                
import argparse                                                                 
import os                                                                       
from subprocess import PIPE, Popen                                              
from time import sleep                                                          
                                              
                                                                                
class SingleSwitchTopo(Topo):                                                   
    "Single switch connected to n (< 256) hosts."                               
    def __init__(self, sw_path, json_path, log_file,                            
                 thrift_port, pcap_dump, n, **opts):                            
        # Initialize topology and default options                               
        Topo.__init__(self, **opts)                                             
                                                                                
        switch = self.addSwitch('s1',                                           
                                sw_path = sw_path,                              
                                json_path = json_path,                          
                                #log_console = False,                             
                                #log_file = log_file,                            
                                thrift_port = thrift_port,                      
                                #enable_debugger = False,                         
                                pcap_dump = pcap_dump)                          
                                                                                
        for h in range(n):                                                     
            host = self.addHost('h%d' % (h + 1),                                
                                ip = "10.0.%d.10/24" % h,                       
                                mac = '00:04:00:00:00:%02x' %h)                 
            print('Adding host', str(host))                                      
            self.addLink(host, switch)

def main():                                                                     
    num_hosts = 2                                                  
    mode = 'l3'                                                            
                                                                                
    topo = SingleSwitchTopo('simple_switch',                                
                            '/root/p4/simple_router.json',                                          
                            None,                                    
                            9090,                                   
                            None,                                     
                            2)                                          
    net = Mininet(topo = topo,                                                  
                  host = P4Host,                                                
                  switch = P4Switch,                                            
                  controller = None)                                            
    
    net.start()                                                                 
                                                                                
                                                                                
    sw_mac = ["00:aa:bb:00:00:%02x" % n for n in range(num_hosts)]             
                                                                                
    sw_addr = ["10.0.%d.1" % n for n in range(num_hosts)]                      
                                                                            
    for n in range(num_hosts):                                                 
        h = net.get('h%d' % (n + 1))                                            
        if mode == "l2":                                                        
            h.setDefaultRoute("dev %s" % h.defaultIntf().name)                  
        else:                                                                   
            h.setARP(sw_addr[n], sw_mac[n])                                     
            h.setDefaultRoute("dev %s via %s" % (h.defaultIntf().name, sw_addr[n]))                                     
                                                                                
    sleep(1)                                                                    
                                                                                
    print('')                                                                   
    print('Reading switch configuration script:', '/root/p4/simple_router.config')        
    with open('/root/p4/simple_router.config', 'r') as config_file:                      
        switch_config = config_file.read()  
    
    print('Configuring switch...')                                           
    proc = Popen(['/usr/lib/python3/dist-packages/runtime_CLI.py'], stdin=PIPE, encoding='utf8')
    proc.communicate(input=switch_config)                                   
                                                                                
    print('Configuration complete.')                                         
    print('')                                                                   
                                                                                
    print('Ready !')                                     
                                                                                
    # tests reachability of all hosts                                           
    h1 = net.get('h1')                                                            
    h2 = net.get('h2')
    
    info(h1.cmd('ping -c 4 10.0.1.10'))
    info('\n')
    info(h2.cmd('ping -c 4 10.0.0.10'))
    info('\n')                                                                  
                                                                                
    net.stop()                                                                              
                                                                                
if __name__ == '__main__':                                                      
    setLogLevel( 'info' )                                                       
    main()
*** Creating network
*** Adding hosts:
h1 
h2 
*** Adding switches:
Adding host h1
Adding host h2
s1 
*** Adding links:
(h1, s1) 
(h2, s1) 

*** Configuring hosts
h1 
h2 

*** Starting controller

*** Starting 1 switches
s1 
Starting P4 switch s1.
simple_switch -i 1@s1-eth1 -i 2@s1-eth2 --thrift-port 9090 --nanolog ipc:///tmp/bm-0-log.ipc --device-id 0 /root/p4/simple_router.json
P4 switch s1 has been started.

Reading switch configuration script: /root/p4/simple_router.config
Configuring switch...
Obtaining JSON from switch...
Done
Control utility for runtime P4 table manipulation
RuntimeCmd: RuntimeCmd: Adding entry to exact match table send_frame
match key:           EXACT-00:01
action:              rewrite_mac
runtime data:        00:aa:bb:00:00:00
Entry has been added with handle 0
RuntimeCmd: Adding entry to exact match table send_frame
match key:           EXACT-00:02
action:              rewrite_mac
runtime data:        00:aa:bb:00:00:01
Entry has been added with handle 1
RuntimeCmd: Adding entry to exact match table forward
match key:           EXACT-0a:00:00:0a
action:              set_dmac
runtime data:        00:04:00:00:00:00
Entry has been added with handle 0
RuntimeCmd: Adding entry to exact match table forward
match key:           EXACT-0a:00:01:0a
action:              set_dmac
runtime data:        00:04:00:00:00:01
Entry has been added with handle 1
RuntimeCmd: Adding entry to lpm match table ipv4_lpm
match key:           LPM-0a:00:00:0a/32
action:              set_nhop
runtime data:        0a:00:00:0a	00:01
Entry has been added with handle 0
RuntimeCmd: Adding entry to lpm match table ipv4_lpm
match key:           LPM-0a:00:01:0a/32
action:              set_nhop
runtime data:        0a:00:01:0a	00:02
Entry has been added with handle 1
RuntimeCmd: 
Configuration complete.

Ready !
PING 10.0.1.10 (10.0.1.10) 56(84) bytes of data.
64 bytes from 10.0.1.10: icmp_seq=1 ttl=63 time=0.931 ms
64 bytes from 10.0.1.10: icmp_seq=2 ttl=63 time=1.03 ms
64 bytes from 10.0.1.10: icmp_seq=3 ttl=63 time=1.09 ms
64 bytes from 10.0.1.10: icmp_seq=4 ttl=63 time=1.09 ms

--- 10.0.1.10 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3004ms
rtt min/avg/max/mdev = 0.931/1.032/1.087/0.063 ms

PING 10.0.0.10 (10.0.0.10) 56(84) bytes of data.
64 bytes from 10.0.0.10: icmp_seq=1 ttl=63 time=0.953 ms
64 bytes from 10.0.0.10: icmp_seq=2 ttl=63 time=1.09 ms
64 bytes from 10.0.0.10: icmp_seq=3 ttl=63 time=0.839 ms
64 bytes from 10.0.0.10: icmp_seq=4 ttl=63 time=0.842 ms

--- 10.0.0.10 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3006ms
rtt min/avg/max/mdev = 0.839/0.930/1.088/0.101 ms

*** Stopping 0 controllers

*** Stopping 2 links
.
.

*** Stopping 1 switches
s1 

*** Stopping 2 hosts
h1 
h2 
*** Done
In [8]:
from mininet.net import Mininet                                                 
from mininet.topo import Topo                                                   
from mininet.log import setLogLevel, info                                       
from mininet.cli import CLI                                                     

import sys
sys.path.append('/usr/lib/python3/dist-packages')
from p4_mininet import P4Switch, P4Host                                         
                                                                                
import argparse                                                                 
import os                                                                       
from subprocess import PIPE, Popen                                              
from time import sleep                                                          
                                              
                                                                                
class SingleSwitchTopo(Topo):                                                   
    "Single switch connected to n (< 256) hosts."                               
    def __init__(self, sw_path, json_path, log_file,                            
                 thrift_port, pcap_dump, n, **opts):                            
        # Initialize topology and default options                               
        Topo.__init__(self, **opts)                                             
                                                                                
        switch = self.addSwitch('s1',                                           
                                sw_path = sw_path,                              
                                json_path = json_path,                          
                                #log_console = False,                             
                                #log_file = log_file,                            
                                thrift_port = thrift_port,                      
                                #enable_debugger = False,                         
                                pcap_dump = pcap_dump)                          
                                                                                
        for h in range(n):                                                     
            host = self.addHost('h%d' % (h + 1),                                
                                ip = "10.0.%d.10/24" % h,                       
                                mac = '00:04:00:00:00:%02x' %h)                 
            print('Adding host', str(host))                                      
            self.addLink(host, switch)

def main():                                                                     
    num_hosts = 2                                                  
    mode = 'l3'                                                            
                                                                                
    topo = SingleSwitchTopo('simple_switch',                                
                            '/root/p4/simple_router.json',                                          
                            None,                                    
                            9090,                                   
                            None,                                     
                            2)                                          
    net = Mininet(topo = topo,                                                  
                  host = P4Host,                                                
                  switch = P4Switch,                                            
                  controller = None)                                            
    
    net.start()                                                                 
                                                                                
                                                                                
    sw_mac = ["00:aa:bb:00:00:%02x" % n for n in range(num_hosts)]             
                                                                                
    sw_addr = ["10.0.%d.1" % n for n in range(num_hosts)]                      
                                                                            
    for n in range(num_hosts):                                                 
        h = net.get('h%d' % (n + 1))                                            
        if mode == "l2":                                                        
            h.setDefaultRoute("dev %s" % h.defaultIntf().name)                  
        else:                                                                   
            h.setARP(sw_addr[n], sw_mac[n])                                     
            h.setDefaultRoute("dev %s via %s" % (h.defaultIntf().name, sw_addr[n]))                                     
                                                                                
    sleep(1)                                                                    
                                                                                
    print('')                                                                   
    print('Reading switch configuration script:', '/root/p4/simple_router.config')        
    with open('/root/p4/simple_router.config', 'r') as config_file:                      
        switch_config = config_file.read()  
    
    print('Configuring switch...')                                           
    proc = Popen(['/usr/lib/python3/dist-packages/runtime_CLI.py'], stdin=PIPE, encoding='utf8')
    proc.communicate(input=switch_config)                                   
                                                                                
    print('Configuration complete.')                                         
    print('')                                                                   
                                                                                
    print('Ready !')                                     
                                                                                
    # tests reachability of all hosts                                           
    h1 = net.get('h1')                                                            
    h2 = net.get('h2')
    
    info(h1.cmd('ping -c 4 10.0.1.10'))
    info('\n')
    info(h2.cmd('ping -c 4 10.0.0.10'))
    info('\n')                                                                  
                                                                                
    net.stop()                                                                              
                                                                                
if __name__ == '__main__':                                                      
    setLogLevel( 'info' )                                                       
    main()
*** Creating network
*** Adding hosts:
h1 
h2 
*** Adding switches:
s1 
*** Adding links:
(h1, s1) 
(h2, s1) 

*** Configuring hosts
h1 
h2 
Adding host h1
Adding host h2

*** Starting controller

*** Starting 1 switches
s1 
Starting P4 switch s1.
simple_switch -i 1@s1-eth1 -i 2@s1-eth2 --thrift-port 9090 --nanolog ipc:///tmp/bm-2-log.ipc --device-id 2 /root/p4/simple_router.json
P4 switch s1 has been started.

Reading switch configuration script: /root/p4/simple_router.config
Configuring switch...
Obtaining JSON from switch...
Done
Control utility for runtime P4 table manipulation
RuntimeCmd: RuntimeCmd: Adding entry to exact match table send_frame
match key:           EXACT-00:01
action:              rewrite_mac
runtime data:        00:aa:bb:00:00:00
Entry has been added with handle 0
RuntimeCmd: Adding entry to exact match table send_frame
match key:           EXACT-00:02
action:              rewrite_mac
runtime data:        00:aa:bb:00:00:01
Entry has been added with handle 1
RuntimeCmd: Adding entry to exact match table forward
match key:           EXACT-0a:00:00:0a
action:              set_dmac
runtime data:        00:04:00:00:00:00
Entry has been added with handle 0
RuntimeCmd: Adding entry to exact match table forward
match key:           EXACT-0a:00:01:0a
action:              set_dmac
runtime data:        00:04:00:00:00:01
Entry has been added with handle 1
RuntimeCmd: Adding entry to lpm match table ipv4_lpm
match key:           LPM-0a:00:00:0a/32
action:              set_nhop
runtime data:        0a:00:00:0a	00:01
Entry has been added with handle 0
RuntimeCmd: Adding entry to lpm match table ipv4_lpm
match key:           LPM-0a:00:01:0a/32
action:              set_nhop
runtime data:        0a:00:01:0a	00:02
Entry has been added with handle 1
RuntimeCmd: 
Configuration complete.

Ready !
PING 10.0.1.10 (10.0.1.10) 56(84) bytes of data.
64 bytes from 10.0.1.10: icmp_seq=1 ttl=63 time=0.887 ms
64 bytes from 10.0.1.10: icmp_seq=2 ttl=63 time=1.10 ms
64 bytes from 10.0.1.10: icmp_seq=3 ttl=63 time=1.04 ms
64 bytes from 10.0.1.10: icmp_seq=4 ttl=63 time=1.06 ms

--- 10.0.1.10 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3004ms
rtt min/avg/max/mdev = 0.887/1.023/1.102/0.081 ms

PING 10.0.0.10 (10.0.0.10) 56(84) bytes of data.
64 bytes from 10.0.0.10: icmp_seq=1 ttl=63 time=0.877 ms
64 bytes from 10.0.0.10: icmp_seq=2 ttl=63 time=1.06 ms
64 bytes from 10.0.0.10: icmp_seq=3 ttl=63 time=1.01 ms
64 bytes from 10.0.0.10: icmp_seq=4 ttl=63 time=1.05 ms

--- 10.0.0.10 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3004ms
rtt min/avg/max/mdev = 0.877/0.999/1.059/0.072 ms

*** Stopping 0 controllers

*** Stopping 2 links
.
.

*** Stopping 1 switches
s1 

*** Stopping 2 hosts
h1 
h2 
*** Done

Your task in this problem is extending the given example router with IPv6 support.

a) [1 credits] Extend the header information for IPv6 into the given example code.

In [9]:
header6 = """
header ethernet_t {                                                                                                     
    bit<48> dstAddr;                                                                                                    
    bit<48> srcAddr;                                                                                                    
    bit<16> etherType;                                                                                                  
}                                                                                                                       
                                                                                                                        
header ipv4_t {                                                                                                         
    bit<4>  version;                                                                                                    
    bit<4>  ihl;                                                                                                        
    bit<8>  diffserv;                                                                                                   
    bit<16> totalLen;                                                                                                   
    bit<16> identification;                                                                                             
    bit<3>  flags;                                                                                                      
    bit<13> fragOffset;                                                                                                 
    bit<8>  ttl;                                                                                                        
    bit<8>  protocol;                                                                                                   
    bit<16> hdrChecksum;                                                                                                
    bit<32> srcAddr;                                                                                                    
    bit<32> dstAddr;                                                                                                    
}                                                                                                                       

//# begin insert code

header ipv6_t {                                                                                                         
    bit<4>   version;                                                                                                    
    bit<8>   trafficclass;                                                                                                        
    bit<20>  flowlabel;                                                                                                   
    bit<16>  payloadlength;                                                                                                   
    bit<8>   nextheader;                                                                                             
    bit<8>   hoplimit;                                                                                                      
    bit<128> srcAddr;                                                                                                 
    bit<128> dstAddr;                                                                                                                                                                                                          
}

//# end insert code

struct ingress_metadata_t {                                                                                             
    bit<32> nhop_ipv4;
    
//# begin insert code
    
    // user-defined metadata
    // used to transmit address information from routing to forwarding table
    bit<128> nhop_ipv6;

//# end insert code
    
}   

struct metadata {   
    @name("ingress_metadata")
    ingress_metadata_t   ingress_metadata;
}                                                                                                                       
                                                                                                                        
struct headers {  
    @name("ethernet")                                                                                                   
    ethernet_t ethernet;                                                                                                
    @name("ipv4")                                                                                                       
    ipv4_t     ipv4;
//# begin insert code

    @name("ipv6")                                                                                                   
    ipv6_t ipv6; 

//# end insert code
}                   
"""

b) [1 credits] Extend the parser and deparser information for IPv6 in the given example code.

In [10]:
parser6 = """
parser ParserImpl(packet_in packet, out headers hdr, inout metadata meta, inout standard_metadata_t standard_metadata) {
    state parse_ethernet {                                                                                              
        packet.extract(hdr.ethernet);                                                                                   
        transition select(hdr.ethernet.etherType) {                                                                     
            // 16w for a 16 bit integer
            16w0x800: parse_ipv4;
            
//# begin insert code

            16w0x86dd: parse_ipv6;

//# end insert code            
    
        default: accept;                                                                                            
        }                                                                                                               
    }                                                                                                                   
    state parse_ipv4 {                                                                                                  
        packet.extract(hdr.ipv4);                                                                                       
        transition accept;                                                                                              
    }
    
//# begin insert code

    state parse_ipv6 {                                                                                                  
        packet.extract(hdr.ipv6);                                                                                       
        transition accept;                                                                                              
    }
    
//# end insert code 
    
    state start {                                                                                                       
        transition parse_ethernet;                                                                                      
    }                                                                                                                   
}                                                                                                                       
                                                                                                                        
control DeparserImpl(packet_out packet, in headers hdr) {                                                               
    apply {                                                                                                             
        packet.emit(hdr.ethernet);                                                                                      
        packet.emit(hdr.ipv4); 

//# begin insert code

        // emit checks for validity before creating a header
        packet.emit(hdr.ipv6);
    
//# end insert code 
        
    }                                                                                                                   
}                                                                                                                       
                                                                                                                        
control verifyChecksum(inout headers hdr, inout metadata meta) {                                                        
    apply { 
        // checksum verification ignored 
    }                                                                                                           
}                                                                                                                       
                                                                                                                        
control computeChecksum(inout headers hdr, inout metadata meta) {                                                       
    apply {                                                                                                             
        update_checksum(                                                                                                
                hdr.ipv4.isValid(),                                                                                     
                { hdr.ipv4.version, hdr.ipv4.ihl, hdr.ipv4.diffserv,                                                    
                hdr.ipv4.totalLen, hdr.ipv4.identification,                                                             
                hdr.ipv4.flags, hdr.ipv4.fragOffset, hdr.ipv4.ttl,                                                      
                hdr.ipv4.protocol, hdr.ipv4.srcAddr, hdr.ipv4.dstAddr },                                                
                hdr.ipv4.hdrChecksum,                                                                                   
                HashAlgorithm.csum16);                                                                                  
    }                                                                                                                   
}                   
"""

c) [2 credits] Extend the ingress block information for IPv6 in the given example code.

In [11]:
router6 = """
#include <core.p4>                                                                                                      
#include <v1model.p4>                                                                                                   
                                                                                                                        
#include "header.p4"                                                                                                    
#include "parser.p4"                                                                                                    
                                                                                                                        
control egress(inout headers hdr, inout metadata meta, inout standard_metadata_t standard_metadata) {                   
    action rewrite_mac(bit<48> smac) {                                                                                  
        hdr.ethernet.srcAddr = smac;                                                                                    
    }                                                                                                                   
    action my_drop() {                                                                                                    
        mark_to_drop(standard_metadata);                                                                                                 
    }                                                                                                                   
    table send_frame {                                                                                                  
        actions = {                                                                                                     
            rewrite_mac;                                                                                                
            my_drop;                                                                                                      
            NoAction;                                                                                                   
        }                                                                                                               
        key = {                                                                                                         
            standard_metadata.egress_port: exact;                                                                       
        }                                                                                                               
        size = 256;                                                                                                     
        default_action = NoAction();                                                                                    
    }                                                                                                                   
    apply {                                                                                                             
        if (hdr.ipv4.isValid()) {                                                                                       
          send_frame.apply();                                                                                           
        }

//# begin insert code

        else if (hdr.ipv6.isValid()) {                                                                                       
          send_frame.apply();                                                                                           
        }
        
//# end insert code
        
    }                                                                                                                   
}                                                                                                                       
                                                                                                                        
control ingress(inout headers hdr, inout metadata meta, inout standard_metadata_t standard_metadata) {                  
    action my_drop() {                                                                                                    
        mark_to_drop(standard_metadata);                                                                                                 
    }                                                                                                                   
    action set_nhop(bit<32> nhop_ipv4, bit<9> port) {                                                                   
        meta.ingress_metadata.nhop_ipv4 = nhop_ipv4;                                                                    
        standard_metadata.egress_spec = port;                                                                           
        hdr.ipv4.ttl = hdr.ipv4.ttl + 8w255;                                                                            
    }

//# begin insert code

    action set_nhop6(bit<128> nhop_ipv6, bit<9> port) {                                                                   
        meta.ingress_metadata.nhop_ipv6 = nhop_ipv6;                                                                    
        standard_metadata.egress_spec = port;                                                                           
        hdr.ipv6.hoplimit = hdr.ipv6.hoplimit + 8w255;                                                                            
    }
        
//# end insert code   
    
    action set_dmac(bit<48> dmac) {                                                                                     
        hdr.ethernet.dstAddr = dmac;                                                                                    
    }                                                                                                                   
    table ipv4_lpm {                                                                                                    
        actions = {                                                                                                     
            my_drop;                                                                                                      
            set_nhop;                                                                                                   
            NoAction;                                                                                                   
        }                                                                                                               
        key = {                                                                                                         
            hdr.ipv4.dstAddr: lpm;                                                                                      
        }                                                                                                               
        size = 1024;                                                                                                    
        default_action = NoAction();                                                                                    
    }
//# begin insert code

    table ipv6_lpm {                                                                                                    
        actions = {                                                                                                     
            my_drop;                                                                                                      
            set_nhop6;                                                                                                   
            NoAction;                                                                                                   
        }                                                                                                               
        key = {                                                                                                         
            hdr.ipv6.dstAddr: lpm;                                                                                      
        }                                                                                                               
        size = 1024;                                                                                                    
        default_action = NoAction();                                                                                    
    }
        
//# end insert code
    table forward {                                                                                                     
        actions = {                                                                                                     
            set_dmac;                                                                                                   
            my_drop;                                                                                                      
            NoAction;                                                                                                   
        }                                                                                                               
        key = {                                                                                                         
            meta.ingress_metadata.nhop_ipv4: exact;                                                                     
        }                                                                                                               
        size = 512;                                                                                                     
        default_action = NoAction();                                                                                    
    }
//# begin insert code

    table forward6 {                                                                                                     
        actions = {                                                                                                     
            set_dmac;                                                                                                   
            my_drop;                                                                                                      
            NoAction;                                                                                                   
        }                                                                                                               
        key = {                                                                                                         
            meta.ingress_metadata.nhop_ipv6: exact;                                                                     
        }                                                                                                               
        size = 512;                                                                                                     
        default_action = NoAction();                                                                                    
    }
        
//# end insert code
    apply {                                                                                                             
        if (hdr.ipv4.isValid()) {                                                                                       
          ipv4_lpm.apply();                                                                                         
          forward.apply();                                                                                              
        }

//# begin insert code

        if (hdr.ipv6.isValid()) {                                                                                       
          ipv6_lpm.apply();                                                                                         
          forward6.apply();                                                                                              
        }
        
//# end insert code

    }                                                                                                                   
}                                                                                                                       
                                                                                                                        
V1Switch(ParserImpl(), verifyChecksum(), ingress(), egress(), computeChecksum(), DeparserImpl()) main;
"""

d) [2 credits] Extend the table configuration for IPv6 in the given example code.

  • The MAC addresses of clients h1, h2 remain unchanged (00:04:00:00:00:00, 00:04:00:00:00:01).
  • The MAC addresses of router interfaces h1 -> r, h2 -> 2 also remain unchanged (00:aa:bb:00:00:00, 00:aa:bb:00:00:01).

In [12]:
table6 = """                                                                                       
table_add send_frame rewrite_mac 1 => 00:aa:bb:00:00:00                                                                 
table_add send_frame rewrite_mac 2 => 00:aa:bb:00:00:01                                                                 
table_add forward set_dmac 10.0.0.10 => 00:04:00:00:00:00                                                               
table_add forward set_dmac 10.0.1.10 => 00:04:00:00:00:01                                                               
table_add ipv4_lpm set_nhop 10.0.0.10/32 => 10.0.0.10 1                                                                 
table_add ipv4_lpm set_nhop 10.0.1.10/32 => 10.0.1.10 2
table_add forward6 set_dmac 0x20020000000000000000000000000010 => 00:04:00:00:00:00                                                               
table_add forward6 set_dmac 0x20020001000000000000000000000010 => 00:04:00:00:00:01
table_add ipv6_lpm set_nhop6 0x20020000000000000000000000000010/128 => 0x20020000000000000000000000000010 1                                                                 
table_add ipv6_lpm set_nhop6 0x20020001000000000000000000000010/128 => 0x20020001000000000000000000000010 2
"""
In [13]:
# use this cell to compile the ipv6 router
compileP4('/root/p4own', header6, parser6, table6, router6)
Compilation ended: /root/p4own

Execute the following cell to test the IPv6 router program. It may take a while to execute the pings. Do not forget to compile.

In [14]:
from mininet.net import Mininet                                                 
from mininet.topo import Topo                                                   
from mininet.log import setLogLevel, info                                       
from mininet.cli import CLI                                                     

import sys
sys.path.append('/usr/lib/python3/dist-packages')
from p4_mininet import P4Switch, P4Host                                         
                                                                                
import argparse                                                                 
import os                                                                       
from subprocess import PIPE, Popen                                              
from time import sleep                                                          
                                              
                                                                                
class SingleSwitchTopo(Topo):                                                   
    "Single switch connected to n (< 256) hosts."                               
    def __init__(self, sw_path, json_path, log_file,                            
                 thrift_port, pcap_dump, n, **opts):                            
        # Initialize topology and default options                               
        Topo.__init__(self, **opts)                                             
                                                                                
        switch = self.addSwitch('s1',                                           
                                sw_path = sw_path,                              
                                json_path = json_path,                          
                                log_console = True,                             
                                log_file = log_file,                            
                                thrift_port = thrift_port,                      
                                #enable_debugger = True,                         
                                pcap_dump = pcap_dump)                          
                                                                                
        for h in range(n):                                                     
            host = self.addHost('h%d' % (h + 1),                                
                                ip = "10.0.%d.10/24" % h,                       
                                mac = '00:04:00:00:00:%02x' %h)                 
            print('Adding host', str(host))                                      
            self.addLink(host, switch)

def main():                                                                     
    num_hosts = 2                                                  
    mode = 'l3'                                                            
                                                                                
    topo = SingleSwitchTopo('simple_switch',                                
                            '/root/p4own/simple_router.json',                                          
                            None,                                    
                            9090,                                   
                            None,                                     
                            2)                                          
    net = Mininet(topo = topo,                                                  
                  host = P4Host,                                                
                  switch = P4Switch,                                            
                  controller = None)                                            
    net.start()                                                                 
                                                                                
                                                                                
    sw_mac = ["00:aa:bb:00:00:%02x" % n for n in range(num_hosts)]             
                                                                                
    sw_addr = ["10.0.%d.1" % n for n in range(num_hosts)]                      
                                                                                
    for n in range(num_hosts):                                                 
        h = net.get('h%d' % (n + 1))                                            
        if mode == "l2":                                                        
            h.setDefaultRoute("dev %s" % h.defaultIntf().name)                  
        else:                                                                   
            h.setARP(sw_addr[n], sw_mac[n])                                     
            h.setDefaultRoute("dev %s via %s" % (h.defaultIntf().name, sw_addr[n]))
                                                                                
    #for n in range(num_hosts):                                                 
    #    h = net.get('h%d' % (n + 1))                                            
    #    h.describe(sw_addr[n] + " " + sw_mac[n])                                       
                                                                                
    sleep(1)                                                                    
                                                                                
    print('')                                                                   
    print('Reading switch configuration script:', '/root/p4own/simple_router.config')        
    with open('/root/p4own/simple_router.config', 'r') as config_file:                      
        switch_config = config_file.read()                                  
                                                                                
    print('Configuring switch...')                                           
    proc = Popen(['/usr/lib/python3/dist-packages/runtime_CLI.py'], stdin=PIPE, encoding='utf8')
    proc.communicate(input=switch_config)                                   
                                                                                
    print('Configuration complete.')                                         
    print('')                                                                   
                                                                                
    print('Ready !')                                     
                                                                                
    h1 = net.get('h1')                                                            
    h2 = net.get('h2')

    # test ipv4 reachability
    info(h1.cmd('ping -c 4 10.0.1.10'))
    info('\n')
    info(h2.cmd('ping -c 4 10.0.0.10'))
    info('\n')                                                                  

    info('\n')
    info('Information about host1:\n')
    # disable deactivation => enable ipv6
    info(h1.cmd('sysctl net.ipv6.conf.all.disable_ipv6=0'))
    # add ipv6 address for h1
    info(h1.cmd('ip -6 addr add 2002::10/128 dev eth0'))
    # add mac address for h2 (mac resolution will not work due to missing lookup table entries)
    info(h1.cmd('ip -6 neighbor add 2002:1::10 lladdr 00:aa:bb:00:00:00 dev eth0 nud permanent'))
    # add route entry for h2's subnet
    info(h1.cmd('ip -6 route add 2002:1::/64 dev eth0'))
    info('\n')
    
    # information for debugging
    info(h1.cmd('ip a'))
    info(h1.cmd('ip neigh show'))
    info(h1.cmd('ip route show'))
    info('\n') 

    info('\n')
    info('Information about host2:\n')
    info(h2.cmd('sysctl net.ipv6.conf.all.disable_ipv6=0'))
    info(h2.cmd('ip -6 addr add 2002:1::10/128 dev eth0'))
    info(h2.cmd('ip -6 neighbor add 2002::10 lladdr 00:aa:bb:00:00:01 dev eth0 nud permanent'))
    info(h2.cmd('ip -6 route add 2002::/64 dev eth0'))
    info(h2.cmd('ip a'))
    info(h2.cmd('ip neigh show'))
    info('\n')
    
    sleep(5) # magic sleep, fixes stuff, do NOT remove
    
    info('\n')
    info('IPv6 ping (h1 -> h2):\n')
    info(h1.cmd('ping -6 -c 4 2002:1::10'))
    info('\n')

    info('\n')
    info('IPv6 ping (h2 -> h1):\n')
    info(h2.cmd('ping -6 -c 4 2002::10'))
    info('\n')
    
    net.stop()                                                                              
                                                                                
if __name__ == '__main__':                                                      
    setLogLevel( 'info' )                                                       
    main()
*** Creating network
Adding host h1
Adding host h2
*** Adding hosts:
h1 
h2 
*** Adding switches:
s1 
*** Adding links:
(h1, s1) 
(h2, s1) 

*** Configuring hosts
h1 
h2 

*** Starting controller

*** Starting 1 switches
s1 
Starting P4 switch s1.
simple_switch -i 1@s1-eth1 -i 2@s1-eth2 --thrift-port 9090 --nanolog ipc:///tmp/bm-4-log.ipc --device-id 4 /root/p4own/simple_router.json --log-console
P4 switch s1 has been started.

Reading switch configuration script: /root/p4own/simple_router.config
Configuring switch...
Obtaining JSON from switch...
Done
Control utility for runtime P4 table manipulation
RuntimeCmd: RuntimeCmd: Adding entry to exact match table send_frame
match key:           EXACT-00:01
action:              rewrite_mac
runtime data:        00:aa:bb:00:00:00
Entry has been added with handle 0
RuntimeCmd: Adding entry to exact match table send_frame
match key:           EXACT-00:02
action:              rewrite_mac
runtime data:        00:aa:bb:00:00:01
Entry has been added with handle 1
RuntimeCmd: Adding entry to exact match table forward
match key:           EXACT-0a:00:00:0a
action:              set_dmac
runtime data:        00:04:00:00:00:00
Entry has been added with handle 0
RuntimeCmd: Adding entry to exact match table forward
match key:           EXACT-0a:00:01:0a
action:              set_dmac
runtime data:        00:04:00:00:00:01
Entry has been added with handle 1
RuntimeCmd: Adding entry to lpm match table ipv4_lpm
match key:           LPM-0a:00:00:0a/32
action:              set_nhop
runtime data:        0a:00:00:0a	00:01
Entry has been added with handle 0
RuntimeCmd: Adding entry to lpm match table ipv4_lpm
match key:           LPM-0a:00:01:0a/32
action:              set_nhop
runtime data:        0a:00:01:0a	00:02
Entry has been added with handle 1
RuntimeCmd: Adding entry to exact match table forward6
match key:           EXACT-20:02:00:00:00:00:00:00:00:00:00:00:00:00:00:10
action:              set_dmac
runtime data:        00:04:00:00:00:00
Entry has been added with handle 0
RuntimeCmd: Adding entry to exact match table forward6
match key:           EXACT-20:02:00:01:00:00:00:00:00:00:00:00:00:00:00:10
action:              set_dmac
runtime data:        00:04:00:00:00:01
Entry has been added with handle 1
RuntimeCmd: Adding entry to lpm match table ipv6_lpm
match key:           LPM-20:02:00:00:00:00:00:00:00:00:00:00:00:00:00:10/128
action:              set_nhop6
runtime data:        20:02:00:00:00:00:00:00:00:00:00:00:00:00:00:10	00:01
Entry has been added with handle 0
RuntimeCmd: Adding entry to lpm match table ipv6_lpm
match key:           LPM-20:02:00:01:00:00:00:00:00:00:00:00:00:00:00:10/128
action:              set_nhop6
runtime data:        20:02:00:01:00:00:00:00:00:00:00:00:00:00:00:10	00:02
Entry has been added with handle 1
RuntimeCmd: 
Configuration complete.

Ready !
PING 10.0.1.10 (10.0.1.10) 56(84) bytes of data.
64 bytes from 10.0.1.10: icmp_seq=1 ttl=63 time=1.22 ms
64 bytes from 10.0.1.10: icmp_seq=2 ttl=63 time=1.49 ms
64 bytes from 10.0.1.10: icmp_seq=3 ttl=63 time=1.48 ms
64 bytes from 10.0.1.10: icmp_seq=4 ttl=63 time=1.44 ms

--- 10.0.1.10 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3005ms
rtt min/avg/max/mdev = 1.218/1.406/1.488/0.110 ms

PING 10.0.0.10 (10.0.0.10) 56(84) bytes of data.
64 bytes from 10.0.0.10: icmp_seq=1 ttl=63 time=1.34 ms
64 bytes from 10.0.0.10: icmp_seq=2 ttl=63 time=1.44 ms
64 bytes from 10.0.0.10: icmp_seq=3 ttl=63 time=1.47 ms
64 bytes from 10.0.0.10: icmp_seq=4 ttl=63 time=1.46 ms

--- 10.0.0.10 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3005ms
rtt min/avg/max/mdev = 1.344/1.430/1.471/0.051 ms


Information about host1:
net.ipv6.conf.all.disable_ipv6 = 0

1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: eth0@if84: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 00:04:00:00:00:00 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 10.0.0.10/24 brd 10.0.0.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 2002::10/128 scope global tentative 
       valid_lft forever preferred_lft forever
    inet6 fe80::204:ff:fe00:0/64 scope link tentative 
       valid_lft forever preferred_lft forever
10.0.0.1 dev eth0 lladdr 00:aa:bb:00:00:00 PERMANENT 
2002:1::10 dev eth0 lladdr 00:aa:bb:00:00:00 PERMANENT 
default via 10.0.0.1 dev eth0 
10.0.0.0/24 dev eth0 proto kernel scope link src 10.0.0.10 


Information about host2:
net.ipv6.conf.all.disable_ipv6 = 0
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: eth0@if85: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 00:04:00:00:00:01 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 10.0.1.10/24 brd 10.0.1.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 2002:1::10/128 scope global tentative 
       valid_lft forever preferred_lft forever
    inet6 fe80::204:ff:fe00:1/64 scope link tentative 
       valid_lft forever preferred_lft forever
10.0.1.1 dev eth0 lladdr 00:aa:bb:00:00:01 PERMANENT 
2002::10 dev eth0 lladdr 00:aa:bb:00:00:01 PERMANENT 


IPv6 ping (h1 -> h2):
PING 2002:1::10(2002:1::10) 56 data bytes
64 bytes from 2002:1::10: icmp_seq=1 ttl=63 time=1.58 ms
64 bytes from 2002:1::10: icmp_seq=2 ttl=63 time=1.54 ms
64 bytes from 2002:1::10: icmp_seq=3 ttl=63 time=1.58 ms
64 bytes from 2002:1::10: icmp_seq=4 ttl=63 time=1.64 ms

--- 2002:1::10 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3005ms
rtt min/avg/max/mdev = 1.542/1.585/1.639/0.034 ms


IPv6 ping (h2 -> h1):
PING 2002::10(2002::10) 56 data bytes
64 bytes from 2002::10: icmp_seq=1 ttl=63 time=1.24 ms
64 bytes from 2002::10: icmp_seq=2 ttl=63 time=1.52 ms
64 bytes from 2002::10: icmp_seq=3 ttl=63 time=1.61 ms
64 bytes from 2002::10: icmp_seq=4 ttl=63 time=1.50 ms

--- 2002::10 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3005ms
rtt min/avg/max/mdev = 1.244/1.466/1.606/0.134 ms

*** Stopping 0 controllers

*** Stopping 2 links
.
.

*** Stopping 1 switches
s1 

*** Stopping 2 hosts
h1 
h2 
*** Done

Advanced Computer Networking by Prof. Dr.-Ing. Georg Carle

Teaching assistants: Christian Dietze, Sebastian Gallenmüller, Max Helm, Benedikt Jaeger, Marcel Kempf, Jihye Kim, Patrick Sattler