P4_learning——srv6基础实验

实验背景
报文转发流程
采用示例说明SRv6的报文转发流程。

如图所示,假设有报文需要从主机1转发到主机2,主机1将报文发送给节点A处理。节点A、B、D、E均支持SRv6,节点C不支持SRv6,只支持IPv6。我们在源节点A上进行网络编程,希望报文经过B-C、C-D链路,送达节点E,由E节点送达主机2。
在这里插入图片描述
报文转发流程分为以下几步:

  • 源节点A将SRv6路径信息封装在SRH中,指定B-C,C-D链路的SID,另外封装E点发布的SID A5::10(此SID对应于节点E的一个IPv4 VPN),共3个SID,按照逆序形式压入SID序列。此时SL(Segment Left)=2,将Segment List[2]值复制到目的地址DA字段,按照最长匹配原则查找IPv6路由表,将其转发到节点B。
  • 报文到达节点B,B节点查找本地SID表(存储本节点生成的SRv6 SID信息),命中自身的SID(End.X SID),执行SID对应的指令动作。SL值减1,并将Segment List[1]值复制到DA字段,同时将报文从SID绑定的链路(B-C)发送出去。
    报文到达节点C,C无SRv6能力,无法识别SRH,按照正常IPv6报文处理流程,按照最长匹配原则查找IPv6路由表,将其转发到当前目的地址所代表的节点D。
  • 节点D收报文后根据目的地址A4::45查找本地SID表,命中自身的SID(End.X SID)。同节点B,SL值减1,将A5::10作为DA,并将报文发送出去。
  • 节点E收到报文后根据A5::10查找本地SID表,命中自身SID(End.DT4 SID),执行对应的指令动作,解封装报文,去除IPv6报文头,并将内层IPv4报文在SID绑定的VPN实例的IPv4路由表中进程查表转发,最终将报文发送给主机2。

srv6简化实验

本实验进行了简化,仅使用两个交换机作为源节点和end节点
在这里插入图片描述

实验实现流程:当h1向h2通信时,s1作为源节点,插入路径s1、s2。当s2接收到来在s1的报文,检查发现自己为end节点,执行srv6_pop操作,去掉srv6报文头并将安装ipv6地址进行转发到达h2

p4文件

#include <core.p4>
#include <v1model.p4>

/************************************************************************
************************** Defines **************************************
*************************************************************************/

#define ETH_TYPE_IPV4 0x0800
#define ETH_TYPE_IPV6 0x86dd
#define IP_PROTO_TCP 8w6
#define IP_PROTO_UDP 8w17
#define IP_PROTO_SRV6 8w43
#define IP_VERSION_4 4w4

#define SRV6_MAX_HOPS 6

typedef bit<48>  mac_addr_t;
typedef bit<32>  ipv4_addr_t;
typedef bit<128> ipv6_addr_t;
typedef bit<9>   port_t;

const port_t CPU_PORT = 255;



/************************************************************************
************************** Headers **************************************
*************************************************************************/

@controller_header("packet_in")
header packet_in_header_t {
    bit<9> ingress_port;
    bit<7> _padding;
}

@controller_header("packet_out")
header packet_out_header_t {
    bit<9> egress_port;
    bit<7> _padding;
}

header ethernet_t {
    bit<48> dst_addr;
    bit<48> src_addr;
    bit<16> ether_type;
}

header ipv4_t {
    bit<4>  version;
    bit<4>  ihl;
    bit<6>  dscp;
    bit<2>  ecn;
    bit<16> len;
    bit<16> identification;
    bit<3>  flags;
    bit<13> frag_offset;
    bit<8>  ttl;
    bit<8>  protocol;
    bit<16> hdr_checksum;
    bit<32> src_addr;
    bit<32> dst_addr;
}

header ipv6_t {
    bit<4> version;
    bit<8> traffic_class;
    bit<20> flow_label;
    bit<16> payload_len;
    bit<8> next_hdr;
    bit<8> hop_limit;
    bit<128> src_addr;
    bit<128> dst_addr;
}

header srv6_header_t {
    bit<8> next_hdr;
    bit<8> hdr_ext_len;
    bit<8> routing_type;
    bit<8> segment_left;
    bit<8> last_entry;
    bit<8> flags;
    bit<16> tag;
}

header srv6_segment_list_t{
    bit<128> sid;
}

header tcp_t {
    bit<16> src_port;
    bit<16> dst_port;
    bit<32> seq_no;
    bit<32> ack_no;
    bit<4>  data_offset;
    bit<3>  res;
    bit<3>  ecn;
    bit<6>  ctrl;
    bit<16> window;
    bit<16> checksum;
    bit<16> urgent_ptr;
}

header udp_t {
    bit<16> src_port;
    bit<16> dst_port;
    bit<16> length_;
    bit<16> checksum;
}

