P4实验之防火墙

实验要求

        在p4的tutorials-master\exercises\firewall中设计了一个防火墙实验,通过将p4代码编译成json部署到交换机中以实现防火墙的功能,网络的拓扑如下

         

        在readme中可以看到,我们需要在s1上部署防火墙功能,以使得h1 h2能够在内网建立连接,h1 h2也能够从内网与h3 h4建立连接,但是h3 h4并不能与h1 h2建立连接。在本次实验中,控制平面的相关代码已经写好了,所以只需对数据平面实现相关的功能即可。

实验内容

step1:先进行一个TCP测试

        在实现防火墙.p4前,我们先做一个小测试,make run进入mininet的命令行之后,对内网,内->外,外->内进行了iperf测试,可以看到都是可以建立连接的。

类型测试结果
内网->内网

succeed

内网->外网succeed
外网->内网succeed

step2:实现防火墙.p4的功能

在这里readme也给了我们很贴心的一些提示

1. Header type definitions for Ethernet (`ethernet_t`), IPv4 (`ipv4_t`) and TCP (`tcp_t`).

        对于第一条,要求我们定义好以太网、IP、TCP的头部,其实在p4文件中已经帮我们定义好了。

/*************************************************************************
*********************** 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<1>  cwr;
    bit<1>  ece;
    bit<1>  urg;
    bit<1>  ack;
    bit<1>  psh;
    bit<1>  rst;
    bit<1>  syn;
    bit<1>  fin;
    bit<16> window;
    bit<16> checksum;
    bit<16> urgentPtr;
}

struct metadata {
    /* empty */
}

struct headers {
    ethernet_t   ethernet;
    ipv4_t       ipv4;
    tcp_t        tcp;
}

2. Parsers for Ethernet, IPv4 and TCP that populate `ethernet_t`, `ipv4_t` and `tcp_t` fields.

        用解析器把上诉的几个包头解析出来,这也帮我们实现好了

/*************************************************************************
*********************** 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 select(hdr.ipv4.protocol){
            TYPE_TCP: tcp;
            default: accept;
        }
    }

    state tcp {
       packet.extract(hdr.tcp);
       transition accept;
    }
}

3. An action to drop a packet, using `mark_to_drop()`.

        丢弃数据包的操作,也帮我们实现好了

action drop() {
        mark_to_drop(standard_metadata);
    }

4. An action (called `compute_hashes`) to compute the bloom filter's two hashes using hash algorithms `crc16` and `crc32`. The hashes will be computed on the packet 5-tuple consisting of IPv4 source and  destination addresses, source and destination port numbers and the IPv4 protocol type.

        哈希的操作,计算两个不同的哈希算法得到的哈希值,哈希算法所依赖的参数是5个元素,分别是ipv4的源地址,目的地址,两个端口,ipv4的协议号,这里的hash算法是来自于v1model.p4中的,计算完哈希值以后,把相应的值填到布隆过滤器对应的桶中,哈希的值在调用了hash函数后,被保存在了MyIngress的

bit<32> reg_pos_one; bit<32> reg_pos_two;

bit<1> reg_val_one; bit<1> reg_val_two;中

action compute_hashes(ip4Addr_t ipAddr1, ip4Addr_t ipAddr2, bit<16> port1, bit<16> port2){
       //Get register position
       hash(reg_pos_one, HashAlgorithm.crc16, (bit<32>)0, {ipAddr1,
                                                           ipAddr2,
                                                           port1,
                                                           port2,
                                                           hdr.ipv4.protocol},
                                                           (bit<32>)BLOOM_FILTER_ENTRIES);

       hash(reg_pos_two, HashAlgorithm.crc32, (bit<32>)0, {ipAddr1,
                                                           ipAddr2,
                                                           port1,
                                                           port2,
                                                           hdr.ipv4.protocol},
                                                           (bit<32>)BLOOM_FILTER_ENTRIES);
    }

这个哈希方法在源码中可以看到,它的四个参数分别是结果,算法类型,数据,最大值

/***
 * Calculate a hash function of the value specified by the data
 * parameter.  The value written to the out parameter named result
 * will always be in the range [base, base+max-1] inclusive, if max >=
 * 1.  If max=0, the value written to result will always be base.
 *
 * Note that the types of all of the parameters may be the same as, or
 * different from, each other, and thus their bit widths are allowed
 * to be different.
 *
 * @param O          Must be a type bit<W>
 * @param D          Must be a tuple type where all the fields are bit-fields (type bit<W> or int<W>) or varbits.
 * @param T          Must be a type bit<W>
 * @param M          Must be a type bit<W>
 */
