Tutorial 5 - SDN & VLANs

Submission process:

  • Submission deadline is January 22, 14:00 CET (before the lecture) .
  • Commit and push your solution as separate notebook files per subtask via git as ./tutorial/tutorial5/tutorial5_2.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 27, 16: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 a separate file per subtask via git as ./tutorial/tutorial5/tutorial5_2.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 or contains major flaws a deduction of up to 50% of the achieved points will be applied
  • A sample solution is provided after January 27, 16:00 CET eventually.
  • Do NOT duplicate cells or change the type of cells, otherwise you might receive zero points for your submission
  • Please use acn@net.in.tum.de for questions regarding lecture, tutorial, and project of ACN.

This cell is used to verify the structure of the submission

DO NOT DELETE OR CHANGE THIS CELL, otherwise the task will be graded with 0 points

Problem 2- P4 [6 credits]

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

In [1]:
# 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.13/site-packages (2.2.0)
In [2]:
# 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 [3]:
import subprocess
import os

header4 = """
header ethernet_t {                                                                                                     
    bit<48> destination_address;                                                                                                    
    bit<48> source_address;                                                                                                    
    bit<16> etherType;                                                                                                  
}                                                                                                                       
                                                                                                                        
header ipv4_t {                                                                                                         
    bit<4>  version;                                                                                                    
    bit<4>  ihl;                                                                                                        
    bit<8>  diffserv;                                                                                                   
    bit<16> total_length;                                                                                                   
    bit<16> identification;                                                                                             
    bit<3>  flags;                                                                                                      
    bit<13> frag_offset;                                                                                                 
    bit<8>  ttl;                                                                                                        
    bit<8>  protocol;                                                                                                   
    bit<16> hdr_checksum;                                                                                                
    bit<32> source_address;                                                                                                    
    bit<32> destination_address;                                                                                                    
}                                                                                                                       

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.total_length, hdr.ipv4.identification,                                                             
                hdr.ipv4.flags, hdr.ipv4.frag_offset, hdr.ipv4.ttl,                                                      
                hdr.ipv4.protocol, hdr.ipv4.source_address, hdr.ipv4.destination_address },                                                
                hdr.ipv4.hdr_checksum,                                                                                   
                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.source_address = 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.destination_address = dmac;                                                                                    
    }                                                                                                                   
    table ipv4_lpm {                                                                                                    
        actions = {                                                                                                     
            my_drop;                                                                                                      
            set_nhop;                                                                                                   
            NoAction;                                                                                                   
        }                                                                                                               
        key = {                                                                                                         
            hdr.ipv4.destination_address: 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)"""

def compileP4(app_path, header, parser, table, router):
    header_path = os.path.join(app_path, 'header.p4')
    parser_path = os.path.join(app_path, 'parser.p4')
    config_path = os.path.join(app_path, 'simple_router.config')
    application_path = os.path.join(app_path, 'simple_router.p4')
    output_json = os.path.join(app_path, 'simple_router.json')

    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)

    cmd = ["p4c-bm2-ss", "--target", "bmv2","--arch", "v1model", application_path, "-o", output_json]

    result = subprocess.run(
        cmd, 
        capture_output=True, # Captures stdout and stderr
        text=True            # Returns strings instead of bytes
    )

    if result.returncode != 0:
        error_message = (
            f"P4 Compilation Failed!\n"
            f"----------------------\n"
            f"{result.stderr}"  
        )
        raise RuntimeError(error_message)

    print('Compilation ended successfully: ' + app_path)
    
compileP4('/root/p4', header4, parser4, table4, router4)
Compilation ended successfully: /root/p4

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

In [4]:
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-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=1.19 ms
64 bytes from 10.0.1.10: icmp_seq=2 ttl=63 time=1.08 ms
64 bytes from 10.0.1.10: icmp_seq=3 ttl=63 time=1.06 ms
64 bytes from 10.0.1.10: icmp_seq=4 ttl=63 time=1.04 ms

--- 10.0.1.10 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3004ms
rtt min/avg/max/mdev = 1.043/1.094/1.187/0.055 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.08 ms
64 bytes from 10.0.0.10: icmp_seq=2 ttl=63 time=1.20 ms
64 bytes from 10.0.0.10: icmp_seq=3 ttl=63 time=1.14 ms
64 bytes from 10.0.0.10: icmp_seq=4 ttl=63 time=1.17 ms

--- 10.0.0.10 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3004ms
rtt min/avg/max/mdev = 1.082/1.147/1.196/0.041 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. The fields of the IPv6 header should be named according to RFC 8200 using snake case. The name for the header should be "ipv6" and the field for the ingress metadata should be called "nhop_ipv6".

