P4 Switch(P4-16): Anti-Sync Attack

 

Part of work was referred to https://github.com/zhangmenghao/p4research.

[topology]

 

Syn proxy can set up a tcp connection with client first. If the third ack sent by client (the third packet of TCP three-way handshake) is received by the syn proxy, it will modify this ack pkt to initialize another tcp connect with web server. If the client (attacker) sends the syn pkt but does not sends the third ack pkt, the syn proxy will count the number of syn pkts sent by this client. When the number is greater than 3, the syn proxy will block the ip address of this client.

 

[basic.p4]

/* -*- P4_16 -*- */

#include <core.p4>

#include <v1model.p4>

 

const bit<16> TYPE_IPV4 = 0x800;

 

#define PROXY_OFF 0

#define PROXY_ON 1

#define TCP_FLAG_URG 0x20

#define TCP_FLAG_ACK 0x10

#define TCP_FLAG_PSH 0x08

#define TCP_FLAG_RST 0x04

#define TCP_FLAG_SYN 0x02

#define TCP_FLAG_FIN 0x01

#define TCP_FLAG_SA 0x12

#define FALSE 0

#define TRUE 1

#define VALID 0x1

 

register<bit<2>>(65536)  mycounter;  

register<bit<33>>(65536) syn_proxy_table;     // 32 bit offset + 1 bit is_valid

 

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

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

}

 

header tcp_t {

    bit<16> srcPort;

    bit<16> dstPort;

    bit<32> seqNo;

    bit<32> ackNo;

    bit<4>  dataOffset;

    bit<4>  res;

    bit<8>  flags;

    bit<16> window;

    bit<16> checksum;

    bit<16> urgentPtr;

}

 

header udp_t {

    bit<16> srcPort;

    bit<16> dstPort;

    bit<16> udplength;

    bit<16> checksum;

}

 

struct metadata {

    bit<48>     eth_sa;

    bit<48>    eth_da;

    bit<32>    ipv4_sa;

    bit<32>    ipv4_da;

    bit<16>    tcp_sp;

    bit<16>    tcp_dp;

    bit<16>    tcp_length;

    bit<32>    tcp_ack_no;

    bit<32>    tcp_seq_no;

 

    // forward information

    bit<32>     nhop_ipv4; // ipv4 next hop

  

    // for control flow

    bit<1>      syn_proxy_status;    // 0 for PROXY_OFF, 1 for PROXY_ON

    bit<1>      to_drop;

   

    // seq# offset

    bit<32>    seq_no_offset;

 

    // for syn-cookie

    bit<32>   cookie_key;

    bit<32>     cookie_val;

 

    // for check_syn_proxy_table

    bit<16>    syn_proxy_table_hash_val;

    bit<33>    syn_proxy_table_entry_val;

 

    bit<13>    count_hash;

    bit<2>      count;     

}

 

struct headers {

    ethernet_t   ethernet;

    ipv4_t       ipv4;

    tcp_t        tcp;

    udp_t        udp;

}

 

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

*********************** 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 {

        transition parse_ethernet;

    }

 

    state parse_ethernet {

        packet.extract(hdr.ethernet);

        meta.eth_da=hdr.ethernet.dstAddr;

        meta.eth_sa=hdr.ethernet.srcAddr;

        transition select(hdr.ethernet.etherType) {

            TYPE_IPV4: parse_ipv4;

            default: accept;

        }

    }

 

    state parse_ipv4 {

        packet.extract(hdr.ipv4);

        meta.ipv4_sa=hdr.ipv4.srcAddr;

        meta.ipv4_da=hdr.ipv4.dstAddr;

        meta.tcp_length=hdr.ipv4.totalLen - 20;

        transition select(hdr.ipv4.protocol) {

            0x06: parse_tcp;

            0x11: parse_udp;

            default: accept;

        }

    }

 

    state parse_tcp {

        packet.extract(hdr.tcp);

        meta.tcp_sp=hdr.tcp.srcPort;

        meta.tcp_dp=hdr.tcp.dstPort;

        meta.tcp_seq_no=hdr.tcp.seqNo;

        meta.tcp_ack_no=hdr.tcp.ackNo;

        meta.to_drop=TRUE;

        transition accept; 

    }

 

    state parse_udp {

        packet.extract(hdr.udp);

        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 {  }

}

 

