ONOS IPFIX demo app, OpenFlow statistics export over IPFIX
Change-Id: Ia14f4268545cc2cd5ce03a530aac21be62df7e83
diff --git a/ipfix/NOTICE.txt b/ipfix/NOTICE.txt
new file mode 100644
index 0000000..258f9e5
--- /dev/null
+++ b/ipfix/NOTICE.txt
@@ -0,0 +1,8 @@
+Code in the org.onosproject.ipfix.packet package is developed
+and modified from the source of jFlowLib library, which is the
+open source software and it is originally developed by
+DE-CIX Management GmbH <http://www.de-cix.net>
+and by the author Thomas King <thomas.king@de-cix.net>
+It is licensed under Apache License 2.0.
+More information about jFlowLib library can be found on GitHub at
+https://github.com/de-cix/jFlowLib
\ No newline at end of file
diff --git a/ipfix/README.md b/ipfix/README.md
new file mode 100644
index 0000000..4882128
--- /dev/null
+++ b/ipfix/README.md
@@ -0,0 +1,150 @@
+ONOS IPFIX demo application
+===========================
+
+##Introduction
+ONOS IPFIX application is the demo application that shows the concept of the OpenFlow statistics export over IPFIX protocol and possibly other similar protocols (e.g NetFlow).
+
+Currently, application supports two scenarios of the OpenFlow statistics export:
+ 1. Export of the flow statistics for ONOS Reactive Forwarding application flows.
+ 2. Export of the switch port statistics
+
+The intent of the application is to demonstrate the concept which can be possibly extended to export OpenFlow flow statistics for other ONOS applications, like bgprouter, sdnip, mfwd, etc.
+
+
+
+#User guide
+
+###Installation
+ONOS IPFIX requires at least ONOS version 1.3.
+
+ONOS IPFIX application can be downloaded from the onos-app-samples GIT repository with the following command:
+```
+git clone https://gerrit.onosproject.org/onos-app-samples.git
+```
+Enter the ONOS IPFIX application directory and compile the application:
+```
+cd onos-app-samples/onos-app-ipfix
+mvn clean install
+```
+After successful compilation of the application, it can be installed on the running ONOS instance with the following command:
+```
+onos-app <ip-address-of-ONOS-instance> reinstall! target/onos-app-ipfix-1.3.0-SNAPSHOT.oar
+```
+
+###General configuration
+ONOS IPFIX application generally requires that user configures IP address and port of the IPFIX collector. This is configured with the following ONOS configuration commands:
+```
+set org.onosporject.ipfix.IpfixManager CollectorAddress <ip-address>
+cfg set org.onosporject.ipfix.IpfixManager CollectorPort <udp-port>
+```
+IPFIX packets are transported over UDP and default port is 2055.
+
+###Flow statistics export for ONOS Reactive Forwarding application
+The export of the Flow statistics for ONOS Reactive Forwarding application is enabled by default. It is realized over Flow Rule Listener. When the flow rule created by the ONOS reactive forwarding application is removed from ONOS, IPFIX application will collect its statistics, covert them to the appropriate IPFIX format and export them over IPFIX protocol.
+
+Currently, ONOS IPFIX supports three IPFIX record templates that are used for exporting of these flows:
+
+ - **MAC template** (template ID = 331) - matches only MAC addresses, VLAN and switch ports. This template is used with default configuration of the reactive forwarding application that matches only source and destination MAC address and input port. This template has following IPFIX information elements (IEs):
+ - ID 130 - *exporterIPv4Address* - IPv4 address of the OpenFlow switch where flow was installed.
+ - ID 131 - *exporterIPv6Address* - OpenFlow switch DPID where flow was installed is embedded in the last 8 bytes of the fields.
+ - ID - 152 - *flowStartMilliseconds* - The absolute timestamp when the flow is installed by ONOS at the switch.
+ - ID - 153 - *flowEndMilliseconds* - The absolute timestamp when the flow is removed by the ONOS from the switch.
+ - ID 1 - *octetDeltaCount* - number of bytes matched by the flow.
+ - ID 2 - *packeDeltaCount* - number of packets matched by the flow.
+ - ID 10 - *ingressInterface* - OpenFlow switch input port of the flow.
+ - ID 14 - *egressInterface* - OpenFlow switch output port from the flow rule action structure.
+ - ID 56 - *sourceMacAddress* - source MAC address matched by the flow rule.
+ - ID 80 - *destinationMacAddress* - destination MAC address matched by the flow rule.
+ - ID 256 - *etherType* - Ethertype field matched by flow rule (0 if not matched)
+ - ID 14 - *vlanId* - VLAN ID matched by the flow rule (0 if not matched)
+
+ - **IPv4 template** (template ID = 332) - matches MAC template with addition to IPv4 addresses, Protocol, DSCP and TCP/UDP port. This template will be used if the matching of the IPv4 address is enabled in the ONOS Reactive Forwarding Application. This template has following IPFIX information elements (IEs) in addition to MAC template:
+ - ID 8 - *sourceIPv4Address* - source IPv4 address matched by the flow rule.
+ - ID 12 - *destinationIPv4Address* - destination IPv4 address matched by the flow rule.
+ - ID 4 - *protocolIdentifier* - IPv4Protocol field matched by flow rule (255 if not matched)
+ - ID 5 - *ipClassOfService* - IPv4 ToS field matched by the flow rule, constructed from DSCP and ECN OpenFlow matching conditions (0 if not matched).
+ - ID 7 - *sourceTransportPort* - source TCP/UDP port matched by the flow rule.
+ - ID 11 - *destinationTransportPort* - destination TCP/UDP port matched by the flow rule.
+
+
+ - **IPv6 template** (template ID = 333) - matches MAC template with addition to IPv6 addresses, Next-Header, Flow Label and TCP/UDP port. This template will be used if the matching of the IPv6 address is enabled in the ONOS Reactive Forwarding application. This template has following IPFIX information elements (IEs) in addition to MAC template:
+ - ID 27 - *sourceIPv6Address* - source IPv6 address matched by the flow rule.
+ - ID 28 - *destinationIPv6Address* - destination IPv6 address matched by the flow rule.
+ - ID 31 - *flowLabel* - IPv6 Flow Label field matched by flow rule (0 if not matched)
+ - ID 4 - *protocolIdentifier* - IPv4Protocol field matched by flow rule (255 if not matched)
+ - ID 5 - *ipClassOfService* - IPv4 ToS field matched by the flow rule, constructed from DSCP and ECN OpenFlow matching conditions (0 if not matched).
+ - ID 7 - *sourceTransportPort* - source TCP/UDP port matched by the flow rule.
+ - ID 11 - *destinationTransportPort* - destination TCP/UDP port matched by the flow rule.
+
+User can enable matching of the IPv4 address and other IPv4 fields in ONOS Reactive Forwarding application with following ONOS commands:
+```
+cfg set org.onosproject.fwd.ReactiveForwarding matchIpv4Addresses true
+cfg set org.onosproject.fwd.ReactiveForwarding matchIpv4Dscp true
+```
+
+User can enable matching of the IPv4 address and other IPv4 fields in ONOS Reactive Forwarding application with following ONOS commands:
+```
+cfg set org.onosproject.proxyarp.ProxyArp ipv6NeighborDiscovery true
+cfg set org.onosproject.provider.host.impl.HostLocationProvider ipv6NeighborDiscovery true
+cfg set org.onosproject.fwd.ReactiveForwarding ipv6Forwarding true
+cfg set org.onosproject.fwd.ReactiveForwarding matchIpv6Addresses true
+cfg set org.onosproject.fwd.ReactiveForwarding matchIpv6FlowLabel true
+```
+
+User can enable matching of the VLAN, TCP/UDP ports and ICMP type and code fields in ONOS Reactive Forwarding application with following ONOS commands:
+```
+cfg set org.onosproject.fwd.ReactiveForwarding matchVlan true
+cfg set org.onosproject.fwd.ReactiveForwarding matchTcpUdpPorts true
+cfg set org.onosproject.fwd.ReactiveForwarding matchIcmpFields true
+```
+
+To disable IPFIX export of the flow statistics for Reactive Forwarding application flows use following ONOS command:
+```
+cfg set org.onosproject.ipfix.IpfixManager ReactiveForwardingExport false
+```
+
+###Export of the switch port statistics
+Export of the switch port statistics over IPFIX is disabled by default.
+To enable it, use following ONOS command:
+```
+cfg set org.onosproject.ipfix.IpfixManager PortStatsFlowExport true
+```
+
+Export of the switch port statistics is realized over DeviceListener. When ONOS updates its internal port statistics for the OpenFlow switch, IPFIX application will export port statistics over IPFIX protocol. The export is done only by the ONOS instance that is “master” for the specific OpenFlow switch.
+The exported values represent difference between switch port counters collected by ONOS in the current and the previous polling. Polling interval is controlled by the ONOS OpenFlow Device Provider which actually collects statistics and by default is very frequent on 5 seconds interval. IPFIX application will export port statistics for every ONOS update of the statistics.
+It is recommended to configure ONOS port statistics polling interval to appropriate value with the following command:
+```
+cfg set org.onosproject.org.provider.of.device.impl.OpenFlowDeviceProvider PortStatsPollFrequency 30
+```
+
+The export of the switch port statistics uses two IPFIX record templates:
+
+ - **Received traffic** - Template ID 341
+ - **Transmitted traffic**- Template ID 342
+
+These templates consist of the following information elements (IEs):
+
+- ID 130 - *exporterIPv4Address* - IPv4 address of the OpenFlow switch
+- ID 131 - *exporterIPv6Address* - OpenFlow switch DPID is embedded in the last 8 bytes of the fields
+- *ingressInterface* or *egressInterface* - switch port number:
+ - ID 10 - *ingressInterface* IE is used for received traffic statistics
+ - ID 14 - *egressInterface* IE is used for transmitted traffic statistics
+- ID 1 - *octetDeltaCount* - number of bytes received/transmitted on the port between two polling intervals
+- ID 2 - *packeDeltaCount* - number of packets received/transmitted on the port between two polling intervals
+- ID - 152 - *flowStartMilliseconds* - The absolute timestamp for the previous polling of the statistics
+- ID - 153 - *flowEndMilliseconds* - The absolute timestamp for the current polling of the statistics
+
+##Known shortcomings and issues
+The purpose of the application is demonstration of the possibility for export of the OpenFlow statistics over IPFIX protocol. For this reason, export of IPFIX records is realized in very simplified way:
+
+- For export of the Reactive Forwarding application flows, each flow (Data record) is exported with its corresponding Template record in a single UDP packet.
+- For export of switch port statistics, for each OpenFlow switch and each traffic direction single IPFIX packet is created. This packet has Template Record for corresponding traffic direction and Data records for each port of the OpenFlow switch.
+- Currently, export to only one IPFIX collector is supported.
+
+Some of the IPFIX analyzer application will use source IP address of the IPFIX packets to identify “IPFIX exporter”. Because the ONOS IPFIX application exports IPFIX records on behalf of OpenFlow switches, IPFIX packets have ONOS controller IP address. ONOS IPFIX application uses *exporterIPv4Address* and *exporterIPv6Address* to further identify OpenFlow switch that matched the flow and on which behalf statistics are exported:
+
+- The *exporterIPv4Address* IE has IPv4 address of the OpenFlow switch that is connected over OpenFlow channel to ONOS controller.
+- The *exporterIPv6Address* IE embeds the OpenFlow switch DPID in the last 8 bytes of the information element.
+
+
+
diff --git a/ipfix/pom.xml b/ipfix/pom.xml
new file mode 100644
index 0000000..20f55c3
--- /dev/null
+++ b/ipfix/pom.xml
@@ -0,0 +1,159 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright 2014 Open Networking Laboratory
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<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/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-app-samples</artifactId>
+ <version>1.3.0-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <artifactId>onos-app-ipfix</artifactId>
+ <packaging>bundle</packaging>
+
+ <description>OpenFlow statistics export over IPFIX</description>
+ <url>http://onosproject.org</url>
+
+ <properties>
+ <onos.app.name>org.onosproject.ipfix</onos.app.name>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onlab-osgi</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>4.11</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onlab-junit</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-api</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ <classifier>tests</classifier>
+ </dependency>
+
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-of-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.compendium</artifactId>
+ <version>5.0.0</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.scr.annotations</artifactId>
+ <version>1.9.8</version>
+ <scope>provided</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <version>2.5.3</version>
+ <extensions>true</extensions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>2.5.1</version>
+ <configuration>
+ <source>1.8</source>
+ <target>1.8</target>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-scr-plugin</artifactId>
+ <version>1.20.0</version>
+ <executions>
+ <execution>
+ <id>generate-scr-srcdescriptor</id>
+ <goals>
+ <goal>scr</goal>
+ </goals>
+ </execution>
+ </executions>
+ <configuration>
+ <supportedProjectTypes>
+ <supportedProjectType>bundle</supportedProjectType>
+ <supportedProjectType>war</supportedProjectType>
+ </supportedProjectTypes>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-maven-plugin</artifactId>
+ <version>1.5</version>
+ <executions>
+ <execution>
+ <id>cfg</id>
+ <phase>generate-resources</phase>
+ <goals>
+ <goal>cfg</goal>
+ </goals>
+ </execution>
+ <execution>
+ <id>swagger</id>
+ <phase>generate-sources</phase>
+ <goals>
+ <goal>swagger</goal>
+ </goals>
+ </execution>
+ <execution>
+ <id>app</id>
+ <phase>package</phase>
+ <goals>
+ <goal>app</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
diff --git a/ipfix/src/main/java/org/onosproject/ipfix/DataRecordPortStatsIn.java b/ipfix/src/main/java/org/onosproject/ipfix/DataRecordPortStatsIn.java
new file mode 100644
index 0000000..32341ac
--- /dev/null
+++ b/ipfix/src/main/java/org/onosproject/ipfix/DataRecordPortStatsIn.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.ipfix;
+
+import java.util.List;
+
+import org.onlab.packet.Ip6Address;
+import org.onlab.packet.IpAddress;
+import org.onosproject.ipfix.packet.DataRecord;
+import org.onosproject.ipfix.packet.HeaderException;
+import org.onosproject.ipfix.packet.Ie;
+import org.onosproject.ipfix.packet.InformationElement;
+import org.onosproject.ipfix.packet.TemplateRecord;
+
+import com.google.common.primitives.Ints;
+import com.google.common.primitives.Longs;
+
+/**
+ * IPFIX Data record for switch port receive statistics.
+ */
+public class DataRecordPortStatsIn extends DataRecord {
+ public static final int TEMPLATE_ID = 341;
+ public static final int FIELD_COUNT = 7;
+ public static final int LENGTH = 56;
+
+ private IpAddress exporterIPv4Address;
+ private Ip6Address exporterIPv6Address;
+ private int ingressInterface;
+ private long octetDeltaCount;
+ private long packetDeltaCount;
+ private long flowStartMilliseconds;
+ private long flowEndMilliseconds;
+
+ /**
+ * IPFIX Data record for switch port receive statistics.
+ *
+ * @param exporterIpv4 IPv4 address of the IPFIX exporter
+ * @param exporterIpv6 IPv6 address of the IPFIX exporter, used for DPID
+ * @param intf Ingress/receiving interface number
+ * @param octets number of bytes received
+ * @param packets number of packets received
+ * @param start start timestamp of the flow
+ * @param end end timestamp of the flow
+ */
+ public DataRecordPortStatsIn(IpAddress exporterIpv4, Ip6Address exporterIpv6,
+ int intf, long octets, long packets, long start, long end) {
+
+ exporterIPv4Address = exporterIpv4;
+ exporterIPv6Address = exporterIpv6;
+ ingressInterface = intf;
+ octetDeltaCount = octets;
+ packetDeltaCount = packets;
+ flowStartMilliseconds = start;
+ flowEndMilliseconds = end;
+ }
+ @Override
+ public int getLength() {
+ return LENGTH;
+ }
+
+ @Override
+ public byte[] getBytes() throws HeaderException {
+ try {
+ byte[] data = new byte[LENGTH];
+
+ System.arraycopy(exporterIPv4Address.toOctets(), 0, data, 0, 4);
+ System.arraycopy(exporterIPv6Address.toOctets(), 0, data, 4, 16);
+ System.arraycopy(Ints.toByteArray(ingressInterface), 0, data, 20, 4);
+ System.arraycopy(Longs.toByteArray(octetDeltaCount), 0, data, 24, 8);
+ System.arraycopy(Longs.toByteArray(packetDeltaCount), 0, data, 32, 8);
+ System.arraycopy(Longs.toByteArray(flowStartMilliseconds), 0, data, 40, 8);
+ System.arraycopy(Longs.toByteArray(flowEndMilliseconds), 0, data, 48, 8);
+
+ return data;
+ } catch (Exception e) {
+ throw new HeaderException("Error while generating the bytes: " + e.getMessage());
+ }
+ }
+
+ /**
+ * IPFIX Template Record for switch port receive statistics.
+ *
+ * @return TemplateRecord IPFIX Template Record
+ */
+ public static TemplateRecord portStatsInTemplateRecord() {
+
+ TemplateRecord tr = new TemplateRecord();
+ tr.setTemplateID(TEMPLATE_ID);
+ tr.setFieldCount(FIELD_COUNT);
+
+ List<InformationElement> ieTemp = tr.getInformationElements();
+
+ ieTemp.add(new InformationElement(Ie.exporterIPv4Address));
+ ieTemp.add(new InformationElement(Ie.exporterIPv6Address));
+ ieTemp.add(new InformationElement(Ie.ingressInterface));
+ ieTemp.add(new InformationElement(Ie.octetDeltaCount));
+ ieTemp.add(new InformationElement(Ie.packetDeltaCount));
+ ieTemp.add(new InformationElement(Ie.flowStartMilliseconds));
+ ieTemp.add(new InformationElement(Ie.flowEndMilliseconds));
+
+ return tr;
+ }
+}
diff --git a/ipfix/src/main/java/org/onosproject/ipfix/DataRecordPortStatsOut.java b/ipfix/src/main/java/org/onosproject/ipfix/DataRecordPortStatsOut.java
new file mode 100644
index 0000000..74f6a78
--- /dev/null
+++ b/ipfix/src/main/java/org/onosproject/ipfix/DataRecordPortStatsOut.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.ipfix;
+
+import java.util.List;
+
+import org.onlab.packet.Ip6Address;
+import org.onlab.packet.IpAddress;
+import org.onosproject.ipfix.packet.DataRecord;
+import org.onosproject.ipfix.packet.HeaderException;
+import org.onosproject.ipfix.packet.Ie;
+import org.onosproject.ipfix.packet.InformationElement;
+import org.onosproject.ipfix.packet.TemplateRecord;
+
+import com.google.common.primitives.Ints;
+import com.google.common.primitives.Longs;
+
+/**
+ * IPFIX Data record for switch port transmit statistics.
+ */
+public class DataRecordPortStatsOut extends DataRecord {
+ public static final int TEMPLATE_ID = 342;
+ public static final int FIELD_COUNT = 7;
+ public static final int LENGTH = 56;
+
+ private IpAddress exporterIPv4Address;
+ private Ip6Address exporterIPv6Address;
+ private int egressInterface;
+ private long octetDeltaCount;
+ private long packetDeltaCount;
+ private long flowStartMilliseconds;
+ private long flowEndMilliseconds;
+
+ /**
+ * IPFIX Data record for switch port transmit statistics.
+ *
+ * @param exporterIpv4 IPv4 address of the IPFIX exporter
+ * @param exporterIpv6 IPv6 address of the IPFIX exporter, used for DPID
+ * @param intf Egress/trasmiting interface number
+ * @param octets number of bytes trasmitted
+ * @param packets number of packets trasmitted
+ * @param start start timestamp of the flow
+ * @param end end timestamp of the flow
+ */
+ public DataRecordPortStatsOut(IpAddress exporterIpv4, Ip6Address exporterIpv6,
+ int intf, long octets, long packets, long start, long end) {
+
+ exporterIPv4Address = exporterIpv4;
+ exporterIPv6Address = exporterIpv6;
+ egressInterface = intf;
+ octetDeltaCount = octets;
+ packetDeltaCount = packets;
+ flowStartMilliseconds = start;
+ flowEndMilliseconds = end;
+ }
+
+ @Override
+ public int getLength() {
+ return LENGTH;
+ }
+
+ @Override
+ public byte[] getBytes() throws HeaderException {
+ try {
+ byte[] data = new byte[LENGTH];
+
+ System.arraycopy(exporterIPv4Address.toOctets(), 0, data, 0, 4);
+ System.arraycopy(exporterIPv6Address.toOctets(), 0, data, 4, 16);
+ System.arraycopy(Ints.toByteArray(egressInterface), 0, data, 20, 4);
+ System.arraycopy(Longs.toByteArray(octetDeltaCount), 0, data, 24, 8);
+ System.arraycopy(Longs.toByteArray(packetDeltaCount), 0, data, 32, 8);
+ System.arraycopy(Longs.toByteArray(flowStartMilliseconds), 0, data, 40, 8);
+ System.arraycopy(Longs.toByteArray(flowEndMilliseconds), 0, data, 48, 8);
+
+ return data;
+ } catch (Exception e) {
+ throw new HeaderException("Error while generating the bytes: " + e.getMessage());
+ }
+ }
+
+ /**
+ * IPFIX template record for switch port transmit statistics.
+ *
+ * @return TemplateRecord IPFIX Template Record
+ */
+ public static TemplateRecord portStatsOutTemplateRecord() {
+
+ TemplateRecord tr = new TemplateRecord();
+ tr.setTemplateID(TEMPLATE_ID);
+ tr.setFieldCount(FIELD_COUNT);
+
+ List<InformationElement> ieTemp = tr.getInformationElements();
+
+ ieTemp.add(new InformationElement(Ie.exporterIPv4Address));
+ ieTemp.add(new InformationElement(Ie.exporterIPv6Address));
+ ieTemp.add(new InformationElement(Ie.egressInterface));
+ ieTemp.add(new InformationElement(Ie.octetDeltaCount));
+ ieTemp.add(new InformationElement(Ie.packetDeltaCount));
+ ieTemp.add(new InformationElement(Ie.flowStartMilliseconds));
+ ieTemp.add(new InformationElement(Ie.flowEndMilliseconds));
+
+ return tr;
+ }
+}
diff --git a/ipfix/src/main/java/org/onosproject/ipfix/DataRecordRfwdIpv4.java b/ipfix/src/main/java/org/onosproject/ipfix/DataRecordRfwdIpv4.java
new file mode 100644
index 0000000..266bd8c
--- /dev/null
+++ b/ipfix/src/main/java/org/onosproject/ipfix/DataRecordRfwdIpv4.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.ipfix;
+
+import java.util.List;
+
+import org.onlab.packet.Ip6Address;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onosproject.ipfix.packet.DataRecord;
+import org.onosproject.ipfix.packet.HeaderException;
+import org.onosproject.ipfix.packet.Ie;
+import org.onosproject.ipfix.packet.InformationElement;
+import org.onosproject.ipfix.packet.TemplateRecord;
+
+import com.google.common.primitives.Ints;
+import com.google.common.primitives.Longs;
+import com.google.common.primitives.Shorts;
+
+/**
+ * IPFIX Data record for Reactive Forwarding application with IPv4 fields matching.
+ */
+public class DataRecordRfwdIpv4 extends DataRecord {
+ public static final int TEMPLATE_ID = 332;
+ public static final int FIELD_COUNT = 18;
+ public static final int LENGTH = 90;
+
+ private IpAddress exporterIPv4Address;
+ private IpAddress exporterIPv6Address;
+ private long flowStartMilliseconds;
+ private long flowEndMilliseconds;
+ private long octetDeltaCount;
+ private long packetDeltaCount;
+ private int ingressInterface;
+ private int egressInterface;
+ private MacAddress sourceMacAddress;
+ private MacAddress destinationMacAddress;
+ private short ethernetType;
+ private short vlanId;
+ private IpAddress sourceIPv4Address;
+ private IpAddress destinationIPv4Address;
+ private byte protocolIdentifier;
+ private byte ipClassOfService;
+ private short sourceTransportPort;
+ private short destinationTransportPort;
+
+ //CHECKSTYLE:OFF
+ /**
+ * IPFIX Data record for Reactive Forwarding application with IPv4 fields matching.
+ *
+ * @param exporterIpv4 IPv4 address of the IPFIX exporter
+ * @param exporterIpv6 IPv6 address of the IPFIX exporter, used for DPID
+ * @param start start timestamp of the flow
+ * @param end end timestamp of the flow
+ * @param octets number of bytes matched by the flow
+ * @param packets number of packets matched by the flow
+ * @param intfIn switch input interface of the flow
+ * @param intfOut switch output interface of the flow
+ * @param srcMac source MAC address
+ * @param dstMac destination MAC address
+ * @param etherType etherType field of the flow
+ * @param vlan VLAN ID of the flow
+ * @param srcIp source IPv4 address of the flow
+ * @param dstIp destination IPv4 address of the flow
+ * @param proto IPv4 Protocol field of the flow
+ * @param tos IPv4 ToS field of the flow
+ * @param srcPort source tranport protocol port
+ * @param dstPort destination transport protocol port
+ */
+ public DataRecordRfwdIpv4(IpAddress exporterIpv4,
+ Ip6Address exporterIpv6,
+ long start,
+ long end,
+ long octets,
+ long packets,
+ int intfIn,
+ int intfOut,
+ MacAddress srcMac,
+ MacAddress dstMac,
+ short etherType,
+ short vlan,
+ IpAddress srcIp,
+ IpAddress dstIp,
+ byte proto,
+ byte tos,
+ short srcPort,
+ short dstPort) {
+ //CHECKSTYLE:ON
+
+ exporterIPv4Address = exporterIpv4;
+ exporterIPv6Address = exporterIpv6;
+ flowStartMilliseconds = start;
+ flowEndMilliseconds = end;
+ octetDeltaCount = octets;
+ packetDeltaCount = packets;
+ ingressInterface = intfIn;
+ egressInterface = intfOut;
+ sourceMacAddress = srcMac;
+ destinationMacAddress = dstMac;
+ ethernetType = etherType;
+ vlanId = vlan;
+ sourceIPv4Address = srcIp;
+ destinationIPv4Address = dstIp;
+ protocolIdentifier = proto;
+ ipClassOfService = tos;
+ sourceTransportPort = srcPort;
+ destinationTransportPort = dstPort;
+ }
+
+ @Override
+ public int getLength() {
+ return LENGTH;
+ }
+
+ @Override
+ public byte[] getBytes() throws HeaderException {
+ try {
+ byte[] data = new byte[LENGTH];
+
+ System.arraycopy(exporterIPv4Address.toOctets(), 0, data, 0, 4);
+ System.arraycopy(exporterIPv6Address.toOctets(), 0, data, 4, 16);
+ System.arraycopy(Longs.toByteArray(flowStartMilliseconds), 0, data, 20, 8);
+ System.arraycopy(Longs.toByteArray(flowEndMilliseconds), 0, data, 28, 8);
+ System.arraycopy(Longs.toByteArray(octetDeltaCount), 0, data, 36, 8);
+ System.arraycopy(Longs.toByteArray(packetDeltaCount), 0, data, 44, 8);
+ System.arraycopy(Ints.toByteArray(ingressInterface), 0, data, 52, 4);
+ System.arraycopy(Ints.toByteArray(egressInterface), 0, data, 56, 4);
+ System.arraycopy(sourceMacAddress.toBytes(), 0, data, 60, 6);
+ System.arraycopy(destinationMacAddress.toBytes(), 0, data, 66, 6);
+ System.arraycopy(Shorts.toByteArray(ethernetType), 0, data, 72, 2);
+ System.arraycopy(Shorts.toByteArray(vlanId), 0, data, 74, 2);
+ System.arraycopy(sourceIPv4Address.toOctets(), 0, data, 76, 4);
+ System.arraycopy(destinationIPv4Address.toOctets(), 0, data, 80, 4);
+ data[84] = protocolIdentifier;
+ data[85] = ipClassOfService;
+ System.arraycopy(Shorts.toByteArray(sourceTransportPort), 0, data, 86, 2);
+ System.arraycopy(Shorts.toByteArray(destinationTransportPort), 0, data, 88, 2);
+
+ return data;
+ } catch (Exception e) {
+ throw new HeaderException("Error while generating the bytes: " + e.getMessage());
+ }
+ }
+
+ /**
+ * IPFIX Template record for Reactive Forwarding application with IPv4 fields matching.
+ *
+ * @return TemplateRecord IPFIX Template Record
+ */
+ public static TemplateRecord getTemplateRecord() {
+
+ TemplateRecord tr = new TemplateRecord();
+ tr.setTemplateID(TEMPLATE_ID);
+ tr.setFieldCount(FIELD_COUNT);
+
+ List<InformationElement> ieTemp = tr.getInformationElements();
+
+ ieTemp.add(new InformationElement(Ie.exporterIPv4Address));
+ ieTemp.add(new InformationElement(Ie.exporterIPv6Address));
+ ieTemp.add(new InformationElement(Ie.flowStartMilliseconds));
+ ieTemp.add(new InformationElement(Ie.flowEndMilliseconds));
+ ieTemp.add(new InformationElement(Ie.octetDeltaCount));
+ ieTemp.add(new InformationElement(Ie.packetDeltaCount));
+ ieTemp.add(new InformationElement(Ie.ingressInterface));
+ ieTemp.add(new InformationElement(Ie.egressInterface));
+ ieTemp.add(new InformationElement(Ie.sourceMacAddress));
+ ieTemp.add(new InformationElement(Ie.destinationMacAddress));
+ ieTemp.add(new InformationElement(Ie.ethernetType));
+ ieTemp.add(new InformationElement(Ie.vlanId));
+ ieTemp.add(new InformationElement(Ie.sourceIPv4Address));
+ ieTemp.add(new InformationElement(Ie.destinationIPv4Address));
+ ieTemp.add(new InformationElement(Ie.protocolIdentifier));
+ ieTemp.add(new InformationElement(Ie.ipClassOfService));
+ ieTemp.add(new InformationElement(Ie.sourceTransportPort));
+ ieTemp.add(new InformationElement(Ie.destinationTransportPort));
+
+ return tr;
+ }
+}
diff --git a/ipfix/src/main/java/org/onosproject/ipfix/DataRecordRfwdIpv6.java b/ipfix/src/main/java/org/onosproject/ipfix/DataRecordRfwdIpv6.java
new file mode 100644
index 0000000..a916f78
--- /dev/null
+++ b/ipfix/src/main/java/org/onosproject/ipfix/DataRecordRfwdIpv6.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.ipfix;
+
+import java.util.List;
+
+import org.onlab.packet.Ip6Address;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onosproject.ipfix.packet.DataRecord;
+import org.onosproject.ipfix.packet.HeaderException;
+import org.onosproject.ipfix.packet.Ie;
+import org.onosproject.ipfix.packet.InformationElement;
+import org.onosproject.ipfix.packet.TemplateRecord;
+
+import com.google.common.primitives.Ints;
+import com.google.common.primitives.Longs;
+import com.google.common.primitives.Shorts;
+
+/**
+ * IPFIX Data record for Reactive Forwarding application with IPv4 fields matching.
+ */
+public class DataRecordRfwdIpv6 extends DataRecord {
+ public static final int TEMPLATE_ID = 333;
+ public static final int FIELD_COUNT = 19;
+ public static final int LENGTH = 118;
+
+ private IpAddress exporterIPv4Address;
+ private IpAddress exporterIPv6Address;
+ private long flowStartMilliseconds;
+ private long flowEndMilliseconds;
+ private long octetDeltaCount;
+ private long packetDeltaCount;
+ private int ingressInterface;
+ private int egressInterface;
+ private MacAddress sourceMacAddress;
+ private MacAddress destinationMacAddress;
+ private short ethernetType;
+ private short vlanId;
+ private Ip6Address sourceIPv6Address;
+ private Ip6Address destinationIPv6Address;
+ private int flowLabelIpv6;
+ private byte protocolIdentifier;
+ private byte ipClassOfService;
+ private short sourceTransportPort;
+ private short destinationTransportPort;
+
+ //CHECKSTYLE:OFF
+ /**
+ * IPFIX Data record for Reactive Forwarding application with IPv6 fields matching.
+ *
+ * @param exporterIpv4 IPv4 address of the IPFIX exporter
+ * @param exporterIpv6 IPv6 address of the IPFIX exporter, used for DPID
+ * @param start start timestamp of the flow
+ * @param end end timestamp of the flow
+ * @param octets number of bytes matched by the flow
+ * @param packets number of packets matched by the flow
+ * @param intfIn switch input interface of the flow
+ * @param intfOut switch output interface of the flow
+ * @param srcMac source MAC address
+ * @param dstMac destination MAC address
+ * @param etherType etherType field of the flow
+ * @param vlan VLAN ID of the flow
+ * @param srcIp6 source IPv6 address of the flow
+ * @param dstIp6 destination IPv6 address of the flow
+ * @param flowLabel IPv6 flow label of the flow
+ * @param proto IPv6 next-header of the flow
+ * @param tos IPv6 Traffic Class field
+ * @param srcPort source transport protocol port
+ * @param dstPort destination transport protocol port
+ */
+ public DataRecordRfwdIpv6(IpAddress exporterIpv4,
+ Ip6Address exporterIpv6,
+ long start,
+ long end,
+ long octets,
+ long packets,
+ int intfIn,
+ int intfOut,
+ MacAddress srcMac,
+ MacAddress dstMac,
+ short etherType,
+ short vlan,
+ Ip6Address srcIp6,
+ Ip6Address dstIp6,
+ int flowLabel,
+ byte proto,
+ byte tos,
+ short srcPort,
+ short dstPort) {
+ //CHECKSTYLE:ON
+
+ exporterIPv4Address = exporterIpv4;
+ exporterIPv6Address = exporterIpv6;
+ flowStartMilliseconds = start;
+ flowEndMilliseconds = end;
+ octetDeltaCount = octets;
+ packetDeltaCount = packets;
+ ingressInterface = intfIn;
+ egressInterface = intfOut;
+ sourceMacAddress = srcMac;
+ destinationMacAddress = dstMac;
+ ethernetType = etherType;
+ vlanId = vlan;
+ sourceIPv6Address = srcIp6;
+ destinationIPv6Address = dstIp6;
+ flowLabelIpv6 = flowLabel;
+ protocolIdentifier = proto;
+ ipClassOfService = tos;
+ sourceTransportPort = srcPort;
+ destinationTransportPort = dstPort;
+ }
+
+ @Override
+ public int getLength() {
+ return LENGTH;
+ }
+
+ @Override
+ public byte[] getBytes() throws HeaderException {
+ try {
+ byte[] data = new byte[LENGTH];
+
+ System.arraycopy(exporterIPv4Address.toOctets(), 0, data, 0, 4);
+ System.arraycopy(exporterIPv6Address.toOctets(), 0, data, 4, 16);
+ System.arraycopy(Longs.toByteArray(flowStartMilliseconds), 0, data, 20, 8);
+ System.arraycopy(Longs.toByteArray(flowEndMilliseconds), 0, data, 28, 8);
+ System.arraycopy(Longs.toByteArray(octetDeltaCount), 0, data, 36, 8);
+ System.arraycopy(Longs.toByteArray(packetDeltaCount), 0, data, 44, 8);
+ System.arraycopy(Ints.toByteArray(ingressInterface), 0, data, 52, 4);
+ System.arraycopy(Ints.toByteArray(egressInterface), 0, data, 56, 4);
+ System.arraycopy(sourceMacAddress.toBytes(), 0, data, 60, 6);
+ System.arraycopy(destinationMacAddress.toBytes(), 0, data, 66, 6);
+ System.arraycopy(Shorts.toByteArray(ethernetType), 0, data, 72, 2);
+ System.arraycopy(Shorts.toByteArray(vlanId), 0, data, 74, 2);
+ System.arraycopy(sourceIPv6Address.toOctets(), 0, data, 76, 16);
+ System.arraycopy(destinationIPv6Address.toOctets(), 0, data, 92, 16);
+ System.arraycopy(Ints.toByteArray(flowLabelIpv6), 0, data, 108, 4);
+ data[112] = protocolIdentifier;
+ data[113] = ipClassOfService;
+ System.arraycopy(Shorts.toByteArray(sourceTransportPort), 0, data, 114, 2);
+ System.arraycopy(Shorts.toByteArray(destinationTransportPort), 0, data, 116, 2);
+
+ return data;
+ } catch (Exception e) {
+ throw new HeaderException("Error while generating the bytes: " + e.getMessage());
+ }
+ }
+
+ /**
+ * IPFIX Template record for Reactive Forwarding application with IPv4 fields matching.
+ *
+ * @return TemplateRecord IPFIX Template Record
+ */
+ public static TemplateRecord getTemplateRecord() {
+
+ TemplateRecord tr = new TemplateRecord();
+ tr.setTemplateID(TEMPLATE_ID);
+ tr.setFieldCount(FIELD_COUNT);
+
+ List<InformationElement> ieTemp = tr.getInformationElements();
+
+ ieTemp.add(new InformationElement(Ie.exporterIPv4Address));
+ ieTemp.add(new InformationElement(Ie.exporterIPv6Address));
+ ieTemp.add(new InformationElement(Ie.flowStartMilliseconds));
+ ieTemp.add(new InformationElement(Ie.flowEndMilliseconds));
+ ieTemp.add(new InformationElement(Ie.octetDeltaCount));
+ ieTemp.add(new InformationElement(Ie.packetDeltaCount));
+ ieTemp.add(new InformationElement(Ie.ingressInterface));
+ ieTemp.add(new InformationElement(Ie.egressInterface));
+ ieTemp.add(new InformationElement(Ie.sourceMacAddress));
+ ieTemp.add(new InformationElement(Ie.destinationMacAddress));
+ ieTemp.add(new InformationElement(Ie.ethernetType));
+ ieTemp.add(new InformationElement(Ie.vlanId));
+ ieTemp.add(new InformationElement(Ie.sourceIPv6Address));
+ ieTemp.add(new InformationElement(Ie.destinationIPv6Address));
+ ieTemp.add(new InformationElement(Ie.flowLabelIPv6));
+ ieTemp.add(new InformationElement(Ie.protocolIdentifier));
+ ieTemp.add(new InformationElement(Ie.ipClassOfService));
+ ieTemp.add(new InformationElement(Ie.sourceTransportPort));
+ ieTemp.add(new InformationElement(Ie.destinationTransportPort));
+
+ return tr;
+ }
+}
diff --git a/ipfix/src/main/java/org/onosproject/ipfix/DataRecordRfwdMac.java b/ipfix/src/main/java/org/onosproject/ipfix/DataRecordRfwdMac.java
new file mode 100644
index 0000000..9598829
--- /dev/null
+++ b/ipfix/src/main/java/org/onosproject/ipfix/DataRecordRfwdMac.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.ipfix;
+
+import java.util.List;
+
+import org.onlab.packet.Ip6Address;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onosproject.ipfix.packet.DataRecord;
+import org.onosproject.ipfix.packet.HeaderException;
+import org.onosproject.ipfix.packet.Ie;
+import org.onosproject.ipfix.packet.InformationElement;
+import org.onosproject.ipfix.packet.TemplateRecord;
+
+import com.google.common.primitives.Ints;
+import com.google.common.primitives.Longs;
+import com.google.common.primitives.Shorts;
+
+/**
+ * IPFIX Data record for Reactive Forwarding application with L2 fields matching.
+ */
+public class DataRecordRfwdMac extends DataRecord {
+ public static final int TEMPLATE_ID = 331;
+ public static final int FIELD_COUNT = 12;
+ public static final int LENGTH = 76;
+
+ private IpAddress exporterIPv4Address;
+ private IpAddress exporterIPv6Address;
+ private long flowStartMilliseconds;
+ private long flowEndMilliseconds;
+ private long octetDeltaCount;
+ private long packetDeltaCount;
+ private int ingressInterface;
+ private int egressInterface;
+ private MacAddress sourceMacAddress;
+ private MacAddress destinationMacAddress;
+ private short ethernetType;
+ private short vlanId;
+
+ /**
+ * IPFIX Data record for Reactive Forwarding application with L2 fields matching.
+ *
+ * @param exporterIpv4 IPv4 address of the IPFIX exporter
+ * @param exporterIpv6 IPv6 address of the IPFIX exporter, used for DPID
+ * @param start start timestamp of the flow
+ * @param end end timestamp of the flow
+ * @param octets number of bytes matched by the flow
+ * @param packets number of packets matched by the flow
+ * @param intfIn switch input interface of the flow
+ * @param intfOut switch output interface of the flow
+ * @param srcMac source MAC address
+ * @param dstMac destination MAC address
+ * @param etherType etherType field of the flow
+ * @param vlan VLAN ID of the flow
+ */
+ public DataRecordRfwdMac(IpAddress exporterIpv4,
+ Ip6Address exporterIpv6,
+ long start,
+ long end,
+ long octets,
+ long packets,
+ int intfIn,
+ int intfOut,
+ MacAddress srcMac,
+ MacAddress dstMac,
+ short etherType,
+ short vlan) {
+
+ exporterIPv4Address = exporterIpv4;
+ exporterIPv6Address = exporterIpv6;
+ flowStartMilliseconds = start;
+ flowEndMilliseconds = end;
+ octetDeltaCount = octets;
+ packetDeltaCount = packets;
+ ingressInterface = intfIn;
+ egressInterface = intfOut;
+ sourceMacAddress = srcMac;
+ destinationMacAddress = dstMac;
+ ethernetType = etherType;
+ vlanId = vlan;
+ }
+
+ @Override
+ public int getLength() {
+ return LENGTH;
+ }
+
+ @Override
+ public byte[] getBytes() throws HeaderException {
+ try {
+ byte[] data = new byte[LENGTH];
+
+ System.arraycopy(exporterIPv4Address.toOctets(), 0, data, 0, 4);
+ System.arraycopy(exporterIPv6Address.toOctets(), 0, data, 4, 16);
+ System.arraycopy(Longs.toByteArray(flowStartMilliseconds), 0, data, 20, 8);
+ System.arraycopy(Longs.toByteArray(flowEndMilliseconds), 0, data, 28, 8);
+ System.arraycopy(Longs.toByteArray(octetDeltaCount), 0, data, 36, 8);
+ System.arraycopy(Longs.toByteArray(packetDeltaCount), 0, data, 44, 8);
+ System.arraycopy(Ints.toByteArray(ingressInterface), 0, data, 52, 4);
+ System.arraycopy(Ints.toByteArray(egressInterface), 0, data, 56, 4);
+ System.arraycopy(sourceMacAddress.toBytes(), 0, data, 60, 6);
+ System.arraycopy(destinationMacAddress.toBytes(), 0, data, 66, 6);
+ System.arraycopy(Shorts.toByteArray(ethernetType), 0, data, 72, 2);
+ System.arraycopy(Shorts.toByteArray(vlanId), 0, data, 74, 2);
+
+ return data;
+ } catch (Exception e) {
+ throw new HeaderException("Error while generating the bytes: " + e.getMessage());
+ }
+ }
+
+ /**
+ * IPFIX Template record for Reactive Forwarding application with L2 fields matching.
+ *
+ * @return TemplateRecord IPFIX Template Record
+ */
+ public static TemplateRecord getTemplateRecord() {
+
+ TemplateRecord tr = new TemplateRecord();
+ tr.setTemplateID(TEMPLATE_ID);
+ tr.setFieldCount(FIELD_COUNT);
+
+ List<InformationElement> ieTemp = tr.getInformationElements();
+
+ ieTemp.add(new InformationElement(Ie.exporterIPv4Address));
+ ieTemp.add(new InformationElement(Ie.exporterIPv6Address));
+ ieTemp.add(new InformationElement(Ie.flowStartMilliseconds));
+ ieTemp.add(new InformationElement(Ie.flowEndMilliseconds));
+ ieTemp.add(new InformationElement(Ie.octetDeltaCount));
+ ieTemp.add(new InformationElement(Ie.packetDeltaCount));
+ ieTemp.add(new InformationElement(Ie.ingressInterface));
+ ieTemp.add(new InformationElement(Ie.egressInterface));
+ ieTemp.add(new InformationElement(Ie.sourceMacAddress));
+ ieTemp.add(new InformationElement(Ie.destinationMacAddress));
+ ieTemp.add(new InformationElement(Ie.ethernetType));
+ ieTemp.add(new InformationElement(Ie.vlanId));
+
+ return tr;
+ }
+}
diff --git a/ipfix/src/main/java/org/onosproject/ipfix/FlowRemovedListener.java b/ipfix/src/main/java/org/onosproject/ipfix/FlowRemovedListener.java
new file mode 100644
index 0000000..3cab6a0
--- /dev/null
+++ b/ipfix/src/main/java/org/onosproject/ipfix/FlowRemovedListener.java
@@ -0,0 +1,244 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.ipfix;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.IPv4;
+import org.onlab.packet.IPv6;
+import org.onlab.packet.Ip6Address;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onosproject.ipfix.packet.DataRecord;
+import org.onosproject.net.flow.FlowEntry;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.FlowRuleEvent;
+import org.onosproject.net.flow.FlowRuleListener;
+import org.onosproject.net.flow.criteria.EthCriterion;
+import org.onosproject.net.flow.criteria.EthTypeCriterion;
+import org.onosproject.net.flow.criteria.IPCriterion;
+import org.onosproject.net.flow.criteria.IPDscpCriterion;
+import org.onosproject.net.flow.criteria.IPEcnCriterion;
+import org.onosproject.net.flow.criteria.IPProtocolCriterion;
+import org.onosproject.net.flow.criteria.IPv6FlowLabelCriterion;
+import org.onosproject.net.flow.criteria.IcmpCodeCriterion;
+import org.onosproject.net.flow.criteria.IcmpTypeCriterion;
+import org.onosproject.net.flow.criteria.Icmpv6CodeCriterion;
+import org.onosproject.net.flow.criteria.Icmpv6TypeCriterion;
+import org.onosproject.net.flow.criteria.PortCriterion;
+import org.onosproject.net.flow.criteria.TcpPortCriterion;
+import org.onosproject.net.flow.criteria.UdpPortCriterion;
+import org.onosproject.net.flow.criteria.VlanIdCriterion;
+import org.onosproject.net.flow.criteria.Criterion.Type;
+import org.onosproject.net.flow.instructions.Instruction;
+import org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
+import org.onosproject.openflow.controller.Dpid;
+
+import com.google.common.primitives.Longs;
+
+/**
+ * Flow Rule Listener for detecting flow removal or flow statistics update by ONOS.
+ */
+public class FlowRemovedListener implements FlowRuleListener {
+
+ private IpfixManager ipfixManager;
+
+ /**
+ * Flow Event listerner for Flow removed events of Reactive Forwarding application.
+ *
+ * @param ipfixManager ipfix manager instance
+ */
+ public FlowRemovedListener(IpfixManager ipfixManager) {
+ this.ipfixManager = ipfixManager;
+ }
+
+ @Override
+ public void event(FlowRuleEvent event) {
+ switch (event.type()) {
+ case RULE_REMOVED:
+ FlowRule rule = event.subject();
+ FlowEntry entry = (FlowEntry) rule;
+ if (entry.appId() == ipfixManager.coreService.getAppId("org.onosproject.fwd").id()) {
+ flowRemovedRfwd(entry);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ /**
+ * Handle ONOS Reactive forwarding application flow removal.
+ * When flow is removed, generate and send IPFIX record.
+ *
+ * @param entry flow entry removed from ONOS
+ */
+ private void flowRemovedRfwd(FlowEntry entry) {
+
+ //Log
+ ipfixManager.log.trace("Flow Removed from Reactive Forwarding, id={}, device={}, selector={}, treatment={}",
+ entry.id(), entry.deviceId(), entry.selector(), entry.treatment());
+
+ // Exporters
+ IpAddress exporterIpv4 = IpAddress.valueOf(ipfixManager.deviceService.getDevice(
+ entry.deviceId()).annotations().toString().split("=")[2].split(":")[0]);
+
+ long dpid = Dpid.dpid(entry.deviceId().uri()).value();
+ byte[] byteExporterIpv6 = new byte[16];
+ System.arraycopy(Longs.toByteArray(0), 0, byteExporterIpv6, 0, 8);
+ System.arraycopy(Longs.toByteArray(dpid), 0, byteExporterIpv6, 8, 8);
+ Ip6Address exporterIpv6 = Ip6Address.valueOf(byteExporterIpv6);
+
+ // Timestamps, octets, packets
+ long start = System.currentTimeMillis() - (1000 * entry.life());
+ long end = System.currentTimeMillis();
+ long octets = entry.bytes();
+ long packets = entry.packets();
+
+ // Input and Output ports
+ PortCriterion portCrit = (PortCriterion) entry.selector().getCriterion(Type.IN_PORT);
+ int intfIn = (portCrit == null) ? 0 : (int) portCrit.port().toLong();
+ List<Instruction> instructions = entry.treatment().allInstructions();
+ int intfOut = 0;
+ for (Instruction instruction : instructions) {
+ if (instruction.type() == Instruction.Type.OUTPUT) {
+ OutputInstruction outputInstruction = (OutputInstruction) instruction;
+ intfOut = (outputInstruction == null) ? 0 : (int) outputInstruction.port().toLong();
+ }
+ }
+
+ // Ethernet MACs, Ethertype and VLAN
+ EthCriterion ethCrit;
+ ethCrit = (EthCriterion) entry.selector().getCriterion(Type.ETH_SRC);
+ MacAddress srcMac = (ethCrit == null) ? MacAddress.valueOf("00:00:00:00:00:00") : ethCrit.mac();
+ ethCrit = (EthCriterion) entry.selector().getCriterion(Type.ETH_DST);
+ MacAddress dstMac = (ethCrit == null) ? MacAddress.valueOf("00:00:00:00:00:00") : ethCrit.mac();
+
+ EthTypeCriterion ethTypeCrit = (EthTypeCriterion) entry.selector().getCriterion(Type.ETH_TYPE);
+ Short ethType = (ethTypeCrit == null) ? 0x0000 : ethTypeCrit.ethType().toShort();
+
+ VlanIdCriterion vlanCrit = (VlanIdCriterion) entry.selector().getCriterion(Type.VLAN_VID);
+ Short vlan = (vlanCrit == null) ? 0x0000 : vlanCrit.vlanId().toShort();
+
+ // IP Criterion check
+ IPCriterion srcIpCrit = (IPCriterion) entry.selector().getCriterion(Type.IPV4_SRC);
+ IPCriterion dstIpCrit = (IPCriterion) entry.selector().getCriterion(Type.IPV4_DST);
+ IPCriterion srcIp6Crit = (IPCriterion) entry.selector().getCriterion(Type.IPV6_SRC);
+ IPCriterion dstIp6Crit = (IPCriterion) entry.selector().getCriterion(Type.IPV6_DST);
+
+ // If IP criterions are null send MAC Data Record, else send IPv4 or IPv6 Data Record
+ if (srcIpCrit == null && dstIpCrit == null && srcIp6Crit == null && dstIp6Crit == null) {
+ DataRecordRfwdMac record = new DataRecordRfwdMac(
+ exporterIpv4, exporterIpv6,
+ start, end,
+ octets, packets,
+ intfIn, intfOut,
+ srcMac, dstMac,
+ ethType, vlan);
+ List<DataRecord> recordList = new ArrayList<DataRecord>();
+ recordList.add(record);
+ ipfixManager.ipfixSender.sendRecords(DataRecordRfwdMac.getTemplateRecord(),
+ recordList, dpid, IpfixManager.collectorIp, IpfixManager.collectorPort);
+ } else {
+ // Checking IPv4 and IPv6 criterions
+ IPProtocolCriterion protocolCrit = (IPProtocolCriterion) entry.selector().getCriterion(Type.IP_PROTO);
+ byte ipProtocol = (protocolCrit == null) ? (byte) 0xff : (byte) protocolCrit.protocol();
+
+ IPDscpCriterion dscpCrit = (IPDscpCriterion) entry.selector().getCriterion(Type.IP_DSCP);
+ byte dscp = (dscpCrit == null) ? 0x00 : dscpCrit.ipDscp();
+ IPEcnCriterion ecnCrit = (IPEcnCriterion) entry.selector().getCriterion(Type.IP_ECN);
+ byte ecn = (ecnCrit == null) ? 0x00 : ecnCrit.ipEcn();
+ byte tos = (byte) ((byte) (dscp << 2) | ecn);
+
+ IPv6FlowLabelCriterion flowLabelCrit =
+ (IPv6FlowLabelCriterion) entry.selector().getCriterion(Type.IPV6_FLABEL);
+ int flowLabelIpv6 = (flowLabelCrit == null) ? 0 : flowLabelCrit.flowLabel();
+
+ int srcPort = 0;
+ int dstPort = 0;
+ if (ipProtocol == IPv4.PROTOCOL_TCP) {
+ TcpPortCriterion tcpCrit;
+ tcpCrit = (TcpPortCriterion) entry.selector().getCriterion(Type.TCP_SRC);
+ srcPort = (tcpCrit == null) ? 0 : tcpCrit.tcpPort().toInt();
+ tcpCrit = (TcpPortCriterion) entry.selector().getCriterion(Type.TCP_DST);
+ dstPort = (tcpCrit == null) ? 0 : tcpCrit.tcpPort().toInt();
+ } else if (ipProtocol == IPv4.PROTOCOL_UDP) {
+ UdpPortCriterion udpCrit;
+ udpCrit = (UdpPortCriterion) entry.selector().getCriterion(Type.UDP_SRC);
+ srcPort = (udpCrit == null) ? 0 : udpCrit.udpPort().toInt();
+ udpCrit = (UdpPortCriterion) entry.selector().getCriterion(Type.UDP_DST);
+ dstPort = (udpCrit == null) ? 0 : udpCrit.udpPort().toInt();
+ } else if (ipProtocol == IPv4.PROTOCOL_ICMP) {
+ IcmpTypeCriterion icmpTypeCrit = (IcmpTypeCriterion) entry.selector().getCriterion(Type.ICMPV4_TYPE);
+ Short icmpType = (icmpTypeCrit == null) ? 0 : icmpTypeCrit.icmpType();
+ IcmpCodeCriterion icmpCodeCrit = (IcmpCodeCriterion) entry.selector().getCriterion(Type.ICMPV4_CODE);
+ Short icmpCode = (icmpCodeCrit == null) ? 0 : icmpCodeCrit.icmpCode();
+ dstPort = 256 * icmpType + icmpCode;
+ } else if (ipProtocol == IPv6.PROTOCOL_ICMP6) {
+ Icmpv6TypeCriterion icmpv6TypeCrit =
+ (Icmpv6TypeCriterion) entry.selector().getCriterion(Type.ICMPV6_TYPE);
+ Short icmpType = (icmpv6TypeCrit == null) ? 0 : icmpv6TypeCrit.icmpv6Type();
+ Icmpv6CodeCriterion icmpv6CodeCrit =
+ (Icmpv6CodeCriterion) entry.selector().getCriterion(Type.ICMPV6_CODE);
+ Short icmpCode = (icmpv6CodeCrit == null) ? 0 : icmpv6CodeCrit.icmpv6Code();
+ dstPort = 256 * icmpType + icmpCode;
+ }
+ // If IPv4 than send IPv4 Data record
+ if ((srcIpCrit != null || dstIpCrit != null) && ethType == Ethernet.TYPE_IPV4) {
+ IpAddress srcIp = (srcIpCrit == null) ? IpAddress.valueOf(0) : srcIpCrit.ip().address();
+ IpAddress dstIp = (dstIpCrit == null) ? IpAddress.valueOf(0) : dstIpCrit.ip().address();
+ DataRecordRfwdIpv4 record = new DataRecordRfwdIpv4(
+ exporterIpv4, exporterIpv6,
+ start, end,
+ octets, packets,
+ intfIn, intfOut,
+ srcMac, dstMac,
+ ethType, vlan,
+ srcIp, dstIp,
+ ipProtocol, tos,
+ (short) srcPort, (short) dstPort);
+ List<DataRecord> recordList = new ArrayList<DataRecord>();
+ recordList.add(record);
+ ipfixManager.ipfixSender.sendRecords(DataRecordRfwdIpv4.getTemplateRecord(),
+ recordList, dpid, IpfixManager.collectorIp, IpfixManager.collectorPort);
+ }
+ // If IPv6 than send IPv6 Data record
+ if ((srcIp6Crit != null || dstIp6Crit != null) && ethType == Ethernet.TYPE_IPV6) {
+ Ip6Address srcIp6 = (srcIp6Crit == null) ?
+ Ip6Address.valueOf("0:0:0:0:0:0:0:0") : srcIp6Crit.ip().address().getIp6Address();
+ Ip6Address dstIp6 = (dstIp6Crit == null) ?
+ Ip6Address.valueOf("0:0:0:0:0:0:0:0") : dstIp6Crit.ip().address().getIp6Address();
+ DataRecordRfwdIpv6 record = new DataRecordRfwdIpv6(
+ exporterIpv4, exporterIpv6,
+ start, end,
+ octets, packets,
+ intfIn, intfOut,
+ srcMac, dstMac,
+ ethType, vlan,
+ srcIp6, dstIp6,
+ flowLabelIpv6,
+ ipProtocol, tos,
+ (short) srcPort, (short) dstPort);
+ List<DataRecord> recordList = new ArrayList<DataRecord>();
+ recordList.add(record);
+ ipfixManager.ipfixSender.sendRecords(DataRecordRfwdIpv6.getTemplateRecord(),
+ recordList, dpid, IpfixManager.collectorIp, IpfixManager.collectorPort);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/ipfix/src/main/java/org/onosproject/ipfix/IpfixManager.java b/ipfix/src/main/java/org/onosproject/ipfix/IpfixManager.java
new file mode 100644
index 0000000..a1a2be0
--- /dev/null
+++ b/ipfix/src/main/java/org/onosproject/ipfix/IpfixManager.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.ipfix;
+
+import static com.google.common.base.Strings.isNullOrEmpty;
+import static org.onlab.util.Tools.get;
+
+import java.util.Dictionary;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Modified;
+import org.apache.felix.scr.annotations.Property;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onlab.packet.IpAddress;
+import org.onosproject.app.ApplicationService;
+import org.onosproject.cfg.ComponentConfigService;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.flow.FlowRuleService;
+import org.onosproject.net.flowobjective.FlowObjectiveService;
+import org.onosproject.net.host.HostService;
+import org.onosproject.net.packet.PacketService;
+import org.onosproject.net.statistic.StatisticStore;
+import org.onosproject.net.topology.TopologyService;
+import org.osgi.service.component.ComponentContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Strings;
+
+/**
+ * OpenFlow to IPFIX Manager.
+ */
+@Component(immediate = true)
+public class IpfixManager {
+
+ protected final Logger log = LoggerFactory.getLogger(getClass());
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected TopologyService topologyService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected ApplicationService applicationService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected PacketService packetService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected HostService hostService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected DeviceService deviceService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected FlowRuleService flowRuleService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected StatisticStore statisticStore;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected FlowObjectiveService flowObjectiveService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected CoreService coreService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected ComponentConfigService cfgService;
+
+ private ApplicationId appId;
+
+ protected FlowRemovedListener flowRemovedListener = null;
+ protected PortStatsListener portStatsListener = null;
+ protected IpfixSender ipfixSender = null;
+
+ private static final boolean R_FWD_FLOWS_EXPORT = true;
+ @Property(name = "ReactiveForwardingFlowExport", boolValue = R_FWD_FLOWS_EXPORT,
+ label = "Reactive Forwarding application flows exported over IPFIX when removed")
+ private boolean reactiveForwardingFlowExport = R_FWD_FLOWS_EXPORT;
+
+ private static final boolean PORTSTATS_FLOWS_EXPORT = false;
+ @Property(name = "PortStatsFlowExport", boolValue = PORTSTATS_FLOWS_EXPORT,
+ label = "Switch Port Statistics exported over IPFIX")
+ private boolean portStatsFlowExport = PORTSTATS_FLOWS_EXPORT;
+
+ private static final String COLLECTOR_ADDRESS = "127.0.0.1";
+ @Property(name = "CollectorAddress", value = COLLECTOR_ADDRESS,
+ label = "IPFIX Collector IP Address")
+ private String collectorAddress = COLLECTOR_ADDRESS;
+ protected static IpAddress collectorIp;
+
+ private static final int COLLECTOR_PORT = 2055;
+ @Property(name = "CollectorPort", intValue = COLLECTOR_PORT,
+ label = "IPFIX Collector UDP Port")
+ protected static int collectorPort = COLLECTOR_PORT;
+
+ @Activate
+ public void activate(ComponentContext context) {
+ appId = coreService.registerApplication("net.sdnmon.of2ipfix");
+ cfgService.registerProperties(getClass());
+ getProperties(context);
+ collectorIp = IpAddress.valueOf(collectorAddress);
+ if (reactiveForwardingFlowExport) {
+ flowRemovedListener = new FlowRemovedListener(this);
+ flowRuleService.addListener(flowRemovedListener);
+ }
+ if (portStatsFlowExport) {
+ portStatsListener = new PortStatsListener(this);
+ deviceService.addListener(portStatsListener);
+ }
+ ipfixSender = new IpfixSender(this);
+ log.info("Started. reactiveForwardingFlowExport={}, portStatsFlowExport={}, IPFIX collector: ip={}, port={}",
+ reactiveForwardingFlowExport, portStatsFlowExport, collectorAddress, collectorPort);
+ }
+
+ @Deactivate
+ public void deactivate(ComponentContext context) {
+ cfgService.unregisterProperties(getClass(), false);
+ if (flowRemovedListener != null) {
+ flowRuleService.removeListener(flowRemovedListener);
+ flowRemovedListener = null;
+ }
+ if (portStatsListener != null) {
+ deviceService.removeListener(portStatsListener);
+ portStatsListener = null;
+ }
+ ipfixSender = null;
+ log.info("Stopped");
+ }
+
+ @Modified
+ public void modified(ComponentContext context) {
+ getProperties(context);
+ if (reactiveForwardingFlowExport) {
+ if (flowRemovedListener == null) {
+ flowRemovedListener = new FlowRemovedListener(this);
+ flowRuleService.addListener(flowRemovedListener);
+ }
+ } else if (flowRemovedListener != null) {
+ // reactiveForwardingFlowExport is false
+ flowRuleService.removeListener(flowRemovedListener);
+ flowRemovedListener = null;
+ }
+ if (portStatsFlowExport) {
+ if (portStatsListener == null) {
+ portStatsListener = new PortStatsListener(this);
+ deviceService.addListener(portStatsListener);
+ }
+ } else if (portStatsListener != null) {
+ // portStatsFlowExport is false
+ deviceService.removeListener(portStatsListener);
+ portStatsListener = null;
+ }
+ log.info("Modified. reactiveForwardingFlowExport={}, portStatsFlowExport={}, IPFIX collector: ip={}, port={}",
+ reactiveForwardingFlowExport, portStatsFlowExport, collectorAddress, collectorPort);
+ }
+
+ public void getProperties(ComponentContext context) {
+ Dictionary<?, ?> properties = context.getProperties();
+
+ // parse CollectorPort Property
+ String s = get(properties, "CollectorPort");
+ try {
+ collectorPort = isNullOrEmpty(s) ? collectorPort : Integer.parseInt(s.trim());
+ } catch (NumberFormatException | ClassCastException e) {
+ log.info("CollectorPort Format Exception");
+ }
+
+ // parse CollectorAddress Property
+ s = get(properties, "CollectorAddress");
+ collectorAddress = isNullOrEmpty(s) ? collectorAddress : s;
+ collectorIp = IpAddress.valueOf(collectorAddress);
+
+ // parse reactiveForwardingFlowExport Property
+ s = get(properties, "ReactiveForwardingFlowExport");
+ reactiveForwardingFlowExport = Strings.isNullOrEmpty(s) ? R_FWD_FLOWS_EXPORT : Boolean.valueOf(s);
+
+ // parse portStatsFlowExport Property
+ s = get(properties, "PortStatsFlowExport");
+ portStatsFlowExport = Strings.isNullOrEmpty(s) ? PORTSTATS_FLOWS_EXPORT : Boolean.valueOf(s);
+ }
+
+}
\ No newline at end of file
diff --git a/ipfix/src/main/java/org/onosproject/ipfix/IpfixSender.java b/ipfix/src/main/java/org/onosproject/ipfix/IpfixSender.java
new file mode 100644
index 0000000..3d4a05a
--- /dev/null
+++ b/ipfix/src/main/java/org/onosproject/ipfix/IpfixSender.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.ipfix;
+
+import java.io.IOException;
+import java.net.DatagramPacket;
+import java.net.DatagramSocket;
+import java.net.InetAddress;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+import java.util.Date;
+import java.util.List;
+
+import org.onlab.packet.IpAddress;
+import org.onosproject.ipfix.packet.DataRecord;
+import org.onosproject.ipfix.packet.HeaderException;
+import org.onosproject.ipfix.packet.MessageHeader;
+import org.onosproject.ipfix.packet.SetHeader;
+import org.onosproject.ipfix.packet.TemplateRecord;
+
+/**
+ * Sends IPFIX records.
+ */
+public class IpfixSender {
+
+ private static int seqNumber;
+ private static final int IPFIX_VERSION = 10;
+ private static final int TEMPLATE_SETID = 2;
+
+ private IpfixManager ipfixManager;
+
+ /**
+ * Creates instance of the IPFIX Sender.
+ *
+ * @param ipfixManager IpfixManager instance
+ */
+ public IpfixSender(IpfixManager ipfixManager) {
+ this.ipfixManager = ipfixManager;
+ seqNumber = 0;
+ }
+
+ /**
+ * Send IPFIX Template and coresponding list of data records.
+ *
+ * @param tr Template Record to send
+ * @param recordsList List of corresponding IPFIX records to send
+ * @param oid observation domain ID
+ * @param collector IPFIX collector IP address
+ * @param port IPFIX collector UDP port
+ */
+ public void sendRecords(TemplateRecord tr, List<DataRecord> recordsList,
+ long oid, IpAddress collector, int port) {
+
+ MessageHeader mh = new MessageHeader();
+ mh.setVersionNumber(IPFIX_VERSION);
+ mh.setObservationDomainID(oid);
+ seqNumber++;
+ mh.setSequenceNumber(seqNumber);
+ mh.setExportTime(new Date());
+
+ // Set header for the template
+ SetHeader shTemplate = new SetHeader();
+ shTemplate.setSetID(TEMPLATE_SETID);
+
+ // Add template record
+ List<TemplateRecord> trTemp = shTemplate.getTemplateRecords();
+ trTemp.add(tr);
+ shTemplate.setTemplateRecords(trTemp);
+
+ // Set header for the Data Records
+ SetHeader shData = new SetHeader();
+ shData.setSetID(tr.getTemplateID());
+
+ // Add Data records from the recordsList
+ List<DataRecord> drTemp = shData.getDataRecords();
+ for (DataRecord tempRecord : recordsList) {
+ drTemp.add(tempRecord);
+ }
+ shData.setDataRecords(drTemp);
+
+ // Make Set Headers from Template and Data
+ List<SetHeader> shTemp = mh.getSetHeaders();
+ shTemp.add(shTemplate);
+ shTemp.add(shData);
+ mh.setSetHeaders(shTemp);
+
+ // socket handling and IPFIX UDP packet sending
+ InetAddress collectorAddress = null;
+ try {
+ collectorAddress = InetAddress.getByAddress(collector.toOctets());
+ } catch (UnknownHostException e) {
+ ipfixManager.log.warn("IPFIX Collector IP address format problem: " + e.getMessage());
+ return;
+ }
+ DatagramSocket dSocket = null;
+ DatagramPacket dPacket = null;
+ try {
+ dSocket = new DatagramSocket();
+ } catch (SocketException e) {
+ ipfixManager.log.warn("IPFIX datagram socket problem: " + e.getMessage());
+ return;
+ }
+ try {
+ dPacket = new DatagramPacket(mh.getBytes(), mh.getBytes().length, collectorAddress, port);
+ } catch (HeaderException e) {
+ ipfixManager.log.warn("IPFIX datagram packet problem: " + e.getMessage());
+ dSocket.close();
+ return;
+ }
+ try {
+ dSocket.send(dPacket);
+ } catch (IOException e) {
+ ipfixManager.log.warn("IPFIX packet send IO exception: " + e.getMessage());
+ dSocket.close();
+ return;
+ }
+ dSocket.close();
+ }
+
+}
diff --git a/ipfix/src/main/java/org/onosproject/ipfix/PortStatsListener.java b/ipfix/src/main/java/org/onosproject/ipfix/PortStatsListener.java
new file mode 100644
index 0000000..87e78b2
--- /dev/null
+++ b/ipfix/src/main/java/org/onosproject/ipfix/PortStatsListener.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.ipfix;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+import org.onlab.packet.Ip6Address;
+import org.onlab.packet.IpAddress;
+import org.onosproject.ipfix.packet.DataRecord;
+import org.onosproject.ipfix.packet.TemplateRecord;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.Device;
+import org.onosproject.net.MastershipRole;
+import org.onosproject.net.device.DeviceEvent;
+import org.onosproject.net.device.DeviceListener;
+import org.onosproject.net.device.PortStatistics;
+import org.onosproject.openflow.controller.Dpid;
+
+import com.google.common.primitives.Longs;
+
+/**
+ * Internal PortStats Listener.
+ * Exports IPFIX statistics when PortStats are updated.
+ */
+public class PortStatsListener implements DeviceListener {
+
+ public Map<ConnectPoint, List<Long>> prevStatsMap = new HashMap<ConnectPoint, List<Long>>();
+
+ private IpfixManager ipfixManager;
+
+ /**
+ * Internal PortStats Listener.
+ * Exports IPFIX statistics when PortStats are updated.
+ *
+ * @param ipfixManager ipfix manager instance
+ */
+ public PortStatsListener(IpfixManager ipfixManager) {
+ this.ipfixManager = ipfixManager;
+ }
+
+ @Override
+ public void event(DeviceEvent event) {
+
+ Device device = event.subject();
+
+ switch (event.type()) {
+ case PORT_STATS_UPDATED:
+ if (ipfixManager.deviceService.getRole(device.id()) == MastershipRole.MASTER) {
+ ipfixManager.log.trace("PortStats Updated: I am MASTER for deviceId={}", device.id().toString());
+
+ List<DataRecord> recordsInList = new ArrayList<DataRecord>();
+ List<DataRecord> recordsOutList = new ArrayList<DataRecord>();
+
+ IpAddress exporterIpv4 =
+ IpAddress.valueOf(device.annotations().toString().split("=")[2].split(":")[0]);
+ long dpid = Dpid.dpid(device.id().uri()).value();
+ byte[] byteExporterIpv6 = new byte[16];
+ System.arraycopy(Longs.toByteArray(0), 0, byteExporterIpv6, 0, 8);
+ System.arraycopy(Longs.toByteArray(dpid), 0, byteExporterIpv6, 8, 8);
+ Ip6Address exporterIpv6 = Ip6Address.valueOf(byteExporterIpv6);
+
+ for (PortStatistics stat : ipfixManager.deviceService.getPortDeltaStatistics(device.id())) {
+
+ final String format = "PortStatsListener Delta Stats: port={}, pktRx={}, pktTx={},"
+ + "bytesRx={}, bytesTx={}, pktRxDrp={}, pktTxDrp={}, Dur={}.{}";
+ ipfixManager.log.trace(format, stat.port(), stat.packetsReceived(), stat.packetsSent(),
+ stat.bytesReceived(), stat.bytesSent(),
+ stat.packetsRxDropped(), stat.packetsTxDropped(),
+ stat.durationSec(), (stat.durationNano() / 1000000));
+
+ long inBytes = stat.bytesReceived();
+ long inPackets = stat.packetsReceived();
+ long outBytes = stat.bytesSent();
+ long outPackets = stat.packetsSent();
+ long endTime = System.currentTimeMillis();
+ long startTime = endTime - (stat.durationSec() * TimeUnit.SECONDS.toMillis(1) +
+ stat.durationNano() / TimeUnit.MILLISECONDS.toNanos(1));
+
+ DataRecordPortStatsIn recordIn = new DataRecordPortStatsIn(exporterIpv4, exporterIpv6,
+ stat.port(), inBytes, inPackets, startTime, endTime);
+ DataRecordPortStatsOut recordOut = new DataRecordPortStatsOut(exporterIpv4, exporterIpv6,
+ stat.port(), outBytes, outPackets, startTime, endTime);
+
+ recordsInList.add(recordIn);
+ recordsOutList.add(recordOut);
+ }
+
+ if (recordsInList.isEmpty() && recordsOutList.isEmpty()) {
+ ipfixManager.log.trace("PortStats: Previous PortStats for device={} where zero,"
+ + "not sending IPFIX flow", device.id());
+ } else {
+ TemplateRecord trIn = DataRecordPortStatsIn.portStatsInTemplateRecord();
+ TemplateRecord trOut = DataRecordPortStatsOut.portStatsOutTemplateRecord();
+ ipfixManager.ipfixSender.sendRecords(trIn, recordsInList, dpid,
+ IpfixManager.collectorIp, IpfixManager.collectorPort);
+ ipfixManager.ipfixSender.sendRecords(trOut, recordsOutList, dpid,
+ IpfixManager.collectorIp, IpfixManager.collectorPort);
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+}
diff --git a/ipfix/src/main/java/org/onosproject/ipfix/packet/AbstractIpfixPacketEntity.java b/ipfix/src/main/java/org/onosproject/ipfix/packet/AbstractIpfixPacketEntity.java
new file mode 100644
index 0000000..8c5413f
--- /dev/null
+++ b/ipfix/src/main/java/org/onosproject/ipfix/packet/AbstractIpfixPacketEntity.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.ipfix.packet;
+
+/**
+ * Abstract entity of the IPFIX Packet entities.
+ * Used for Records and Headers of variable length.
+ */
+public abstract class AbstractIpfixPacketEntity {
+
+ protected int length;
+
+ /**
+ * Sets the length of the IPFIX entity.
+ *
+ * @param length length of the entity
+ */
+ protected void setLength(int length) {
+ this.length = length;
+ }
+
+ /**
+ * Returns the length of the IPFIX entity.
+ *
+ * @return length length of the entity
+ */
+ public int getLength() {
+ return length;
+ }
+}
diff --git a/ipfix/src/main/java/org/onosproject/ipfix/packet/AbstractIpfixPacketInterface.java b/ipfix/src/main/java/org/onosproject/ipfix/packet/AbstractIpfixPacketInterface.java
new file mode 100644
index 0000000..cec3bbe
--- /dev/null
+++ b/ipfix/src/main/java/org/onosproject/ipfix/packet/AbstractIpfixPacketInterface.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package org.onosproject.ipfix.packet;
+
+/**
+ * Abstract Interface for IPFIX entities.
+ * Used for Records, Headers and Information Elements.
+ */
+public interface AbstractIpfixPacketInterface {
+
+ /**
+ * Returns string representation of the IPFIX entity.
+ *
+ * @return String String representation of the IPFIX entity
+ */
+ public String toString();
+
+ /**
+ * Returns byte array of the IPFIX entity.
+ * Used for IPFIX packet serialization.
+ *
+ * @return byte[] byte array of the IPFIX entity
+ * @throws HeaderException header exception
+ */
+ public byte[] getBytes() throws HeaderException;
+}
diff --git a/ipfix/src/main/java/org/onosproject/ipfix/packet/DataRecord.java b/ipfix/src/main/java/org/onosproject/ipfix/packet/DataRecord.java
new file mode 100644
index 0000000..37b65b0
--- /dev/null
+++ b/ipfix/src/main/java/org/onosproject/ipfix/packet/DataRecord.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.ipfix.packet;
+
+/**
+ * IPFIX Data Record entity.
+ */
+public abstract class DataRecord extends Record {
+}
diff --git a/ipfix/src/main/java/org/onosproject/ipfix/packet/HeaderException.java b/ipfix/src/main/java/org/onosproject/ipfix/packet/HeaderException.java
new file mode 100644
index 0000000..504fd40
--- /dev/null
+++ b/ipfix/src/main/java/org/onosproject/ipfix/packet/HeaderException.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.ipfix.packet;
+
+/**
+ * Exception for IPFIX Header parsing or serialization.
+ */
+public class HeaderException extends Exception {
+
+ private static final long serialVersionUID = -2628460416419275548L;
+
+ public HeaderException(String msg) {
+ super(msg);
+ }
+}
diff --git a/ipfix/src/main/java/org/onosproject/ipfix/packet/Ie.java b/ipfix/src/main/java/org/onosproject/ipfix/packet/Ie.java
new file mode 100644
index 0000000..494a810
--- /dev/null
+++ b/ipfix/src/main/java/org/onosproject/ipfix/packet/Ie.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.ipfix.packet;
+
+/**
+ * IPFIX Information Elements from IANA.
+ */
+public enum Ie {
+ octetDeltaCount(1, 8),
+ packetDeltaCount(2, 8),
+ protocolIdentifier(4, 1),
+ ipClassOfService(5, 1),
+ sourceTransportPort(7, 2),
+ sourceIPv4Address(8, 4),
+ ingressInterface(10, 4),
+ destinationTransportPort(11, 2),
+ destinationIPv4Address(12, 4),
+ egressInterface(14, 4),
+ sourceIPv6Address(27, 16),
+ destinationIPv6Address(28, 16),
+ flowLabelIPv6(31, 4),
+ sourceMacAddress(56, 6),
+ vlanId(58, 2),
+ destinationMacAddress(80, 6),
+ exporterIPv4Address(130, 4),
+ exporterIPv6Address(131, 16),
+ droppedOctetDeltaCount(132, 8),
+ droppedPacketDeltaCount(133, 8),
+ flowStartMilliseconds(152, 8),
+ flowEndMilliseconds(153, 8),
+ ethernetType(256, 2);
+
+ /**
+ * IPFIX Information Element IDs from IANA.
+ * Available at: http://www.iana.org/assignments/ipfix/ipfix.xhtml.
+ */
+ private final int id;
+
+ /**
+ * IPFIX Information Element length in bytes.
+ */
+ private final int length;
+
+ /**
+ * IPFIX Information Element constructor.
+ *
+ * @param id IPFIX Information Element id
+ * @param length IPFIX Information Element length in bytes
+ */
+ private Ie(int id, int length) {
+ this.id = id;
+ this.length = length;
+ }
+
+ public int getId() {
+ return id;
+ }
+
+ public int getLength() {
+ return length;
+ }
+}
\ No newline at end of file
diff --git a/ipfix/src/main/java/org/onosproject/ipfix/packet/InformationElement.java b/ipfix/src/main/java/org/onosproject/ipfix/packet/InformationElement.java
new file mode 100644
index 0000000..788acb3
--- /dev/null
+++ b/ipfix/src/main/java/org/onosproject/ipfix/packet/InformationElement.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.ipfix.packet;
+
+import com.google.common.primitives.Ints;
+import com.google.common.primitives.Shorts;
+
+/**
+ * IPFIX Information Element entity.
+ */
+public class InformationElement implements AbstractIpfixPacketInterface {
+ public static final int LENGTH = 4;
+
+ private int informationElementID;
+ private int fieldLength;
+
+ /**
+ * @param ie Information Element construtor from Ie Enum.
+ */
+ public InformationElement(Ie ie) {
+ this.informationElementID = ie.getId();
+ this.fieldLength = ie.getLength();
+ }
+
+ /**
+ * Information Element construtor from blank.
+ */
+ public InformationElement() {
+ this.informationElementID = 0;
+ this.fieldLength = 0;
+ }
+
+ public int getInformationElementID() {
+ return informationElementID;
+ }
+
+ public void setInformationElementID(int informationElementID) {
+ this.informationElementID = informationElementID;
+ }
+
+ public int getFieldLength() {
+ return fieldLength;
+ }
+
+ public void setFieldLength(int fieldLength) {
+ this.fieldLength = fieldLength;
+ }
+
+ public static InformationElement parse(byte[] data) throws HeaderException {
+ try {
+ if (data.length < LENGTH) {
+ throw new HeaderException("Data array too short.");
+ }
+ InformationElement ie = new InformationElement();
+ // information element ID
+ byte[] informationElementID = new byte[2];
+ System.arraycopy(data, 0, informationElementID, 0, 2);
+ ie.setInformationElementID(Ints.fromByteArray(informationElementID));
+ // field length
+ byte[] fieldLength = new byte[2];
+ System.arraycopy(data, 2, fieldLength, 0, 2);
+ ie.setFieldLength(Ints.fromByteArray(fieldLength));
+ return ie;
+ } catch (Exception e) {
+ throw new HeaderException("Parse error: " + e.getMessage());
+ }
+ }
+
+ public byte[] getBytes() throws HeaderException {
+ try {
+ byte[] data = new byte[LENGTH];
+ // information element ID
+ System.arraycopy(Shorts.toByteArray((short) getInformationElementID()), 0, data, 0, 2);
+ // field length
+ System.arraycopy(Shorts.toByteArray((short) getFieldLength()), 0, data, 2, 2);
+ return data;
+ } catch (Exception e) {
+ throw new HeaderException("Error while generating the bytes: " + e.getMessage());
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("[InformationElement]: ");
+ sb.append("ID: ");
+ sb.append(informationElementID);
+ sb.append(", ");
+ sb.append("Field length: ");
+ sb.append(fieldLength);
+ return sb.toString();
+ }
+}
diff --git a/ipfix/src/main/java/org/onosproject/ipfix/packet/MessageHeader.java b/ipfix/src/main/java/org/onosproject/ipfix/packet/MessageHeader.java
new file mode 100644
index 0000000..78db882
--- /dev/null
+++ b/ipfix/src/main/java/org/onosproject/ipfix/packet/MessageHeader.java
@@ -0,0 +1,214 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.ipfix.packet;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.google.common.primitives.Ints;
+import com.google.common.primitives.Longs;
+import com.google.common.primitives.Shorts;
+
+/**
+ * IPFIX Message Header.
+ */
+public class MessageHeader extends AbstractIpfixPacketEntity implements AbstractIpfixPacketInterface {
+ private static final Logger LOGGER = Logger.getLogger(SetHeader.class.getName());
+ private static final int HEADER_LENGTH = 16;
+ private static final int IPFIX_VERSION = 10;
+
+ private int versionNumber;
+ private int length;
+ private Date exportTime;
+ private long sequenceNumber;
+ private long observationDomainID;
+ private List<SetHeader> setHeaders = new ArrayList<SetHeader>();
+
+ /**
+ * Message Header format.
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Version Number | Length |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Export Time |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Sequence Number |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Observation Domain ID |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ */
+
+ public int getVersionNumber() {
+ return IPFIX_VERSION;
+ }
+
+ public void setVersionNumber(int versionNumber) {
+ this.versionNumber = versionNumber;
+ }
+
+ public int getLength() {
+ return length;
+ }
+
+ public void setLength(int length) {
+ this.length = length;
+ }
+
+ public Date getExportTime() {
+ return exportTime;
+ }
+
+ public void setExportTime(Date exportTime) {
+ this.exportTime = exportTime;
+ }
+
+ public long getSequenceNumber() {
+ return sequenceNumber;
+ }
+
+ public void setSequenceNumber(long sequenceNumber) {
+ this.sequenceNumber = sequenceNumber;
+ }
+
+ public long getObservationDomainID() {
+ return observationDomainID;
+ }
+
+ public void setObservationDomainID(long observationDomainID) {
+ this.observationDomainID = observationDomainID;
+ }
+
+ public List<SetHeader> getSetHeaders() {
+ return setHeaders;
+ }
+
+ public void setSetHeaders(List<SetHeader> setHeaders) {
+ this.setHeaders = setHeaders;
+ int length = HEADER_LENGTH;
+ for (SetHeader header : setHeaders) {
+ length += header.getLength();
+ }
+ setLength(length);
+ }
+
+ public static MessageHeader parse(byte[] data) throws HeaderException {
+ try {
+ if (data.length < HEADER_LENGTH) {
+ throw new HeaderException("Data array too short.");
+ }
+ MessageHeader mh = new MessageHeader();
+ // version number
+ byte[] versionNumber = new byte[2];
+ System.arraycopy(data, 0, versionNumber, 0, 2);
+ mh.setVersionNumber(Ints.fromByteArray(versionNumber));
+ // length
+ byte[] length = new byte[2];
+ System.arraycopy(data, 2, length, 0, 2);
+ mh.setLength(Ints.fromByteArray(length));
+ // export time
+ byte[] exportTime = new byte[4];
+ System.arraycopy(data, 4, exportTime, 0, 4);
+ long secondsSinceEpoche = Longs.fromByteArray(exportTime);
+ long milliSecondsSinceEpoche = secondsSinceEpoche * 1000;
+ mh.setExportTime(new Date(milliSecondsSinceEpoche));
+ // sequence number
+ byte[] sequenceNumber = new byte[4];
+ System.arraycopy(data, 8, sequenceNumber, 0, 4);
+ mh.setSequenceNumber(Longs.fromByteArray(sequenceNumber));
+ // observation domain id
+ byte[] observationDomainID = new byte[4];
+ System.arraycopy(data, 12, observationDomainID, 0, 4);
+ mh.setObservationDomainID(Longs.fromByteArray(observationDomainID));
+ // set header
+ int offset = HEADER_LENGTH;
+
+ while ((mh.getLength() - offset) > 0) {
+ byte[] subData = new byte[mh.getLength() - offset];
+ System.arraycopy(data, offset, subData, 0, subData.length);
+ SetHeader sh = SetHeader.parse(subData);
+ mh.getSetHeaders().add(sh);
+ offset += sh.getLength();
+ }
+ if ((mh.getLength() - offset) != 0) {
+ LOGGER.log(Level.INFO, "Unused bytes: " + (mh.getLength() - offset));
+ }
+ return mh;
+ } catch (Exception e) {
+ throw new HeaderException("Parse error: " + e.getMessage());
+ }
+ }
+
+ public byte[] getBytes() throws HeaderException {
+ try {
+ byte[] data = new byte[length];
+ // version number
+ System.arraycopy(Shorts.toByteArray((short) versionNumber), 0, data, 0, 2);
+ // length
+ System.arraycopy(Shorts.toByteArray((short) length), 0, data, 2, 2);
+ // export time
+ System.arraycopy(Ints.toByteArray((int) exportTime.getTime() / 1000), 0, data, 4, 4);
+ // sequence number
+ System.arraycopy(Ints.toByteArray((int) sequenceNumber), 0, data, 8, 4);
+ // observation domain id
+ System.arraycopy(Ints.toByteArray((int) observationDomainID), 0, data, 12, 4);
+ // set header
+ int offset = HEADER_LENGTH;
+ for (SetHeader sh : setHeaders) {
+ byte[] temp = sh.getBytes();
+ System.arraycopy(temp, 0, data, offset, temp.length);
+ offset += temp.length;
+ }
+ return data;
+ } catch (Exception e) {
+ e.printStackTrace();
+ throw new HeaderException("Error while generating the bytes: " + e.getMessage());
+ }
+ }
+
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("[MessageHeader]: ");
+ sb.append("Version Number: ");
+ sb.append(versionNumber);
+ sb.append(", ");
+ sb.append("Length: ");
+ sb.append(length);
+ sb.append(", ");
+ sb.append("Export time: ");
+ sb.append(exportTime);
+ sb.append(", ");
+ sb.append("Sequence number: ");
+ sb.append(sequenceNumber);
+ sb.append(", ");
+ sb.append("Observation Domain ID: ");
+ sb.append(observationDomainID);
+ sb.append(", ");
+ sb.append("SetHeaders: ");
+ sb.append(setHeaders.size());
+ sb.append(", ");
+ for (SetHeader sh : setHeaders) {
+ sb.append(sh);
+ sb.append(", ");
+ }
+ return sb.toString();
+ }
+}
diff --git a/ipfix/src/main/java/org/onosproject/ipfix/packet/OptionTemplateRecord.java b/ipfix/src/main/java/org/onosproject/ipfix/packet/OptionTemplateRecord.java
new file mode 100644
index 0000000..fafad48
--- /dev/null
+++ b/ipfix/src/main/java/org/onosproject/ipfix/packet/OptionTemplateRecord.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.ipfix.packet;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.google.common.primitives.Ints;
+import com.google.common.primitives.Shorts;
+
+/**
+ * IPFIX Option Template Header.
+ */
+public class OptionTemplateRecord extends Record {
+ private static final int HEADER_LENGTH = 6;
+
+ private int templateID;
+ private int fieldCount;
+ private int scopeFieldCount;
+ private List<InformationElement> informationElements = new ArrayList<InformationElement>();
+ private List<InformationElement> scopeInformationElements = new ArrayList<InformationElement>();
+
+ /**
+ * Option Template Header format.
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Template ID (larger than 255) | Field Count |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Scope Field Count |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ */
+
+ public int getTemplateID() {
+ return templateID;
+ }
+
+ public void setTemplateID(int templateID) {
+ this.templateID = templateID;
+ }
+
+ public int getFieldCount() {
+ return fieldCount;
+ }
+
+ public void setFieldCount(int fieldCount) {
+ this.fieldCount = fieldCount;
+ }
+
+ public int getScopeFieldCount() {
+ return scopeFieldCount;
+ }
+
+ public void setScopeFieldCount(int scopeFieldCount) {
+ this.scopeFieldCount = scopeFieldCount;
+ }
+
+ public List<InformationElement> getInformationElements() {
+ return informationElements;
+ }
+
+ public void setInformationElements(List<InformationElement> informationElements) {
+ this.informationElements = informationElements;
+ }
+
+ public List<InformationElement> getScopeInformationElements() {
+ return scopeInformationElements;
+ }
+
+ public void setScopeInformationElements(List<InformationElement> scopeInformationElements) {
+ this.scopeInformationElements = scopeInformationElements;
+ }
+
+ public static OptionTemplateRecord parse(byte[] data) throws HeaderException {
+ try {
+ if (data.length < HEADER_LENGTH) {
+ throw new HeaderException("Data array too short.");
+ }
+ OptionTemplateRecord otr = new OptionTemplateRecord();
+ // template ID
+ byte[] templateID = new byte[2];
+ System.arraycopy(data, 0, templateID, 0, 2);
+ otr.setTemplateID(Ints.fromByteArray(templateID));
+ // field count
+ byte[] fieldCount = new byte[2];
+ System.arraycopy(data, 2, fieldCount, 0, 2);
+ otr.setFieldCount(Ints.fromByteArray(fieldCount));
+ // scope field count
+ byte[] scopeFieldCount = new byte[2];
+ System.arraycopy(data, 4, scopeFieldCount, 0, 2);
+ otr.setScopeFieldCount(Ints.fromByteArray(scopeFieldCount));
+ int offset = HEADER_LENGTH;
+ for (int i = 0; i < otr.getFieldCount(); i++) {
+ byte[] subData = new byte[InformationElement.LENGTH];
+ System.arraycopy(data, offset +
+ (i * InformationElement.LENGTH), subData, 0, InformationElement.LENGTH);
+ InformationElement ie = InformationElement.parse(subData);
+ if (i < otr.getScopeFieldCount()) {
+ otr.getScopeInformationElements().add(ie);
+ } else {
+ otr.getInformationElements().add(ie);
+ }
+ }
+ return otr;
+ } catch (Exception e) {
+ throw new HeaderException("Parse error: " + e.getMessage());
+ }
+ }
+
+ public byte[] getBytes() throws HeaderException {
+ try {
+ int length = HEADER_LENGTH + (scopeInformationElements.size() * InformationElement.LENGTH) +
+ (informationElements.size() * InformationElement.LENGTH);
+ if (length % 4 != 0) {
+ length += (length % 4); // padding
+ }
+ byte[] data = new byte[length];
+ // template ID
+ System.arraycopy(Shorts.toByteArray((short) getTemplateID()), 0, data, 0, 2);
+ // field count
+ System.arraycopy(Shorts.toByteArray((short) getFieldCount()), 0, data, 2, 2);
+ // scope field count
+ System.arraycopy(Shorts.toByteArray((short) getScopeFieldCount()), 0, data, 4, 2);
+ // information elements
+ int offset = HEADER_LENGTH;
+ for (InformationElement ie : scopeInformationElements) {
+ System.arraycopy(ie.getBytes(), 0, data, offset, InformationElement.LENGTH);
+ offset += InformationElement.LENGTH;
+ }
+ for (InformationElement ie : informationElements) {
+ System.arraycopy(ie.getBytes(), 0, data, offset, InformationElement.LENGTH);
+ offset += InformationElement.LENGTH;
+ }
+ return data;
+ } catch (Exception e) {
+ throw new HeaderException("Error while generating the bytes: " + e.getMessage());
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("[OptionTemplateRecord]: ");
+ sb.append("Template ID: ");
+ sb.append(templateID);
+ sb.append(", Field count: ");
+ sb.append(fieldCount);
+ sb.append(", Scope field count: ");
+ sb.append(scopeFieldCount);
+ sb.append(", Information elements: ");
+ sb.append(informationElements);
+ sb.append(", Scope Information elements: ");
+ sb.append(scopeInformationElements.size());
+ sb.append(", ");
+ for (InformationElement ie : informationElements) {
+ sb.append(ie);
+ sb.append(", ");
+ }
+ return sb.toString();
+ }
+}
diff --git a/ipfix/src/main/java/org/onosproject/ipfix/packet/Record.java b/ipfix/src/main/java/org/onosproject/ipfix/packet/Record.java
new file mode 100644
index 0000000..dcafcc8
--- /dev/null
+++ b/ipfix/src/main/java/org/onosproject/ipfix/packet/Record.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.ipfix.packet;
+
+/**
+ * IPFIX Record entity.
+ */
+public abstract class Record extends AbstractIpfixPacketEntity implements AbstractIpfixPacketInterface {
+}
diff --git a/ipfix/src/main/java/org/onosproject/ipfix/packet/SamplingDataRecord.java b/ipfix/src/main/java/org/onosproject/ipfix/packet/SamplingDataRecord.java
new file mode 100644
index 0000000..df9fd11
--- /dev/null
+++ b/ipfix/src/main/java/org/onosproject/ipfix/packet/SamplingDataRecord.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.ipfix.packet;
+
+import com.google.common.primitives.Ints;
+import com.google.common.primitives.Longs;
+
+/**
+ * IPFIX Sampling Data Record.
+ */
+public class SamplingDataRecord extends DataRecord {
+ private static final int HEADER_LENGTH = 16; // 2 bytes padding
+
+ private long observationDomainId;
+ private int selectorAlgorithm;
+ private long samplingPacketInterval;
+ private long samplingPacketSpace;
+
+ public long getObservationDomainId() {
+ return observationDomainId;
+ }
+
+ public void setObservationDomainId(long observationDomainId) {
+ this.observationDomainId = observationDomainId;
+ }
+
+ public int getSelectorAlgorithm() {
+ return selectorAlgorithm;
+ }
+
+ public void setSelectorAlgorithm(int selectorAlgorithm) {
+ this.selectorAlgorithm = selectorAlgorithm;
+ }
+
+ public long getSamplingPacketInterval() {
+ return samplingPacketInterval;
+ }
+
+ public void setSamplingPacketInterval(long samplingPacketInterval) {
+ this.samplingPacketInterval = samplingPacketInterval;
+ }
+
+ public long getSamplingPacketSpace() {
+ return samplingPacketSpace;
+ }
+
+ public void setSamplingPacketSpace(long samplingPacketSpace) {
+ this.samplingPacketSpace = samplingPacketSpace;
+ }
+
+ public static SamplingDataRecord parse(byte[] data) throws HeaderException {
+ try {
+ if (data.length < HEADER_LENGTH) {
+ throw new HeaderException("Data array too short.");
+ }
+ SamplingDataRecord sdr = new SamplingDataRecord();
+ // observationDomainId
+ byte[] observationDomainId = new byte[4];
+ System.arraycopy(data, 0, observationDomainId, 0, 4);
+ sdr.setObservationDomainId(Longs.fromByteArray(observationDomainId));
+ // selectorAlgorithm
+ byte[] selectorAlgorithm = new byte[2];
+ System.arraycopy(data, 4, selectorAlgorithm, 0, 2);
+ sdr.setSelectorAlgorithm(Ints.fromByteArray(selectorAlgorithm));
+ // samplingPacketInterval
+ byte[] samplingPacketInterval = new byte[4];
+ System.arraycopy(data, 6, samplingPacketInterval, 0, 4);
+ sdr.setSamplingPacketInterval(Longs.fromByteArray(samplingPacketInterval));
+ // samplingPacketSpace
+ byte[] samplingPacketSpace = new byte[4];
+ System.arraycopy(data, 10, samplingPacketSpace, 0, 4);
+ sdr.setSamplingPacketSpace(Longs.fromByteArray(samplingPacketSpace));
+ return sdr;
+ } catch (Exception e) {
+ throw new HeaderException("Parse error: " + e.getMessage());
+ }
+ }
+
+ public byte[] getBytes() throws HeaderException {
+ try {
+ byte[] data = new byte[HEADER_LENGTH];
+ // observationDomainId
+ System.arraycopy(Ints.toByteArray((int) getObservationDomainId()), 0, data, 0, 4);
+ // selectorAlgorithm
+ System.arraycopy(Ints.toByteArray((int) getSelectorAlgorithm()), 0, data, 4, 2);
+ // samplingPacketInterval
+ System.arraycopy(Ints.toByteArray((int) getSamplingPacketInterval()), 0, data, 6, 4);
+ // samplingPacketSpace
+ System.arraycopy(Ints.toByteArray((int) getSamplingPacketSpace()), 0, data, 10, 4);
+ return data;
+ } catch (Exception e) {
+ throw new HeaderException("Error while generating the bytes: " + e.getMessage());
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("[SamplingDataRecord]: ");
+ sb.append(" Observation domain ID: ");
+ sb.append(observationDomainId);
+ sb.append(", Selector algorithm: ");
+ sb.append(selectorAlgorithm);
+ sb.append(", Sampling packet interval: ");
+ sb.append(samplingPacketInterval);
+ sb.append(", Sampling packet space: ");
+ sb.append(samplingPacketSpace);
+ return sb.toString();
+ }
+}
diff --git a/ipfix/src/main/java/org/onosproject/ipfix/packet/SetHeader.java b/ipfix/src/main/java/org/onosproject/ipfix/packet/SetHeader.java
new file mode 100644
index 0000000..ee90d54
--- /dev/null
+++ b/ipfix/src/main/java/org/onosproject/ipfix/packet/SetHeader.java
@@ -0,0 +1,231 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.ipfix.packet;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.google.common.primitives.Ints;
+import com.google.common.primitives.Shorts;
+
+/**
+ * IPFIX Set Header format.
+ */
+public class SetHeader extends AbstractIpfixPacketEntity implements AbstractIpfixPacketInterface {
+ private static final Logger LOGGER = Logger.getLogger(SetHeader.class.getName());
+ private static final int HEADER_LENGTH = 4;
+ private static final int TEMPLATE_SET_ID = 2;
+ private static final int OPTION_SET_ID = 3;
+
+ private int setID;
+ private int length;
+ private List<DataRecord> dataRecords = new ArrayList<DataRecord>();
+ private List<TemplateRecord> templateRecords = new ArrayList<TemplateRecord>();
+ private List<OptionTemplateRecord> optionTemplateRecords = new ArrayList<OptionTemplateRecord>();
+
+ /**
+ * Set Header format.
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Set ID | Length |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ */
+
+ /**
+ * A value of 2 is reserved for Template Sets. A value of 3 is reserved
+ * for Options Template Sets. Values from 4 to 255 are reserved for
+ * future use. Values 256 and above are used for Data Sets. The Set ID
+ * values of 0 and 1 are not used, for historical reasons.
+ *
+ * @return Set ID
+ */
+ public int getSetID() {
+ return setID;
+ }
+
+ public void setSetID(int setID) {
+ this.setID = setID;
+ }
+
+ public int getLength() {
+ return length;
+ }
+
+ public void setLength(int length) {
+ this.length = length;
+ }
+
+ public List<DataRecord> getDataRecords() {
+ return dataRecords;
+ }
+
+ public void setDataRecords(List<DataRecord> dataRecords) {
+ this.dataRecords = dataRecords;
+ updateLength();
+ }
+
+ public List<TemplateRecord> getTemplateRecords() {
+ return templateRecords;
+ }
+
+ public void setTemplateRecords(List<TemplateRecord> templateRecords) {
+ this.templateRecords = templateRecords;
+ updateLength();
+ }
+
+ public List<OptionTemplateRecord> getOptionTemplateRecords() {
+ return optionTemplateRecords;
+ }
+
+ public void setOptionTemplateRecords(List<OptionTemplateRecord> optionTemplateRecords) {
+ this.optionTemplateRecords = optionTemplateRecords;
+ updateLength();
+ }
+
+ /**
+ * Updates SetHeaderlength when new records are set/added.
+ */
+ private void updateLength() {
+ int newLength = 0;
+ for (TemplateRecord template : templateRecords) {
+ newLength += template.getLength();
+ }
+ for (OptionTemplateRecord optionTemplate : optionTemplateRecords) {
+ newLength += optionTemplate.getLength();
+ }
+ for (DataRecord record : dataRecords) {
+ newLength += record.getLength();
+ }
+ this.length = newLength + HEADER_LENGTH;
+ }
+
+ public static SetHeader parse(byte[] data) throws HeaderException {
+ try {
+ if (data.length < HEADER_LENGTH) {
+ throw new HeaderException("Data array too short.");
+ }
+ SetHeader sh = new SetHeader();
+ // set id
+ byte[] setID = new byte[2];
+ System.arraycopy(data, 0, setID, 0, 2);
+ sh.setSetID(Ints.fromByteArray(setID));
+ // length
+ byte[] length = new byte[2];
+ System.arraycopy(data, 2, length, 0, 2);
+ sh.setLength(Ints.fromByteArray(length));
+ // 2 -> template sets;
+ if (sh.getSetID() == TEMPLATE_SET_ID) {
+ int offset = HEADER_LENGTH;
+ byte[] subData = new byte[sh.getLength() - offset];
+ System.arraycopy(data, offset, subData, 0, subData.length);
+ TemplateRecord tr = TemplateRecord.parse(subData);
+ sh.getTemplateRecords().add(tr);
+ } else if (sh.getSetID() == OPTION_SET_ID) { // 3 -> template option sets
+ int offset = HEADER_LENGTH;
+ byte[] subData = new byte[sh.getLength() - offset];
+ System.arraycopy(data, offset, subData, 0, subData.length);
+ OptionTemplateRecord otr = OptionTemplateRecord.parse(subData);
+ sh.getOptionTemplateRecords().add(otr);
+ } else if (sh.getSetID() == 256) { // > 256 -> data record;
+ int offset = HEADER_LENGTH;
+ byte[] subData = new byte[sh.getLength() - offset];
+ System.arraycopy(data, offset, subData, 0, subData.length);
+ SamplingDataRecord sdr = SamplingDataRecord.parse(subData);
+ sh.getDataRecords().add(sdr);
+ } else {
+ LOGGER.log(Level.INFO, "Set ID " + sh.getSetID() + " is unknown and not handled");
+ }
+ return sh;
+ } catch (Exception e) {
+ throw new HeaderException("Parse error: " + e.getMessage());
+ }
+ }
+
+ public byte[] getBytes() throws HeaderException {
+ try {
+ int length = HEADER_LENGTH;
+ for (DataRecord dr : dataRecords) {
+ length += dr.getLength();
+ }
+ for (TemplateRecord tr : templateRecords) {
+ length += tr.getLength();
+ }
+ for (OptionTemplateRecord otr : optionTemplateRecords) {
+ length += otr.getLength();
+ }
+ byte[] data = new byte[length];
+ // set id
+ System.arraycopy(Shorts.toByteArray((short) getSetID()), 0, data, 0, 2);
+ // length
+ System.arraycopy(Shorts.toByteArray((short) getLength()), 0, data, 2, 2);
+ // data record
+ int offset = HEADER_LENGTH;
+ for (DataRecord record : dataRecords) {
+ System.arraycopy(record.getBytes(), 0, data, offset, record.getLength());
+ offset += record.getLength();
+ }
+ for (TemplateRecord record : templateRecords) {
+ byte[] recordData = record.getBytes();
+ System.arraycopy(recordData, 0, data, offset, record.getLength());
+ offset += recordData.length;
+ }
+ for (OptionTemplateRecord record : optionTemplateRecords) {
+ byte[] recordData = record.getBytes();
+ System.arraycopy(recordData, 0, data, offset, record.getLength());
+ offset += recordData.length;
+ }
+ return data;
+ } catch (Exception e) {
+ throw new HeaderException("Error while generating the bytes: " + e.getMessage());
+ }
+ }
+
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("[SetHeader]: ");
+ sb.append("Set ID: ");
+ sb.append(setID);
+ sb.append(", Length: ");
+ sb.append(length);
+ sb.append(", Data records: ");
+ sb.append(dataRecords.size());
+ sb.append(", ");
+ for (DataRecord record : dataRecords) {
+ sb.append(record);
+ sb.append(", ");
+ }
+ sb.append("Template records: ");
+ sb.append(templateRecords.size());
+ sb.append(", ");
+ for (TemplateRecord record : templateRecords) {
+ sb.append(record);
+ sb.append(", ");
+ }
+ sb.append("Option template records: ");
+ sb.append(optionTemplateRecords.size());
+ sb.append(", ");
+ for (OptionTemplateRecord record : optionTemplateRecords) {
+ sb.append(record);
+ sb.append(", ");
+ }
+ return sb.toString();
+ }
+}
\ No newline at end of file
diff --git a/ipfix/src/main/java/org/onosproject/ipfix/packet/TemplateRecord.java b/ipfix/src/main/java/org/onosproject/ipfix/packet/TemplateRecord.java
new file mode 100644
index 0000000..12ea916
--- /dev/null
+++ b/ipfix/src/main/java/org/onosproject/ipfix/packet/TemplateRecord.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.ipfix.packet;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.google.common.primitives.Ints;
+import com.google.common.primitives.Shorts;
+
+/**
+ * IPFIX Template Record header.
+ */
+public class TemplateRecord extends Record {
+ private static final int HEADER_LENGTH = 4;
+ private int templateID;
+ private int fieldCount;
+ private List<InformationElement> informationElements = new ArrayList<InformationElement>();
+
+ /**
+ * Template Record header.
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Template ID (larger than 255) | Field Count |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ */
+
+ public int getTemplateID() {
+ return templateID;
+ }
+
+ public void setTemplateID(int templateID) {
+ this.templateID = templateID;
+ }
+
+ public int getFieldCount() {
+ return fieldCount;
+ }
+
+ public void setFieldCount(int fieldCount) {
+ this.fieldCount = fieldCount;
+ }
+
+ public List<InformationElement> getInformationElements() {
+ return informationElements;
+ }
+
+ public void setInformationElements(List<InformationElement> informationElements) {
+ this.informationElements = informationElements;
+ this.fieldCount = this.informationElements.size();
+ }
+
+ public int getLength() {
+ return HEADER_LENGTH + (informationElements.size() * InformationElement.LENGTH);
+ }
+
+ public static TemplateRecord parse(byte[] data) throws HeaderException {
+ try {
+ if (data.length < HEADER_LENGTH) {
+ throw new HeaderException("Data array too short.");
+ }
+ TemplateRecord tr = new TemplateRecord();
+ // template ID
+ byte[] templateID = new byte[2];
+ System.arraycopy(data, 0, templateID, 0, 2);
+ tr.setTemplateID(Ints.fromByteArray(templateID));
+ // field count
+ byte[] fieldCount = new byte[2];
+ System.arraycopy(data, 2, fieldCount, 0, 2);
+ tr.setFieldCount(Ints.fromByteArray(fieldCount));
+ int offset = HEADER_LENGTH;
+ for (int i = 0; i < tr.getFieldCount(); i++) {
+ byte[] subData = new byte[InformationElement.LENGTH];
+ System.arraycopy(data, offset +
+ (i * InformationElement.LENGTH), subData, 0, InformationElement.LENGTH);
+ InformationElement ie = InformationElement.parse(subData);
+ tr.getInformationElements().add(ie);
+ }
+ return tr;
+ } catch (Exception e) {
+ throw new HeaderException("Parse error: " + e.getMessage());
+ }
+ }
+
+ public byte[] getBytes() throws HeaderException {
+ try {
+ int length = HEADER_LENGTH + (informationElements.size() * InformationElement.LENGTH);
+ byte[] data = new byte[length];
+ // template ID
+ System.arraycopy(Shorts.toByteArray((short) getTemplateID()), 0, data, 0, 2);
+ // field count
+ System.arraycopy(Shorts.toByteArray((short) getFieldCount()), 0, data, 2, 2);
+ // information elements
+ int offset = HEADER_LENGTH;
+ for (InformationElement ie : informationElements) {
+ System.arraycopy(ie.getBytes(), 0, data, offset, InformationElement.LENGTH);
+ offset += InformationElement.LENGTH;
+ }
+ return data;
+ } catch (Exception e) {
+ throw new HeaderException("Error while generating the bytes: " + e.getMessage());
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("[TemplateRecord]: ");
+ sb.append("Template ID: ");
+ sb.append(templateID);
+ sb.append(", Field count: ");
+ sb.append(fieldCount);
+ sb.append(", Information elements: ");
+ sb.append(informationElements.size());
+ sb.append(", ");
+ for (InformationElement ie : informationElements) {
+ sb.append(ie);
+ sb.append(", ");
+ }
+
+ return sb.toString();
+ }
+}
\ No newline at end of file
diff --git a/ipfix/src/test/java/org/onosproject/ipfix/Of2IpfixTest.java b/ipfix/src/test/java/org/onosproject/ipfix/Of2IpfixTest.java
new file mode 100644
index 0000000..da690d5
--- /dev/null
+++ b/ipfix/src/test/java/org/onosproject/ipfix/Of2IpfixTest.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.ipfix;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onosproject.cfg.ComponentConfigAdapter;
+
+/**
+ * Set of tests for the ONOS IPFIX application.
+ */
+public class Of2IpfixTest {
+
+ private IpfixManager component;
+
+ @Before
+ public void setUp() {
+ component = new IpfixManager();
+ component.cfgService = new ComponentConfigAdapter();
+ // component.activate(null);
+
+ }
+
+ @After
+ public void tearDown() {
+ // component.deactivate(null);
+ }
+
+ @Test
+ public void basics() {
+
+ }
+
+}
diff --git a/pom.xml b/pom.xml
index 8aae095..5105151 100644
--- a/pom.xml
+++ b/pom.xml
@@ -56,6 +56,7 @@
<module>mfwd</module>
<module>igmp</module>
<module>flowtest</module>
+ <module>ipfix</module>
</modules>
<properties>