@pure
extern void hash<O, T, D, M>(out O result, in HashAlgorithm algo, in T base, in D data, in M max);

5. An action (`ipv4_forward`) and a table (`ipv4_lpm`) that will perform basic

IPv4 forwarding (adopted from `basic.p4`).

        这个操作其实在basic.p4中就有实现了,如果做过第一个实验应该不陌生

action ipv4_forward(macAddr_t dstAddr, egressSpec_t port) {
        standard_metadata.egress_spec = port;
        hdr.ethernet.srcAddr = hdr.ethernet.dstAddr;
        hdr.ethernet.dstAddr = dstAddr;
        hdr.ipv4.ttl = hdr.ipv4.ttl - 1;
    }

    table ipv4_lpm {
        key = {
            hdr.ipv4.dstAddr: lpm;
        }
        actions = {
            ipv4_forward;
            drop;
            NoAction;
        }
        size = 1024;
        default_action = drop();
    }

6. An action (called `set_direction`) that will simply set a one-bit direction variable as per the action's parameter.

一个简单的设置方向的操作

action set_direction(bit<1> dir) {
        direction = dir;
    }

7. A table (called `check_ports`) that will read the ingress and egress port of a packet (after IPv4 forwarding) and invoke `set_direction`. The direction will be set to `1`, if the packet is incoming into the internal network. Otherwise, the direction will be set to `0`. To achieve this, the file `pod-topo/s1-runtime.json` contains the appropriate control plane entries for the `check_ports` table.

有一张检查端口的表,如果发现一个数据包要进内网,就把方向设1,否则设0,表中的端口的相关数据信息已经在控制面中完成了

table check_ports {
        key = {
            standard_metadata.ingress_port: exact;
            standard_metadata.egress_spec: exact;
        }
        actions = {
            set_direction;
            NoAction;
        }
        size = 1024;
        default_action = NoAction();
    }

8. A control that will:

    1. First apply the table `ipv4_lpm` if the packet has a valid IPv4 header.

    2. Then if the TCP header is valid, apply the `check_ports` table to determine the direction.

    3. Apply the `compute_hashes` action to compute the two hash values which are the bit positions  in the two register arrays of the bloom filter (`reg_pos_one` and `reg_pos_two`).   When the direction is `1` i.e. the packet is incoming into the internal network,   `compute_hashes` will be invoked by swapping the source and destination IPv4 addresses and the source and destination ports. This is to check against bloom filter's set bits when the TCP connection was initially made from the internal network.

1和2是检查ipv4的包头和tcp包头是否合法,这个不过多阐述,要实现防火墙的单向tcp建立连接,就要用到布隆过滤器了,具体的工作思想如下,来自于内网的主机想要与外网建立连接,同一连接上,这个外网肯定也要能够访问内网。因此,对于s1交换机来说,就要对外网来的连接进行审核,也就是用布隆过滤器审核,如果这个外网来的包所在的连接在布隆过滤器中,说明这个连接是从内网建立出来的,允许通行,否则禁止。