/************************************************************************
*********************** Custom Headers  *********************************
*************************************************************************/

struct headers_t {
    packet_out_header_t packet_out;
    packet_in_header_t packet_in;
    ethernet_t ethernet;
    ipv4_t ipv4;
    ipv6_t ipv6;
    srv6_header_t srh;
    srv6_segment_list_t[SRV6_MAX_HOPS] segment_list;
    tcp_t tcp;
    udp_t udp;
}

struct local_metadata_t {
    bit<16>        l4_src_port;
    bit<16>        l4_dst_port;
    ipv6_addr_t next_sid;
    bit<8>         ip_proto;
}


/************************************************************************
**************************** Parser *************************************
*************************************************************************/

parser parser_impl(packet_in packet,
                  out headers_t hdr,
                  inout local_metadata_t local_metadata,
                  inout standard_metadata_t standard_metadata) {

    state start {
        transition select(standard_metadata.ingress_port) {
            CPU_PORT: parse_packet_out;
            default: parse_ethernet;
        }
    }

    state parse_packet_out {
        packet.extract(hdr.packet_out);
        transition parse_ethernet;
    }

    state parse_ethernet {
        packet.extract(hdr.ethernet);
        transition select(hdr.ethernet.ether_type) {
            ETH_TYPE_IPV4: parse_ipv4;
            ETH_TYPE_IPV6: parse_ipv6;
            default: accept;
        }
    }

    state parse_ipv4 {
        packet.extract(hdr.ipv4);
        transition select(hdr.ipv4.protocol) {
            IP_PROTO_TCP: parse_tcp;
            IP_PROTO_UDP: parse_udp;
            default: accept;
        }
    }

    state parse_ipv6 {
        packet.extract(hdr.ipv6);
        local_metadata.ip_proto = hdr.ipv6.next_hdr;
        transition select(hdr.ipv6.next_hdr) {
            IP_PROTO_TCP: parse_tcp;
            IP_PROTO_UDP: parse_udp;
            IP_PROTO_SRV6: parse_srv6;
            default: accept;
        }
    }

    state parse_tcp {
        packet.extract(hdr.tcp);
        local_metadata.l4_src_port = hdr.tcp.src_port;
        local_metadata.l4_dst_port = hdr.tcp.dst_port;
        transition accept;
    }

    state parse_udp {
        packet.extract(hdr.udp);
        local_metadata.l4_src_port = hdr.udp.src_port;
        local_metadata.l4_dst_port = hdr.udp.dst_port;
        transition accept;
    }

    state parse_srv6 {
        packet.extract(hdr.srh);
        transition parse_segment_list;
    }

    state parse_segment_list {
        packet.extract(hdr.segment_list.next);
        bool next_sid = (bit<32>)hdr.srh.segment_left - 1 == (bit<32>)hdr.segment_list.lastIndex;
        transition select(next_sid){
            true: mark_next_sid;
            default: check_last_sid;
        }
    }

    state mark_next_sid{
        local_metadata.next_sid = hdr.segment_list.last.sid;
        transition check_last_sid;
    }

    state check_last_sid {
        bool last_sid = (bit<32>)hdr.srh.last_entry == (bit<32>)hdr.segment_list.lastIndex;
        transition select(last_sid){
            true: parse_srv6_next_hdr;
            false: parse_segment_list;
        }
    }

    state parse_srv6_next_hdr{
        transition select(hdr.srh.next_hdr){
            IP_PROTO_TCP: parse_tcp;
            IP_PROTO_UDP: parse_udp;
            default: accept;
        }
    }
}


/************************************************************************
*********************** Verify Checksum *********************************
*************************************************************************/

control verify_checksum_control(inout headers_t hdr,
                                inout local_metadata_t local_metadata) {
    apply {
        // Assume checksum is always correct.
    }
}


/************************************************************************
*********************** Ingress Pipeline*********************************
*************************************************************************/

