[SONA] Add a configuration to choose the SNATing mechanism between OVS SNAT and Controller SNAT.
Change-Id: Idd82a7c2e0d220d52e5445b3ca1d166663cf3b4a
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/Constants.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/Constants.java
index bcd31d0..a24a85e 100644
--- a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/Constants.java
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/Constants.java
@@ -34,7 +34,7 @@
public static final int PRIORITY_TUNNEL_TAG_RULE = 30000;
public static final int PRIORITY_FLOATING_INTERNAL = 42000;
public static final int PRIORITY_FLOATING_EXTERNAL = 41000;
- public static final int PRIORITY_OVS_SNAT_RULE = 40500;
+ public static final int PRIORITY_STATEFUL_SNAT_RULE = 40500;
public static final int PRIORITY_ICMP_RULE = 43000;
public static final int PRIORITY_INTERNAL_ROUTING_RULE = 28000;
public static final int PRIORITY_EXTERNAL_ROUTING_RULE = 25000;
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingHandler.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingHandler.java
index fafd667..adf4b8c 100644
--- a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingHandler.java
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingHandler.java
@@ -20,14 +20,19 @@
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.Ethernet;
+import org.onlab.packet.ICMP;
import org.onlab.packet.IPv4;
import org.onlab.packet.IpAddress;
import org.onlab.packet.IpPrefix;
import org.onlab.packet.MacAddress;
import org.onlab.packet.VlanId;
+import org.onlab.util.Tools;
+import org.onosproject.cfg.ComponentConfigService;
import org.onosproject.cluster.ClusterService;
import org.onosproject.cluster.LeadershipService;
import org.onosproject.cluster.NodeId;
@@ -65,9 +70,11 @@
import org.openstack4j.model.network.Router;
import org.openstack4j.model.network.RouterInterface;
import org.openstack4j.model.network.Subnet;
+import org.osgi.service.component.ComponentContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.util.Dictionary;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
@@ -84,7 +91,7 @@
import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_EXTERNAL_ROUTING_RULE;
import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ICMP_RULE;
import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_INTERNAL_ROUTING_RULE;
-import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_OVS_SNAT_RULE;
+import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_STATEFUL_SNAT_RULE;
import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_SWITCHING_RULE;
import static org.onosproject.openstacknetworking.api.Constants.ROUTING_TABLE;
import static org.onosproject.openstacknetworking.impl.RulePopulatorUtil.buildExtension;
@@ -103,6 +110,11 @@
private static final String MSG_DISABLED = "Disabled ";
private static final String ERR_SET_FLOWS = "Failed to set flows for router %s:";
private static final String ERR_UNSUPPORTED_NET_TYPE = "Unsupported network type";
+ private static final boolean USE_STATEFUL_SNAT = false;
+
+ @Property(name = "useStatefulSnat", boolValue = USE_STATEFUL_SNAT,
+ label = "Use Stateful SNAT for source NATing")
+ private boolean useStatefulSnat = USE_STATEFUL_SNAT;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected CoreService coreService;
@@ -137,6 +149,9 @@
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected DriverService driverService;
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected ComponentConfigService configService;
+
private final ExecutorService eventExecutor = newSingleThreadScheduledExecutor(
groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
private final OpenstackNodeListener osNodeListener = new InternalNodeEventListener();
@@ -154,6 +169,7 @@
osNodeService.addListener(osNodeListener);
osRouterService.addListener(osRouterListener);
instancePortService.addListener(instancePortListener);
+ configService.registerProperties(getClass());
log.info("Started");
}
@@ -164,11 +180,30 @@
osNodeService.removeListener(osNodeListener);
instancePortService.removeListener(instancePortListener);
leadershipService.withdraw(appId.name());
+ configService.unregisterProperties(getClass(), false);
eventExecutor.shutdown();
log.info("Stopped");
}
+ @Modified
+ protected void modified(ComponentContext context) {
+ Dictionary<?, ?> properties = context.getProperties();
+ Boolean flag;
+
+ flag = Tools.isPropertyEnabled(properties, "useStatefulSnat");
+ if (flag == null) {
+ log.info("useStatefulSnat is not configured, " +
+ "using current value of {}", useStatefulSnat);
+ } else {
+ useStatefulSnat = flag;
+ log.info("Configured. useStatefulSnat is {}",
+ useStatefulSnat ? "enabled" : "disabled");
+ }
+
+ resetSnatRules();
+ }
+
private void routerUpdated(Router osRouter) {
ExternalGateway exGateway = osRouter.getExternalGatewayInfo();
if (exGateway == null) {
@@ -229,7 +264,29 @@
install);
});
- IpAddress natAddress = getGatewayIpAddress(osRouter);
+ if (useStatefulSnat) {
+ setStatefulSnatRules(routerIface, install);
+ } else {
+ setReactiveSnatRules(routerIface, install);
+ }
+
+ final String updateStr = install ? MSG_ENABLED : MSG_DISABLED;
+ log.info(updateStr + "external access for subnet({})", osSubnet.getCidr());
+ }
+
+ private void setStatefulSnatRules(RouterInterface routerIface, boolean install) {
+ Subnet osSubnet = osNetworkService.subnet(routerIface.getSubnetId());
+ Network osNet = osNetworkService.network(osSubnet.getNetworkId());
+
+ Optional<Router> osRouter = osRouterService.routers().stream()
+ .filter(router -> osRouterService.routerInterfaces(routerIface.getId()) != null)
+ .findAny();
+
+ if (!osRouter.isPresent()) {
+ log.error("Cannot find a router for router interface {} ", routerIface);
+ return;
+ }
+ IpAddress natAddress = getGatewayIpAddress(osRouter.get());
if (natAddress == null) {
return;
}
@@ -251,9 +308,19 @@
natAddress, Long.parseLong(osNet.getProviderSegID()),
gwNode.patchPortNum(), install);
});
+ }
- final String updateStr = install ? MSG_ENABLED : MSG_DISABLED;
- log.info(updateStr + "external access for subnet({})", osSubnet.getCidr());
+ private void setReactiveSnatRules(RouterInterface routerIface, boolean install) {
+ Subnet osSubnet = osNetworkService.subnet(routerIface.getSubnetId());
+ Network osNet = osNetworkService.network(osSubnet.getNetworkId());
+
+ osNodeService.completeNodes(GATEWAY)
+ .forEach(gwNode -> setRulesToController(
+ gwNode.intgBridge(),
+ osNet.getProviderSegID(),
+ IpPrefix.valueOf(osSubnet.getCidr()),
+ osNet.getNetworkType(),
+ install));
}
private IpAddress getGatewayIpAddress(Router osRouter) {
@@ -271,6 +338,24 @@
return IpAddress.valueOf(extSubnet.get().getGateway());
}
+ private void resetSnatRules() {
+ if (useStatefulSnat) {
+ osRouterService.routerInterfaces().forEach(
+ routerIface -> {
+ setReactiveSnatRules(routerIface, false);
+ setStatefulSnatRules(routerIface, true);
+ }
+ );
+ } else {
+ osRouterService.routerInterfaces().forEach(
+ routerIface -> {
+ setStatefulSnatRules(routerIface, false);
+ setReactiveSnatRules(routerIface, true);
+ }
+ );
+ }
+ }
+
private void setGatewayIcmp(Subnet osSubnet, boolean install) {
if (Strings.isNullOrEmpty(osSubnet.getGateway())) {
// do nothing if no gateway is set
@@ -628,7 +713,7 @@
deviceId,
selector,
treatment,
- PRIORITY_OVS_SNAT_RULE,
+ PRIORITY_STATEFUL_SNAT_RULE,
GW_COMMON_TABLE,
install);
}
@@ -660,11 +745,72 @@
deviceId,
selector,
treatment,
- PRIORITY_OVS_SNAT_RULE,
+ PRIORITY_STATEFUL_SNAT_RULE,
GW_COMMON_TABLE,
install);
}
+ private void setRulesToController(DeviceId deviceId, String segmentId, IpPrefix srcSubnet,
+ NetworkType networkType, boolean install) {
+ TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
+ .matchEthType(Ethernet.TYPE_IPV4)
+ .matchIPSrc(srcSubnet);
+
+ switch (networkType) {
+ case VXLAN:
+ sBuilder.matchTunnelId(Long.parseLong(segmentId))
+ .matchEthDst(Constants.DEFAULT_GATEWAY_MAC);
+ break;
+ case VLAN:
+ sBuilder.matchVlanId(VlanId.vlanId(segmentId))
+ .matchEthDst(osNodeService.node(deviceId).vlanPortMac());
+ break;
+ default:
+ final String error = String.format(
+ ERR_UNSUPPORTED_NET_TYPE + "%s",
+ networkType.toString());
+ throw new IllegalStateException(error);
+ }
+
+ TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder()
+ .setEthDst(Constants.DEFAULT_GATEWAY_MAC);
+
+ if (networkType.equals(NetworkType.VLAN)) {
+ tBuilder.popVlan();
+ }
+
+ tBuilder.setOutput(PortNumber.CONTROLLER);
+
+ osFlowRuleService.setRule(
+ appId,
+ deviceId,
+ sBuilder.build(),
+ tBuilder.build(),
+ PRIORITY_EXTERNAL_ROUTING_RULE,
+ GW_COMMON_TABLE,
+ install);
+
+
+ // Sends ICMP response to controller for SNATing ingress traffic
+ TrafficSelector selector = DefaultTrafficSelector.builder()
+ .matchEthType(Ethernet.TYPE_IPV4)
+ .matchIPProtocol(IPv4.PROTOCOL_ICMP)
+ .matchIcmpType(ICMP.TYPE_ECHO_REPLY)
+ .build();
+
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+ .setOutput(PortNumber.CONTROLLER)
+ .build();
+
+ osFlowRuleService.setRule(
+ appId,
+ deviceId,
+ selector,
+ treatment,
+ PRIORITY_INTERNAL_ROUTING_RULE,
+ GW_COMMON_TABLE,
+ install);
+ }
private class InternalRouterEventListener implements OpenstackRouterListener {