In [5]:
header6 = """
header ethernet_t {                                                                                                     
    bit<48> destination_address;                                                                                                    
    bit<48> source_address;                                                                                                    
    bit<16> etherType;                                                                                                  
}                                                                                                                       
                                                                                                                        
header ipv4_t {                                                                                                         
    bit<4>  version;                                                                                                    
    bit<4>  ihl;                                                                                                        
    bit<8>  diffserv;                                                                                                   
    bit<16> total_length;                                                                                                   
    bit<16> identification;                                                                                             
    bit<3>  flags;                                                                                                      
    bit<13> frag_offset;                                                                                                 
    bit<8>  ttl;                                                                                                        
    bit<8>  protocol;                                                                                                   
    bit<16> hdr_checksum;                                                                                                
    bit<32> source_address;                                                                                                    
    bit<32> destination_address;                                                                                                    
}                                                                                                                       

# begin insert code


header ipv6_t {                                                                                                         
    bit<4>   version;                                                                                                    
    bit<8>   traffic_class;                                                                                                        
    bit<20>  flow_label;                                                                                                   
    bit<16>  payload_length;                                                                                                   
    bit<8>   next_header;                                                                                             
    bit<8>   hop_limit;                                                                                                      
    bit<128> source_address;                                                                                                 
    bit<128> destination_address;                                                                                                                                                                                                          
}

# 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 [6]:
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.total_length, hdr.ipv4.identification,                                                             
                hdr.ipv4.flags, hdr.ipv4.frag_offset, hdr.ipv4.ttl,                                                      
                hdr.ipv4.protocol, hdr.ipv4.source_address, hdr.ipv4.destination_address },                                                
                hdr.ipv4.hdr_checksum,                                                                                   
                HashAlgorithm.csum16);                                                                                  
    }                                                                                                                   
}                   
"""

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

In [7]:
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.source_address = 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;                                                                            
    }



    action set_nhop6(bit<128> nhop_ipv6, bit<9> port) {    
    # begin insert code
        meta.ingress_metadata.nhop_ipv6 = nhop_ipv6;                                                                    
        standard_metadata.egress_spec = port;                                                                           
        hdr.ipv6.hop_limit = hdr.ipv6.hop_limit + 8w255;     
# end insert code                                                                          
    }
        

    
    action set_dmac(bit<48> dmac) {                                                                                     
        hdr.ethernet.destination_address = dmac;                                                                                    
    }                                                                                                                   
    table ipv4_lpm {                                                                                                    
        actions = {                                                                                                     
            my_drop;                                                                                                      
            set_nhop;                                                                                                   
            NoAction;                                                                                                   
        }                                                                                                               
        key = {                                                                                                         
            hdr.ipv4.destination_address: lpm;                                                                                      
        }                                                                                                               
        size = 1024;                                                                                                    
        default_action = NoAction();                                                                                    
    }


    table ipv6_lpm {    
    # begin insert code
        actions = {                                                                                                     
            my_drop;                                                                                                      
            set_nhop6;                                                                                                   
            NoAction;                                                                                                   
        }                                                                                                               
        key = {                                                                                                         
            hdr.ipv6.destination_address: 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();                                                                                    
    }


    table forward6 {   
    # begin insert code
        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 -> 1, h2 -> 2 also remain unchanged (00:aa:bb:00:00:00, 00:aa:bb:00:00:01).
  • The IPv6 addresses of clients h1, h2 are 2002::10 and 2002:1::10 respectively

In [8]:
#Example Code
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
"""


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 [9]:
# use this cell to compile the ipv6 router
compileP4('/root/p4own', header6, parser6, table6, router6)
Compilation ended successfully: /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 [10]:
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('/root/bmv2/mininet')
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 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-2-log.ipc --device-id 2 /root/p4own/simple_router.json --log-console
P4 switch s1 has been started.

Adding host h1
Adding host h2
Reading switch configuration script: /root/p4own/simple_router.config
Configuring switch...
Obtaining JSON from switch...
Done
Control utility for runtime P4 table manipulation
RuntimeCmd: *** Unknown syntax: "
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=0.923 ms
64 bytes from 10.0.1.10: icmp_seq=2 ttl=63 time=0.874 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=11.9 ms

--- 10.0.1.10 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3032ms
rtt min/avg/max/mdev = 0.874/3.675/11.868/4.730 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.770 ms
64 bytes from 10.0.0.10: icmp_seq=2 ttl=63 time=0.983 ms
64 bytes from 10.0.0.10: icmp_seq=3 ttl=63 time=1.31 ms
64 bytes from 10.0.0.10: icmp_seq=4 ttl=63 time=1.40 ms

--- 10.0.0.10 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3032ms
rtt min/avg/max/mdev = 0.770/1.115/1.395/0.251 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 proto kernel_lo 
       valid_lft forever preferred_lft forever
2: eth0@if1000: <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 proto kernel_ll 
       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 proto kernel_lo 
       valid_lft forever preferred_lft forever
2: eth0@if1001: <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 proto kernel_ll 
       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.35 ms
64 bytes from 2002:1::10: icmp_seq=2 ttl=63 time=1.04 ms
64 bytes from 2002:1::10: icmp_seq=3 ttl=63 time=1.26 ms
64 bytes from 2002:1::10: icmp_seq=4 ttl=63 time=1.20 ms

--- 2002:1::10 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3004ms
rtt min/avg/max/mdev = 1.041/1.212/1.352/0.113 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.22 ms
64 bytes from 2002::10: icmp_seq=2 ttl=63 time=1.27 ms
64 bytes from 2002::10: icmp_seq=3 ttl=63 time=1.65 ms
64 bytes from 2002::10: icmp_seq=4 ttl=63 time=2.17 ms

--- 2002::10 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3005ms
rtt min/avg/max/mdev = 1.221/1.578/2.173/0.381 ms

*** Stopping 0 controllers

*** Stopping 2 links
.
.

*** Stopping 1 switches
s1 

*** Stopping 2 hosts
h1 
h2 
*** Done
In [ ]:
 
In [ ]:
 
In [ ]:
 
In [ ]:
 
In [ ]:
 
In [ ]:
 
In [ ]:
 

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

Teaching assistants: Christian Dietze, Sebastian Gallenmüller, Marcel Kempf, Lorenz Lehle, Nikolas Gauder, Patrick Dirks