One of the actions that an OpenFlow switch can take is to punt a packet to the controller. This example will take a look at how we can see those packets, and do something with them. I hope to follow this up with another post that does something more exciting, but for now I’ll just try to print out what type of packet it is. This is one of the things that (as far as I know) you would only be able to do with an OSGi module, and is not available via the REST API.
First we create our Maven pom.xml with the required imports. In this case we’ll need some parts of the SAL and switchmanager:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> | |
<modelVersion>4.0.0</modelVersion> | |
<parent> | |
<groupId>org.opendaylight.controller</groupId> | |
<artifactId>commons.opendaylight</artifactId> | |
<version>1.4.0-SNAPSHOT</version> | |
<relativePath>../../../controller/opendaylight/commons/opendaylight</relativePath> | |
</parent> | |
<groupId>com.example</groupId> | |
<artifactId>getpackets</artifactId> | |
<name>getpackets</name> | |
<url>http://maven.apache.org</url> | |
<packaging>bundle</packaging> | |
<build> | |
<plugins> | |
<plugin> | |
<groupId>org.apache.felix</groupId> | |
<artifactId>maven-bundle-plugin</artifactId> | |
<version>2.3.6</version> | |
<extensions>true</extensions> | |
<configuration> | |
<instructions> | |
<Import-Package> | |
org.opendaylight.controller.sal.core, | |
org.opendaylight.controller.sal.packet, | |
org.opendaylight.controller.sal.utils, | |
org.opendaylight.controller.switchmanager, | |
org.slf4j, | |
org.apache.felix.dm | |
</Import-Package> | |
<Bundle-Activator> | |
com.example.getpackets.Activator | |
</Bundle-Activator> | |
<Export-Package> | |
com.example.getpackets | |
</Export-Package> | |
</instructions> | |
</configuration> | |
</plugin> | |
</plugins> | |
</build> | |
<dependencies> | |
<dependency> | |
<groupId>junit</groupId> | |
<artifactId>junit</artifactId> | |
<version>3.8.1</version> | |
<scope>test</scope> | |
</dependency> | |
<dependency> | |
<groupId>org.opendaylight.controller</groupId> | |
<artifactId>switchmanager</artifactId> | |
<version>0.4.0-SNAPSHOT</version> | |
</dependency> | |
<dependency> | |
<groupId>org.opendaylight.controller</groupId> | |
<artifactId>sal</artifactId> | |
<version>0.5.0-SNAPSHOT</version> | |
</dependency> | |
</dependencies> | |
</project> |
Now we can create our activator. The key ingredient here is to register for callbacks from the Data Packet Service via OSGi in our public void configureInstance
method:
c.add(createContainerServiceDependency(containerName).setService( IDataPacketService.class).setCallbacks( "setDataPacketService", "unsetDataPacketService") .setRequired(true));
This ties into methods that we implement in our GetPackets class:
void setDataPacketService(IDataPacketService s) { this.dataPacketService = s; } void unsetDataPacketService(IDataPacketService s) { if (this.dataPacketService == s) { this.dataPacketService = null; } }
We make the class implement the IListenDataPacket interface to get notified of packets received on the controller:
public class GetPackets implements IListenDataPacket
And we override the public PacketResult receiveDataPacket(RawPacket inPkt)
method:
@Override public PacketResult receiveDataPacket(RawPacket inPkt) { if (inPkt == null) { return PacketResult.IGNORED; } log.trace("Received a frame of size: {}", inPkt.getPacketData().length); Packet formattedPak = this.dataPacketService.decodeDataPacket(inPkt); System.out.println("packet"); System.out.println(formattedPak); if (formattedPak instanceof Ethernet) { System.out.println(formattedPak); Object nextPak = formattedPak.getPayload(); if (nextPak instanceof IPv4) { IPv4 ipPak = (IPv4)nextPak; System.out.println("IP"); log.trace("Handled IP packet"); int sipAddr = ipPak.getSourceAddress(); InetAddress sip = NetUtils.getInetAddress(sipAddr); int dipAddr = ipPak.getDestinationAddress(); InetAddress dip = NetUtils.getInetAddress(dipAddr); System.out.println("SRC IP:"); System.out.println(sip); System.out.println("DST IP:"); System.out.println(dip); Object frame = ipPak.getPayload(); if (frame instanceof ICMP) { System.out.println("ICMP from instance"); } String protocol = IPProtocols.getProtocolName(ipPak.getProtocol()); if (protocol == IPProtocols.ICMP.toString()) { ICMP icmpPak = (ICMP)ipPak.getPayload(); System.out.println("ICMP from checking protocol"); handleICMPPacket((Ethernet) formattedPak, icmpPak, inPkt.getIncomingNodeConnector()); } } } return PacketResult.IGNORED; }
You’ll notice that we can keep going into the different payloads of the frame/packet to get to the next network layer. However, using instanceof
can be slow, so an alternative is to pull out the protocol field, and do a comparison. In my example I’ve specifically handled ICMP packets, and used both methods for determining if the IP packet is ICMP.
Excellent Stuff indeed Fred… I was wondering whether the controller need to examine/snoop all packets on the network but your “Punting” method is really Nice.
This is great, i am just getting started with OpenDaylight and mininet and I have found this very helpful, please keep it up.
Thanks!
Thanks a lot for your post!
But why I don’t get the system.out.println result after start the bundle?
Can you be more specific? This function only gets called when a packet is received.
Hi fredhsu, the system.out.println is used inside the receiveDataPacket function, and now opendaylight log shows some TRACE infos, but still no system.out.println string.
Are you sending traffic that would get punted to ODL?
Yes, I can see my log trace, which indicates that packets were received
Hmm, my next guess would be that perhaps the way ODL was started isn’t giving you stdout?
is there any solution? how to set the option? BTW I use the pre-build odl package.
Hi,
Thanks for uploading your knowledge. Its really helpful.
Actually i was trying to create an ODL application which would capture a packet from OVS and reply to ovs switch. But i needed OF 1.3, so i used integration version of ODL. The directory structure is different than the controller version of ODL.
can you suggest me what are the things that i need to consider in this case?
Thanks,
Pankaj
Hi Pankaj, sorry for the delayed response. I haven’t really looked at ODL lately, but I’m guessing the wiki or IRC channel are your best bet. If you have something specific that you’re trying to do I can try to help.
Hi, please tell me in more detail how to make the above code work.
I’m new to maven and osgi features.
[SwitchEvent Thread] WARN o.o.c.s.i.internal.DataPacketService – Failed to decode packet: 133
please let me know how to solve this issue.
Hello Fred
I am trying to receive packets from switch on my opendaylight. I am using receiveDataPackets() routine to receive packets, following ODL examples. For some reason, I am not receiving any packet from the switch. I would appreciate your insight
thanks
Kavita