Mininet-Wifi (P4): Roaming

[topology]

Initially, sta1 and sta3 are connecting to ap1. Later, sta3 will disconnect with ap1 and then connect to ap2. There is a local controller running in ap1. This controller will monitor the status of connected stations. When the station is disconnected from h1. The controller will remove the rule from ap1 for this station. When the station is connected to ap1, the controller will add rule for this station. Similarly, there is another local controller for ap2.

 

Later, when sta3 is disconnected with ap1 and connected to ap2.

 

Note. You need to install the mininet-wifi with p4 first.

 

[p4.py]

#!/usr/bin/python

 

"""

You need to activate the following apps if you want to run this code with ONOS:

 

onos> app activate fwd drivers.bmv2 drivers.mellanox pipelines.fabric

      proxyarp lldpprovider hostprovider segmentrouting

"""

 

 

import os

import sys

 

from mininet.log import setLogLevel, info

from mn_wifi.cli import CLI

from mn_wifi.net import Mininet_wifi

from mn_wifi.bmv2 import P4AP, P4Host

from mininet.node import RemoteController

 

 

def topology(remote_controller):

    'Create a network.'

    net = Mininet_wifi()

 

    info('*** Adding stations/hosts\n')

    h1 = net.addHost('h1', ip='10.0.0.1/24', mac="00:00:00:00:00:01")

    h2 = net.addHost('h2', ip='10.0.0.2/24', mac="00:00:00:00:00:02")

    br = net.addHost('br')

    sta1 = net.addStation('sta1', ip='10.0.0.3/24', mac="00:00:00:00:00:03")

    sta2 = net.addStation('sta2', ip='10.0.0.4/24', mac="00:00:00:00:00:04")

    sta3 = net.addStation('sta3', ip='10.0.0.5/24', mac="00:00:00:00:00:05")

 

    args1 = dict()

    args2 = dict()

    if not remote_controller:

        path = os.path.dirname(os.path.abspath(__file__))

        json_file = path + '/ap-runtime.json'

        config1 = path + '/commands_ap1.txt'

        config2 = path + '/commands_ap2.txt'

        args1 = {'json': json_file, 'switch_config': config1}

        args2 = {'json': json_file, 'switch_config': config2}

 

    info('*** Adding P4APs\n')

    ap1 = net.addAccessPoint('ap1', ssid='ssid-ap1', cls=P4AP, netcfg=True, **args1)

    ap2 = net.addAccessPoint('ap2', ssid='ssid-ap2', cls=P4AP, netcfg=True, **args2)

 

    if remote_controller:

        info('*** Adding Controller\n')

        net.addController('c0', controller=RemoteController)

 

    net.configureWifiNodes()

 

    info('*** Creating links\n')

    net.addLink(sta1, ap1)

    net.addLink(sta2, ap2)

    net.addLink(sta3, ap1)

    #net.addLink(h1, ap1)

    #net.addLink(h2, ap2)

    net.addLink(ap1, br)

    net.addLink(ap2, br)

    net.addLink(br, h1)

    net.addLink(br, h2)

 

    info('*** Starting network\n')

    net.start()

    ap1,ap2,br,h1=net.get('ap1','ap2','br', 'h1')

    #ap1.cmd("python get-stations.py &")

    br.cmd("brctl addbr mybr")

    br.cmd("ifconfig br-eth0 0")

    br.cmd("ifconfig br-eth1 0")

    br.cmd("ifconfig br-eth2 0")

    br.cmd("ifconfig br-eth3 0")

    br.cmd("brctl addif mybr br-eth0")

    br.cmd("brctl addif mybr br-eth1")

    br.cmd("brctl addif mybr br-eth2")

    br.cmd("brctl addif mybr br-eth3")

    br.cmd("ifconfig mybr up")

    br.cmd("brctl setageing mybr 1")

   

    if not remote_controller:

        net.staticArp()

 

    h1.cmd("arp -s 10.0.0.3 00:00:00:00:00:03")

    h2.cmd("arp -s 10.0.0.3 00:00:00:00:00:03")

    sta2.cmd("arp -s 10.0.0.3 00:00:00:00:00:03")

    sta3.cmd("arp -s 10.0.0.3 00:00:00:00:00:03")

 

    info('*** Running CLI\n')

    CLI(net)

 

    info('*** Stopping network\n')

    net.stop()

 

 

if __name__ == '__main__':

    #setLogLevel('debug')

    setLogLevel('info')

    remote_controller = True if '-r' in sys.argv else False

    topology(remote_controller)

 

[basic.p4]

/* -*- P4_16 -*- */

#include <core.p4>

#include <v1model.p4>

 

const bit<16> TYPE_IPV4 = 0x800;

 

