Floodlight 入门 之 起步篇 - 如何处理PacketIN消息

本文介绍了在Floodlight环境中如何解析和处理Packet-IN消息,包括如何从消息中提取以太网帧及其负载,进一步获取IPv4和ARP等高层协议信息。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Floodlight 入门 之 起步篇 - 如何处理PacketIN消息

2017-3-2

小论文写不出来好纠结,今天还在做Floodlight开发,好烦躁。这边博文介绍如何处理PacketIN消息,是从官网翻译的,原味教程大家有兴趣的话就嚼一嚼。


介绍

在OpenFlow-SDN网络中,当一个交换机在一个端口上接受到一个packet时,它会将这个packet与交换机默认的流表中的流表项进行匹配。如果交换机不能定位到一个能匹配该packet的流表项,默认情况下它将会发送一个packet给控制器(这个packet就是PACKET_IN),交由控制器处理(原文是 it will by default* send the packet to the controller as a packet-in for closer inspection and processing,不太理解“for closer inspection and processing”的意思,所以这里暂且没有翻译。)(OpenFLow1.0,1.1和1.2中流表展示了默认情况下的行为,而在1.3及其以上版本中,需要由控制器将缺省的流插入到交换机中,这里Floodlight会为你做这些工作。)

Floodlight 入门 之 起步篇 - 建立一个Floodlight模块这一篇中,我们已经知道如何在一个自定义的模块中接受一个packet-in消息了。该教程会教你如何解释你接收到的packet的header和payload。

检索来自控制器内核的以太网帧

任何一个监听PacketIn消息的模块都很有可能试图去检查Payload。与其每个独立的模块都对PacketIn消息的字节数组形式的Payload进行一次并行化的操作,还不如让Floodlight内核来做,这样每个消息只要进行一次并行化的操作就可以了。所以,在我们自定义的模块中,我们不需要对PacketIn消息的字节数组形式的Payload进行并行化的操作,只需要使用与该packet关联的FloodlightContext来获得已经并行化的Ethernet对象就可以了。

/*
 * Overridden IOFMessageListener's receive() function.
 */
@Override
public Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) {
    switch (msg.getType()) {
    case PACKET_IN:
        /* Retrieve the deserialized packet in message */
        Ethernet eth = IFloodlightProviderService.bcStore.get(cntx, IFloodlightProviderService.CONTEXT_PI_PAYLOAD);

        /* We will fill in the rest here shortly */

        break;
    default:
        break;
    }
    return Command.CONTINUE;
}

如上所示,我们使用FloodlightContext获取以太网帧。在后台(Under the hood),Floodlight通过并列化OpenFlow的PacketIn消息的Payload来创建Ethernet(对象)。

当我们获得Ethernet对象后,我们就能立马使用Ethernet对象的getters和setters了。这能让我们获得源MAC地址,或者VLAN ID之类的信息。

/*
 * Overridden IOFMessageListener's receive() function.
 */
@Override
public Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) {
    switch (msg.getType()) {
    case PACKET_IN:
        /* Retrieve the deserialized packet in message */
        Ethernet eth = IFloodlightProviderService.bcStore.get(cntx, IFloodlightProviderService.CONTEXT_PI_PAYLOAD);

        /* Various getters and setters are exposed in Ethernet */
        MacAddress srcMac = eth.getSourceMACAddress();
        VlanVid vlanId = VlanVid.ofVlan(eth.getVlanID());

        /* Still more to come... */

        break;
    default:
        break;
    }
    return Command.CONTINUE;
}

获取Ethernet的Payload(例如IPv4或者ARP)

注意我们现在仅仅能够访问以太网帧的header,并不能访问更高层的header。要获取更高层的header的访问权限的话,我们要先确定以太网帧的Payload。然后,我们就能获得代表该Payload的并行化的packet(原文 After we do this, we can retrieve the deserialized packet representing the payload.)。这通常是一个IPv4或者ARP的packet。

/*
 * Overridden IOFMessageListener's receive() function.
 */
@Override
public Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) {
    switch (msg.getType()) {
    case PACKET_IN:
        /* Retrieve the deserialized packet in message */
        Ethernet eth = IFloodlightProviderService.bcStore.get(cntx, IFloodlightProviderService.CONTEXT_PI_PAYLOAD);

        /* Various getters and setters are exposed in Ethernet */
        MacAddress srcMac = eth.getSourceMACAddress();
        VlanVid vlanId = VlanVid.ofVlan(eth.getVlanID());

        /*
         * Check the ethertype of the Ethernet frame and retrieve the appropriate payload.
         * Note the shallow equality check. EthType caches and reuses instances for valid types.
         */
        if (eth.getEtherType() == EthType.IPv4) {
            /* We got an IPv4 packet; get the payload from Ethernet */
            IPv4 ipv4 = (IPv4) eth.getPayload();

            /* More to come here */
        } else if (eth.getEtherType() == EthType.ARP) {
            /* We got an ARP packet; get the payload from Ethernet */
            ARP arp = (ARP) eth.getPayload();

            /* More to come here */

        } else {
            /* Unhandled ethertype */
        }
        break;
    default:
        break;
    }
    return Command.CONTINUE;
}

如上所示,一旦我们确定了Payload的类型,我们就可以将Payload强制转换成正确的class。所有在net.floodlightcontroller.packet中被定义的packet都会继承BasePacket(他实现了IPacket)。因为Floodlight内核已经对packet进行并行化了,我们所需要做的就是讲packet强制转换成它对应的对象类型上。