control syn_proxy (inout headers hdr,

                  inout metadata meta,

                  inout standard_metadata_t standard_metadata) {

 

        action confirm_connection() {

                meta.to_drop=FALSE;

                syn_proxy_table.write((bit<32>)meta.syn_proxy_table_hash_val, ((bit<33>)(hdr.tcp.ackNo-1))<<1);

                //p4_logger(((bit<33>)(hdr.tcp.ackNo-1))<<1);

                hdr.tcp.seqNo=hdr.tcp.seqNo - 1;

                hdr.tcp.flags=TCP_FLAG_SYN;

                hdr.tcp.ackNo=0;

        }

        table confirm_connection_table {

                actions = {

                        confirm_connection;

                }

        }

 

        action reply_sa() {          

                meta.to_drop=FALSE;

                syn_proxy_table.write((bit<32>)meta.syn_proxy_table_hash_val, 0);

                hdr.ethernet.srcAddr=meta.eth_da;

                hdr.ethernet.dstAddr=meta.eth_sa;

                hdr.ipv4.srcAddr=meta.ipv4_da;

                hdr.ipv4.dstAddr=meta.ipv4_sa;

                hdr.tcp.srcPort=meta.tcp_dp;

                hdr.tcp.dstPort=meta.tcp_sp;

                hdr.tcp.flags=TCP_FLAG_ACK | TCP_FLAG_SYN;

                hdr.tcp.ackNo=hdr.tcp.seqNo + 1;

                hdr.tcp.seqNo=meta.cookie_val;

                hdr.tcp.window=0;

        }

        table reply_sa_table {

                actions = {

                        reply_sa;

                }

        }

 

        action _drop() {

                mark_to_drop(standard_metadata);

        }

        action calculate_syn_cookie_from_client(){

                meta.cookie_key=(bit<32>)0xf;

                hash(meta.cookie_val, HashAlgorithm.crc32, (bit<32>)0, { hdr.ipv4.srcAddr, hdr.ipv4.dstAddr, hdr.tcp.srcPort, hdr.tcp.dstPort, hdr.ipv4.protocol, meta.cookie_key }, (bit<32>)65536);

        }

        action calculate_syn_cookie_from_server(){

                meta.cookie_key=(bit<32>)0xf;

                hash(meta.cookie_val, HashAlgorithm.crc32, (bit<32>)0, { hdr.ipv4.dstAddr, hdr.ipv4.srcAddr, hdr.tcp.dstPort, hdr.tcp.srcPort, hdr.ipv4.protocol, meta.cookie_key}, (bit<32>)65536);

        }

        table calculate_syn_cookie_table {

                key = {

                        hdr.tcp.flags : ternary;

                }

                actions = {

                        _drop;

                        calculate_syn_cookie_from_client;

                        calculate_syn_cookie_from_server;

                }

        }

 

        action open_window() {

                meta.to_drop=FALSE;

                meta.seq_no_offset=(bit<32>)((meta.syn_proxy_table_entry_val & 0x1FFFFFFFE) >> 1);

                meta.seq_no_offset = hdr.tcp.seqNo - meta.seq_no_offset;

                hdr.tcp.seqNo=(bit<32>)((meta.syn_proxy_table_entry_val & 0x1FFFFFFFE) >> 1);

                syn_proxy_table.write((bit<32>)meta.syn_proxy_table_hash_val, ((bit<33>)meta.seq_no_offset) << 1 | (bit<33>)0x1);

        }

        table open_window_table {

                actions = {

                        open_window;

                }

        }

 

        action add_delta_to_ack() {          

                meta.to_drop=FALSE;

                hdr.tcp.ackNo=hdr.tcp.ackNo+(bit<32>)(meta.syn_proxy_table_entry_val >> 1);

        }

        table add_delta_to_ack_table {

                actions = {

                        add_delta_to_ack;

                }

        }

        action sub_delta_to_seq() {   

                meta.to_drop=FALSE;     

                hdr.tcp.seqNo=hdr.tcp.seqNo-(bit<32>)(meta.syn_proxy_table_entry_val >> 1);

        }

        table sub_delta_to_seq_table {

                actions = {

                        sub_delta_to_seq;

                }

        }

  

        apply {    

                if(standard_metadata.ingress_port == 2){

                   hash(meta.syn_proxy_table_hash_val, HashAlgorithm.crc16, (bit<16>)0, { hdr.ipv4.dstAddr, hdr.ipv4.srcAddr, hdr.tcp.dstPort, hdr.tcp.srcPort, hdr.ipv4.protocol }, (bit<32>)8192);

                   syn_proxy_table.read(meta.syn_proxy_table_entry_val,  (bit<32>)meta.syn_proxy_table_hash_val);  

                } else {

                    hash(meta.syn_proxy_table_hash_val, HashAlgorithm.crc16, (bit<16>)0, { hdr.ipv4.srcAddr, hdr.ipv4.dstAddr, hdr.tcp.srcPort, hdr.tcp.dstPort, hdr.ipv4.protocol }, (bit<32>)8192);

                    syn_proxy_table.read(meta.syn_proxy_table_entry_val,  (bit<32>)meta.syn_proxy_table_hash_val);

                    hash(meta.count_hash, HashAlgorithm.crc16, (bit<16>)0, { hdr.ipv4.srcAddr }, (bit<32>)8192);

                    mycounter.read(meta.count, (bit<32>)meta.count_hash);

                }

 

                if(meta.syn_proxy_table_entry_val & 0x1 == VALID){

                        if(standard_metadata.ingress_port == 2){

                                sub_delta_to_seq_table.apply();

                        }else {

                                add_delta_to_ack_table.apply();

                        }

                }else {

 

                        if(hdr.tcp.flags & (TCP_FLAG_ACK | TCP_FLAG_SYN) == (TCP_FLAG_ACK | TCP_FLAG_SYN)){

                                open_window_table.apply();

                        } else{

                                calculate_syn_cookie_table.apply();

                                if(hdr.tcp.flags & TCP_FLAG_SYN == TCP_FLAG_SYN){

                                        if(meta.count==3){

                                          _drop();      

                                        }

                                        if(meta.count<3) {                                  

                                          meta.count=meta.count+1;

                                          hash(meta.count_hash, HashAlgorithm.crc16, (bit<16>)0, { hdr.ipv4.srcAddr }, (bit<32>)8192);

                                          mycounter.write((bit<32>)meta.count_hash, meta.count);                    

                                          reply_sa_table.apply();

                                        }                                      

                                } else if(hdr.tcp.flags & TCP_FLAG_ACK == TCP_FLAG_ACK) {

                                        meta.count=0;

                                        mycounter.write((bit<32>)hdr.ipv4.srcAddr, meta.count);            

                                      if(hdr.tcp.ackNo == (meta.cookie_val + 1)){

                                                confirm_connection_table.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 forward(bit<48> dstAddr, egressSpec_t port) {

        standard_metadata.egress_spec = port;

            hdr.ethernet.dstAddr=dstAddr;

    }

    table ip_forward {

        key = {

            hdr.ipv4.dstAddr: exact;

        }

        actions = {

            forward;

            drop;

        }

        size = 1024;

        default_action = drop();

    }

   

    apply {

       

        if(hdr.tcp.isValid()) {

          syn_proxy.apply(hdr, meta, standard_metadata);

       }

 

        if(meta.to_drop==TRUE) {

                drop();

        } else {

                ip_forward.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);

       

        update_checksum_with_payload(true, { hdr.ipv4.srcAddr, hdr.ipv4.dstAddr, 8w0, hdr.ipv4.protocol, meta.tcp_length, hdr.tcp.srcPort, hdr.tcp.dstPort, hdr.tcp.seqNo, hdr.tcp.ackNo, hdr.tcp.dataOffset, hdr.tcp.res, hdr.tcp.flags, hdr.tcp.window, hdr.tcp.urgentPtr }, hdr.tcp.checksum, HashAlgorithm.csum16);

 

    }

}

 

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

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

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

 

control MyDeparser(packet_out packet, in headers hdr) {

    apply {

        packet.emit(hdr.ethernet);

        packet.emit(hdr.ipv4);

        packet.emit(hdr.tcp);

        packet.emit(hdr.udp);   

    }

}

 

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

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

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

 

V1Switch(

MyParser(),

MyVerifyChecksum(),

MyIngress(),

MyEgress(),

MyComputeChecksum(),

MyDeparser()

) main;

 

[basic2.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 {

        transition parse_ethernet;

    }

 

    state parse_ethernet {

        packet.extract(hdr.ethernet);

        transition select(hdr.ethernet.etherType) {

            TYPE_IPV4: parse_ipv4;

            default: accept;

        }

    }

 

    state parse_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 forward(bit<48> dstAddr, egressSpec_t port) {

        standard_metadata.egress_spec = port;

        hdr.ethernet.dstAddr=dstAddr;

    }

   

    table ip_forward {

        key = {

            hdr.ipv4.dstAddr: exact;

        }

        actions = {

            forward;

            drop;

        }

        size = 1024;

        default_action = drop();

    }

   

    apply {

        ip_forward.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 {

        packet.emit(hdr.ethernet);

        packet.emit(hdr.ipv4);

    }

}

 

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

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

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

 

V1Switch(

MyParser(),

MyVerifyChecksum(),

MyIngress(),

MyEgress(),

MyComputeChecksum(),

MyDeparser()

) main;

 

[test_topo.py]

import os

from mininet.net import Mininet

from mininet.topo import Topo

from mininet.link import TCLink

from mininet.log import setLogLevel, info

from mininet.cli import CLI

from mininet.node import RemoteController

 

from p4_mininet import P4Switch, P4Host

 

import argparse

from time import sleep

 

parser = argparse.ArgumentParser(description='Mininet demo')

parser.add_argument('--behavioral-exe', help='Path to behavioral executable',

                    type=str, action="store", required=False, default='simple_switch' )

parser.add_argument('--thrift-port', help='Thrift server port for table updates',

                    type=int, action="store", default=9090)

parser.add_argument('--num-hosts', help='Number of hosts to connect to switch',

                    type=int, action="store", default=2)

parser.add_argument('--mode', choices=['l2', 'l3'], type=str, default='l3')

parser.add_argument('--json1', help='Path to JSON config file',

                    type=str, action="store", required=True)

parser.add_argument('--json2', help='Path to JSON config file',

                    type=str, action="store", required=True)

parser.add_argument('--pcap-dump', help='Dump packets on interfaces to pcap files',

                    type=str, action="store", required=False, default=False)

 

 

args = parser.parse_args()

 

 

class SingleSwitchTopo(Topo):

    def __init__(self, sw_path, json_path1, json_path2, thrift_port, pcap_dump, **opts):

        Topo.__init__(self, **opts)

 

        switch1 = self.addSwitch('s1', sw_path = sw_path, json_path = json_path1, thrift_port = thrift_port,cls = P4Switch ,pcap_dump = pcap_dump)

        switch2 = self.addSwitch('s2', sw_path = sw_path, json_path = json_path2, thrift_port = thrift_port + 1, cls = P4Switch ,pcap_dump = pcap_dump)

        switch3 = self.addSwitch('s3', sw_path = sw_path, json_path = json_path1, thrift_port = thrift_port + 2, cls = P4Switch ,pcap_dump = pcap_dump)

                       

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

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

        host3 = self.addHost('h3', mac = '00:00:00:00:00:03', ip="10.0.0.3/24")

 

        self.addLink(host1, switch1, port1 = 0, port2 = 1, bw=10)

        self.addLink(host2, switch2, port1 = 0, port2 = 1, bw=10)

        self.addLink(host3, switch3, port1 = 0, port2 = 1, bw=10)

        self.addLink(switch1, switch2, port1 = 2, port2 = 2, bw=10)

        self.addLink(switch3, switch2, port1 = 2, port2 = 3, bw=10)    

       

def main():

    topo = SingleSwitchTopo(args.behavioral_exe, args.json1, args.json2, args.thrift_port, args.pcap_dump)

    net = Mininet(topo = topo, host = P4Host, controller = None, link=TCLink)

    net.start()

  

    h1,h2,h3=net.get('h1','h2','h3')

    h1.cmd("arp -s 10.0.0.2 00:00:00:00:00:02")

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

    h2.cmd("arp -s 10.0.0.1 00:00:00:00:00:01")

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

    h3.cmd("arp -s 10.0.0.1 00:00:00:00:00:01")

    h3.cmd("arp -s 10.0.0.2 00:00:00:00:00:02")

 

    sleep(1)

 

    print('\033[0;32m'),

    print "Gotcha!"

    print('\033[0m')

 

    CLI(net)

    try:

        net.stop()

    except:

        print('\033[0;31m'),

        print('Stop error! Trying sudo mn -c')

        print('\033[0m')

        os.system('sudo mn -c')

        print('\033[0;32m'),

        print ('Stop successfully!')

        print('\033[0m')

 

if __name__ == '__main__':

    setLogLevel('info')

    main()

 

[start_test_topo.py]

import os

 

os.system("sudo python test_topo.py --behavioral-exe /home/vagrant/behavioral-model/targets/simple_switch/simple_switch --json1 basic.json --json2 basic2.json")

 

[cmd.txt]

table_set_default sub_delta_to_seq_table sub_delta_to_seq

table_set_default add_delta_to_ack_table add_delta_to_ack

table_set_default calculate_syn_cookie_table _drop

table_set_default open_window_table open_window

table_set_default reply_sa_table reply_sa

table_set_default confirm_connection_table confirm_connection

table_add calculate_syn_cookie_table calculate_syn_cookie_from_client 0x02&&&0x02 => 1

table_add calculate_syn_cookie_table calculate_syn_cookie_from_client 0x10&&&0x10 => 1

table_add calculate_syn_cookie_table calculate_syn_cookie_from_server 0x12&&&0x12 => 2

table_add ip_forward forward 10.0.0.1 => 00:00:00:00:00:01 1

table_add ip_forward forward 10.0.0.2 => 00:00:00:00:02:02 2

table_add ip_forward forward 10.0.0.3 => 00:00:00:00:02:02 2

 

[cmd2.txt]

table_add ip_forward forward 10.0.0.1 => 00:00:00:00:01:01 2

table_add ip_forward forward 10.0.0.2 => 00:00:00:00:00:02 1

table_add ip_forward forward 10.0.0.3 => 00:00:00:00:03:03 3

 

[cmd3.txt]

table_set_default sub_delta_to_seq_table sub_delta_to_seq

table_set_default add_delta_to_ack_table add_delta_to_ack

table_set_default calculate_syn_cookie_table _drop

table_set_default open_window_table open_window

table_set_default reply_sa_table reply_sa

table_set_default confirm_connection_table confirm_connection

table_add calculate_syn_cookie_table calculate_syn_cookie_from_client 0x02&&&0x02 => 1

table_add calculate_syn_cookie_table calculate_syn_cookie_from_client 0x10&&&0x10 => 1

table_add calculate_syn_cookie_table calculate_syn_cookie_from_server 0x12&&&0x12 => 2

table_add ip_forward forward 10.0.0.1 => 00:00:00:00:02:02 2

table_add ip_forward forward 10.0.0.2 => 00:00:00:00:02:02 2

table_add ip_forward forward 10.0.0.3 => 00:00:00:00:00:03 1

 

[cmd_add.py]

import os

 

os.system('sudo /home/vagrant/behavioral-model/targets/simple_switch/simple_switch_CLI --thrift-port=9090 < cmd.txt')

os.system('sudo /home/vagrant/behavioral-model/targets/simple_switch/simple_switch_CLI --thrift-port=9091 < cmd2.txt')

os.system('sudo /home/vagrant/behavioral-model/targets/simple_switch/simple_switch_CLI --thrift-port=9092 < cmd3.txt')

 

[p4_mininet.py]

from mininet.net import Mininet

from mininet.node import Switch, Host

from mininet.log import setLogLevel, info, error, debug

from mininet.moduledeps import pathCheck

from sys import exit

import os

import tempfile

import socket

 

class P4Host(Host):

    def config(self, **params):

        r = super(Host, self).config(**params)

 

        self.defaultIntf().rename("eth0")

 

        for off in ["rx", "tx", "sg"]:

            cmd = "/sbin/ethtool --offload eth0 %s off" % off

            self.cmd(cmd)

 

        # disable IPv6

        self.cmd("sysctl -w net.ipv6.conf.all.disable_ipv6=1")

        self.cmd("sysctl -w net.ipv6.conf.default.disable_ipv6=1")

        self.cmd("sysctl -w net.ipv6.conf.lo.disable_ipv6=1")

 

        return r

 

    def describe(self):

        print "**********"

        print self.name

        print "default interface: %s\t%s\t%s" %(

            self.defaultIntf().name,

            self.defaultIntf().IP(),

            self.defaultIntf().MAC()

        )

        print "**********"

 

class P4Switch(Switch):

    """P4 virtual switch"""

    device_id = 0

 

    def __init__(self, name, sw_path = None, json_path = None,

                 thrift_port = None,

                 pcap_dump = False,

                 log_console = True,

                 verbose = True,

                 device_id = None,

                 enable_debugger = False,

                 **kwargs):

        Switch.__init__(self, name, **kwargs)

        assert(sw_path)

        assert(json_path)

        # make sure that the provided sw_path is valid

        pathCheck(sw_path)

        # make sure that the provided JSON file exists

        if not os.path.isfile(json_path):

            error("Invalid JSON file.\n")

            exit(1)

        self.sw_path = sw_path

        self.json_path = json_path

        self.verbose = verbose

        logfile = "/tmp/p4s.{}.log".format(self.name)

        self.output = open(logfile, 'w')

        self.thrift_port = thrift_port

        self.pcap_dump = pcap_dump

        self.enable_debugger = enable_debugger

        self.log_console = log_console

        if device_id is not None:

            self.device_id = device_id

            P4Switch.device_id = max(P4Switch.device_id, device_id)

        else:

            self.device_id = P4Switch.device_id

            P4Switch.device_id += 1

        self.nanomsg = "ipc:///tmp/bm-{}-log.ipc".format(self.device_id)

 

    @classmethod

    def setup(cls):

        pass

 

    def check_switch_started(self, pid):

        """While the process is running (pid exists), we check if the Thrift

        server has been started. If the Thrift server is ready, we assume that

        the switch was started successfully. This is only reliable if the Thrift

        server is started at the end of the init process"""

        while True:

            if not os.path.exists(os.path.join("/proc", str(pid))):

                return False

            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

            sock.settimeout(0.5)

            result = sock.connect_ex(("localhost", self.thrift_port))

            if result == 0:

                return  True

 

    def start(self, controllers):

        "Start up a new P4 switch"

        info("Starting P4 switch {}.\n".format(self.name))

        args = [self.sw_path]

        for port, intf in self.intfs.items():

            if not intf.IP():

                args.extend(['-i', str(port) + "@" + intf.name])

 

        #wuwzhs edit in 2017/11/10

        #args.extend(['-i 3@veth1'])

 

        if self.pcap_dump:

            args.append("--pcap")

            # args.append("--useFiles")

        if self.thrift_port:

            args.extend(['--thrift-port', str(self.thrift_port)])

        if self.nanomsg:

            args.extend(['--nanolog', self.nanomsg])

        args.extend(['--device-id', str(self.device_id)])

        P4Switch.device_id += 1

        args.append(self.json_path)

        if self.enable_debugger:

            args.append("--debugger")

        if self.log_console:

            args.append("--log-console")

        logfile = "/tmp/p4s.{}.log".format(self.name)

        info(' '.join(args) + "\n")

 

        pid = None

        with tempfile.NamedTemporaryFile() as f:

            # self.cmd(' '.join(args) + ' > /dev/null 2>&1 &')

            self.cmd(' '.join(args) + ' >' + logfile + ' 2>&1 & echo $! >> ' + f.name)

            pid = int(f.read())

        debug("P4 switch {} PID is {}.\n".format(self.name, pid))

        if not self.check_switch_started(pid):

            error("P4 switch {} did not start correctly.\n".format(self.name))

            exit(1)

        info("P4 switch {} has been started.\n".format(self.name))

 

    def stop(self):

        "Terminate P4 switch."

        self.output.flush()

        self.cmd('kill %' + self.sw_path)

        self.cmd('wait')

        self.deleteIntfs()

 

    def attach(self, intf):

        "Connect a data port"

        assert(0)

 

    def detach(self, intf):

        "Disconnect a data port"

        assert(0)

 

[Execution]

Case 1: All switches are using basic2.p4 (ip forwarder). No sync proxy.

Only web client

 

Start attacking

 

H1 will need more time to fetch the webpage.

 

Case 2: S1 and S3 implement the syn proxy.

We can see that h1 needs more time to fetch the webpage from server (h2). Because there are two TCP connections involved.

 

Start attacking.

We can see that the time needed by h1 to fetch the webpage seems unaffected.

 

Dr. Chih-Heng Ke (smallko@gmail.com)

Department of Computer Science and Information Engineering,

National Quemoy University, Kinmen, Taiwan.