/*************************************************************************

*********************** H E A D E R S  ***********************************

*************************************************************************/

 

typedef bit<9>  egressSpec_t;

typedef bit<48> macAddr_t;

typedef bit<32> ip4Addr_t;

 

header ethernet_t {

    macAddr_t dstAddr;

    macAddr_t 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;

    ip4Addr_t srcAddr;

    ip4Addr_t dstAddr;

}

 

struct metadata {

    /* empty */

}

 

struct headers {

    ethernet_t   ethernet;

    ipv4_t       ipv4;

}

 

/*************************************************************************

*********************** P A R S E R  ***********************************

*************************************************************************/

 

parser MyParser(packet_in packet,

                out headers hdr,

                inout metadata meta,

                inout standard_metadata_t standard_metadata) {

 

    state start {

        packet.extract(hdr.ethernet);

        transition select(hdr.ethernet.etherType){

            TYPE_IPV4: ipv4;

            default: accept;

        }

    }

 

    state ipv4 {

        packet.extract(hdr.ipv4);

        transition accept;

    }

 

}

 

 

/*************************************************************************

************   C H E C K S U M    V E R I F I C A T I O N   *************

*************************************************************************/

 

control MyVerifyChecksum(inout headers hdr, inout metadata meta) {

    apply {  }

}

 

 

/*************************************************************************

**************  I N G R E S S   P R O C E S S I N G   *******************

*************************************************************************/

 

control MyIngress(inout headers hdr,

                  inout metadata meta,

                  inout standard_metadata_t standard_metadata) {

 

    action drop() {

        mark_to_drop(standard_metadata);

    }

 

    action ipv4_forward(egressSpec_t port) {

        standard_metadata.egress_spec = port;

    }

 

    table ipv4_lpm {

        key = {

            hdr.ipv4.dstAddr: exact;

        }

        actions = {

            ipv4_forward;

            drop;

            NoAction;

        }

        size = 1024;

        default_action = drop();

    }

 

    apply {

        if (hdr.ipv4.isValid()){

            ipv4_lpm.apply();

 

        }

    }

}

 

/*************************************************************************

****************  E G R E S S   P R O C E S S I N G   *******************

*************************************************************************/

 

control MyEgress(inout headers hdr,

                 inout metadata meta,

                 inout standard_metadata_t standard_metadata) {

    apply {  }

}

 

/*************************************************************************

*************   C H E C K S U M    C O M P U T A T I O N   **************

*************************************************************************/

 

control MyComputeChecksum(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);

    }

}

 

 

/*************************************************************************

***********************  D E P A R S E R  *******************************

*************************************************************************/

 

control MyDeparser(packet_out packet, in headers hdr) {

    apply {

 

        //parsed headers have to be added again into the packet.

        packet.emit(hdr.ethernet);

        packet.emit(hdr.ipv4);

 

    }

}

 

/*************************************************************************

***********************  S W I T C H  *******************************

*************************************************************************/

 

//switch architecture

V1Switch(

MyParser(),

MyVerifyChecksum(),

MyIngress(),

MyEgress(),

MyComputeChecksum(),

MyDeparser()

) main;

 

[commands_ap1.txt]

table_set_default ipv4_lpm drop

table_add ipv4_lpm ipv4_forward 10.0.0.1 => 2

table_add ipv4_lpm ipv4_forward 10.0.0.2 => 2

table_add ipv4_lpm ipv4_forward 10.0.0.3 => 1

table_add ipv4_lpm ipv4_forward 10.0.0.4 => 2

table_add ipv4_lpm ipv4_forward 10.0.0.5 => 1

 

[commands_ap2.txt]

table_set_default ipv4_lpm drop

table_add MyIngress.ipv4_lpm ipv4_forward 10.0.0.1 => 2

table_add MyIngress.ipv4_lpm ipv4_forward 10.0.0.2 => 2

table_add MyIngress.ipv4_lpm ipv4_forward 10.0.0.3 => 2

table_add MyIngress.ipv4_lpm ipv4_forward 10.0.0.4 => 1

table_add MyIngress.ipv4_lpm ipv4_forward 10.0.0.5 => 2

 

[run1.py]

from subprocess import check_output

import time

 

ap1_sta=['00:00:00:00:00:03', '00:00:00:00:00:05']

 

f=open("/tmp/bmv2-ap1-thrift-port", "r")

port=f.read()

print(port)

f.close

 

