Dusan Pajin
Committed by Ray Milkey

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
......@@ -19,6 +19,7 @@ import static org.slf4j.LoggerFactory.getLogger;
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.Property;
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 @@ import org.slf4j.Logger;
@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 @@ public class ReactiveForwarding {
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 @@ public class ReactiveForwarding {
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 @@ public class ReactiveForwarding {
*/
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 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");
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 @@ public class ReactiveForwarding {
* @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 @@ public class ReactiveForwarding {
// 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 @@ public class ReactiveForwarding {
// 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());
// If configured Match Vlan ID
if (matchVlanId && inPkt.getVlanID() != Ethernet.VLAN_UNTAGGED) {
builder.matchVlanId(VlanId.vlanId(inPkt.getVlanID()));
}
//
// 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);
if (matchIpv4Dscp) {
int dscp = ipv4Packet.getDiffServ() >>> 2;
int ecn = ipv4Packet.getDiffServ() % 4;
builder.matchIPDscp((byte) (dscp))
.matchIPEcn((byte) (ecn));
}
TrafficTreatment.Builder treat = DefaultTrafficTreatment.builder();
treat.setOutput(portNumber);
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());
}
}
FlowRule f = new DefaultFlowRule(context.inPacket().receivedFrom().deviceId(),
builder.build(), treat.build(), PRIORITY, appId, TIMEOUT, false);
//
// 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());
}
flowRuleService.applyFlowRules(f);
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);
}
}
}
......
#
# 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
......