Additional configuration parameters in onos-app-fwd
Additional configuration parameters that can be configured in the configuration file (org.onosproject.fwd.ReactiveForwarding.cfg),
that determine application behavior:
- packetOutOfppTable - application will use OFPP_TABLE port in PacketOut message, sending packet back to the OpenFlow pipeline, instead of using switch port
- flowTimeout - configuring reactively installed flow timeout
- flowPriority - configuring reactively installed flow priority
- matchDstMacOnly - reactively installed flows will match only destination MAC address - behavior as legacy L2 switches. This options overrides all other options below.
- matchVlanId - reactively installed flows will match default condition with additionally with VLAN ID field
- matchIpv4Address - reactively installed flows will match default conditions, plus IPv4 address and Protocol field
- matchIpv4Dscp - reactively installed flows will match default condition, IPv4 + with IPv4 DSCP and ECN fields (need matchIPv4Address enabled)
- matchIpv6Address - reactively installed flows will match default condition with IPv6 address and NextHeader field
- matchIpv6FlowLabel - reactively installed flows will match default condition with IPv6 address and IPv6 Flow Label (need matchIPv6Address enabled)
- matchTcpUdpPorts - reactively installed flows will match default condition with IPv4 or IPv6 address and TCP/UDP ports (need matchIPv4Address or matchIPv6Address enabled)
- matchIcmpFields - reactively installed flows will match default condition with IPv4 or IPv6 address and ICMP type and code fields (need matchIPv4Address or matchIPv6Address enabled)
Change-Id: Ieef67a1a12f6341d4de3b07e1226affec66d361a
diff --git a/apps/fwd/src/main/java/org/onosproject/fwd/ReactiveForwarding.java b/apps/fwd/src/main/java/org/onosproject/fwd/ReactiveForwarding.java
index 0cf2939..7abf134 100644
--- a/apps/fwd/src/main/java/org/onosproject/fwd/ReactiveForwarding.java
+++ b/apps/fwd/src/main/java/org/onosproject/fwd/ReactiveForwarding.java
@@ -19,6 +19,7 @@
import java.util.Dictionary;
import java.util.Set;
+import static com.google.common.base.Strings.isNullOrEmpty;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
@@ -28,6 +29,15 @@
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.onlab.packet.Ethernet;
+import org.onlab.packet.IPv4;
+import org.onlab.packet.IPv6;
+import org.onlab.packet.TCP;
+import org.onlab.packet.UDP;
+import org.onlab.packet.ICMP;
+import org.onlab.packet.ICMP6;
+import org.onlab.packet.Ip4Prefix;
+import org.onlab.packet.Ip6Prefix;
+import org.onlab.packet.VlanId;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
import org.onosproject.net.Host;
@@ -57,8 +67,8 @@
@Component(immediate = true)
public class ReactiveForwarding {
- private static final int TIMEOUT = 10;
- private static final int PRIORITY = 10;
+ private static final int DEFAULT_TIMEOUT = 10;
+ private static final int DEFAULT_PRIORITY = 10;
private final Logger log = getLogger(getClass());
@@ -85,10 +95,59 @@
label = "Enable packet-out only forwarding; default is false")
private boolean packetOutOnly = false;
+ @Property(name = "packetOutOfppTable", boolValue = false,
+ label = "Enable first packet forwarding using OFPP_TABLE port " +
+ "instead of PacketOut with actual port; default is false")
+ private boolean packetOutOfppTable = false;
+
+ @Property(name = "flowTimeout", intValue = DEFAULT_TIMEOUT,
+ label = "Configure Flow Timeout for installed flow rules; " +
+ "default is 10 sec")
+ private int flowTimeout = DEFAULT_TIMEOUT;
+
+ @Property(name = "flowPriority", intValue = DEFAULT_PRIORITY,
+ label = "Configure Flow Priority for installed flow rules; " +
+ "default is 10")
+ private int flowPriority = DEFAULT_PRIORITY;
+
@Property(name = "ipv6Forwarding", boolValue = false,
label = "Enable IPv6 forwarding; default is false")
private boolean ipv6Forwarding = false;
+ @Property(name = "matchDstMacOnly", boolValue = false,
+ label = "Enable matching Dst Mac Only; default is false")
+ private boolean matchDstMacOnly = false;
+
+ @Property(name = "matchVlanId", boolValue = false,
+ label = "Enable matching Vlan ID; default is false")
+ private boolean matchVlanId = false;
+
+ @Property(name = "matchIpv4Address", boolValue = false,
+ label = "Enable matching IPv4 Addresses; default is false")
+ private boolean matchIpv4Address = false;
+
+ @Property(name = "matchIpv4Dscp", boolValue = false,
+ label = "Enable matching IPv4 DSCP and ECN; default is false")
+ private boolean matchIpv4Dscp = false;
+
+ @Property(name = "matchIpv6Address", boolValue = false,
+ label = "Enable matching IPv6 Addresses; default is false")
+ private boolean matchIpv6Address = false;
+
+ @Property(name = "matchIpv6FlowLabel", boolValue = false,
+ label = "Enable matching IPv6 FlowLabel; default is false")
+ private boolean matchIpv6FlowLabel = false;
+
+ @Property(name = "matchTcpUdpPorts", boolValue = false,
+ label = "Enable matching TCP/UDP ports; default is false")
+ private boolean matchTcpUdpPorts = false;
+
+ @Property(name = "matchIcmpFields", boolValue = false,
+ label = "Enable matching ICMPv4 and ICMPv6 fields; " +
+ "default is false")
+ private boolean matchIcmpFields = false;
+
+
@Activate
public void activate(ComponentContext context) {
appId = coreService.registerApplication("org.onosproject.fwd");
@@ -98,7 +157,17 @@
TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
selector.matchEthType(Ethernet.TYPE_IPV4);
- packetService.requestPackets(selector.build(), PacketPriority.REACTIVE, appId);
+ packetService.requestPackets(selector.build(), PacketPriority.REACTIVE,
+ appId);
+ selector.matchEthType(Ethernet.TYPE_ARP);
+ packetService.requestPackets(selector.build(), PacketPriority.REACTIVE,
+ appId);
+
+ if (ipv6Forwarding) {
+ selector.matchEthType(Ethernet.TYPE_IPV6);
+ packetService.requestPackets(selector.build(),
+ PacketPriority.REACTIVE, appId);
+ }
log.info("Started with Application ID {}", appId.id());
}
@@ -123,18 +192,123 @@
*/
private void readComponentConfiguration(ComponentContext context) {
Dictionary<?, ?> properties = context.getProperties();
- boolean packetOutOnlyEnabled = isPropertyEnabled(properties, "packetOutOnly");
+ boolean packetOutOnlyEnabled =
+ isPropertyEnabled(properties, "packetOutOnly");
if (packetOutOnly != packetOutOnlyEnabled) {
packetOutOnly = packetOutOnlyEnabled;
log.info("Configured. Packet-out only forwarding is {}",
- packetOutOnly ? "enabled" : "disabled");
+ packetOutOnly ? "enabled" : "disabled");
}
- boolean ipv6ForwardingEnabled = isPropertyEnabled(properties, "ipv6Forwarding");
+ boolean packetOutOfppTableEnabled =
+ isPropertyEnabled(properties, "packetOutOfppTable");
+ if (packetOutOfppTable != packetOutOfppTableEnabled) {
+ packetOutOfppTable = packetOutOfppTableEnabled;
+ log.info("Configured. Forwarding using OFPP_TABLE port is {}",
+ packetOutOfppTable ? "enabled" : "disabled");
+ }
+ boolean ipv6ForwardingEnabled =
+ isPropertyEnabled(properties, "ipv6Forwarding");
if (ipv6Forwarding != ipv6ForwardingEnabled) {
ipv6Forwarding = ipv6ForwardingEnabled;
log.info("Configured. IPv6 forwarding is {}",
- ipv6Forwarding ? "enabled" : "disabled");
+ ipv6Forwarding ? "enabled" : "disabled");
}
+ boolean matchDstMacOnlyEnabled =
+ isPropertyEnabled(properties, "matchDstMacOnly");
+ if (matchDstMacOnly != matchDstMacOnlyEnabled) {
+ matchDstMacOnly = matchDstMacOnlyEnabled;
+ log.info("Configured. Match Dst MAC Only is {}",
+ matchDstMacOnly ? "enabled" : "disabled");
+ }
+ boolean matchVlanIdEnabled =
+ isPropertyEnabled(properties, "matchVlanId");
+ if (matchVlanId != matchVlanIdEnabled) {
+ matchVlanId = matchVlanIdEnabled;
+ log.info("Configured. Matching Vlan ID is {}",
+ matchVlanId ? "enabled" : "disabled");
+ }
+ boolean matchIpv4AddressEnabled =
+ isPropertyEnabled(properties, "matchIpv4Address");
+ if (matchIpv4Address != matchIpv4AddressEnabled) {
+ matchIpv4Address = matchIpv4AddressEnabled;
+ log.info("Configured. Matching IPv4 Addresses is {}",
+ matchIpv4Address ? "enabled" : "disabled");
+ }
+ boolean matchIpv4DscpEnabled =
+ isPropertyEnabled(properties, "matchIpv4Dscp");
+ if (matchIpv4Dscp != matchIpv4DscpEnabled) {
+ matchIpv4Dscp = matchIpv4DscpEnabled;
+ log.info("Configured. Matching IPv4 DSCP and ECN is {}",
+ matchIpv4Dscp ? "enabled" : "disabled");
+ }
+ boolean matchIpv6AddressEnabled =
+ isPropertyEnabled(properties, "matchIpv6Address");
+ if (matchIpv6Address != matchIpv6AddressEnabled) {
+ matchIpv6Address = matchIpv6AddressEnabled;
+ log.info("Configured. Matching IPv6 Addresses is {}",
+ matchIpv6Address ? "enabled" : "disabled");
+ }
+ boolean matchIpv6FlowLabelEnabled =
+ isPropertyEnabled(properties, "matchIpv6FlowLabel");
+ if (matchIpv6FlowLabel != matchIpv6FlowLabelEnabled) {
+ matchIpv6FlowLabel = matchIpv6FlowLabelEnabled;
+ log.info("Configured. Matching IPv6 FlowLabel is {}",
+ matchIpv6FlowLabel ? "enabled" : "disabled");
+ }
+ boolean matchTcpUdpPortsEnabled =
+ isPropertyEnabled(properties, "matchTcpUdpPorts");
+ if (matchTcpUdpPorts != matchTcpUdpPortsEnabled) {
+ matchTcpUdpPorts = matchTcpUdpPortsEnabled;
+ log.info("Configured. Matching TCP/UDP fields is {}",
+ matchTcpUdpPorts ? "enabled" : "disabled");
+ }
+ boolean matchIcmpFieldsEnabled =
+ isPropertyEnabled(properties, "matchIcmpFields");
+ if (matchIcmpFields != matchIcmpFieldsEnabled) {
+ matchIcmpFields = matchIcmpFieldsEnabled;
+ log.info("Configured. Matching ICMP (v4 and v6) fields is {}",
+ matchIcmpFields ? "enabled" : "disabled");
+ }
+ Integer flowTimeoutConfigured =
+ getIntegerProperty(properties, "flowTimeout");
+ if (flowTimeoutConfigured == null) {
+ log.info("Flow Timeout is not configured, default value is {}",
+ flowTimeout);
+ } else {
+ flowTimeout = flowTimeoutConfigured;
+ log.info("Configured. Flow Timeout is configured to {}",
+ flowTimeout, " seconds");
+ }
+ Integer flowPriorityConfigured =
+ getIntegerProperty(properties, "flowPriority");
+ if (flowPriorityConfigured == null) {
+ log.info("Flow Priority is not configured, default value is {}",
+ flowPriority);
+ } else {
+ flowPriority = flowPriorityConfigured;
+ log.info("Configured. Flow Priority is configured to {}",
+ flowPriority);
+ }
+ }
+
+ /**
+ * Get Integer property from the propertyName
+ * Return null if propertyName is not found.
+ *
+ * @param properties properties to be looked up
+ * @param propertyName the name of the property to look up
+ * @return value when the propertyName is defined or return null
+ */
+ private static Integer getIntegerProperty(Dictionary<?, ?> properties,
+ String propertyName) {
+ Integer value = null;
+ try {
+ String s = (String) properties.get(propertyName);
+ value = isNullOrEmpty(s) ? value : Integer.parseInt(s.trim());
+ } catch (NumberFormatException e) {
+ value = null;
+ }
+ return value;
}
/**
@@ -144,7 +318,8 @@
* @param propertyName the name of the property to look up
* @return true when the propertyName is defined and set to true
*/
- private static boolean isPropertyEnabled(Dictionary<?, ?> properties, String propertyName) {
+ private static boolean isPropertyEnabled(Dictionary<?, ?> properties,
+ String propertyName) {
boolean enabled = false;
try {
String flag = (String) properties.get(propertyName);
@@ -213,9 +388,10 @@
// Otherwise, get a set of paths that lead from here to the
// destination edge switch.
- Set<Path> paths = topologyService.getPaths(topologyService.currentTopology(),
- pkt.receivedFrom().deviceId(),
- dst.location().deviceId());
+ Set<Path> paths =
+ topologyService.getPaths(topologyService.currentTopology(),
+ pkt.receivedFrom().deviceId(),
+ dst.location().deviceId());
if (paths.isEmpty()) {
// If there are no paths, flood and bail.
flood(context);
@@ -279,27 +455,137 @@
// Install a rule forwarding the packet to the specified port.
private void installRule(PacketContext context, PortNumber portNumber) {
- // We don't yet support bufferids in the flowservice so packet out first.
- packetOut(context, portNumber);
- if (!packetOutOnly) {
- // Install the flow rule to handle this type of message from now on.
- Ethernet inPkt = context.inPacket().parsed();
- TrafficSelector.Builder builder = DefaultTrafficSelector.builder();
- builder.matchEthType(inPkt.getEtherType())
+ //
+ // We don't support (yet) buffer IDs in the Flow Service so
+ // packet out first.
+ //
+ Ethernet inPkt = context.inPacket().parsed();
+ TrafficSelector.Builder builder = DefaultTrafficSelector.builder();
+
+ // If PacketOutOnly or ARP packet than forward directly to output port
+ if (packetOutOnly || inPkt.getEtherType() == Ethernet.TYPE_ARP) {
+ packetOut(context, portNumber);
+ return;
+ }
+
+ //
+ // If matchDstMacOnly
+ // Create flows matching dstMac only
+ // Else
+ // Create flows with default matching and include configured fields
+ //
+ if (matchDstMacOnly) {
+ builder.matchEthDst(inPkt.getDestinationMAC());
+ } else {
+ builder.matchInPort(context.inPacket().receivedFrom().port())
.matchEthSrc(inPkt.getSourceMAC())
.matchEthDst(inPkt.getDestinationMAC())
- .matchInPort(context.inPacket().receivedFrom().port());
+ .matchEthType(inPkt.getEtherType());
- TrafficTreatment.Builder treat = DefaultTrafficTreatment.builder();
- treat.setOutput(portNumber);
+ // If configured Match Vlan ID
+ if (matchVlanId && inPkt.getVlanID() != Ethernet.VLAN_UNTAGGED) {
+ builder.matchVlanId(VlanId.vlanId(inPkt.getVlanID()));
+ }
- FlowRule f = new DefaultFlowRule(context.inPacket().receivedFrom().deviceId(),
- builder.build(), treat.build(), PRIORITY, appId, TIMEOUT, false);
+ //
+ // If configured and EtherType is IPv4 - Match IPv4 and
+ // TCP/UDP/ICMP fields
+ //
+ if (matchIpv4Address && inPkt.getEtherType() == Ethernet.TYPE_IPV4) {
+ IPv4 ipv4Packet = (IPv4) inPkt.getPayload();
+ byte ipv4Protocol = ipv4Packet.getProtocol();
+ Ip4Prefix matchIp4SrcPrefix =
+ Ip4Prefix.valueOf(ipv4Packet.getSourceAddress(),
+ Ip4Prefix.MAX_MASK_LENGTH);
+ Ip4Prefix matchIp4DstPrefix =
+ Ip4Prefix.valueOf(ipv4Packet.getDestinationAddress(),
+ Ip4Prefix.MAX_MASK_LENGTH);
+ builder.matchIPSrc(matchIp4SrcPrefix)
+ .matchIPDst(matchIp4DstPrefix)
+ .matchIPProtocol(ipv4Protocol);
- flowRuleService.applyFlowRules(f);
+ if (matchIpv4Dscp) {
+ int dscp = ipv4Packet.getDiffServ() >>> 2;
+ int ecn = ipv4Packet.getDiffServ() % 4;
+ builder.matchIPDscp((byte) (dscp))
+ .matchIPEcn((byte) (ecn));
+ }
+
+ if (matchTcpUdpPorts && ipv4Protocol == IPv4.PROTOCOL_TCP) {
+ TCP tcpPacket = (TCP) ipv4Packet.getPayload();
+ builder.matchTcpSrc(tcpPacket.getSourcePort())
+ .matchTcpDst(tcpPacket.getDestinationPort());
+ }
+ if (matchTcpUdpPorts && ipv4Protocol == IPv4.PROTOCOL_UDP) {
+ UDP udpPacket = (UDP) ipv4Packet.getPayload();
+ builder.matchUdpSrc(udpPacket.getSourcePort())
+ .matchUdpDst(udpPacket.getDestinationPort());
+ }
+ if (matchIcmpFields && ipv4Protocol == IPv4.PROTOCOL_ICMP) {
+ ICMP icmpPacket = (ICMP) ipv4Packet.getPayload();
+ builder.matchIcmpType(icmpPacket.getIcmpType())
+ .matchIcmpCode(icmpPacket.getIcmpCode());
+ }
+ }
+
+ //
+ // If configured and EtherType is IPv6 - Match IPv6 and
+ // TCP/UDP/ICMP fields
+ //
+ if (matchIpv6Address && inPkt.getEtherType() == Ethernet.TYPE_IPV6) {
+ IPv6 ipv6Packet = (IPv6) inPkt.getPayload();
+ byte ipv6NextHeader = ipv6Packet.getNextHeader();
+ Ip6Prefix matchIp6SrcPrefix =
+ Ip6Prefix.valueOf(ipv6Packet.getSourceAddress(),
+ Ip6Prefix.MAX_MASK_LENGTH);
+ Ip6Prefix matchIp6DstPrefix =
+ Ip6Prefix.valueOf(ipv6Packet.getDestinationAddress(),
+ Ip6Prefix.MAX_MASK_LENGTH);
+ builder.matchIPv6Src(matchIp6SrcPrefix)
+ .matchIPv6Dst(matchIp6DstPrefix)
+ .matchIPProtocol(ipv6NextHeader);
+
+ if (matchIpv6FlowLabel) {
+ builder.matchIPv6FlowLabel(ipv6Packet.getFlowLabel());
+ }
+
+ if (matchTcpUdpPorts && ipv6NextHeader == IPv6.PROTOCOL_TCP) {
+ TCP tcpPacket = (TCP) ipv6Packet.getPayload();
+ builder.matchTcpSrc(tcpPacket.getSourcePort())
+ .matchTcpDst(tcpPacket.getDestinationPort());
+ }
+ if (matchTcpUdpPorts && ipv6NextHeader == IPv6.PROTOCOL_UDP) {
+ UDP udpPacket = (UDP) ipv6Packet.getPayload();
+ builder.matchUdpSrc(udpPacket.getSourcePort())
+ .matchUdpDst(udpPacket.getDestinationPort());
+ }
+ if (matchIcmpFields && ipv6NextHeader == IPv6.PROTOCOL_ICMP6) {
+ ICMP6 icmp6Packet = (ICMP6) ipv6Packet.getPayload();
+ builder.matchIcmpv6Type(icmp6Packet.getIcmpType())
+ .matchIcmpv6Code(icmp6Packet.getIcmpCode());
+ }
+ }
+ }
+ TrafficTreatment.Builder treat = DefaultTrafficTreatment.builder();
+ treat.setOutput(portNumber);
+
+ FlowRule f =
+ new DefaultFlowRule(context.inPacket().receivedFrom().deviceId(),
+ builder.build(), treat.build(), flowPriority,
+ appId, flowTimeout, false);
+
+ flowRuleService.applyFlowRules(f);
+
+ //
+ // If packetOutOfppTable
+ // Send packet back to the OpenFlow pipeline to match installed flow
+ // Else
+ // Send packet direction on the appropriate port
+ //
+ if (packetOutOfppTable) {
+ packetOut(context, PortNumber.TABLE);
+ } else {
+ packetOut(context, portNumber);
}
}
-
}
-
-
diff --git a/tools/package/etc/org.onosproject.fwd.ReactiveForwarding.cfg b/tools/package/etc/org.onosproject.fwd.ReactiveForwarding.cfg
index f3c8e8e..923eeeb 100644
--- a/tools/package/etc/org.onosproject.fwd.ReactiveForwarding.cfg
+++ b/tools/package/etc/org.onosproject.fwd.ReactiveForwarding.cfg
@@ -1,6 +1,9 @@
#
# Sample configuration for onos-app-fwd.
# This configuration file would be placed at: $(KARAF_ROOT)/etc.
+
+#
+# Reactive flows default matching is InPort, Src MAC, Dst MAC and EtherType fields
#
#
@@ -10,6 +13,67 @@
# packetOutOnly = true
#
+# Enable forwarding of the first packet by using OFPP_TABLE port in the
+# PacketOut message instead of sending it directly to the switch port
+#
+# packetOutOfppTable = true
+
+#
+# Timeout of reactively installed flows (in seconds).
+# Default is 10 sec
+#
+# flowTimeout = 10
+
+#
+# Priority of reactively installed flows
+#
+# flowPriority = 10
+
+#
# Enable IPv6 forwarding.
#
# ipv6Forwarding = true
+
+#
+# Flows matching destination MAC only - as legacy L2 switches
+# - This option overrides all other options below
+#
+# matchDstMacOnly = true
+
+#
+# Matching of VLAN ID in Ethernet header
+#
+# matchVlanId = true
+
+#
+# Matching of IPv4 addresses and Protocol field
+# - must be enabled to match IPv4 DSCP, TCP/UDP ports and ICMP type/code
+#
+# matchIpv4Address = true
+
+#
+# Matching of IPv4 DSCP and ECN fields
+#
+# matchIpv4Dscp = true
+
+#
+# Matching of IPv6 addresses and Next-Header field
+# - must be enabled to match IPv6 Flow Label, TCP/UDP ports and ICMP type/code
+#
+# matchIpv6Address = true
+
+#
+# Matching of IPv6 Flow Label
+#
+# matchIpv6FlowLabel = true
+
+#
+# Matching of TCP/UDP ports for IPv4 and IPv6
+#
+# matchTcpUdpPorts = true
+
+#
+# Matching of ICMP Type and Code fields for IPv4 and IPv6
+#
+# matchIcmpFields = true
+