How to Create a Packet Out Message
Table of Contents
Introduction
It is oftentimes useful to inject a packet from the controller into the data plane of your OpenFlow SDN. This is accomplished by sending a packet-out message from the controller to the switch in question. Typically, this data is some Ethernet frame in conjunction with higher-layer payloads. For example, the net.floodlightcontroller.dhcpserver.DHCPServer module sends packet-out messages containing DHCP reply messages in response to DHCP requests sent as packet-in messages to the module. (These DHCP replies are each an Ethernet frame with an IPv4 packet as payload. The IPv4 payload has a UDP packet as its payload, and lastly the UDP packet's payload is a DHCP packet.)
To compose a packet-out message and inject the packet within into the data plane, at a high level, we must do the following:
- Create each layer of the data to be injected, setting header fields appropriately
- Set the payload of each layer as the next highest layer (e.g. Ethernet's payload could be IPv4)
- Serialize the Ethernet into a byte[]
- Compose a packet-out and set the payload as the serialized Ethernet
- Set the output and input ports of the packet-out
- Send the packet out to the switch
Steps 1 and 2 can be interleaved in any logical way in so long as the Ethernet is complete and all payloads of higher layers are set prior to step 3.
Note that all packets are defined in the net.floodlightcontroller.packet package.
As an example, we will create a UDP packet that we'll then inject into the data plane as a packet-out. This example is intended to demonstrate the process to required to send a packet-out within a module. It will not run by itself without an underlying module.
Layer 1
Not our problem. Moving on to Layer 2...
Layer 2
We will start by creating an Ethernet and setting the header fields as desired. Note the use of IPv4 as our ethertype. This implies that we will have an IPv4 packet in the payload of the Ethernet frame.
Ethernet l2 = new Ethernet(); l2.setSourceMACAddress(MacAddress.of("00:00:00:00:00:01")); l2.setDestinationMACAddress(MacAddress.BROADCAST); l2.setEtherType(EthType.IPv4);
Layer 3
We will then create an IPv4 to house our IPv4 header data and UDP payload. Like, the Ethernet above, note that we specify the type of our payload. In this case, we state that a UDP packet will be in the payload of our IPv4 packet.
IPv4 l3 = new IPv4(); l3.setSourceAddress(IPv4.of("192.168.1.1")); l3.setDestinationAddress(IPv4.of("192.168.1.255")); l3.setTtl((byte) 64); l3.setProtocol(IpProtocol.UDP);
Layer 4
Next, we need to create our UDP instance. This will contain all relevant UDP header attributes and will hold the payload of the UDP packet.
UDP l4 = new UDP(); l4.setSourcePort(TransportPort.of(65003)); l4.setDestinationPort(TransportPort.of(67));
Layer 7
If the UDP packet were to also contain another packet/header within it, we could compose it next (e.g. DHCP); however, let's assume this is as far as we go and let's create some random data for the payload of the UDP packet (think iperf -u). Any data can be set as a payload using the net.floodlightcontroller.packet.Data class. Furthermore, any packet not defined in net.floodlightcontroller.packet can be created manually using a Data. If you feel the packet you're trying to create should be included in net.floodlightcontroller.packet, please let us know. Write to our email list.
Data l7 = new Data(); l7.setData(new byte[1000]);
Set Payloads and Serialize
We have now created each header disjointly. The next step is to set the payloads of each layer.
l2.setPayload(l3); l3.setPayload(l4); l4.setPayload(l7);
Then, we can serialize the Ethernet, which will in turn recursively serialize each payload of each packet within.
byte[] serializedData = l2.serialize();
Create Packet-Out and Write to Switch
Lastly, we need to create the packet-out message and write it to the switch. This involves (1) setting the payload of the packet-out, which is the data we just serialized, (2) specifying the output port through a list of OpenFlow actions, (3) setting the input port, which is the controller itself, (4) building the packet-out object, and (5) writing the packet-out object to the switch. An OFPacketOut is an OFMessage, so it can be written using IOFSwitch.write(OFMessage m).
OFPacketOut po = mySwitch.getOFFactory().buildPacketOut() /* mySwitch is some IOFSwitch object */ .setData(serializedData) .setActions(Collections.singletonList((OFAction) mySwitch.getOFFactory().actions().output(OFPort.FLOOD, 0xffFFffFF))) .setInPort(OFPort.CONTROLLER) .build(); mySwitch.write(po);
Congratulations! You are now a master of the packet-out message! If you have any other questions not answered here, please reach out to the mailing list. Write to our email list.