while True:

  out = check_output(['./get-stations1.sh', ''])

  x=out.split( )

  y = [item for item in ap1_sta if not item in x]

  z = [item for item in x if not item in ap1_sta]

  print "x=", x

  #print "y=", y, "z=", z, "ap1_sta=", ap1_sta

  if y and y[0] in ap1_sta:

    print y," is moving out of ap1"

    ap1_sta.remove(y[0])

    tmp=y[0].split(':')

    tmp2="0a0000"+tmp[5]

    out2 = check_output(['./rm_rules1.sh', tmp2])

    print out2

  if z:

    print z," is connected to ap2"

    ap1_sta.extend(z)

    tmp=z[0].split(':')

    tmp2="0a0000"+tmp[5]

    out2 = check_output(['./add_rules1.sh', tmp2])

    print out2

 

  time.sleep(1)

 

[run2.py]

from subprocess import check_output

import time

 

ap2_sta=['00:00:00:00:00:04']

 

while True:

  out = check_output(['./get-stations2.sh', ''])

  x=out.split( )

  y = [item for item in ap2_sta if not item in x]

  z = [item for item in x if not item in ap2_sta]

  print "x=", x

  if y and y[0] in ap2_sta:

    print y," is moving out of ap2"

    ap2_sta.remove(y[0])

    tmp=y[0].split(':')

    tmp2="0a0000"+tmp[5]

    out2 = check_output(['./rm_rules2.sh', tmp2])

    print out2

  if z:

    print z," is connected to ap2"

    ap2_sta.extend(z)

    tmp=z[0].split(':')

    tmp2="0a0000"+tmp[5]

    out2 = check_output(['./add_rules2.sh', tmp2])

    print out2

 

  time.sleep(1)

 

[get-station1.sh]

#!/bin/bash

 

result=`iw dev ap1-wlan1 station dump|grep Station|awk '{print $2}'`

echo $result

 

[get-station2.sh]

#!/bin/bash

 

result=`iw dev ap2-wlan1 station dump|grep Station|awk '{print $2}'`

echo $result

 

[add_rules1.sh]

#!/bin/bash

 

port=`cat /tmp/bmv2-ap1-thrift-port`

 

no=$(echo "table_dump ipv4_lpm" | simple_switch_CLI --thrift-port $port | grep -B 3 "$1" | grep Dumping | awk '{print $3}' |awk -F"x" '{print $2}'| xargs echo "ibase=16; $2" | bc)

 

echo "table_delete ipv4_lpm $no" | simple_switch_CLI --thrift-port $port

 

tmp=`echo $1 | cut -c 8`

echo "table_add ipv4_lpm ipv4_forward 10.0.0.$tmp => 1"| simple_switch_CLI --thrift-port $port

 

[add_rules2.sh]

#!/bin/bash

 

port=`cat /tmp/bmv2-ap2-thrift-port`

 

no=$(echo "table_dump ipv4_lpm" | simple_switch_CLI --thrift-port $port | grep -B 3 "$1" | grep Dumping | awk '{print $3}' | awk -F"x" '{print $2}'| xargs echo "ibase=16; $2" | bc)

 

echo "table_delete ipv4_lpm $no" | simple_switch_CLI --thrift-port $port

 

tmp=`echo $1 | cut -c 8`

echo "table_add ipv4_lpm ipv4_forward 10.0.0.$tmp => 1"| simple_switch_CLI --thrift-port $port

 

[rm_rules1.sh]

#!/bin/bash

 

port=`cat /tmp/bmv2-ap1-thrift-port`

 

no=$(echo "table_dump ipv4_lpm" | simple_switch_CLI --thrift-port $port | grep -B 3 "$1" | grep Dumping | awk '{print $3}' | awk -F"x" '{print $2}'| xargs echo "ibase=16; $2" | bc)

 

#echo $no

echo "table_delete ipv4_lpm $no" | simple_switch_CLI --thrift-port $port

 

[rm_rules2.sh]

#!/bin/bash

 

port=`cat /tmp/bmv2-ap2-thrift-port`

 

no=$(echo "table_dump ipv4_lpm" | simple_switch_CLI --thrift-port $port | grep -B 3 "$1" | grep Dumping | awk '{print $3}' | awk -F"x" '{print $2}' | xargs echo "ibase=16; $2" | bc)

 

#echo $no

echo "table_delete ipv4_lpm $no" | simple_switch_CLI --thrift-port $port

 

[execution]

Before running the example, please stop NetworkManager.

 

Compile the p4 program.

 

Running p4.py

 

Open terminal for sta3, ap1, and ap2.

 

Running run1.py at ap1, run2.py at ap2, and ping at sta3.

 

Sta3 disconnected with ap1. Ping does not work. We can also see that 00:00:00:00:00:05 (sta3) is moving out of ap1

 

Sta3 connects with ap2. We can see the rule has been added in ap2. Ping works again.

Dr. Chih-Heng Ke

Department of Computer Science and Information Engineering, National Quemoy University, Kinmen, Taiwan

Email: smallko@gmail.com