视频链接

GitHub地址

本组成员

学号 姓名 贡献比例
031702236 王耀鑫(组长) 20%
031702234 林银河 18%
031702241 苏杰隆 18%
031702206 吴雅虹 18%
031702223 郑志强 26%

拓扑图

功能设计

本次大作业我们小组应用P4编程语言实现一个防火墙功能,限制外网对内部网络的访问,而内部网络可以正常访问外网,起到一定程度的安全防护作用。

核心代码


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;    //转移到tcp包头的状态
            default: accept;
        }
    }

    state tcp {
       packet.extract(hdr.tcp); //提取tcp包头
       transition accept;
    }
}


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


control MyIngress(inout headers hdr,
                  inout metadata meta,
                  inout standard_metadata_t standard_metadata) {

    register<bit<BLOOM_FILTER_BIT_WIDTH>>(BLOOM_FILTER_ENTRIES) bloom_filter_1;
    register<bit<BLOOM_FILTER_BIT_WIDTH>>(BLOOM_FILTER_ENTRIES) bloom_filter_2;
    bit<32> reg_pos_one; bit<32> reg_pos_two;
    bit<1> reg_val_one; bit<1> reg_val_two;
    bit<1> direction;

    action drop() {
        mark_to_drop(standard_metadata);
    }
    
    //使用哈希算法和布隆过滤器,哈希将在由IPv4源和目标地址,源和目标端口号以及IPv4协议类型组成的数据包5元组上计算。
    action compute_hashes(ip4Addr_t ipAddr1, ip4Addr_t ipAddr2, bit<16> port1, bit<16> port2){
       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);
    }

    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();
    }

    //根据该动作的参数简单地设置一位方向变量
    action set_direction(bit<1> dir) {
        direction = dir;
    }


    table check_ports {
        key = {
            standard_metadata.ingress_port: exact;  //完全匹配
            standard_metadata.egress_spec: exact;
        }
        actions = {
            set_direction;
            NoAction;
        }
        size = 1024;
        default_action = NoAction();
    }
    
    /*
    在ipv4转发后,如果数据包进入内部网络方向设为1。否则,
    方向设为0,该操作在s1-runtime.json文件中完成。
    */
    
    apply {
        if (hdr.ipv4.isValid()){    //如果数据包具有有效的IPv4标头,首先应用该表。
            ipv4_lpm.apply();
            if (hdr.tcp.isValid()){ //如果数据包具有有效的TCP标头,接着应用check_ports表确定方向。
                direction = 0;  ///默认为0
                if (check_ports.apply().hit) {  //应用compute_hashes操作来计算两个哈希值
                    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);
                    }
                    
                    /*如果TCP数据包正从内部网络传出并且是SYN数据包,则将两个
                    Bloom Bloom过滤器数组都设置在计算出的位位置(reg_pos_one和reg_pos_two)*/
                    
                    if (direction == 0){
                        if (hdr.tcp.syn == 1){
                            bloom_filter_1.write(reg_pos_one, 1);
                            bloom_filter_2.write(reg_pos_two, 1);
                        }
                    }
                    
                    /*否则,如果TCP数据包进入内部网络,则在计算出的位位置
                    读取两个Bloom过滤器数组,如果未设置任何数据包,则丢弃该数据包。*/
                    
                    else if (direction == 1){
                        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();
                        }
                    }
                }
            }
        }
    }
}

control MyEgress(inout headers hdr,
                 inout metadata meta,
                 inout standard_metadata_t standard_metadata) {
    apply {  }
}


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);
    }
}

control MyDeparser(packet_out packet, in headers hdr) {
    apply {
        //顺序不能乱
        packet.emit(hdr.ethernet);
        packet.emit(hdr.ipv4);
        packet.emit(hdr.tcp);
    }
}


V1Switch(
MyParser(),
MyVerifyChecksum(),
MyIngress(),
MyEgress(),
MyComputeChecksum(),
MyDeparser()
) main;

课程收获

  • 王耀鑫:在本学期的课程学习中,我从一开始的一无所知,随着和老师一起学习相关理论知识,辅以实践训练,在那些为SDN熬夜的日子中,我克服睡神的诱惑,深夜与SDN共舞,增长知识,拓展见识,深刻把握未来网络的发展趋势。而此次实验中我们小组充分准备,认真学习相关知识,经过几天熬夜苦战,超额完成预定计划,对P4,对防火墙都有了扎实的认知,在此感谢小组成员,感谢强哥无私的帮助,抱紧强哥大腿。
  • 林银河:通过这门课程,我了解到SDN的一些运用,初步掌握了mininet的使用方法、如何建立与连接拓扑、利用floodlight测试拓扑连接情况、学会了使用ODL下发流表、了解了RYU控制器的一些使用方法、以及数据平面编程。其实我对这些方法还不是很熟悉,只是有了一个比较初略的了解。在本次大作业中,我了解到P4语言在防护墙的一些运用,体验到SDN对未来网络发展的重要意义。我感受到团队的意义所在,在队友的带领下完成了本次大作业,再次感谢队友的帮助。
  • 吴雅虹:在这门课之前,软件定义网络是是一个完全陌生的概念,经过课堂的理论知识,初步了解SDN的基础概念:一种新型网络创新架构,其核心技术OpenFlow通过将网络设备控制面与数据面分离,实现网络流量的灵活控制,为核心网络及应用的创新提供平台;而7次实践课程中,虽然中间一度因为学习程度的不足而导致实验受阻,在老师及同学的帮助下才能及时完成作业并对每次实验所涉及的领域有最基础的涉猎;最后一次的大作业,团队实验主题为NFV,前期通过查询学习NFV相关领域及基础知识,而在较短的时间中,团队完成初步设计。课程短暂,能够学习的也仅为初步的理论知识,实践完成的也是每个领域中基础,但为之后的学习提供了更大的思考空间及操作空间。
  • 苏杰隆:这次实验技术层面上主要是靠着大佬队友带飞,感觉知识并没有掌握得很扎实,仅是简单了解了负载均衡算法内容,还有许多深层次的东西需要挖掘。回顾这半个学期SDN的学习,虽然时间短暂,但由于课程包含许多从未接触过的新知识,还是收获了许多。
  • 郑志强:这学期的SDN这门课对我来说,虽然花得时间也不算很多,因为对于其他科目,比如软工、图形学这些,花得时间算是少了。但这也让我对现在网络前沿的发展方向有了一个大体的认识,并且接触了不少控制器,例如Ryu,OpenDayLight等。 总结前几次作业,SDN课程中我都了解了mininet创建拓扑的几种方法,还有利用mininet实现虚拟网络功能,还有对OpenDayLight控制器的了解,和Ryu控制器的了解等。最后,还大致清楚了数据平面编程的过程,体会到openflow与p4之间不同的侧重点,甚至是相辅相成的两个领域。虽然这门课的课时不是很长,也不能把整个SDN相关的知识都讲解一遍,但是已经帮我们画出的大体轮廓,对于学习网络专业的知识有了一个不错的方向指导,我也从中收获了很多。