control ingress(inout headers_t hdr,
                inout local_metadata_t local_metadata,
                inout standard_metadata_t standard_metadata) {


    action drop(){
    	mark_to_drop(standard_metadata);
    }

    table local_mac_table {

    	key = {
    		hdr.ethernet.dst_addr: exact;
    	}
    	actions = {
    		NoAction;
    	}
    }


    action set_next_hop(mac_addr_t dmac, port_t port){
    	hdr.ethernet.src_addr = hdr.ethernet.dst_addr;
    	hdr.ethernet.dst_addr = dmac;
    	hdr.ipv6.hop_limit = hdr.ipv6.hop_limit - 1;
    	standard_metadata.egress_spec = port;
    }

    table routing_v6_table {
    	key = {
    		hdr.ipv6.dst_addr: lpm;
    	}
    	actions = {
    		set_next_hop;
    	}
    }


    action end(){
        hdr.srh.segment_left = hdr.srh.segment_left - 1;
        hdr.ipv6.dst_addr = local_metadata.next_sid;
    }

    table local_sid_table {
    	key = {
    		hdr.ipv6.dst_addr: lpm;
    	}
    	actions = {
    		end;
    	}

    }

    action insert_srh(bit<8> num_segments){
        hdr.srh.setValid();
        hdr.srh.next_hdr = hdr.ipv6.next_hdr;
        hdr.srh.hdr_ext_len = num_segments * 2;
        hdr.srh.routing_type = 4;
        hdr.srh.segment_left = num_segments - 1;
        hdr.srh.last_entry = num_segments - 1;
        hdr.srh.flags = 0;
        hdr.srh.tag = 0;
        hdr.ipv6.next_hdr = IP_PROTO_SRV6;
    }

    action insert_segment_list_2(ipv6_addr_t s1, ipv6_addr_t s2){
        hdr.ipv6.dst_addr = s1;
        hdr.ipv6.payload_len = hdr.ipv6.payload_len + 40;
        insert_srh(2);
        hdr.segment_list[0].setValid();
        hdr.segment_list[0].sid = s2;
        hdr.segment_list[1].setValid();
        hdr.segment_list[1].sid = s1;
    }

    action insert_segment_list_3(ipv6_addr_t s1, ipv6_addr_t s2, ipv6_addr_t s3){
        hdr.ipv6.dst_addr = s1;
        hdr.ipv6.payload_len = hdr.ipv6.payload_len + 56;
        insert_srh(3);
        hdr.segment_list[0].setValid();
        hdr.segment_list[0].sid = s3;
        hdr.segment_list[1].setValid();
        hdr.segment_list[1].sid = s2;
        hdr.segment_list[2].setValid();
        hdr.segment_list[2].sid = s1;
    }

    table transit_table{
    	key={
    		hdr.ipv6.dst_addr: lpm;
    	}
    	actions = {
    		insert_segment_list_2;
    		insert_segment_list_3;
    	}
    }

    action srv6_pop(){
        hdr.ipv6.next_hdr = hdr.srh.next_hdr;
        bit<16> srh_size = (((bit<16>)hdr.srh.last_entry + 1) << 4) + 8;
        hdr.ipv6.payload_len = hdr.ipv6.payload_len - srh_size;

        hdr.srh.setInvalid();
        hdr.segment_list[0].setInvalid();
        hdr.segment_list[1].setInvalid();
        hdr.segment_list[2].setInvalid();
        hdr.segment_list[3].setInvalid();
        hdr.segment_list[4].setInvalid();
        hdr.segment_list[5].setInvalid();
    }



    apply {
        
        
        if (standard_metadata.ingress_port == CPU_PORT) {
        	// Receive packets from controller, namely packet_out message.
        	// Directly tell switch where the port packets sent to.

        	standard_metadata.egress_spec = hdr.packet_out.egress_port;

        	// pop the header of packet-out packet

        	hdr.packet_out.setInvalid();
        	exit;
        }


        // The logic of how to handle srv6 header
        // simple version, only considering ipv6 packets and srv6 packets
		//当交换机接收到到达自己的报文时,检查其mac地址、检查是否是end节点和源节点,进行相应的操作,然后进行ipv6的转发
    	if(local_mac_table.apply().hit){
    		if(hdr.ipv6.isValid()){
    			if(local_sid_table.apply().hit){
    				if(hdr.srh.isValid() && hdr.srh.segment_left == 0){
    					srv6_pop();
    				}
    			}else{
    				transit_table.apply();
    			}

    			routing_v6_table.apply();

    			if(hdr.ipv6.hop_limit == 0){
    				drop();
    			}
    		}

    	}

     }
}





/************************************************************************
*********************** Egress Pipeline**********************************
*************************************************************************/


control egress(inout headers_t hdr,
               inout local_metadata_t local_metadata,
               inout standard_metadata_t standard_metadata) {

    apply {
        if (standard_metadata.egress_port == CPU_PORT) {
        	// Handle packet-in packet, if egress_port is cpu port, which means this packet is sent to controller
            // Add the packet-in header

            hdr.packet_in.setValid();
            hdr.packet_in.ingress_port = standard_metadata.ingress_port;

        }    
    }
}



/************************************************************************
*********************** Compute Checksum ********************************
*************************************************************************/

control compute_checksum_control(inout headers_t hdr,
                                 inout local_metadata_t local_metadata) {
    apply {
        update_checksum(hdr.ipv4.isValid(),
            {
                hdr.ipv4.version,
                hdr.ipv4.ihl,
                hdr.ipv4.dscp,
                hdr.ipv4.ecn,
                hdr.ipv4.len,
                hdr.ipv4.identification,
                hdr.ipv4.flags,
                hdr.ipv4.frag_offset,
                hdr.ipv4.ttl,
                hdr.ipv4.protocol,
                hdr.ipv4.src_addr,
                hdr.ipv4.dst_addr
            },
            hdr.ipv4.hdr_checksum,
            HashAlgorithm.csum16
        );
    }
}


