CORD-472 Added table 0 and physical access rules
Change-Id: I73fe01ec31cfc379b2a0c8ee99a781415fe70c5d
diff --git a/src/main/java/org/onosproject/cordvtn/CordVtn.java b/src/main/java/org/onosproject/cordvtn/CordVtn.java
index 107f3e7..0104c71 100644
--- a/src/main/java/org/onosproject/cordvtn/CordVtn.java
+++ b/src/main/java/org/onosproject/cordvtn/CordVtn.java
@@ -426,7 +426,7 @@
private void postInit(CordVtnNode node) {
disconnect(node);
- ruleInstaller.init(node.intBrId(), getTunnelPort(node.intBrId()));
+ ruleInstaller.init(node.intBrId(), node.phyPortName(), node.localIp());
hostService.getConnectedHosts(node.intBrId())
.stream()
.forEach(vmHandler::connected);
diff --git a/src/main/java/org/onosproject/cordvtn/CordVtnRuleInstaller.java b/src/main/java/org/onosproject/cordvtn/CordVtnRuleInstaller.java
index 1984629..dd76ea9 100644
--- a/src/main/java/org/onosproject/cordvtn/CordVtnRuleInstaller.java
+++ b/src/main/java/org/onosproject/cordvtn/CordVtnRuleInstaller.java
@@ -18,11 +18,13 @@
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.onlab.packet.Ethernet;
+import org.onlab.packet.IPv4;
import org.onlab.packet.Ip4Address;
import org.onlab.packet.Ip4Prefix;
import org.onlab.packet.IpAddress;
import org.onlab.packet.IpPrefix;
import org.onlab.packet.MacAddress;
+import org.onlab.packet.TpPort;
import org.onlab.util.ItemNotFoundException;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.DefaultGroupId;
@@ -94,15 +96,19 @@
protected final Logger log = getLogger(getClass());
- private static final int TABLE_IN_PORT = 0;
- private static final int TABLE_ACCESS_TYPE = 1;
- private static final int TABLE_IN_SERVICE = 2;
- private static final int TABLE_DST_IP = 3;
- private static final int TABLE_TUNNEL_IN = 4;
+ private static final int TABLE_FIRST = 0;
+ private static final int TABLE_IN_PORT = 1;
+ private static final int TABLE_ACCESS_TYPE = 2;
+ private static final int TABLE_IN_SERVICE = 3;
+ private static final int TABLE_DST_IP = 4;
+ private static final int TABLE_TUNNEL_IN = 5;
private static final int DEFAULT_PRIORITY = 5000;
private static final int LOWER_PRIORITY = 4000;
private static final int LOWEST_PRIORITY = 0;
+ private static final int HIGHER_PRIORITY = 50000;
+
+ private static final int VXLAN_UDP_PORT = 4789;
private final ApplicationId appId;
private final FlowRuleService flowRuleService;
@@ -141,13 +147,18 @@
* Installs table miss rule to a give device.
*
* @param deviceId device id to install the rules
- * @param tunnelPort tunnel port number of the device
+ * @param phyPortName physical port name
+ * @param localIp local data plane ip address
*/
- public void init(DeviceId deviceId, PortNumber tunnelPort) {
+ public void init(DeviceId deviceId, String phyPortName, IpAddress localIp) {
// default is drop packets which can be accomplished without
// a table miss entry for all table.
- populateTunnelInPortRule(deviceId, tunnelPort);
- processAccessTypeTable(deviceId);
+ PortNumber tunnelPort = getTunnelPort(deviceId);
+ PortNumber phyPort = getPhyPort(deviceId, phyPortName);
+
+ processFirstTable(deviceId, phyPort, localIp);
+ processInPortTable(deviceId, tunnelPort, phyPort);
+ processAccessTypeTable(deviceId, phyPort);
}
/**
@@ -376,109 +387,93 @@
}
/**
- * Creates a new group for a given service.
- *
- * @param deviceId device id to create a group
- * @param service cord service
- * @return group id, or null if it fails to create
- */
- private GroupId createServiceGroup(DeviceId deviceId, CordService service) {
- checkNotNull(service);
-
- GroupKey groupKey = getGroupKey(service.id());
- Group group = groupService.getGroup(deviceId, groupKey);
- GroupId groupId = getGroupId(service.id(), deviceId);
-
- if (group != null) {
- log.debug("Group {} is already exist in {}", service.id(), deviceId);
- return groupId;
- }
-
- GroupBuckets buckets = getServiceGroupBuckets(deviceId, service.segmentationId(), service.hosts());
- GroupDescription groupDescription = new DefaultGroupDescription(
- deviceId,
- GroupDescription.Type.SELECT,
- buckets,
- groupKey,
- groupId.id(),
- appId);
-
- groupService.addGroup(groupDescription);
-
- return groupId;
- }
-
- /**
- * Returns group buckets for a given device.
+ * Populates default rules on the first table.
+ * The rules are for shuttling vxlan-encapped packets and supporting physical
+ * network connectivity.
*
* @param deviceId device id
- * @param tunnelId tunnel id
- * @param hosts list of host
- * @return group buckets
+ * @param phyPort physical port number
+ * @param localIp local data plane ip address
*/
- private GroupBuckets getServiceGroupBuckets(DeviceId deviceId, long tunnelId, Map<Host, IpAddress> hosts) {
- List<GroupBucket> buckets = Lists.newArrayList();
+ private void processFirstTable(DeviceId deviceId, PortNumber phyPort, IpAddress localIp) {
+ // take vxlan packet out onto the physical port
+ TrafficSelector selector = DefaultTrafficSelector.builder()
+ .matchInPort(PortNumber.LOCAL)
+ .build();
- for (Map.Entry<Host, IpAddress> entry : hosts.entrySet()) {
- Host host = entry.getKey();
- Ip4Address remoteIp = entry.getValue().getIp4Address();
- DeviceId hostDevice = host.location().deviceId();
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+ .setOutput(phyPort)
+ .build();
- TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment
- .builder()
- .setEthDst(host.mac());
+ FlowRule flowRule = DefaultFlowRule.builder()
+ .fromApp(appId)
+ .withSelector(selector)
+ .withTreatment(treatment)
+ .withPriority(HIGHER_PRIORITY)
+ .forDevice(deviceId)
+ .forTable(TABLE_FIRST)
+ .makePermanent()
+ .build();
- if (deviceId.equals(hostDevice)) {
- tBuilder.setOutput(host.location().port());
- } else {
- ExtensionTreatment tunnelDst = getTunnelDst(deviceId, remoteIp);
- if (tunnelDst == null) {
- continue;
- }
+ processFlowRule(true, flowRule);
- tBuilder.extension(tunnelDst, deviceId)
- .setTunnelId(tunnelId)
- .setOutput(getTunnelPort(hostDevice));
- }
+ // take a vxlan encap'd packet through the Linux stack
+ selector = DefaultTrafficSelector.builder()
+ .matchInPort(phyPort)
+ .matchEthType(Ethernet.TYPE_IPV4)
+ .matchIPProtocol(IPv4.PROTOCOL_UDP)
+ .matchUdpDst(TpPort.tpPort(VXLAN_UDP_PORT))
+ .build();
- buckets.add(DefaultGroupBucket.createSelectGroupBucket(tBuilder.build()));
- }
+ treatment = DefaultTrafficTreatment.builder()
+ .setOutput(PortNumber.LOCAL)
+ .build();
- return new GroupBuckets(buckets);
+ flowRule = DefaultFlowRule.builder()
+ .fromApp(appId)
+ .withSelector(selector)
+ .withTreatment(treatment)
+ .withPriority(HIGHER_PRIORITY)
+ .forDevice(deviceId)
+ .forTable(TABLE_FIRST)
+ .makePermanent()
+ .build();
+
+ processFlowRule(true, flowRule);
+
+ // take all else to the next table
+ selector = DefaultTrafficSelector.builder()
+ .build();
+
+ treatment = DefaultTrafficTreatment.builder()
+ .transition(TABLE_IN_PORT)
+ .build();
+
+ flowRule = DefaultFlowRule.builder()
+ .fromApp(appId)
+ .withSelector(selector)
+ .withTreatment(treatment)
+ .withPriority(LOWEST_PRIORITY)
+ .forDevice(deviceId)
+ .forTable(TABLE_FIRST)
+ .makePermanent()
+ .build();
+
+ processFlowRule(true, flowRule);
}
/**
- * Returns globally unique group ID.
- *
- * @param serviceId service id
- * @param deviceId device id
- * @return group id
- */
- private GroupId getGroupId(CordServiceId serviceId, DeviceId deviceId) {
- return new DefaultGroupId(Objects.hash(serviceId, deviceId));
- }
-
- /**
- * Returns group key of a service.
- *
- * @param serviceId service id
- * @return group key
- */
- private GroupKey getGroupKey(CordServiceId serviceId) {
- return new DefaultGroupKey(serviceId.id().getBytes());
- }
-
- /**
- * Forward table miss rules in ACCESS_TYPE table to IN_SERVICE table.
+ * Forward table miss packets in ACCESS_TYPE table to physical port.
*
* @param deviceId device id
+ * @param phyPort physical port number
*/
- private void processAccessTypeTable(DeviceId deviceId) {
+ private void processAccessTypeTable(DeviceId deviceId, PortNumber phyPort) {
TrafficSelector selector = DefaultTrafficSelector.builder()
.build();
TrafficTreatment treatment = DefaultTrafficTreatment.builder()
- .transition(TABLE_IN_SERVICE)
+ .setOutput(phyPort)
.build();
FlowRule flowRule = DefaultFlowRule.builder()
@@ -495,18 +490,21 @@
}
/**
- * Populates rules for tunnel flows in port in IN_PORT table.
- * All flows from tunnel port are forwarded to TUNNEL_ID table.
+ * Populates default rules for IN_PORT table.
+ * All packets from tunnel port are forwarded to TUNNEL_ID table and all packets
+ * from physical port to ACCESS_TYPE table.
*
* @param deviceId device id to install the rules
- * @param tunnelPort tunnel port
+ * @param tunnelPort tunnel port number
+ * @param phyPort physical port number
*/
- private void populateTunnelInPortRule(DeviceId deviceId, PortNumber tunnelPort) {
+ private void processInPortTable(DeviceId deviceId, PortNumber tunnelPort, PortNumber phyPort) {
checkNotNull(tunnelPort);
TrafficSelector selector = DefaultTrafficSelector.builder()
.matchInPort(tunnelPort)
.build();
+
TrafficTreatment treatment = DefaultTrafficTreatment.builder()
.transition(TABLE_TUNNEL_IN)
.build();
@@ -522,6 +520,26 @@
.build();
processFlowRule(true, flowRule);
+
+ selector = DefaultTrafficSelector.builder()
+ .matchInPort(phyPort)
+ .build();
+
+ treatment = DefaultTrafficTreatment.builder()
+ .transition(TABLE_DST_IP)
+ .build();
+
+ flowRule = DefaultFlowRule.builder()
+ .fromApp(appId)
+ .withSelector(selector)
+ .withTreatment(treatment)
+ .withPriority(DEFAULT_PRIORITY)
+ .forDevice(deviceId)
+ .forTable(TABLE_IN_PORT)
+ .makePermanent()
+ .build();
+
+ processFlowRule(true, flowRule);
}
/**
@@ -539,6 +557,7 @@
.matchEthType(Ethernet.TYPE_IPV4)
.matchIPSrc(srcIp.toIpPrefix())
.build();
+
TrafficTreatment treatment = DefaultTrafficTreatment.builder()
.transition(TABLE_ACCESS_TYPE)
.build();
@@ -559,6 +578,7 @@
selector = DefaultTrafficSelector.builder()
.matchInPort(inPort)
.build();
+
treatment = DefaultTrafficTreatment.builder()
.transition(TABLE_IN_SERVICE)
.build();
@@ -589,6 +609,7 @@
.matchIPSrc(srcRange)
.matchIPDst(dstRange)
.build();
+
TrafficTreatment treatment = DefaultTrafficTreatment.builder()
.transition(TABLE_DST_IP)
.build();
@@ -666,6 +687,7 @@
TrafficSelector selector = DefaultTrafficSelector.builder()
.matchInPort(port)
.build();
+
TrafficTreatment treatment = DefaultTrafficTreatment.builder()
.group(groupId)
.build();
@@ -701,6 +723,7 @@
.matchEthType(Ethernet.TYPE_IPV4)
.matchIPDst(dstIp.toIpPrefix())
.build();
+
TrafficTreatment treatment = DefaultTrafficTreatment.builder()
.setEthDst(dstMac)
.setOutput(inPort)
@@ -815,6 +838,24 @@
}
/**
+ * Returns physical port name of a given device.
+ *
+ * @param deviceId device id
+ * @param phyPortName physical port name
+ * @return physical port number, or null if no physical port exists
+ */
+ private PortNumber getPhyPort(DeviceId deviceId, String phyPortName) {
+ try {
+ return deviceService.getPorts(deviceId).stream()
+ .filter(p -> p.annotations().value("portName").contains(phyPortName) &&
+ p.isEnabled())
+ .findFirst().get().number();
+ } catch (NoSuchElementException e) {
+ return null;
+ }
+ }
+
+ /**
* Returns the inport from a given flow rule if the rule contains the match of it.
*
* @param flowRule flow rule
@@ -923,6 +964,99 @@
}
/**
+ * Creates a new group for a given service.
+ *
+ * @param deviceId device id to create a group
+ * @param service cord service
+ * @return group id, or null if it fails to create
+ */
+ private GroupId createServiceGroup(DeviceId deviceId, CordService service) {
+ checkNotNull(service);
+
+ GroupKey groupKey = getGroupKey(service.id());
+ Group group = groupService.getGroup(deviceId, groupKey);
+ GroupId groupId = getGroupId(service.id(), deviceId);
+
+ if (group != null) {
+ log.debug("Group {} is already exist in {}", service.id(), deviceId);
+ return groupId;
+ }
+
+ GroupBuckets buckets = getServiceGroupBuckets(deviceId, service.segmentationId(), service.hosts());
+ GroupDescription groupDescription = new DefaultGroupDescription(
+ deviceId,
+ GroupDescription.Type.SELECT,
+ buckets,
+ groupKey,
+ groupId.id(),
+ appId);
+
+ groupService.addGroup(groupDescription);
+
+ return groupId;
+ }
+
+ /**
+ * Returns group buckets for a given device.
+ *
+ * @param deviceId device id
+ * @param tunnelId tunnel id
+ * @param hosts list of host
+ * @return group buckets
+ */
+ private GroupBuckets getServiceGroupBuckets(DeviceId deviceId, long tunnelId, Map<Host, IpAddress> hosts) {
+ List<GroupBucket> buckets = Lists.newArrayList();
+
+ for (Map.Entry<Host, IpAddress> entry : hosts.entrySet()) {
+ Host host = entry.getKey();
+ Ip4Address remoteIp = entry.getValue().getIp4Address();
+ DeviceId hostDevice = host.location().deviceId();
+
+ TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment
+ .builder()
+ .setEthDst(host.mac());
+
+ if (deviceId.equals(hostDevice)) {
+ tBuilder.setOutput(host.location().port());
+ } else {
+ ExtensionTreatment tunnelDst = getTunnelDst(deviceId, remoteIp);
+ if (tunnelDst == null) {
+ continue;
+ }
+
+ tBuilder.extension(tunnelDst, deviceId)
+ .setTunnelId(tunnelId)
+ .setOutput(getTunnelPort(hostDevice));
+ }
+
+ buckets.add(DefaultGroupBucket.createSelectGroupBucket(tBuilder.build()));
+ }
+
+ return new GroupBuckets(buckets);
+ }
+
+ /**
+ * Returns globally unique group ID.
+ *
+ * @param serviceId service id
+ * @param deviceId device id
+ * @return group id
+ */
+ private GroupId getGroupId(CordServiceId serviceId, DeviceId deviceId) {
+ return new DefaultGroupId(Objects.hash(serviceId, deviceId));
+ }
+
+ /**
+ * Returns group key of a service.
+ *
+ * @param serviceId service id
+ * @return group key
+ */
+ private GroupKey getGroupKey(CordServiceId serviceId) {
+ return new DefaultGroupKey(serviceId.id().getBytes());
+ }
+
+ /**
* Returns extension instruction to set tunnel destination.
*
* @param deviceId device id