注意,我们确定了packet的类型并获得了它的Payload,那我们就可以访问packet的header attributes了。

/*
 * Overridden IOFMessageListener's receive() function.
 */
@Override
public Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) {
    switch (msg.getType()) {
    case PACKET_IN:
        /* Retrieve the deserialized packet in message */
        Ethernet eth = IFloodlightProviderService.bcStore.get(cntx, IFloodlightProviderService.CONTEXT_PI_PAYLOAD);

        /* Various getters and setters are exposed in Ethernet */
        MacAddress srcMac = eth.getSourceMACAddress();
        VlanVid vlanId = VlanVid.ofVlan(eth.getVlanID());

        /*
         * Check the ethertype of the Ethernet frame and retrieve the appropriate payload.
         * Note the shallow equality check. EthType caches and reuses instances for valid types.
         */
        if (eth.getEtherType() == EthType.IPv4) {
            /* We got an IPv4 packet; get the payload from Ethernet */
            IPv4 ipv4 = (IPv4) eth.getPayload();

            /* Various getters and setters are exposed in IPv4 */
            byte[] ipOptions = ipv4.getOptions();
            IPv4Address dstIp = ipv4.getDestinationAddress();

            /* Still more to come... */

        } else if (eth.getEtherType() == EthType.ARP) {
            /* We got an ARP packet; get the payload from Ethernet */
            ARP arp = (ARP) eth.getPayload();

            /* Various getters and setters are exposed in ARP */
            boolean gratuitous = arp.isGratuitous();

        } else {
            /* Unhandled ethertype */
        }
        break;
    default:
        break;
    }
    return Command.CONTINUE;
}

获取IPv4的Payload(例如TCP或者UDP)

逻辑上,我们能够在深入到IPv4的packet,获得TCP或者UDP的Payload。

/*
 * Overridden IOFMessageListener's receive() function.
 */
@Override
public Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) {
    switch (msg.getType()) {
    case PACKET_IN:
        /* Retrieve the deserialized packet in message */
        Ethernet eth = IFloodlightProviderService.bcStore.get(cntx, IFloodlightProviderService.CONTEXT_PI_PAYLOAD);

        /* Various getters and setters are exposed in Ethernet */
        MacAddress srcMac = eth.getSourceMACAddress();
        VlanVid vlanId = VlanVid.ofVlan(eth.getVlanID());

        /* 
         * Check the ethertype of the Ethernet frame and retrieve the appropriate payload.
         * Note the shallow equality check. EthType caches and reuses instances for valid types.
         */
        if (eth.getEtherType() == EthType.IPv4) {
            /* We got an IPv4 packet; get the payload from Ethernet */
            IPv4 ipv4 = (IPv4) eth.getPayload();

            /* Various getters and setters are exposed in IPv4 */
            byte[] ipOptions = ipv4.getOptions();
            IPv4Address dstIp = ipv4.getDestinationAddress();

            /* 
             * Check the IP protocol version of the IPv4 packet's payload.
             */
            if (ipv4.getProtocol() == IpProtocol.TCP) {
                /* We got a TCP packet; get the payload from IPv4 */
                TCP tcp = (TCP) ipv4.getPayload();

                /* Various getters and setters are exposed in TCP */
                TransportPort srcPort = tcp.getSourcePort();
                TransportPort dstPort = tcp.getDestinationPort();
                short flags = tcp.getFlags();

                /* Your logic here! */
            } else if (ipv4.getProtocol() == IpProtocol.UDP) {
                /* We got a UDP packet; get the payload from IPv4 */
                UDP udp = (UDP) ipv4.getPayload();

                /* Various getters and setters are exposed in UDP */
                TransportPort srcPort = udp.getSourcePort();
                TransportPort dstPort = udp.getDestinationPort();

                /* Your logic here! */
            }

        } else if (eth.getEtherType() == EthType.ARP) {
            /* We got an ARP packet; get the payload from Ethernet */
            ARP arp = (ARP) eth.getPayload();

            /* Various getters and setters are exposed in ARP */
            boolean gratuitous = arp.isGratuitous();

        } else {
            /* Unhandled ethertype */
        }
        break;
    default:
        break;
    }
    return Command.CONTINUE;
}

上述就是这个教程的全部。这个流程同样适用于获取其他类型的Payload, IPv4, ARP, TCP,还有UDP仅仅是教程中的例子。

如果你的packet或者frame的Payload并没有被定义成net.floodlightcontroller.packet中的一个类,你也可以选择性地把它加进去或者用返回的Data代替。所有未知的Payload都会被表示成Data IPackets,它有getData方法来返回字节数组形式的Payload。

Note that deserialization is done one header/payload at a time. This means that if you have an Ethernet frame with an IPv4 payload but an unknown-to-Floodlight transport protocol, the packet will be deserialized up to the transport layer. When Floodlight examine’s the IPv4 IpProtocol and does not find a pre-defined transport packet, the deserialization process will stop and getPayload() of IPv4 will return a Data object instead. The prior successfully deserialized headers and payloads remain intact and are accessible to modules in their deserialized form.(这段翻译的不好,有问题的话还望指正)

注意,一次只能对一个header或Payload进行并行化操作。这意味着如果你有一个以太网帧(携带者一个IPv4的Payload而不是一个Floodlight未知的传输层协议),这个packet会并列化直到传输层。当Floodlight分析到IPv4 IP协议时,并且并没有发现预定义的传输层packet,并行化操作将会停止并且IPv4的getPayload()将会返回一个Data对象。原先成功并列化的header和Payload将会原封不动地保留下来并且可访问。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值