注意,一个连接在布隆过滤器中保存的时候,依赖的参数是建立连接时从内网发出去的包的包头提取出来的信息,所以外部包所在的连接是否在布隆过滤器中,就要把它包头的信息反着读,也就是把目的地址和源地址反过来,才能得到所在连接的审核结果。

    4. **TODO:** If the TCP packet is going out of the internal network and is a SYN packet, set both the bloom filter arrays at the computed bit positions (`reg_pos_one` and `reg_pos_two`).  Else, if the TCP packet is entering the internal network, read both the bloom filter arrays at the computed bit positions and drop the packet if  either is not set.

这里是重点,建立连接的时候,由于下图所示,其实TCP连接一开始会生成一个SYN包,在S1中,如果有一个来自于内网的SYN包到来,就说明有一个新的连接需要建立,这个时候,s1就把这个SYN包抓出来,然后一顿操作,填在布隆过滤器中。

  • 如果是内网的SYN包,存一下连接,通行
  • 如果是内网的普通包,直接通行
  • 如果是外网包,如果所在连接是内网发起的,通行
  • 如果是普通外网包,丢弃,大概示意图如下

 

apply {
        if (hdr.ipv4.isValid()){
            ipv4_lpm.apply();
            if (hdr.tcp.isValid()){
                direction = 0; // default
                if (check_ports.apply().hit) {
                    // test and set the bloom filter
                    if (direction == 0) {
                        compute_hashes(hdr.ipv4.srcAddr, hdr.ipv4.dstAddr, hdr.tcp.srcPort, hdr.tcp.dstPort);
                    }
                    else {
                        compute_hashes(hdr.ipv4.dstAddr, hdr.ipv4.srcAddr, hdr.tcp.dstPort, hdr.tcp.srcPort);
                    }
                    // Packet comes from internal network
                    if (direction == 0){
                        // TODO: this packet is part of an outgoing TCP connection.
                        //   We need to set the bloom filter if this is a SYN packet
                        //   E.g. bloom_filter_1.write(<index>, <value>);
                        if (hdr.tcp.syn == 1){
                            bloom_filter_1.write(reg_pos_one, 1);
                            bloom_filter_2.write(reg_pos_two, 1);
                        }
                    }
                    // Packet comes from outside
                    else if (direction == 1){
                        // TODO: this packet is part of an incomming TCP connection.
                        //   We need to check if this packet is allowed to pass by reading the bloom filter
                        //   E.g. bloom_filter_1.read(<value>, <index>);
                        bloom_filter_1.read(reg_val_one,reg_pos_one);
                        bloom_filter_2.read(reg_val_two,reg_pos_two);
                        if(reg_val_one !=1 || reg_val_two !=1)
                            drop();
                    }
                }
            }
        }
    }

9. A deparser that emits the Ethernet, IPv4 and TCP headers in the right order.

封包操作

control MyDeparser(packet_out packet, in headers hdr) {
    apply {
        packet.emit(hdr.ethernet);
        packet.emit(hdr.ipv4);
        packet.emit(hdr.tcp);
    }
}

10. A `package` instantiation supplied with the parser, control, and deparser.

    > In general, a package also requires instances of checksum verification

    > and recomputation controls. These are not necessary for this tutorial

    > and are replaced with instantiations of empty controls.

这里只是描述了一个包的流程是什么样的,这里不多说了。

Step3:开始实验我的方案

首先,我先试试内网->内网和内网->外网,是ok的

 仔细看我打开的两个终端,我分别让h1和h3都作为服务器试一下,可以看到,h1作服务器是不行的。

类型测试结果
内网->内网

succeed

内网->外网succeed
外网->内网fail

使用wireshark抓包(抓s1的eth1接口)可以发现,如果是10.0.1.1也就是h1是连接的发起者(SYN的source是10.0.1.1)就可以进行握手并通信

 但是如果是反过来,外网访问内网时,就什么都没有

 在靠近内网这一侧的端口上看不到包,因为外网的SYN包在eth3或者eth4时被丢弃了,这里查看了eth3口,可以看到,SYN包试图发了多次并没有得到响应

 

 小结

        通过这一次的实验,了解到了v1model提供的hash函数的用法,以及如何使用寄存器模拟布隆过滤器

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值