/************************************************************************
**************************** Deparser ***********************************
*************************************************************************/

control deparser(packet_out packet, in headers_t hdr) {
    apply {
        packet.emit(hdr.packet_in);
        packet.emit(hdr.ethernet);
        packet.emit(hdr.ipv4);
        packet.emit(hdr.ipv6);
        packet.emit(hdr.srh);
        packet.emit(hdr.segment_list);
        packet.emit(hdr.tcp);
        packet.emit(hdr.udp);
    }
}


/************************************************************************
**************************** Switch *************************************
*************************************************************************/

V1Switch(parser_impl(),
         verify_checksum_control(),
         ingress(),
         egress(),
         compute_checksum_control(),
         deparser()) main;

拓扑文件

{
  "program": "srv6.p4",
  "switch": "simple_switch",
  "compiler": "p4c",
  "options": "--target bmv2 --arch v1model --std p4-16",
  "switch_cli": "simple_switch_CLI",
  "cli": true,
  "pcap_dump": true,
  "enable_log": true,
  "topo_module": {
    "file_path": "",
    "module_name": "p4utils.mininetlib.apptopo",
    "object_name": "AppTopoStrategies"
  },
  "controller_module": null,
  "topodb_module": {
    "file_path": "",
    "module_name": "p4utils.utils.topology",
    "object_name": "Topology"
  },
  "mininet_module": {
    "file_path": "",
    "module_name": "p4utils.mininetlib.p4net",
    "object_name": "P4Mininet"
  },
  "topology": {
    "assignment_strategy": "mixed",
    "auto_arp_tables": "true",
    "auto_gw_arp": "true",
    "links": [["h1", "s1"], ["h2", "s2"], ["s1", "s2"]],
    "hosts": {
      "h1": {
	"ip": "10.0.1.1/24",
	"mac" : "08:00:00:00:01:11",
	"gw" : "10.0.1.10"
      },
      "h2": {
	"ip": "10.0.2.2/24",
	"mac": "08:00:00:00:02:22",
	"gw": "10.0.2.20"
      }
    },
    "switches": {
      "s1": {
        "cli_input": "cmd_s1.txt",
        "program": "srv6.p4"
      },
      "s2": {
        "cli_input": "cmd_s2.txt",
        "program": "srv6.p4"
      }	
    }
  }
}

控制表

第一行是为了确认目的mac为本交换机,第二行是为了验证本交换机为end交换机,第三行根据目的ipv6地址确定插入的路径有哪些

s1

table_add local_mac_table NoAction 08:00:00:00:11:00 => 
table_add local_sid_table end A1:11::11/128 => 
table_add transit_table insert_segment_list_2 2::2/128 => A2:22::22 2::2
table_add routing_v6_table set_next_hop A2:22::22/128 => 08:00:00:00:22:00 2
table_add routing_v6_table set_next_hop 1::1/128 => 08:00:00:00:01:00 1

s2

table_add local_mac_table NoAction 08:00:00:00:22:00 => 
table_add local_sid_table end A2:22::22/128 => 
table_add transit_table insert_segment_list_2 1::1/128 => A1:11::11 1::1
table_add routing_v6_table set_next_hop 2::2/128 => 08:00:00:00:02:00 1
table_add routing_v6_table set_next_hop A1:11::11/128 => 08:00:00:00:11:00 2

ipv6报文发送

#!/usr/bin/env python
import argparse
import sys
import socket
import random
import struct

from scapy.all import sendp, send, get_if_list, get_if_hwaddr
from scapy.all import Packet
from scapy.all import Ether, IPv6, UDP

def get_if():
    ifs=get_if_list()
    iface=None # "h1-eth0"
    for i in get_if_list():
        if "eth0" in i:
            iface=i
            break;
    if not iface:
        print "Cannot find eth0 interface"
        exit(1)
    return iface

def main():

    if len(sys.argv)<3:
        print 'pass 2 arguments: <destination> "<message>"'
        exit(1)
    saddr = sys.argv[1]
    addr = sys.argv[2]
    iface = get_if()
	
    print "sending on interface %s to %s" % (iface, str(addr))
    #注意,这里的目的地址是转发给s1的目的mac地址
    pkt =  Ether(src=get_if_hwaddr(iface), dst='08:00:00:00:11:00') / IPv6(src=saddr,dst=addr) / UDP(dport=4321, sport=1234) / sys.argv[3]
    pkt.show2()
    sendp(pkt, iface=iface, verbose=False)


if __name__ == '__main__':
    main()

部分实验截图

在这里插入图片描述

评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值