diff --git a/apps/openstackinterface/api/src/main/java/org/onosproject/openstackinterface/OpenstackRouterInterface.java b/apps/openstackinterface/api/src/main/java/org/onosproject/openstackinterface/OpenstackRouterInterface.java index 797c9a8b71..93d9b47410 100644 --- a/apps/openstackinterface/api/src/main/java/org/onosproject/openstackinterface/OpenstackRouterInterface.java +++ b/apps/openstackinterface/api/src/main/java/org/onosproject/openstackinterface/OpenstackRouterInterface.java @@ -20,7 +20,7 @@ import java.util.Objects; import static com.google.common.base.Preconditions.checkNotNull; /** - * An Openstack Neutron Router Interface Model. + * An OpenStack Neutron router interface model. */ public final class OpenstackRouterInterface { private final String id; @@ -37,9 +37,9 @@ public final class OpenstackRouterInterface { } /** - * Returns Router Interface ID. + * Returns router interface ID. * - * @return router interface ID + * @return router interface id */ public String id() { return id; @@ -48,7 +48,7 @@ public final class OpenstackRouterInterface { /** * Returns tenant ID. * - * @return tenant ID + * @return tenant id */ public String tenantId() { return tenantId; @@ -57,7 +57,7 @@ public final class OpenstackRouterInterface { /** * Returns subnet ID. * - * @return subnet ID + * @return subnet id */ public String subnetId() { return subnetId; @@ -66,7 +66,7 @@ public final class OpenstackRouterInterface { /** * Returns port ID. * - * @return port ID + * @return port id */ public String portId() { return portId; @@ -96,7 +96,16 @@ public final class OpenstackRouterInterface { } /** - * An Openstack Router Interface Builder class. + * Returns OpenStack router interface builder. + * + * @return openstack router interface builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * An OpenStack Router interface builder class. */ public static final class Builder { private String id; @@ -105,10 +114,10 @@ public final class OpenstackRouterInterface { private String portId; /** - * Sets Router Interface ID. + * Sets router interface ID. * - * @param id router interface ID - * @return Builder object + * @param id router interface id + * @return builder object */ public Builder id(String id) { this.id = id; @@ -119,7 +128,7 @@ public final class OpenstackRouterInterface { * Sets tenant ID. * * @param tenantId tenant ID - * @return Builder object + * @return builder object */ public Builder tenantId(String tenantId) { this.tenantId = tenantId; @@ -130,7 +139,7 @@ public final class OpenstackRouterInterface { * Sets subnet ID. * * @param subnetId subnet ID - * @return Builder object + * @return builder object */ public Builder subnetId(String subnetId) { this.subnetId = subnetId; @@ -141,7 +150,7 @@ public final class OpenstackRouterInterface { * Sets port ID. * * @param portId port ID - * @return Builder object + * @return builder object */ public Builder portId(String portId) { this.portId = portId; @@ -149,14 +158,13 @@ public final class OpenstackRouterInterface { } /** - * Builds an Openstack Router Interface object. + * Builds an OpenStack router interface object. * - * @return OpenstackRouterInterface object + * @return openstack router interface object */ public OpenstackRouterInterface build() { return new OpenstackRouterInterface(checkNotNull(id), checkNotNull(tenantId), checkNotNull(subnetId), checkNotNull(portId)); } - } } diff --git a/apps/openstacknetworking/switching/src/main/java/org/onosproject/openstacknetworking/switching/AbstractVmHandler.java b/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/AbstractVmHandler.java similarity index 92% rename from apps/openstacknetworking/switching/src/main/java/org/onosproject/openstacknetworking/switching/AbstractVmHandler.java rename to apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/AbstractVmHandler.java index d7b7a8a0a5..58b89a662f 100644 --- a/apps/openstacknetworking/switching/src/main/java/org/onosproject/openstacknetworking/switching/AbstractVmHandler.java +++ b/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/AbstractVmHandler.java @@ -13,20 +13,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.onosproject.openstacknetworking.switching; +package org.onosproject.openstacknetworking; import org.onlab.osgi.DefaultServiceDirectory; import org.onlab.osgi.ServiceDirectory; import org.onlab.packet.Ip4Address; import org.onlab.util.Tools; -import org.onosproject.core.ApplicationId; import org.onosproject.core.CoreService; import org.onosproject.mastership.MastershipService; import org.onosproject.net.Host; import org.onosproject.net.host.HostEvent; import org.onosproject.net.host.HostListener; import org.onosproject.net.host.HostService; -import org.onosproject.openstacknetworking.Constants; import org.slf4j.Logger; import java.util.Objects; @@ -53,7 +51,6 @@ public abstract class AbstractVmHandler { protected MastershipService mastershipService; protected HostService hostService; - protected ApplicationId appId; protected HostListener hostListener = new InternalHostListener(); protected void activate() { @@ -61,8 +58,6 @@ public abstract class AbstractVmHandler { coreService = services.get(CoreService.class); mastershipService = services.get(MastershipService.class); hostService = services.get(HostService.class); - - appId = coreService.registerApplication(Constants.APP_ID); hostService.addListener(hostListener); log.info("Started"); @@ -75,9 +70,19 @@ public abstract class AbstractVmHandler { log.info("Stopped"); } - abstract void hostDetected(Host host); + /** + * Performs any action when a host is detected. + * + * @param host detected host + */ + protected abstract void hostDetected(Host host); - abstract void hostRemoved(Host host); + /** + * Performs any action when a host is removed. + * + * @param host removed host + */ + protected abstract void hostRemoved(Host host); protected boolean isValidHost(Host host) { return !host.ipAddresses().isEmpty() && diff --git a/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/Constants.java b/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/Constants.java index 54334b93b1..dbcb521bd9 100644 --- a/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/Constants.java +++ b/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/Constants.java @@ -28,23 +28,27 @@ public final class Constants { private Constants() { } - public static final String APP_ID = "org.onosproject.openstackswitching"; + public static final String SWITCHING_APP_ID = "org.onosproject.openstackswitching"; + public static final String ROUTING_APP_ID = "org.onosproject.openstackrouting"; - public static final String PORTNAME_PREFIX_VM = "tap"; - public static final String PORTNAME_PREFIX_ROUTER = "qr-"; - public static final String PORTNAME_PREFIX_TUNNEL = "vxlan"; + public static final String DEVICE_OWNER_ROUTER_INTERFACE = "network:router_interface"; + public static final String DEVICE_OWNER_ROUTER_GATEWAY = "network:router_gateway"; + public static final String DEVICE_OWNER_FLOATING_IP = "network:floatingip"; - public static final MacAddress GATEWAY_MAC = MacAddress.valueOf("1f:1f:1f:1f:1f:1f"); + public static final String PORT_NAME_PREFIX_VM = "tap"; + public static final String PORT_NAME_PREFIX_TUNNEL = "vxlan"; - // TODO: Please change these valuses following the way vrouter is implemented - public static final MacAddress GW_EXT_INT_MAC = MacAddress.valueOf("56:e6:30:a6:8c:e5"); - public static final MacAddress PHY_ROUTER_MAC = MacAddress.valueOf("00:00:00:00:01:01"); + public static final String DEFAULT_GATEWAY_MAC_STR = "fe:00:00:00:00:02"; + public static final MacAddress DEFAULT_GATEWAY_MAC = MacAddress.valueOf(DEFAULT_GATEWAY_MAC_STR); + // TODO make this configurable + public static final MacAddress DEFAULT_EXTERNAL_ROUTER_MAC = MacAddress.valueOf("fe:00:00:00:00:01"); public static final Ip4Address DNS_SERVER_IP = Ip4Address.valueOf("8.8.8.8"); public static final IpPrefix IP_PREFIX_ANY = Ip4Prefix.valueOf("0.0.0.0/0"); public static final int DHCP_INFINITE_LEASE = -1; public static final String NETWORK_ID = "networkId"; + public static final String SUBNET_ID = "subnetId"; public static final String PORT_ID = "portId"; public static final String VXLAN_ID = "vxlanId"; public static final String TENANT_ID = "tenantId"; @@ -55,4 +59,8 @@ public final class Constants { public static final int TUNNELTAG_RULE_PRIORITY = 30000; public static final int ACL_RULE_PRIORITY = 30000; + public static final int ROUTING_RULE_PRIORITY = 25000; + public static final int FLOATING_RULE_PRIORITY = 42000; + public static final int PNAT_RULE_PRIORITY = 26000; + public static final int PNAT_TIMEOUT = 120; } \ No newline at end of file diff --git a/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/OpenstackFloatingIpService.java b/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/OpenstackFloatingIpService.java new file mode 100644 index 0000000000..5f9f6b3c02 --- /dev/null +++ b/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/OpenstackFloatingIpService.java @@ -0,0 +1,50 @@ +/* + * Copyright 2016-present 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.openstacknetworking; + +import org.onosproject.openstackinterface.OpenstackFloatingIP; + +/** + * Handles floating IP update requests from OpenStack. + */ +public interface OpenstackFloatingIpService { + + enum Action { + ASSOCIATE, + DISSASSOCIATE + } + + /** + * Handles floating IP create request from OpenStack. + * + * @param floatingIp floating IP information + */ + void createFloatingIp(OpenstackFloatingIP floatingIp); + + /** + * Handles floating IP update request from OpenStack. + * + * @param floatingIp floating IP information + */ + void updateFloatingIp(OpenstackFloatingIP floatingIp); + + /** + * Handles floating IP remove request from OpenStack. + * + * @param floatingIpId floating ip identifier + */ + void deleteFloatingIp(String floatingIpId); +} diff --git a/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/OpenstackRoutingService.java b/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/OpenstackRoutingService.java index 327d9e487e..9923e6ff3a 100644 --- a/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/OpenstackRoutingService.java +++ b/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/OpenstackRoutingService.java @@ -15,76 +15,49 @@ */ package org.onosproject.openstacknetworking; -import org.onosproject.openstackinterface.OpenstackFloatingIP; import org.onosproject.openstackinterface.OpenstackRouter; import org.onosproject.openstackinterface.OpenstackRouterInterface; /** - * Supports L3 management REST API for openstack. + * Handles router update requests from OpenStack. */ public interface OpenstackRoutingService { /** - * Stores the floating IP information created by openstack. + * Handles the router create request from OpenStack. * - * @param openstackFloatingIp Floating IP information + * @param osRouter router information */ - void createFloatingIP(OpenstackFloatingIP openstackFloatingIp); + void createRouter(OpenstackRouter osRouter); /** - * Updates flow rules corresponding to the floating IP information updated by openstack. + * Handles the router update request from OpenStack. + * Update router is called when the name, administrative state, or the external + * gateway setting is updated. The external gateway update is the only case + * that openstack routing service cares. * - * @param openstackFloatingIp Floating IP information + * @param osRouter router information */ - void updateFloatingIP(OpenstackFloatingIP openstackFloatingIp); + void updateRouter(OpenstackRouter osRouter); /** - * Removes flow rules corresponding to floating IP information removed by openstack. + * Handles the router remove request from OpenStack. * - * @param id Deleted Floating IP`s ID + * @param osRouterId identifier of the router */ - void deleteFloatingIP(String id); + void removeRouter(String osRouterId); /** - * Stores the router information created by openstack. + * Handles router interface add request from OpenStack. * - * @param openstackRouter Router information + * @param osInterface router interface information */ - void createRouter(OpenstackRouter openstackRouter); + void addRouterInterface(OpenstackRouterInterface osInterface); /** - * Updates flow rules corresponding to the router information updated by openstack. + * Handles router interface remove request from OpenStack. * - * @param openstackRouter Router information + * @param osInterface router interface information */ - void updateRouter(OpenstackRouter openstackRouter); - - /** - * Removes flow rules corresponding to the router information removed by openstack. - * - * @param id Deleted router`s ID - */ - void deleteRouter(String id); - - /** - * Updates flow rules corresponding to the router information updated by openstack. - * - * @param openstackRouterInterface Router interface information - */ - void updateRouterInterface(OpenstackRouterInterface openstackRouterInterface); - - /** - * Removes flow rules corresponding to the router information removed by openstack. - * - * @param openstackRouterInterface Router interface information - */ - void removeRouterInterface(OpenstackRouterInterface openstackRouterInterface); - - /** - * Returns network id for routerInterface. - * - * @param portId routerInterface`s port id - * @return network id - */ - String networkIdForRouterInterface(String portId); + void removeRouterInterface(OpenstackRouterInterface osInterface); } diff --git a/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/RulePopulatorUtil.java b/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/RulePopulatorUtil.java new file mode 100644 index 0000000000..d220b8c20c --- /dev/null +++ b/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/RulePopulatorUtil.java @@ -0,0 +1,102 @@ +/* + * Copyright 2016-present 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.openstacknetworking; + +import org.onlab.packet.Ip4Address; +import org.onosproject.core.ApplicationId; +import org.onosproject.net.Device; +import org.onosproject.net.DeviceId; +import org.onosproject.net.behaviour.ExtensionTreatmentResolver; +import org.onosproject.net.device.DeviceService; +import org.onosproject.net.flow.DefaultTrafficTreatment; +import org.onosproject.net.flow.TrafficSelector; +import org.onosproject.net.flow.instructions.ExtensionPropertyException; +import org.onosproject.net.flow.instructions.ExtensionTreatment; +import org.onosproject.net.flowobjective.DefaultForwardingObjective; +import org.onosproject.net.flowobjective.FlowObjectiveService; +import org.onosproject.net.flowobjective.ForwardingObjective; +import org.slf4j.Logger; + +import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_SET_TUNNEL_DST; +import static org.slf4j.LoggerFactory.getLogger; + +/** + * Provides common methods to help populating flow rules for SONA applications. + */ +public final class RulePopulatorUtil { + + protected static final Logger log = getLogger(RulePopulatorUtil.class); + + private static final String TUNNEL_DST = "tunnelDst"; + + private RulePopulatorUtil() { + } + + /** + * Returns tunnel destination extension treatment object. + * + * @param deviceService driver service + * @param deviceId device id to apply this treatment + * @param remoteIp tunnel destination ip address + * @return extension treatment + */ + public static ExtensionTreatment buildExtension(DeviceService deviceService, + DeviceId deviceId, + Ip4Address remoteIp) { + Device device = deviceService.getDevice(deviceId); + if (device != null && !device.is(ExtensionTreatmentResolver.class)) { + log.error("The extension treatment is not supported"); + return null; + } + + ExtensionTreatmentResolver resolver = device.as(ExtensionTreatmentResolver.class); + ExtensionTreatment treatment = resolver.getExtensionInstruction(NICIRA_SET_TUNNEL_DST.type()); + try { + treatment.setPropertyValue(TUNNEL_DST, remoteIp); + return treatment; + } catch (ExtensionPropertyException e) { + log.warn("Failed to get tunnelDst extension treatment for {}", deviceId); + return null; + } + } + + /** + * Removes flow rules with the supplied information. + * + * @param flowObjectiveService flow objective service + * @param appId application id + * @param deviceId device id to remove this flow rule + * @param selector traffic selector + * @param flag flag + * @param priority priority + */ + public static void removeRule(FlowObjectiveService flowObjectiveService, + ApplicationId appId, + DeviceId deviceId, + TrafficSelector selector, + ForwardingObjective.Flag flag, + int priority) { + ForwardingObjective fo = DefaultForwardingObjective.builder() + .withSelector(selector) + .withTreatment(DefaultTrafficTreatment.builder().build()) + .withFlag(flag) + .withPriority(priority) + .fromApp(appId) + .remove(); + + flowObjectiveService.forward(deviceId, fo); + } +} diff --git a/apps/openstacknetworking/routing/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackFloatingIPHandler.java b/apps/openstacknetworking/routing/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackFloatingIPHandler.java deleted file mode 100644 index 3d1c013576..0000000000 --- a/apps/openstacknetworking/routing/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackFloatingIPHandler.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2016-present 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.openstacknetworking.routing; - -import org.onosproject.net.Host; -import org.onosproject.openstackinterface.OpenstackFloatingIP; - -/** - * Handle FloatingIP Event for Managing Flow Rules In Openstack Nodes. - */ -public class OpenstackFloatingIPHandler implements Runnable { - - public enum Action { - ASSOCIATE, - DISSASSOCIATE - } - - private final OpenstackFloatingIP floatingIP; - private final OpenstackRoutingRulePopulator rulePopulator; - private final Host host; - private final Action action; - - - OpenstackFloatingIPHandler(OpenstackRoutingRulePopulator rulePopulator, - OpenstackFloatingIP openstackFloatingIP, Action action, Host host) { - this.floatingIP = openstackFloatingIP; - this.rulePopulator = rulePopulator; - this.action = action; - this.host = host; - } - - @Override - public void run() { - if (action == Action.ASSOCIATE) { - rulePopulator.populateFloatingIpRules(floatingIP); - } else { - rulePopulator.removeFloatingIpRules(floatingIP, host); - } - } -} diff --git a/apps/openstacknetworking/routing/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackFloatingIpManager.java b/apps/openstacknetworking/routing/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackFloatingIpManager.java new file mode 100644 index 0000000000..ba60fe8713 --- /dev/null +++ b/apps/openstacknetworking/routing/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackFloatingIpManager.java @@ -0,0 +1,329 @@ +/* + * Copyright 2016-present 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.openstacknetworking.routing; + +import com.google.common.base.Strings; +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.Reference; +import org.apache.felix.scr.annotations.ReferenceCardinality; +import org.apache.felix.scr.annotations.Service; +import org.onlab.packet.Ethernet; +import org.onlab.packet.IpAddress; +import org.onlab.util.KryoNamespace; +import org.onlab.util.Tools; +import org.onosproject.core.ApplicationId; +import org.onosproject.core.CoreService; +import org.onosproject.net.DeviceId; +import org.onosproject.net.Host; +import org.onosproject.net.device.DeviceService; +import org.onosproject.net.flow.DefaultTrafficSelector; +import org.onosproject.net.flow.DefaultTrafficTreatment; +import org.onosproject.net.flow.TrafficSelector; +import org.onosproject.net.flow.TrafficTreatment; +import org.onosproject.net.flowobjective.DefaultForwardingObjective; +import org.onosproject.net.flowobjective.FlowObjectiveService; +import org.onosproject.net.flowobjective.ForwardingObjective; +import org.onosproject.net.host.HostService; +import org.onosproject.openstackinterface.OpenstackFloatingIP; +import org.onosproject.openstacknetworking.Constants; +import org.onosproject.openstacknetworking.OpenstackFloatingIpService; +import org.onosproject.openstacknetworking.RulePopulatorUtil; +import org.onosproject.openstacknode.OpenstackNode; +import org.onosproject.openstacknode.OpenstackNodeEvent; +import org.onosproject.openstacknode.OpenstackNodeListener; +import org.onosproject.openstacknode.OpenstackNodeService; +import org.onosproject.scalablegateway.api.GatewayNode; +import org.onosproject.scalablegateway.api.ScalableGatewayService; +import org.onosproject.store.serializers.KryoNamespaces; +import org.onosproject.store.service.ConsistentMap; +import org.onosproject.store.service.Serializer; +import org.onosproject.store.service.StorageService; +import org.onosproject.store.service.Versioned; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Objects; +import java.util.Optional; +import java.util.concurrent.ExecutorService; + +import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor; +import static org.onlab.util.Tools.groupedThreads; +import static org.onosproject.openstacknetworking.Constants.*; +import static org.onosproject.openstacknetworking.RulePopulatorUtil.buildExtension; +import static org.onosproject.openstacknode.OpenstackNodeService.NodeType.GATEWAY; + + +@Service +@Component(immediate = true) +public class OpenstackFloatingIpManager implements OpenstackFloatingIpService { + + private final Logger log = LoggerFactory.getLogger(getClass()); + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected CoreService coreService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected HostService hostService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected DeviceService deviceService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected FlowObjectiveService flowObjectiveService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected StorageService storageService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected OpenstackNodeService nodeService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected ScalableGatewayService gatewayService; + + private static final String NOT_ASSOCIATED = "null"; + private static final KryoNamespace.Builder FLOATING_IP_SERIALIZER = + KryoNamespace.newBuilder().register(KryoNamespaces.API); + + private final ExecutorService eventExecutor = newSingleThreadScheduledExecutor( + groupedThreads(this.getClass().getSimpleName(), "event-handler", log)); + private final InternalNodeListener nodeListener = new InternalNodeListener(); + private ConsistentMap floatingIpMap; + + private ApplicationId appId; + + @Activate + protected void activate() { + appId = coreService.registerApplication(ROUTING_APP_ID); + nodeService.addListener(nodeListener); + floatingIpMap = storageService.consistentMapBuilder() + .withSerializer(Serializer.using(FLOATING_IP_SERIALIZER.build())) + .withName("openstackrouting-floatingip") + .withApplicationId(appId) + .build(); + + log.info("Started"); + } + + @Deactivate + protected void deactivate() { + nodeService.removeListener(nodeListener); + log.info("Stopped"); + } + + @Override + public void createFloatingIp(OpenstackFloatingIP floatingIp) { + } + + @Override + public void updateFloatingIp(OpenstackFloatingIP floatingIp) { + if (Strings.isNullOrEmpty(floatingIp.portId()) || + floatingIp.portId().equals(NOT_ASSOCIATED)) { + eventExecutor.execute(() -> disassociateFloatingIp(floatingIp)); + } else { + eventExecutor.execute(() -> associateFloatingIp(floatingIp)); + } + } + + @Override + public void deleteFloatingIp(String floatingIpId) { + } + + private void associateFloatingIp(OpenstackFloatingIP floatingIp) { + Optional associatedVm = Tools.stream(hostService.getHosts()) + .filter(host -> Objects.equals( + host.annotations().value(PORT_ID), + floatingIp.portId())) + .findAny(); + if (!associatedVm.isPresent()) { + log.warn("Failed to associate floating IP({}) to port:{}", + floatingIp.floatingIpAddress(), + floatingIp.portId()); + return; + } + + floatingIpMap.put(floatingIp.floatingIpAddress(), associatedVm.get()); + populateFloatingIpRules(floatingIp.floatingIpAddress(), associatedVm.get()); + + log.info("Associated floating IP {} to fixed IP {}", + floatingIp.floatingIpAddress(), floatingIp.fixedIpAddress()); + } + + private void disassociateFloatingIp(OpenstackFloatingIP floatingIp) { + Versioned associatedVm = floatingIpMap.remove(floatingIp.floatingIpAddress()); + if (associatedVm == null) { + log.warn("Failed to disassociate floating IP({})", + floatingIp.floatingIpAddress()); + // No VM is actually associated with the floating IP, do nothing + return; + } + + removeFloatingIpRules(floatingIp.floatingIpAddress(), associatedVm.value()); + log.info("Disassociated floating IP {} from fixed IP {}", + floatingIp.floatingIpAddress(), + associatedVm.value().ipAddresses()); + } + + private void populateFloatingIpRules(IpAddress floatingIp, Host associatedVm) { + populateFloatingIpIncomingRules(floatingIp, associatedVm); + populateFloatingIpOutgoingRules(floatingIp, associatedVm); + } + + private void removeFloatingIpRules(IpAddress floatingIp, Host associatedVm) { + Optional fixedIp = associatedVm.ipAddresses().stream().findFirst(); + if (!fixedIp.isPresent()) { + log.warn("Failed to remove floating IP({}) from {}", + floatingIp, associatedVm); + return; + } + + TrafficSelector.Builder sOutgoingBuilder = DefaultTrafficSelector.builder(); + TrafficSelector.Builder sIncomingBuilder = DefaultTrafficSelector.builder(); + + sOutgoingBuilder.matchEthType(Ethernet.TYPE_IPV4) + .matchTunnelId(Long.valueOf(associatedVm.annotations().value(VXLAN_ID))) + .matchIPSrc(fixedIp.get().toIpPrefix()); + + sIncomingBuilder.matchEthType(Ethernet.TYPE_IPV4) + .matchIPDst(floatingIp.toIpPrefix()); + + gatewayService.getGatewayDeviceIds().stream().forEach(deviceId -> { + RulePopulatorUtil.removeRule( + flowObjectiveService, + appId, + deviceId, + sOutgoingBuilder.build(), + ForwardingObjective.Flag.VERSATILE, + FLOATING_RULE_PRIORITY); + + RulePopulatorUtil.removeRule( + flowObjectiveService, + appId, + deviceId, + sIncomingBuilder.build(), + ForwardingObjective.Flag.VERSATILE, + FLOATING_RULE_PRIORITY); + }); + } + + private void populateFloatingIpIncomingRules(IpAddress floatingIp, Host associatedVm) { + DeviceId cnodeId = associatedVm.location().deviceId(); + Optional dataIp = nodeService.dataIp(cnodeId); + Optional fixedIp = associatedVm.ipAddresses().stream().findFirst(); + + if (!fixedIp.isPresent() || !dataIp.isPresent()) { + log.warn("Failed to associate floating IP({})", floatingIp); + return; + } + + TrafficSelector selector = DefaultTrafficSelector.builder() + .matchEthType(Ethernet.TYPE_IPV4) + .matchIPDst(floatingIp.toIpPrefix()) + .build(); + + gatewayService.getGatewayDeviceIds().stream().forEach(gnodeId -> { + TrafficTreatment treatment = DefaultTrafficTreatment.builder() + .setEthSrc(Constants.DEFAULT_GATEWAY_MAC) + .setEthDst(associatedVm.mac()) + .setIpDst(associatedVm.ipAddresses().stream().findFirst().get()) + .setTunnelId(Long.valueOf(associatedVm.annotations().value(VXLAN_ID))) + .extension(buildExtension(deviceService, cnodeId, dataIp.get().getIp4Address()), + cnodeId) + .setOutput(nodeService.tunnelPort(gnodeId).get()) + .build(); + + ForwardingObjective fo = DefaultForwardingObjective.builder() + .withSelector(selector) + .withTreatment(treatment) + .withFlag(ForwardingObjective.Flag.VERSATILE) + .withPriority(FLOATING_RULE_PRIORITY) + .fromApp(appId) + .add(); + + flowObjectiveService.forward(gnodeId, fo); + }); + } + + private void populateFloatingIpOutgoingRules(IpAddress floatingIp, Host associatedVm) { + TrafficSelector selector = DefaultTrafficSelector.builder() + .matchEthType(Ethernet.TYPE_IPV4) + .matchTunnelId(Long.valueOf(associatedVm.annotations().value(VXLAN_ID))) + .matchIPSrc(associatedVm.ipAddresses().stream().findFirst().get().toIpPrefix()) + .build(); + + gatewayService.getGatewayDeviceIds().stream().forEach(gnodeId -> { + TrafficTreatment treatment = DefaultTrafficTreatment.builder() + .setIpSrc(floatingIp) + .setEthSrc(Constants.DEFAULT_GATEWAY_MAC) + .setEthDst(Constants.DEFAULT_EXTERNAL_ROUTER_MAC) + .setOutput(gatewayService.getUplinkPort(gnodeId)) + .build(); + + ForwardingObjective fo = DefaultForwardingObjective.builder() + .withSelector(selector) + .withTreatment(treatment) + .withFlag(ForwardingObjective.Flag.VERSATILE) + .withPriority(FLOATING_RULE_PRIORITY) + .fromApp(appId) + .add(); + + flowObjectiveService.forward(gnodeId, fo); + }); + } + + private void reloadFloatingIpRules() { + floatingIpMap.entrySet().stream().forEach(entry -> { + IpAddress floatingIp = entry.getKey(); + Host associatedVm = entry.getValue().value(); + + populateFloatingIpRules(floatingIp, associatedVm); + log.debug("Reload floating IP {} mapped to {}", + floatingIp, associatedVm.ipAddresses()); + }); + } + + // TODO apply existing floating IPs on service start-up by handling host event + // TODO consider the case that port with associated floating IP is attached to a VM + + private class InternalNodeListener implements OpenstackNodeListener { + + @Override + public void event(OpenstackNodeEvent event) { + OpenstackNode node = event.node(); + + switch (event.type()) { + case COMPLETE: + if (node.type() == GATEWAY) { + log.info("GATEWAY node {} detected", node.hostname()); + GatewayNode gnode = GatewayNode.builder() + .gatewayDeviceId(node.intBridge()) + .dataIpAddress(node.dataIp().getIp4Address()) + .uplinkIntf(node.externalPortName().get()) + .build(); + gatewayService.addGatewayNode(gnode); + eventExecutor.execute(OpenstackFloatingIpManager.this::reloadFloatingIpRules); + } + break; + case INIT: + case DEVICE_CREATED: + case INCOMPLETE: + default: + break; + } + } + } +} diff --git a/apps/openstacknetworking/routing/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackIcmpHandler.java b/apps/openstacknetworking/routing/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackIcmpHandler.java index 7db84bb703..b6bf09acd3 100644 --- a/apps/openstacknetworking/routing/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackIcmpHandler.java +++ b/apps/openstacknetworking/routing/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackIcmpHandler.java @@ -16,322 +16,319 @@ package org.onosproject.openstacknetworking.routing; import com.google.common.collect.Maps; +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.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.Ip4Address; -import org.onlab.packet.IpAddress; -import org.onlab.packet.MacAddress; import org.onosproject.core.ApplicationId; +import org.onosproject.core.CoreService; import org.onosproject.net.DeviceId; import org.onosproject.net.Host; -import org.onosproject.net.Port; -import org.onosproject.net.PortNumber; -import org.onosproject.net.device.DeviceService; import org.onosproject.net.flow.DefaultTrafficSelector; import org.onosproject.net.flow.DefaultTrafficTreatment; import org.onosproject.net.flow.TrafficSelector; import org.onosproject.net.flow.TrafficTreatment; import org.onosproject.net.host.HostService; import org.onosproject.net.packet.DefaultOutboundPacket; +import org.onosproject.net.packet.InboundPacket; import org.onosproject.net.packet.OutboundPacket; import org.onosproject.net.packet.PacketContext; import org.onosproject.net.packet.PacketPriority; +import org.onosproject.net.packet.PacketProcessor; import org.onosproject.net.packet.PacketService; +import org.onosproject.openstackinterface.OpenstackRouter; import org.onosproject.openstacknetworking.Constants; import org.onosproject.openstackinterface.OpenstackInterfaceService; import org.onosproject.openstackinterface.OpenstackPort; import org.onosproject.openstacknode.OpenstackNode; +import org.onosproject.openstacknode.OpenstackNodeEvent; +import org.onosproject.openstacknode.OpenstackNodeListener; import org.onosproject.openstacknode.OpenstackNodeService; import org.onosproject.scalablegateway.api.ScalableGatewayService; import org.slf4j.Logger; import java.nio.ByteBuffer; import java.util.Map; +import java.util.Objects; import java.util.Optional; +import java.util.concurrent.ExecutorService; -import static com.google.common.base.Preconditions.checkNotNull; - +import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor; +import static org.onlab.util.Tools.groupedThreads; +import static org.onosproject.openstacknetworking.Constants.*; +import static org.onosproject.openstacknode.OpenstackNodeService.NodeType.GATEWAY; import static org.slf4j.LoggerFactory.getLogger; /** - * Handle ICMP packet sent from Openstack Gateway nodes. + * Handle ICMP packet sent from OpenStack Gateway nodes. + * For a request to any private network gateway IPs, it generates fake reply. + * For a request to the external network, it does source NAT with a public IP and + * forward the request to the external only if the request instance has external + * connection setups. */ +@Component(immediate = true) public class OpenstackIcmpHandler { protected final Logger log = getLogger(getClass()); + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected CoreService coreService; - private static final String NETWORK_ROUTER_INTERFACE = "network:router_interface"; - private static final String PORTNAME = "portName"; - private static final String NETWORK_ROUTER_GATEWAY = "network:router_gateway"; - private static final String NETWORK_FLOATING_IP = "network:floatingip"; + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected PacketService packetService; - private final PacketService packetService; - private final DeviceService deviceService; - private final ScalableGatewayService gatewayService; - private final HostService hostService; + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected HostService hostService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected OpenstackInterfaceService openstackService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected ScalableGatewayService gatewayService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected OpenstackNodeService nodeService; + + private final ExecutorService eventExecutor = newSingleThreadScheduledExecutor( + groupedThreads(this.getClass().getSimpleName(), "event-handler", log)); + private final InternalPacketProcessor packetProcessor = new InternalPacketProcessor(); + private final InternalNodeListener nodeListener = new InternalNodeListener(); private final Map icmpInfoMap = Maps.newHashMap(); - private final OpenstackInterfaceService openstackService; - private final OpenstackNodeService nodeService; - /** - * Default constructor. - * - * @param packetService packet service - * @param deviceService device service - * @param openstackService openstackInterface service - */ - OpenstackIcmpHandler(PacketService packetService, - DeviceService deviceService, - HostService hostService, - OpenstackInterfaceService openstackService, - OpenstackNodeService nodeService, - ScalableGatewayService gatewayService - ) { - this.packetService = packetService; - this.deviceService = deviceService; - this.hostService = hostService; - this.openstackService = checkNotNull(openstackService); - this.nodeService = nodeService; - this.gatewayService = gatewayService; + ApplicationId appId; + + @Activate + protected void activate() { + appId = coreService.registerApplication(ROUTING_APP_ID); + packetService.addProcessor(packetProcessor, PacketProcessor.director(1)); + nodeService.addListener(nodeListener); + requestPacket(appId); + + log.info("Started"); } - /** - * Requests ICMP packet. - * - * @param appId Application Id - */ - public void requestPacket(ApplicationId appId) { + @Deactivate + protected void deactivate() { + packetService.removeProcessor(packetProcessor); + log.info("Stopped"); + } + + private void requestPacket(ApplicationId appId) { TrafficSelector icmpSelector = DefaultTrafficSelector.builder() .matchEthType(Ethernet.TYPE_IPV4) .matchIPProtocol(IPv4.PROTOCOL_ICMP) .build(); - // TODO: Return the correct gateway node - Optional gwNode = nodeService.nodes().stream() - .filter(n -> n.type().equals(OpenstackNodeService.NodeType.GATEWAY)) - .findFirst(); - - if (!gwNode.isPresent()) { - log.warn("No Gateway is defined."); - return; - } - - packetService.requestPackets(icmpSelector, - PacketPriority.CONTROL, - appId, - Optional.of(gwNode.get().intBridge())); + gatewayService.getGatewayDeviceIds().stream().forEach(gateway -> { + packetService.requestPackets(icmpSelector, + PacketPriority.CONTROL, + appId, + Optional.of(gateway)); + log.debug("Requested ICMP packet on {}", gateway); + }); } - /** - * Handles ICMP packet. - * - * @param context packet context - * @param ethernet ethernet - */ - public void processIcmpPacket(PacketContext context, Ethernet ethernet) { - checkNotNull(context, "context can not be null"); - checkNotNull(ethernet, "ethernet can not be null"); - + private void processIcmpPacket(PacketContext context, Ethernet ethernet) { IPv4 ipPacket = (IPv4) ethernet.getPayload(); - - log.debug("icmpEvent called from ip {}, mac {}", Ip4Address.valueOf(ipPacket.getSourceAddress()).toString(), - ethernet.getSourceMAC().toString()); + log.trace("Processing ICMP packet from ip {}, mac {}", + Ip4Address.valueOf(ipPacket.getSourceAddress()), + ethernet.getSourceMAC()); ICMP icmp = (ICMP) ipPacket.getPayload(); short icmpId = getIcmpId(icmp); - DeviceId deviceId = context.inPacket().receivedFrom().deviceId(); - PortNumber portNumber = context.inPacket().receivedFrom().port(); - if (icmp.getIcmpType() == ICMP.TYPE_ECHO_REQUEST) { - //TODO: Considers icmp between internal subnets which are belonged to the same router. - Optional host = hostService.getHostsByMac(ethernet.getSourceMAC()).stream().findFirst(); - if (!host.isPresent()) { - log.warn("No host found for MAC {}", ethernet.getSourceMAC()); - return; - } + DeviceId srcDevice = context.inPacket().receivedFrom().deviceId(); + switch (icmp.getIcmpType()) { + case ICMP.TYPE_ECHO_REQUEST: + Optional reqHost = hostService.getHostsByMac(ethernet.getSourceMAC()) + .stream().findFirst(); + if (!reqHost.isPresent()) { + log.warn("No host found for MAC {}", ethernet.getSourceMAC()); + return; + } - IpAddress gatewayIp = IpAddress.valueOf(host.get().annotations().value(Constants.GATEWAY_IP)); - if (ipPacket.getDestinationAddress() == gatewayIp.getIp4Address().toInt()) { - processIcmpPacketSentToGateway(ipPacket, icmp, host.get()); - } else { - Ip4Address pNatIpAddress = pNatIpForPort(host.get()); - checkNotNull(pNatIpAddress, "pNatIpAddress can not be null"); - - sendRequestPacketToExt(ipPacket, icmp, deviceId, pNatIpAddress); + // TODO Considers icmp between internal subnets belong to the same router. + // TODO do we have to support ICMP reply for non-existing gateway? + Ip4Address gatewayIp = Ip4Address.valueOf( + reqHost.get().annotations().value(Constants.GATEWAY_IP)); + if (Objects.equals(ipPacket.getDestinationAddress(), gatewayIp.toInt())) { + processRequestToGateway(ipPacket, reqHost.get()); + } else { + Optional srcNatIp = getSrcNatIp(reqHost.get()); + if (!srcNatIp.isPresent()) { + log.trace("VM {} has no external connection", reqHost.get()); + return; + } + sendRequestToExternal(ipPacket, srcDevice, srcNatIp.get()); + String icmpInfoKey = String.valueOf(icmpId) + .concat(String.valueOf(srcNatIp.get().toInt())) + .concat(String.valueOf(ipPacket.getDestinationAddress())); + icmpInfoMap.putIfAbsent(icmpInfoKey, reqHost.get()); + } + break; + case ICMP.TYPE_ECHO_REPLY: String icmpInfoKey = String.valueOf(icmpId) - .concat(String.valueOf(pNatIpAddress.toInt())) - .concat(String.valueOf(ipPacket.getDestinationAddress())); - icmpInfoMap.putIfAbsent(icmpInfoKey, host.get()); - } - } else if (icmp.getIcmpType() == ICMP.TYPE_ECHO_REPLY) { - String icmpInfoKey = String.valueOf(icmpId) - .concat(String.valueOf(ipPacket.getDestinationAddress())) - .concat(String.valueOf(ipPacket.getSourceAddress())); + .concat(String.valueOf(ipPacket.getDestinationAddress())) + .concat(String.valueOf(ipPacket.getSourceAddress())); - processResponsePacketFromExternalToHost(ipPacket, icmp, icmpInfoMap.get(icmpInfoKey)); - - icmpInfoMap.remove(icmpInfoKey); + processReplyFromExternal(ipPacket, icmpInfoMap.get(icmpInfoKey)); + icmpInfoMap.remove(icmpInfoKey); + break; + default: + break; } } - private void processIcmpPacketSentToExtenal(IPv4 icmpRequestIpv4, ICMP icmpRequest, - int destAddr, MacAddress destMac, - DeviceId deviceId, PortNumber portNumber) { - icmpRequest.setChecksum((short) 0); - icmpRequest.setIcmpType(ICMP.TYPE_ECHO_REPLY).resetChecksum(); - icmpRequestIpv4.setSourceAddress(icmpRequestIpv4.getDestinationAddress()) - .setDestinationAddress(destAddr).resetChecksum(); - icmpRequestIpv4.setPayload(icmpRequest); - Ethernet icmpResponseEth = new Ethernet(); - icmpResponseEth.setEtherType(Ethernet.TYPE_IPV4) - // TODO: Get the correct GW MAC - .setSourceMACAddress(Constants.GW_EXT_INT_MAC) - .setDestinationMACAddress(destMac).setPayload(icmpRequestIpv4); - TrafficTreatment treatment = DefaultTrafficTreatment.builder().setOutput(portNumber).build(); - OutboundPacket packet = new DefaultOutboundPacket(deviceId, - treatment, ByteBuffer.wrap(icmpResponseEth.serialize())); - packetService.emit(packet); + // TODO do we have to handle the request to the fake gateway? + private void processRequestToGateway(IPv4 ipPacket, Host reqHost) { + ICMP icmpReq = (ICMP) ipPacket.getPayload(); + icmpReq.setChecksum((short) 0); + icmpReq.setIcmpType(ICMP.TYPE_ECHO_REPLY).resetChecksum(); + + ipPacket.setSourceAddress(ipPacket.getDestinationAddress()) + .setDestinationAddress(ipPacket.getSourceAddress()) + .resetChecksum(); + + ipPacket.setPayload(icmpReq); + Ethernet icmpReply = new Ethernet(); + icmpReply.setEtherType(Ethernet.TYPE_IPV4) + .setSourceMACAddress(Constants.DEFAULT_GATEWAY_MAC) + .setDestinationMACAddress(reqHost.mac()) + .setPayload(icmpReq); + + sendReply(icmpReply, reqHost); } - private void processIcmpPacketSentToGateway(IPv4 icmpRequestIpv4, ICMP icmpRequest, - Host host) { - icmpRequest.setChecksum((short) 0); - icmpRequest.setIcmpType(ICMP.TYPE_ECHO_REPLY) - .resetChecksum(); - - Ip4Address ipAddress = host.ipAddresses().stream().findAny().get().getIp4Address(); - icmpRequestIpv4.setSourceAddress(icmpRequestIpv4.getDestinationAddress()) - .setDestinationAddress(ipAddress.toInt()) - .resetChecksum(); - - icmpRequestIpv4.setPayload(icmpRequest); - - Ethernet icmpResponseEth = new Ethernet(); - - icmpResponseEth.setEtherType(Ethernet.TYPE_IPV4) - .setSourceMACAddress(Constants.GATEWAY_MAC) - .setDestinationMACAddress(host.mac()) - .setPayload(icmpRequestIpv4); - - sendResponsePacketToHost(icmpResponseEth, host); - } - - private void sendRequestPacketToExt(IPv4 icmpRequestIpv4, ICMP icmpRequest, DeviceId deviceId, - Ip4Address pNatIpAddress) { - icmpRequest.resetChecksum(); - icmpRequestIpv4.setSourceAddress(pNatIpAddress.toInt()) - .resetChecksum(); - icmpRequestIpv4.setPayload(icmpRequest); + private void sendRequestToExternal(IPv4 ipPacket, DeviceId srcDevice, Ip4Address srcNatIp) { + ICMP icmpReq = (ICMP) ipPacket.getPayload(); + icmpReq.resetChecksum(); + ipPacket.setSourceAddress(srcNatIp.toInt()).resetChecksum(); + ipPacket.setPayload(icmpReq); Ethernet icmpRequestEth = new Ethernet(); - icmpRequestEth.setEtherType(Ethernet.TYPE_IPV4) - // TODO: Get the correct one - Scalable Gateway ... - .setSourceMACAddress(Constants.GW_EXT_INT_MAC) - .setDestinationMACAddress(Constants.PHY_ROUTER_MAC) - .setPayload(icmpRequestIpv4); - - // TODO: Return the correct gateway node - Optional gwNode = nodeService.nodes().stream() - .filter(n -> n.type().equals(OpenstackNodeService.NodeType.GATEWAY)) - .findFirst(); - - if (!gwNode.isPresent()) { - log.warn("No Gateway is defined."); - return; - } + .setSourceMACAddress(DEFAULT_GATEWAY_MAC) + .setDestinationMACAddress(DEFAULT_EXTERNAL_ROUTER_MAC) + .setPayload(ipPacket); TrafficTreatment treatment = DefaultTrafficTreatment.builder() - // FIXME: please double check this. - .setOutput(getPortForAnnotationPortName(gwNode.get().intBridge(), - // FIXME: please double check this. - org.onosproject.openstacknode.Constants.PATCH_INTG_BRIDGE)) + .setOutput(gatewayService.getUplinkPort(srcDevice)) .build(); - OutboundPacket packet = new DefaultOutboundPacket(deviceId, - treatment, ByteBuffer.wrap(icmpRequestEth.serialize())); + OutboundPacket packet = new DefaultOutboundPacket( + srcDevice, + treatment, + ByteBuffer.wrap(icmpRequestEth.serialize())); packetService.emit(packet); } - private void processResponsePacketFromExternalToHost(IPv4 icmpResponseIpv4, ICMP icmpResponse, - Host host) { - icmpResponse.resetChecksum(); + private void processReplyFromExternal(IPv4 ipPacket, Host dstHost) { + ICMP icmpReply = (ICMP) ipPacket.getPayload(); + icmpReply.resetChecksum(); - Ip4Address ipAddress = host.ipAddresses().stream().findFirst().get().getIp4Address(); - icmpResponseIpv4.setDestinationAddress(ipAddress.toInt()) + Ip4Address ipAddress = dstHost.ipAddresses().stream().findFirst().get().getIp4Address(); + ipPacket.setDestinationAddress(ipAddress.toInt()) .resetChecksum(); - icmpResponseIpv4.setPayload(icmpResponse); + ipPacket.setPayload(icmpReply); Ethernet icmpResponseEth = new Ethernet(); - icmpResponseEth.setEtherType(Ethernet.TYPE_IPV4) - .setSourceMACAddress(Constants.GATEWAY_MAC) - .setDestinationMACAddress(host.mac()) - .setPayload(icmpResponseIpv4); + .setSourceMACAddress(Constants.DEFAULT_GATEWAY_MAC) + .setDestinationMACAddress(dstHost.mac()) + .setPayload(ipPacket); - sendResponsePacketToHost(icmpResponseEth, host); + sendReply(icmpResponseEth, dstHost); } - private void sendResponsePacketToHost(Ethernet icmpResponseEth, Host host) { - + private void sendReply(Ethernet icmpReply, Host dstHost) { TrafficTreatment treatment = DefaultTrafficTreatment.builder() - .setOutput(host.location().port()) + .setOutput(dstHost.location().port()) .build(); - OutboundPacket packet = new DefaultOutboundPacket(host.location().deviceId(), - treatment, ByteBuffer.wrap(icmpResponseEth.serialize())); + OutboundPacket packet = new DefaultOutboundPacket( + dstHost.location().deviceId(), + treatment, + ByteBuffer.wrap(icmpReply.serialize())); packetService.emit(packet); } + private Optional getSrcNatIp(Host host) { + // TODO cache external gateway IP for each network because + // asking Neutron for every ICMP request is a bad idea + Optional osPort = openstackService.ports().stream() + .filter(port -> port.deviceOwner().equals(DEVICE_OWNER_ROUTER_INTERFACE) && + Objects.equals(host.annotations().value(NETWORK_ID), + port.networkId())) + .findAny(); + if (!osPort.isPresent()) { + return Optional.empty(); + } + + OpenstackRouter osRouter = openstackService.router(osPort.get().deviceId()); + if (osRouter == null) { + return Optional.empty(); + } + + return osRouter.gatewayExternalInfo().externalFixedIps() + .values().stream().findAny(); + } + private short getIcmpId(ICMP icmp) { return ByteBuffer.wrap(icmp.serialize(), 4, 2).getShort(); } - private Ip4Address pNatIpForPort(Host host) { + private class InternalPacketProcessor implements PacketProcessor { - OpenstackPort openstackPort = openstackService.ports().stream() - .filter(p -> p.deviceOwner().equals(NETWORK_ROUTER_INTERFACE) && - p.networkId().equals(host.annotations().value(Constants.NETWORK_ID))) - .findAny().orElse(null); + @Override + public void process(PacketContext context) { + if (context.isHandled()) { + return; + } else if (!gatewayService.getGatewayDeviceIds().contains( + context.inPacket().receivedFrom().deviceId())) { + // return if the packet is not from gateway nodes + return; + } - checkNotNull(openstackPort, "openstackPort can not be null"); + InboundPacket pkt = context.inPacket(); + Ethernet ethernet = pkt.parsed(); + if (ethernet == null || ethernet.getEtherType() == Ethernet.TYPE_ARP) { + return; + } - return openstackService.router(openstackPort.deviceId()) - .gatewayExternalInfo().externalFixedIps().values() - .stream().findAny().orElse(null); - } - - private PortNumber getPortForAnnotationPortName(DeviceId deviceId, String match) { - Port port = deviceService.getPorts(deviceId).stream() - .filter(p -> p.annotations().value(PORTNAME).equals(match)) - .findAny().orElse(null); - - checkNotNull(port, "port cannot be null"); - - return port.number(); - } - - private boolean requestToOpenstackRoutingNetwork(int destAddr) { - OpenstackPort port = openstackService.ports().stream() - .filter(p -> p.deviceOwner().equals(NETWORK_ROUTER_GATEWAY) || - p.deviceOwner().equals(NETWORK_FLOATING_IP)) - .filter(p -> p.fixedIps().containsValue( - Ip4Address.valueOf(destAddr))) - .findAny().orElse(null); - if (port == null) { - return false; + IPv4 iPacket = (IPv4) ethernet.getPayload(); + if (iPacket.getProtocol() == IPv4.PROTOCOL_ICMP) { + eventExecutor.execute(() -> processIcmpPacket(context, ethernet)); + } } - return true; } - private Map getExternalInfo() { - Map externalInfoMap = Maps.newHashMap(); - gatewayService.getGatewayDeviceIds().forEach(deviceId -> - externalInfoMap.putIfAbsent(deviceId, gatewayService.getUplinkPort(deviceId))); - return externalInfoMap; + + private class InternalNodeListener implements OpenstackNodeListener { + + @Override + public void event(OpenstackNodeEvent event) { + OpenstackNode node = event.node(); + + switch (event.type()) { + case COMPLETE: + if (node.type() == GATEWAY) { + log.info("GATEWAY node {} detected", node.hostname()); + eventExecutor.execute(() -> requestPacket(appId)); + } + break; + case INIT: + case DEVICE_CREATED: + case INCOMPLETE: + default: + break; + } + } } } diff --git a/apps/openstacknetworking/routing/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackPnatHandler.java b/apps/openstacknetworking/routing/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackPnatHandler.java index 5af208dfcf..3b40aee457 100644 --- a/apps/openstacknetworking/routing/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackPnatHandler.java +++ b/apps/openstacknetworking/routing/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackPnatHandler.java @@ -15,109 +15,156 @@ */ package org.onosproject.openstacknetworking.routing; +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.Reference; +import org.apache.felix.scr.annotations.ReferenceCardinality; import org.onlab.packet.Ethernet; import org.onlab.packet.IPv4; import org.onlab.packet.Ip4Address; +import org.onlab.packet.IpAddress; +import org.onlab.packet.IpPrefix; import org.onlab.packet.MacAddress; import org.onlab.packet.TCP; +import org.onlab.packet.TpPort; import org.onlab.packet.UDP; +import org.onlab.util.KryoNamespace; +import org.onlab.util.Tools; +import org.onosproject.core.ApplicationId; +import org.onosproject.core.CoreService; import org.onosproject.net.DeviceId; -import org.onosproject.net.Port; +import org.onosproject.net.Host; +import org.onosproject.net.device.DeviceService; +import org.onosproject.net.flow.DefaultTrafficSelector; import org.onosproject.net.flow.DefaultTrafficTreatment; +import org.onosproject.net.flow.TrafficSelector; import org.onosproject.net.flow.TrafficTreatment; +import org.onosproject.net.flowobjective.DefaultForwardingObjective; +import org.onosproject.net.flowobjective.FlowObjectiveService; +import org.onosproject.net.flowobjective.ForwardingObjective; +import org.onosproject.net.host.HostService; import org.onosproject.net.packet.DefaultOutboundPacket; import org.onosproject.net.packet.InboundPacket; import org.onosproject.net.packet.PacketContext; +import org.onosproject.net.packet.PacketProcessor; import org.onosproject.net.packet.PacketService; import org.onosproject.openstackinterface.OpenstackInterfaceService; import org.onosproject.openstackinterface.OpenstackPort; import org.onosproject.openstackinterface.OpenstackRouter; -import org.onosproject.scalablegateway.api.GatewayNode; +import org.onosproject.openstacknetworking.RulePopulatorUtil; +import org.onosproject.openstacknode.OpenstackNodeService; import org.onosproject.scalablegateway.api.ScalableGatewayService; +import org.onosproject.store.serializers.KryoNamespaces; +import org.onosproject.store.service.ConsistentMap; +import org.onosproject.store.service.Serializer; +import org.onosproject.store.service.StorageService; import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import java.nio.ByteBuffer; +import java.util.Objects; +import java.util.Optional; +import java.util.concurrent.ExecutorService; -import static com.google.common.base.Preconditions.checkNotNull; -import static org.onlab.osgi.DefaultServiceDirectory.getService; - +import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor; +import static org.onlab.util.Tools.groupedThreads; +import static org.onosproject.openstacknetworking.Constants.*; +import static org.slf4j.LoggerFactory.getLogger; /** - * Handle NAT packet processing for Managing Flow Rules In Openstack Nodes. + * Handle NAT packet processing for managing flow rules in openstack nodes. */ -public class OpenstackPnatHandler implements Runnable { +@Component(immediate = true) +public class OpenstackPnatHandler { + private final Logger log = getLogger(getClass()); - volatile PacketContext context; - private final Logger log = LoggerFactory.getLogger(getClass()); + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected CoreService coreService; - private final OpenstackRoutingRulePopulator rulePopulator; - private final int portNum; - private final OpenstackPort openstackPort; - private final Port port; + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected PacketService packetService; - private static final String DEVICE_OWNER_ROUTER_INTERFACE = "network:router_interface"; - private static final String EXTERNAL_PORT_NULL = "There is no external port in this deviceId []"; + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected HostService hostService; - OpenstackPnatHandler(OpenstackRoutingRulePopulator rulePopulator, PacketContext context, - int portNum, OpenstackPort openstackPort, Port port) { - this.rulePopulator = checkNotNull(rulePopulator); - this.context = checkNotNull(context); - this.portNum = checkNotNull(portNum); - this.openstackPort = checkNotNull(openstackPort); - this.port = checkNotNull(port); + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected StorageService storageService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected FlowObjectiveService flowObjectiveService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected DeviceService deviceService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected OpenstackInterfaceService openstackService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected OpenstackNodeService nodeService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected ScalableGatewayService gatewayService; + + private static final KryoNamespace.Builder NUMBER_SERIALIZER = KryoNamespace.newBuilder() + .register(KryoNamespaces.API); + + private static final int PNAT_PORT_EXPIRE_TIME = 1200 * 1000; + private static final int TP_PORT_MINIMUM_NUM = 1024; + private static final int TP_PORT_MAXIMUM_NUM = 65535; + + private final ExecutorService eventExecutor = newSingleThreadScheduledExecutor( + groupedThreads(this.getClass().getSimpleName(), "event-handler", log)); + private final InternalPacketProcessor packetProcessor = new InternalPacketProcessor(); + + private ConsistentMap tpPortNumMap; + private ApplicationId appId; + + @Activate + protected void activate() { + appId = coreService.registerApplication(ROUTING_APP_ID); + tpPortNumMap = storageService.consistentMapBuilder() + .withSerializer(Serializer.using(NUMBER_SERIALIZER.build())) + .withName("openstackrouting-tpportnum") + .withApplicationId(appId) + .build(); + + packetService.addProcessor(packetProcessor, PacketProcessor.director(1)); + log.info("Started"); } - @Override - public void run() { - InboundPacket inboundPacket = context.inPacket(); - Ethernet ethernet = checkNotNull(inboundPacket.parsed()); + @Deactivate + protected void deactivate() { + packetService.removeProcessor(packetProcessor); + log.info("Stopped"); + } - //TODO: Considers IPV6 - if (ethernet.getEtherType() != Ethernet.TYPE_IPV4) { - log.warn("Now, we just consider IP version 4"); + private void processPnatPacket(PacketContext context, Ethernet ethernet) { + IPv4 iPacket = (IPv4) ethernet.getPayload(); + InboundPacket inboundPacket = context.inPacket(); + + int srcPort = getPortNum(ethernet.getSourceMAC(), iPacket.getDestinationAddress()); + OpenstackPort osPort = getOpenstackPort(ethernet.getSourceMAC()); + if (osPort == null) { + return; + } + Ip4Address externalGatewayIp = getExternalGatewayIp(osPort); + if (externalGatewayIp == null) { return; } - OpenstackRouter router = getOpenstackRouter(openstackPort); + populatePnatFlowRules(context.inPacket(), + osPort, + TpPort.tpPort(srcPort), + externalGatewayIp); - MacAddress externalMac = MacAddress.NONE; - MacAddress routerMac = MacAddress.NONE; - - rulePopulator.populatePnatFlowRules(inboundPacket, openstackPort, portNum, - getExternalIp(router), externalMac, routerMac); - - packetOut((Ethernet) ethernet.clone(), inboundPacket.receivedFrom().deviceId(), portNum, router); + packetOut((Ethernet) ethernet.clone(), + inboundPacket.receivedFrom().deviceId(), + srcPort, + externalGatewayIp); } - private OpenstackRouter getOpenstackRouter(OpenstackPort openstackPort) { - OpenstackInterfaceService networkingService = getService(OpenstackInterfaceService.class); - - OpenstackPort port = networkingService.ports() - .stream() - .filter(p -> p.deviceOwner().equals(DEVICE_OWNER_ROUTER_INTERFACE)) - .filter(p -> checkSameSubnet(p, openstackPort)) - .findAny() - .orElse(null); - - return checkNotNull(networkingService.router(port.deviceId())); - } - - private boolean checkSameSubnet(OpenstackPort p, OpenstackPort openstackPort) { - String key1 = checkNotNull(p.fixedIps().keySet().stream().findFirst().orElse(null)).toString(); - String key2 = checkNotNull(openstackPort.fixedIps().keySet().stream().findFirst().orElse(null)).toString(); - return key1.equals(key2) ? true : false; - } - - private Ip4Address getExternalIp(OpenstackRouter router) { - return router.gatewayExternalInfo().externalFixedIps().values().stream().findAny().orElse(null); - } - - private void packetOut(Ethernet ethernet, DeviceId deviceId, int portNum, OpenstackRouter router) { - PacketService packetService = getService(PacketService.class); - + private void packetOut(Ethernet ethernet, DeviceId deviceId, int portNum, Ip4Address externalIp) { IPv4 iPacket = (IPv4) ethernet.getPayload(); - TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder(); switch (iPacket.getProtocol()) { @@ -136,26 +183,251 @@ public class OpenstackPnatHandler implements Runnable { iPacket.setPayload(udpPacket); break; default: - log.error("Temporally, this method can process UDP and TCP protocol."); + log.trace("Temporally, this method can process UDP and TCP protocol."); return; } - iPacket.setSourceAddress(getExternalIp(router).toString()); + iPacket.setSourceAddress(externalIp.toString()); iPacket.resetChecksum(); iPacket.setParent(ethernet); ethernet.setPayload(iPacket); - ScalableGatewayService gatewayService = getService(ScalableGatewayService.class); - GatewayNode gatewayNode = gatewayService.getGatewayNode(deviceId); - if (gatewayNode.getUplinkIntf() == null) { - log.error(EXTERNAL_PORT_NULL, deviceId.toString()); - return; - } treatment.setOutput(gatewayService.getUplinkPort(deviceId)); - ethernet.resetChecksum(); - - packetService.emit(new DefaultOutboundPacket(deviceId, treatment.build(), + packetService.emit(new DefaultOutboundPacket( + deviceId, + treatment.build(), ByteBuffer.wrap(ethernet.serialize()))); } + + private int getPortNum(MacAddress sourceMac, int destinationAddress) { + int portNum = findUnusedPortNum(); + if (portNum == 0) { + clearPortNumMap(); + portNum = findUnusedPortNum(); + } + tpPortNumMap.put(portNum, sourceMac.toString().concat(":").concat(String.valueOf(destinationAddress))); + return portNum; + } + + private int findUnusedPortNum() { + for (int i = TP_PORT_MINIMUM_NUM; i < TP_PORT_MAXIMUM_NUM; i++) { + if (!tpPortNumMap.containsKey(i)) { + return i; + } + } + return 0; + } + + private void clearPortNumMap() { + tpPortNumMap.entrySet().forEach(e -> { + if (System.currentTimeMillis() - e.getValue().creationTime() > PNAT_PORT_EXPIRE_TIME) { + tpPortNumMap.remove(e.getKey()); + } + }); + } + + // TODO there can be multiple routers connected to a particular openstack port + // TODO cache router information + private Ip4Address getExternalGatewayIp(OpenstackPort osPort) { + Optional routerPort = openstackService.ports().stream() + .filter(p -> p.deviceOwner().equals(DEVICE_OWNER_ROUTER_INTERFACE)) + .filter(p -> checkSameSubnet(p, osPort)) + .findAny(); + if (!routerPort.isPresent()) { + log.warn("No router is connected to network {}", osPort.networkId()); + return null; + } + + OpenstackRouter osRouter = openstackService.router(routerPort.get().deviceId()); + if (osRouter == null) { + log.warn("Failed to get OpenStack router {}", + routerPort.get().deviceId()); + return null; + } + + return osRouter.gatewayExternalInfo().externalFixedIps().values() + .stream().findAny().orElse(null); + } + + private OpenstackPort getOpenstackPort(MacAddress srcMac) { + Optional host = hostService.getHostsByMac(srcMac).stream() + .filter(h -> h.annotations().value(PORT_ID) != null) + .findAny(); + if (!host.isPresent()) { + log.warn("Failed to find a host with MAC:{}", srcMac); + return null; + } + return openstackService.port(host.get().annotations().value(PORT_ID)); + } + + private boolean checkSameSubnet(OpenstackPort osPortA, OpenstackPort osPortB) { + return osPortA.fixedIps().keySet().stream() + .anyMatch(subnetId -> osPortB.fixedIps().keySet().contains(subnetId)); + } + + private void populatePnatFlowRules(InboundPacket inboundPacket, + OpenstackPort osPort, + TpPort patPort, + Ip4Address externalIp) { + long vni = getVni(osPort.networkId()); + populatePnatIncomingFlowRules(vni, externalIp, patPort, inboundPacket); + populatePnatOutgoingFlowRules(vni, externalIp, patPort, inboundPacket); + } + + private long getVni(String netId) { + // TODO remove this and use host vxlan annotation if applicable + return Long.parseLong(openstackService.network(netId).segmentId()); + } + + private void populatePnatOutgoingFlowRules(long vni, Ip4Address externalIp, TpPort patPort, + InboundPacket inboundPacket) { + IPv4 iPacket = (IPv4) inboundPacket.parsed().getPayload(); + + TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder(); + sBuilder.matchEthType(Ethernet.TYPE_IPV4) + .matchIPProtocol(iPacket.getProtocol()) + .matchTunnelId(vni) + .matchIPSrc(IpPrefix.valueOf(iPacket.getSourceAddress(), 32)) + .matchIPDst(IpPrefix.valueOf(iPacket.getDestinationAddress(), 32)); + + TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder(); + switch (iPacket.getProtocol()) { + case IPv4.PROTOCOL_TCP: + TCP tcpPacket = (TCP) iPacket.getPayload(); + sBuilder.matchTcpSrc(TpPort.tpPort(tcpPacket.getSourcePort())) + .matchTcpDst(TpPort.tpPort(tcpPacket.getDestinationPort())); + tBuilder.setTcpSrc(patPort) + .setEthDst(DEFAULT_EXTERNAL_ROUTER_MAC); + break; + case IPv4.PROTOCOL_UDP: + UDP udpPacket = (UDP) iPacket.getPayload(); + sBuilder.matchUdpSrc(TpPort.tpPort(udpPacket.getSourcePort())) + .matchUdpDst(TpPort.tpPort(udpPacket.getDestinationPort())); + tBuilder.setUdpSrc(patPort) + .setEthDst(DEFAULT_EXTERNAL_ROUTER_MAC); + + break; + default: + log.debug("Unsupported IPv4 protocol {}"); + break; + } + + tBuilder.setIpSrc(externalIp); + gatewayService.getGatewayNodes().stream().forEach(gateway -> { + TrafficTreatment.Builder tmpBuilder = tBuilder; + tmpBuilder.setOutput(gatewayService.getUplinkPort(gateway.getGatewayDeviceId())); + ForwardingObjective fo = DefaultForwardingObjective.builder() + .withSelector(sBuilder.build()) + .withTreatment(tmpBuilder.build()) + .withFlag(ForwardingObjective.Flag.VERSATILE) + .withPriority(PNAT_RULE_PRIORITY) + .makeTemporary(PNAT_TIMEOUT) + .fromApp(appId) + .add(); + + flowObjectiveService.forward(gateway.getGatewayDeviceId(), fo); + }); + } + + private void populatePnatIncomingFlowRules(long vni, Ip4Address externalIp, TpPort patPort, + InboundPacket inboundPacket) { + IPv4 iPacket = (IPv4) inboundPacket.parsed().getPayload(); + IpAddress internalIp = IpAddress.valueOf(iPacket.getSourceAddress()); + + TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder(); + sBuilder.matchEthType(Ethernet.TYPE_IPV4) + .matchIPProtocol(iPacket.getProtocol()) + .matchIPDst(IpPrefix.valueOf(externalIp, 32)) + .matchIPSrc(IpPrefix.valueOf(iPacket.getDestinationAddress(), 32)); + + TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder(); + tBuilder.setTunnelId(vni) + .setEthDst(inboundPacket.parsed().getSourceMAC()) + .setIpDst(internalIp); + + switch (iPacket.getProtocol()) { + case IPv4.PROTOCOL_TCP: + TCP tcpPacket = (TCP) iPacket.getPayload(); + sBuilder.matchTcpSrc(TpPort.tpPort(tcpPacket.getDestinationPort())) + .matchTcpDst(patPort); + tBuilder.setTcpDst(TpPort.tpPort(tcpPacket.getSourcePort())); + break; + case IPv4.PROTOCOL_UDP: + UDP udpPacket = (UDP) iPacket.getPayload(); + sBuilder.matchUdpSrc(TpPort.tpPort(udpPacket.getDestinationPort())) + .matchUdpDst(patPort); + tBuilder.setUdpDst(TpPort.tpPort(udpPacket.getSourcePort())); + break; + default: + break; + } + + Optional srcVm = Tools.stream(hostService.getHostsByIp(internalIp)) + .filter(host -> Objects.equals( + host.annotations().value(VXLAN_ID), + String.valueOf(vni))) + .findFirst(); + if (!srcVm.isPresent()) { + log.warn("Failed to find source VM with IP {}", internalIp); + return; + } + + gatewayService.getGatewayDeviceIds().stream().forEach(deviceId -> { + DeviceId srcDeviceId = srcVm.get().location().deviceId(); + TrafficTreatment.Builder tmpBuilder = tBuilder; + tmpBuilder.extension(RulePopulatorUtil.buildExtension( + deviceService, + deviceId, + nodeService.dataIp(srcDeviceId).get().getIp4Address()), deviceId) + .setOutput(nodeService.tunnelPort(deviceId).get()); + + ForwardingObjective fo = DefaultForwardingObjective.builder() + .withSelector(sBuilder.build()) + .withTreatment(tmpBuilder.build()) + .withFlag(ForwardingObjective.Flag.VERSATILE) + .withPriority(PNAT_RULE_PRIORITY) + .makeTemporary(PNAT_TIMEOUT) + .fromApp(appId) + .add(); + + flowObjectiveService.forward(deviceId, fo); + }); + } + + private class InternalPacketProcessor implements PacketProcessor { + + @Override + public void process(PacketContext context) { + if (context.isHandled()) { + return; + } else if (!gatewayService.getGatewayDeviceIds().contains( + context.inPacket().receivedFrom().deviceId())) { + // return if the packet is not from gateway nodes + return; + } + + InboundPacket pkt = context.inPacket(); + Ethernet ethernet = pkt.parsed(); + if (ethernet == null || ethernet.getEtherType() == Ethernet.TYPE_ARP) { + return; + } + + IPv4 iPacket = (IPv4) ethernet.getPayload(); + switch (iPacket.getProtocol()) { + case IPv4.PROTOCOL_ICMP: + break; + case IPv4.PROTOCOL_UDP: + UDP udpPacket = (UDP) iPacket.getPayload(); + if (udpPacket.getDestinationPort() == UDP.DHCP_SERVER_PORT && + udpPacket.getSourcePort() == UDP.DHCP_CLIENT_PORT) { + // don't process DHCP + break; + } + default: + eventExecutor.execute(() -> processPnatPacket(context, ethernet)); + break; + } + } + } } \ No newline at end of file diff --git a/apps/openstacknetworking/routing/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackRoutingArpHandler.java b/apps/openstacknetworking/routing/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackRoutingArpHandler.java index 2c9a3044d2..e31f410d60 100644 --- a/apps/openstacknetworking/routing/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackRoutingArpHandler.java +++ b/apps/openstacknetworking/routing/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackRoutingArpHandler.java @@ -15,99 +15,74 @@ */ package org.onosproject.openstacknetworking.routing; -import com.google.common.collect.Lists; +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.Reference; +import org.apache.felix.scr.annotations.ReferenceCardinality; import org.onlab.packet.ARP; -import org.onlab.packet.EthType; import org.onlab.packet.Ethernet; import org.onlab.packet.Ip4Address; import org.onlab.packet.IpAddress; import org.onlab.packet.MacAddress; -import org.onosproject.core.ApplicationId; -import org.onosproject.net.DeviceId; -import org.onosproject.net.flow.DefaultTrafficSelector; import org.onosproject.net.flow.DefaultTrafficTreatment; -import org.onosproject.net.flow.TrafficSelector; import org.onosproject.net.flow.TrafficTreatment; import org.onosproject.net.packet.DefaultOutboundPacket; +import org.onosproject.net.packet.InboundPacket; import org.onosproject.net.packet.PacketContext; -import org.onosproject.net.packet.PacketPriority; +import org.onosproject.net.packet.PacketProcessor; import org.onosproject.net.packet.PacketService; import org.onosproject.openstackinterface.OpenstackInterfaceService; import org.onosproject.openstackinterface.OpenstackPort; import org.onosproject.scalablegateway.api.ScalableGatewayService; import org.onosproject.openstacknetworking.Constants; -import org.onosproject.openstacknode.OpenstackNodeService; import org.slf4j.Logger; import java.nio.ByteBuffer; -import java.util.List; -import java.util.Optional; +import java.util.concurrent.ExecutorService; -import static com.google.common.base.Preconditions.checkNotNull; +import static java.util.concurrent.Executors.newSingleThreadExecutor; +import static org.onlab.util.Tools.groupedThreads; +import static org.onosproject.openstacknetworking.Constants.DEVICE_OWNER_FLOATING_IP; +import static org.onosproject.openstacknetworking.Constants.DEVICE_OWNER_ROUTER_GATEWAY; import static org.slf4j.LoggerFactory.getLogger; /** - * Handle ARP packet sent from Openstack Gateway nodes. + * Handle ARP, ICMP and NAT packets from gateway nodes. */ +@Component(immediate = true) public class OpenstackRoutingArpHandler { - protected final Logger log = getLogger(getClass()); + private final Logger log = getLogger(getClass()); - private final PacketService packetService; - private final OpenstackInterfaceService openstackService; - private final ScalableGatewayService gatewayService; - private final OpenstackNodeService nodeService; - private static final String NETWORK_ROUTER_GATEWAY = "network:router_gateway"; - private static final String NETWORK_FLOATING_IP = "network:floatingip"; + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected PacketService packetService; - /** - * Default constructor. - * - * @param packetService packet service - * @param openstackService openstackInterface service - * @param gatewayService gateway service - * @param nodeService openstackNodeService - */ - OpenstackRoutingArpHandler(PacketService packetService, OpenstackInterfaceService openstackService, - OpenstackNodeService nodeService, ScalableGatewayService gatewayService) { - this.packetService = packetService; - this.openstackService = checkNotNull(openstackService); - this.nodeService = nodeService; - this.gatewayService = gatewayService; + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected OpenstackInterfaceService openstackService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected ScalableGatewayService gatewayService; + + private final ExecutorService executorService = + newSingleThreadExecutor(groupedThreads("onos/openstackrouting", "packet-event", log)); + + private final InternalPacketProcessor packetProcessor = new InternalPacketProcessor(); + + @Activate + protected void activate() { + packetService.addProcessor(packetProcessor, PacketProcessor.director(1)); + log.info("Started"); } - /** - * Requests ARP packet to GatewayNode. - * - * @param appId application id - */ - public void requestPacket(ApplicationId appId) { - - TrafficSelector arpSelector = DefaultTrafficSelector.builder() - .matchEthType(EthType.EtherType.ARP.ethType().toShort()) - .build(); - - getExternalInfo().forEach(deviceId -> - packetService.requestPackets(arpSelector, - PacketPriority.CONTROL, - appId, - Optional.of(deviceId)) - ); + @Deactivate + protected void deactivate() { + packetService.removeProcessor(packetProcessor); + log.info("Stopped"); } - /** - * Handles ARP packet. - * - * @param context packet context - * @param ethernet ethernet - */ - public void processArpPacketFromRouter(PacketContext context, Ethernet ethernet) { - checkNotNull(context, "context can not be null"); - checkNotNull(ethernet, "ethernet can not be null"); - - + private void processArpPacket(PacketContext context, Ethernet ethernet) { ARP arp = (ARP) ethernet.getPayload(); - - log.debug("arpEvent called from {} to {}", + log.trace("arpEvent called from {} to {}", Ip4Address.valueOf(arp.getSenderProtocolAddress()).toString(), Ip4Address.valueOf(arp.getTargetProtocolAddress()).toString()); @@ -116,13 +91,11 @@ public class OpenstackRoutingArpHandler { } IpAddress targetIp = Ip4Address.valueOf(arp.getTargetProtocolAddress()); - if (getTargetMacForTargetIp(targetIp.getIp4Address()) == MacAddress.NONE) { - return; + return; } - // FIXME: Set the correct gateway - MacAddress targetMac = Constants.GW_EXT_INT_MAC; + MacAddress targetMac = Constants.DEFAULT_EXTERNAL_ROUTER_MAC; Ethernet ethReply = ARP.buildArpReply(targetIp.getIp4Address(), targetMac, ethernet); @@ -136,22 +109,35 @@ public class OpenstackRoutingArpHandler { ByteBuffer.wrap(ethReply.serialize()))); } + private class InternalPacketProcessor implements PacketProcessor { + + @Override + public void process(PacketContext context) { + if (context.isHandled()) { + return; + } else if (!gatewayService.getGatewayDeviceIds().contains( + context.inPacket().receivedFrom().deviceId())) { + // return if the packet is not from gateway nodes + return; + } + + InboundPacket pkt = context.inPacket(); + Ethernet ethernet = pkt.parsed(); + if (ethernet != null && + ethernet.getEtherType() == Ethernet.TYPE_ARP) { + executorService.execute(() -> processArpPacket(context, ethernet)); + } + } + } + + // TODO make a cache for the MAC, not a good idea to REST call every time it gets ARP request private MacAddress getTargetMacForTargetIp(Ip4Address targetIp) { OpenstackPort port = openstackService.ports().stream() - .filter(p -> p.deviceOwner().equals(NETWORK_ROUTER_GATEWAY) || - p.deviceOwner().equals(NETWORK_FLOATING_IP)) + .filter(p -> p.deviceOwner().equals(DEVICE_OWNER_ROUTER_GATEWAY) || + p.deviceOwner().equals(DEVICE_OWNER_FLOATING_IP)) .filter(p -> p.fixedIps().containsValue(targetIp.getIp4Address())) .findAny().orElse(null); - if (port == null) { - return MacAddress.NONE; - } - return port.macAddress(); - } - - private List getExternalInfo() { - List externalInfoList = Lists.newArrayList(); - gatewayService.getGatewayDeviceIds().forEach(externalInfoList::add); - return externalInfoList; + return port == null ? MacAddress.NONE : port.macAddress(); } } diff --git a/apps/openstacknetworking/routing/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackRoutingManager.java b/apps/openstacknetworking/routing/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackRoutingManager.java index 9f19a28b70..09b4b8a90d 100644 --- a/apps/openstacknetworking/routing/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackRoutingManager.java +++ b/apps/openstacknetworking/routing/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackRoutingManager.java @@ -15,8 +15,7 @@ */ package org.onosproject.openstacknetworking.routing; -import com.google.common.collect.Lists; -import com.google.common.collect.Sets; +import com.google.common.collect.ImmutableSet; import org.apache.felix.scr.annotations.Activate; import org.apache.felix.scr.annotations.Component; import org.apache.felix.scr.annotations.Deactivate; @@ -24,82 +23,62 @@ import org.apache.felix.scr.annotations.Reference; import org.apache.felix.scr.annotations.ReferenceCardinality; import org.apache.felix.scr.annotations.Service; import org.onlab.packet.Ethernet; -import org.onlab.packet.IPv4; import org.onlab.packet.Ip4Address; -import org.onlab.packet.IpAddress; import org.onlab.packet.MacAddress; -import org.onlab.packet.UDP; -import org.onlab.util.KryoNamespace; +import org.onlab.util.Tools; import org.onosproject.core.ApplicationId; -import org.onosproject.core.CoreService; -import org.onosproject.mastership.MastershipService; -import org.onosproject.net.DefaultAnnotations; +import org.onosproject.core.GroupId; import org.onosproject.net.DeviceId; import org.onosproject.net.Host; -import org.onosproject.net.Port; +import org.onosproject.net.PortNumber; import org.onosproject.net.device.DeviceService; -import org.onosproject.net.driver.DriverService; +import org.onosproject.net.flow.DefaultTrafficSelector; +import org.onosproject.net.flow.DefaultTrafficTreatment; +import org.onosproject.net.flow.TrafficSelector; +import org.onosproject.net.flow.TrafficTreatment; +import org.onosproject.net.flowobjective.DefaultForwardingObjective; import org.onosproject.net.flowobjective.FlowObjectiveService; -import org.onosproject.net.host.DefaultHostDescription; -import org.onosproject.net.host.HostDescription; -import org.onosproject.net.host.HostEvent; -import org.onosproject.net.host.HostListener; -import org.onosproject.net.host.HostProvider; -import org.onosproject.net.host.HostProviderRegistry; -import org.onosproject.net.host.HostProviderService; -import org.onosproject.net.host.HostService; -import org.onosproject.net.packet.InboundPacket; -import org.onosproject.net.packet.PacketContext; -import org.onosproject.net.packet.PacketProcessor; -import org.onosproject.net.packet.PacketService; -import org.onosproject.net.provider.AbstractProvider; -import org.onosproject.net.provider.ProviderId; -import org.onosproject.openstackinterface.OpenstackFloatingIP; +import org.onosproject.net.flowobjective.ForwardingObjective; import org.onosproject.openstackinterface.OpenstackInterfaceService; +import org.onosproject.openstackinterface.OpenstackNetwork; import org.onosproject.openstackinterface.OpenstackPort; import org.onosproject.openstackinterface.OpenstackRouter; import org.onosproject.openstackinterface.OpenstackRouterInterface; -import org.onosproject.openstacknetworking.OpenstackRoutingService; -import org.onosproject.scalablegateway.api.ScalableGatewayService; -import org.onosproject.openstacknetworking.routing.OpenstackFloatingIPHandler.Action; +import org.onosproject.openstackinterface.OpenstackSubnet; +import org.onosproject.openstacknetworking.AbstractVmHandler; import org.onosproject.openstacknetworking.Constants; +import org.onosproject.openstacknetworking.OpenstackRoutingService; +import org.onosproject.openstacknetworking.RulePopulatorUtil; import org.onosproject.openstacknode.OpenstackNode; import org.onosproject.openstacknode.OpenstackNodeEvent; import org.onosproject.openstacknode.OpenstackNodeListener; import org.onosproject.openstacknode.OpenstackNodeService; -import org.onosproject.store.serializers.KryoNamespaces; -import org.onosproject.store.service.ConsistentMap; -import org.onosproject.store.service.Serializer; -import org.onosproject.store.service.StorageService; +import org.onosproject.scalablegateway.api.GatewayNode; +import org.onosproject.scalablegateway.api.ScalableGatewayService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.Collection; -import java.util.List; +import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; import java.util.stream.Collectors; -import static com.google.common.base.Preconditions.checkNotNull; +import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor; import static org.onlab.util.Tools.groupedThreads; -import static org.onosproject.net.AnnotationKeys.PORT_NAME; +import static org.onosproject.openstacknetworking.Constants.*; +import static org.onosproject.openstacknetworking.RulePopulatorUtil.buildExtension; +import static org.onosproject.openstacknode.OpenstackNodeService.NodeType.COMPUTE; +import static org.onosproject.openstacknode.OpenstackNodeService.NodeType.GATEWAY; @Component(immediate = true) @Service -/** - * Populates flow rules about L3 functionality for VMs in Openstack. - */ -public class OpenstackRoutingManager implements OpenstackRoutingService { +public class OpenstackRoutingManager extends AbstractVmHandler implements OpenstackRoutingService { private final Logger log = LoggerFactory.getLogger(getClass()); @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) - protected CoreService coreService; - - @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) - protected PacketService packetService; + protected FlowObjectiveService flowObjectiveService; @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected DeviceService deviceService; @@ -108,620 +87,392 @@ public class OpenstackRoutingManager implements OpenstackRoutingService { protected OpenstackInterfaceService openstackService; @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) - protected FlowObjectiveService flowObjectiveService; - - @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) - protected DriverService driverService; - - @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) - protected StorageService storageService; - - @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) - protected HostService hostService; - - @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) - protected HostProviderRegistry hostProviderRegistry; + protected OpenstackNodeService nodeService; @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected ScalableGatewayService gatewayService; - @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) - protected OpenstackNodeService nodeService; - - @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) - protected MastershipService mastershipService; + private final ExecutorService eventExecutor = newSingleThreadScheduledExecutor( + groupedThreads(this.getClass().getSimpleName(), "event-handler", log)); + private final InternalNodeListener nodeListener = new InternalNodeListener(); private ApplicationId appId; - private ConsistentMap tpPortNumMap; // Map - private ConsistentMap floatingIpMap; // Map - // Map - private ConsistentMap routerInterfaceMap; - private static final ProviderId PID = new ProviderId("of", "org.onosproject.openstackroutering", true); - private static final String APP_ID = "org.onosproject.openstackrouting"; - private static final String DEVICE_OWNER_ROUTER_INTERFACE = "network:router_interface"; - private static final String FLOATING_IP_MAP_NAME = "openstackrouting-floatingip"; - private static final String TP_PORT_MAP_NAME = "openstackrouting-tpportnum"; - private static final String ROUTER_INTERFACE_MAP_NAME = "openstackrouting-routerinterface"; - private static final String COLON = ":"; - private static final int PNAT_PORT_EXPIRE_TIME = 1200 * 1000; - private static final int TP_PORT_MINIMUM_NUM = 1024; - private static final int TP_PORT_MAXIMUM_NUM = 65535; - - private static final KryoNamespace.Builder FLOATING_IP_SERIALIZER = KryoNamespace.newBuilder() - .register(KryoNamespaces.API) - .register(OpenstackFloatingIP.FloatingIpStatus.class) - .register(OpenstackFloatingIP.class); - - private static final KryoNamespace.Builder NUMBER_SERIALIZER = KryoNamespace.newBuilder() - .register(KryoNamespaces.API); - - private static final KryoNamespace.Builder ROUTER_INTERFACE_SERIALIZER = KryoNamespace.newBuilder() - .register(KryoNamespaces.API); - - private InternalPacketProcessor internalPacketProcessor = new InternalPacketProcessor(); - private InternalHostListener internalHostListener = new InternalHostListener(); - private InternalOpenstackNodeListener internalNodeListener = new InternalOpenstackNodeListener(); - private ExecutorService l3EventExecutorService = - Executors.newSingleThreadExecutor(groupedThreads("onos/openstackrouting", "L3-event")); - private ExecutorService icmpEventExecutorService = - Executors.newSingleThreadExecutor(groupedThreads("onos/openstackrouting", "icmp-event")); - private ExecutorService arpEventExecutorService = - Executors.newSingleThreadExecutor(groupedThreads("onos/openstackrouting", "arp-event")); - private OpenstackIcmpHandler openstackIcmpHandler; - private OpenstackRoutingArpHandler openstackArpHandler; - private OpenstackRoutingRulePopulator rulePopulator; - - private HostProviderService hostProviderService; - private final HostProvider hostProvider = new InternalHostProvider(); @Activate protected void activate() { - appId = coreService.registerApplication(APP_ID); - hostService.addListener(internalHostListener); - nodeService.addListener(internalNodeListener); - hostProviderService = hostProviderRegistry.register(hostProvider); - - floatingIpMap = storageService.consistentMapBuilder() - .withSerializer(Serializer.using(FLOATING_IP_SERIALIZER.build())) - .withName(FLOATING_IP_MAP_NAME) - .withApplicationId(appId) - .build(); - tpPortNumMap = storageService.consistentMapBuilder() - .withSerializer(Serializer.using(NUMBER_SERIALIZER.build())) - .withName(TP_PORT_MAP_NAME) - .withApplicationId(appId) - .build(); - routerInterfaceMap = storageService.consistentMapBuilder() - .withSerializer(Serializer.using(ROUTER_INTERFACE_SERIALIZER.build())) - .withName(ROUTER_INTERFACE_MAP_NAME) - .withApplicationId(appId) - .build(); - - log.info("started"); + super.activate(); + appId = coreService.registerApplication(ROUTING_APP_ID); + nodeService.addListener(nodeListener); } @Deactivate protected void deactivate() { - packetService.removeProcessor(internalPacketProcessor); - hostService.removeListener(internalHostListener); - nodeService.removeListener(internalNodeListener); - - l3EventExecutorService.shutdown(); - icmpEventExecutorService.shutdown(); - arpEventExecutorService.shutdown(); - - floatingIpMap.clear(); - tpPortNumMap.clear(); - routerInterfaceMap.clear(); - + nodeService.removeListener(nodeListener); log.info("stopped"); } - @Override - public void createFloatingIP(OpenstackFloatingIP openstackFloatingIp) { - floatingIpMap.put(openstackFloatingIp.id(), openstackFloatingIp); + public void createRouter(OpenstackRouter osRouter) { } @Override - public void updateFloatingIP(OpenstackFloatingIP openstackFloatingIp) { - if (!floatingIpMap.containsKey(openstackFloatingIp.id())) { - log.warn("There`s no information about {} in FloatingIpMap", openstackFloatingIp.id()); + public void updateRouter(OpenstackRouter osRouter) { + if (osRouter.gatewayExternalInfo().externalFixedIps().size() > 0) { + openstackService.ports().stream() + .filter(osPort -> osPort.deviceOwner().equals(DEVICE_OWNER_ROUTER_INTERFACE) && + osPort.deviceId().equals(osRouter.id())) + .forEach(osPort -> setExternalConnection(osRouter, osPort.networkId())); + + log.info("Connected external gateway {} to router {}", + osRouter.gatewayExternalInfo().externalFixedIps(), + osRouter.name()); + } else { + openstackService.ports().stream() + .filter(osPort -> osPort.deviceOwner().equals(DEVICE_OWNER_ROUTER_INTERFACE) && + osPort.deviceId().equals(osRouter.id())) + .forEach(osPort -> unsetExternalConnection(osRouter, osPort.networkId())); + + log.info("Disconnected external gateway from router {}", + osRouter.name()); + } + } + + @Override + public void removeRouter(String osRouterId) { + // TODO implement this + } + + @Override + public void addRouterInterface(OpenstackRouterInterface routerIface) { + OpenstackRouter osRouter = openstackRouter(routerIface.id()); + OpenstackPort osPort = openstackService.port(routerIface.portId()); + if (osRouter == null || osPort == null) { + log.warn("Failed to add router interface {}", routerIface); return; } - if (openstackFloatingIp.portId() == null || openstackFloatingIp.portId().equals("null")) { - OpenstackFloatingIP floatingIp = floatingIpMap.get(openstackFloatingIp.id()).value(); - // XXX When the VM has been removed, host information has been removed or not ??? - Optional host = hostService.getHostsByIp(openstackFloatingIp.fixedIpAddress().getIp4Address()) - .stream() - .findFirst(); - if (!host.isPresent()) { - log.warn("No Host info with the VM IP the Floating IP address {} is found", - openstackFloatingIp.floatingIpAddress()); - return; - } - l3EventExecutorService.execute( - new OpenstackFloatingIPHandler(rulePopulator, floatingIp, Action.DISSASSOCIATE, host.get())); - floatingIpMap.replace(floatingIp.id(), openstackFloatingIp); - registerFloatingIpToHostService(openstackFloatingIp, Action.DISSASSOCIATE); - } else { - floatingIpMap.put(openstackFloatingIp.id(), openstackFloatingIp); - l3EventExecutorService.execute( - new OpenstackFloatingIPHandler(rulePopulator, openstackFloatingIp, Action.ASSOCIATE, null)); - registerFloatingIpToHostService(openstackFloatingIp, Action.ASSOCIATE); + + setRoutes(osRouter, Optional.empty()); + if (osRouter.gatewayExternalInfo().externalFixedIps().size() > 0) { + setExternalConnection(osRouter, osPort.networkId()); } + log.info("Connected {} to router {}", osPort.fixedIps(), osRouter.name()); } @Override - public void deleteFloatingIP(String id) { - floatingIpMap.remove(id); - } - - @Override - public void createRouter(OpenstackRouter openstackRouter) { - } - - @Override - public void updateRouter(OpenstackRouter openstackRouter) { - if (openstackRouter.gatewayExternalInfo().externalFixedIps().size() > 0) { - checkExternalConnection(openstackRouter, getOpenstackRouterInterface(openstackRouter)); - } else { - unsetExternalConnection(); - } - } - - private void unsetExternalConnection() { - Collection internalRouters = getExternalRouter(false); - internalRouters.forEach(r -> - getOpenstackRouterInterface(r).forEach(i -> rulePopulator.removeExternalRules(i))); - } - - private Collection getExternalRouter(boolean externalConnection) { - List routers; - if (externalConnection) { - routers = openstackService.routers() - .stream() - .filter(r -> (r.gatewayExternalInfo().externalFixedIps().size() > 0)) - .collect(Collectors.toList()); - } else { - routers = openstackService.routers() - .stream() - .filter(r -> (r.gatewayExternalInfo().externalFixedIps().size() == 0)) - .collect(Collectors.toList()); - } - return routers; - } - - @Override - public void deleteRouter(String id) { - //TODO : In now, there`s nothing to do for deleteRouter process. It is reserved. - } - - @Override - public void updateRouterInterface(OpenstackRouterInterface routerInterface) { - List routerInterfaces = Lists.newArrayList(); - routerInterfaces.add(routerInterface); - checkExternalConnection(getOpenstackRouter(routerInterface.id()), routerInterfaces); - setL3Connection(getOpenstackRouter(routerInterface.id()), null); - routerInterfaceMap.put(routerInterface.portId(), openstackService.port(routerInterface.portId()).networkId()); - } - - /** - * Set flow rules for traffic between two different subnets when more than one subnets - * connected to a router. - * - * @param openstackRouter OpenstackRouter Info - * @param openstackPort OpenstackPort Info - */ - private void setL3Connection(OpenstackRouter openstackRouter, OpenstackPort openstackPort) { - Collection interfaceList = getOpenstackRouterInterface(openstackRouter); - - if (interfaceList.size() < 2) { + public void removeRouterInterface(OpenstackRouterInterface routerIface) { + OpenstackRouter osRouter = openstackService.router(routerIface.id()); + if (osRouter == null) { + log.warn("Failed to remove router interface {}", routerIface); return; } - if (openstackPort == null) { - interfaceList.forEach(i -> { - OpenstackPort interfacePort = openstackService.port(i.portId()); - openstackService.ports() - .stream() - .filter(p -> p.networkId().equals(interfacePort.networkId()) - && !p.deviceOwner().equals(DEVICE_OWNER_ROUTER_INTERFACE)) - .forEach(p -> rulePopulator.populateL3Rules(p, - getL3ConnectionList(p.networkId(), interfaceList))); - }); - } else { - rulePopulator.populateL3Rules(openstackPort, getL3ConnectionList(openstackPort.networkId(), interfaceList)); + OpenstackSubnet osSubnet = openstackService.subnet(routerIface.subnetId()); + OpenstackNetwork osNet = openstackService.network(osSubnet.networkId()); + + unsetRoutes(osRouter, osNet); + if (osRouter.gatewayExternalInfo().externalFixedIps().size() > 0) { + unsetExternalConnection(osRouter, osNet.id()); } - + log.info("Disconnected {} from router {}", osSubnet.cidr(), osRouter.name()); } - private List getL3ConnectionList(String networkId, - Collection interfaceList) { - List targetList = Lists.newArrayList(); - interfaceList.forEach(i -> { - OpenstackPort port = openstackService.port(i.portId()); - if (!port.networkId().equals(networkId)) { - targetList.add(i); - } - }); - return targetList; - } - - @Override - public void removeRouterInterface(OpenstackRouterInterface routerInterface) { - OpenstackRouter router = openstackService.router(routerInterface.id()); - Collection interfaceList = getOpenstackRouterInterface(router); - if (interfaceList.size() == 1) { - List newList = Lists.newArrayList(); - newList.add(routerInterface); - interfaceList.forEach(i -> removeL3RulesForRouterInterface(i, router, newList)); - } - removeL3RulesForRouterInterface(routerInterface, router, null); - rulePopulator.removeExternalRules(routerInterface); - routerInterfaceMap.remove(routerInterface.portId()); - } - - private void removeL3RulesForRouterInterface(OpenstackRouterInterface routerInterface, OpenstackRouter router, - List newList) { - if (!routerInterfaceMap.containsKey(routerInterface.portId())) { - log.warn("No router interface information found for {}", routerInterface.portId()); + private void setExternalConnection(OpenstackRouter osRouter, String osNetId) { + if (!osRouter.gatewayExternalInfo().isEnablePnat()) { + log.debug("Source NAT is disabled"); return; } - openstackService.ports(routerInterfaceMap.get(routerInterface.portId()).value()).forEach(p -> { - Ip4Address vmIp = (Ip4Address) p.fixedIps().values().toArray()[0]; - if (newList == null) { - rulePopulator.removeL3Rules(vmIp, - getL3ConnectionList(p.networkId(), getOpenstackRouterInterface(router))); - } else { - rulePopulator.removeL3Rules(vmIp, newList); - } - } - ); + + // FIXME router interface is subnet specific, not network + OpenstackNetwork osNet = openstackService.network(osNetId); + populateExternalRules(osNet); } - @Override - public String networkIdForRouterInterface(String portId) { - return routerInterfaceMap.get(portId).value(); - } - - private Collection associatedFloatingIps() { - List fIps = Lists.newArrayList(); - floatingIpMap.values() - .stream() - .filter(fIp -> fIp.value().portId() != null) - .forEach(fIp -> fIps.add(fIp.value())); - return fIps; - } - - private void reloadInitL3Rules() { - - l3EventExecutorService.execute(() -> - openstackService.ports() - .stream() - .forEach(p -> - { - if (p.deviceOwner().equals(DEVICE_OWNER_ROUTER_INTERFACE)) { - updateRouterInterface(portToRouterInterface(p)); - } else { - Optional vmIp = p.fixedIps().values().stream().findAny(); - if (vmIp.isPresent()) { - OpenstackFloatingIP floatingIP = getOpenstackFloatingIp(vmIp.get()); - if (floatingIP != null) { - updateFloatingIP(floatingIP); - } - } - } - }) - ); - } - - private OpenstackRouterInterface portToRouterInterface(OpenstackPort p) { - OpenstackRouterInterface.Builder osBuilder = new OpenstackRouterInterface.Builder() - .id(checkNotNull(p.deviceId())) - .tenantId(checkNotNull(openstackService.network(p.networkId()).tenantId())) - .subnetId(checkNotNull(p.fixedIps().keySet().stream().findFirst().orElse(null)).toString()) - .portId(checkNotNull(p.id())); - - return osBuilder.build(); - } - - private class InternalPacketProcessor implements PacketProcessor { - - @Override - public void process(PacketContext context) { - - DeviceId senderDeviceId = context.inPacket().receivedFrom().deviceId(); - if (!nodeService.routerBridge(senderDeviceId).isPresent()) { - log.warn("No router bridge for {} is found.", senderDeviceId); - return; - } - if (context.isHandled()) { - return; - } else if (!checkGatewayNode(context.inPacket().receivedFrom().deviceId())) { - return; - } - - InboundPacket pkt = context.inPacket(); - Ethernet ethernet = pkt.parsed(); - - //TODO: Considers IPv6 later. - if (ethernet == null) { - return; - } else if (ethernet.getEtherType() == Ethernet.TYPE_IPV4) { - IPv4 iPacket = (IPv4) ethernet.getPayload(); - switch (iPacket.getProtocol()) { - case IPv4.PROTOCOL_ICMP: - - icmpEventExecutorService.execute(() -> - openstackIcmpHandler.processIcmpPacket(context, ethernet)); - break; - case IPv4.PROTOCOL_UDP: - // don't process DHCP - UDP udpPacket = (UDP) iPacket.getPayload(); - if (udpPacket.getDestinationPort() == UDP.DHCP_SERVER_PORT && - udpPacket.getSourcePort() == UDP.DHCP_CLIENT_PORT) { - break; - } - default: - int portNum = getPortNum(ethernet.getSourceMAC(), iPacket.getDestinationAddress()); - DeviceId deviceId = pkt.receivedFrom().deviceId(); - Port port = null; - port = deviceService.getPort(deviceId, - gatewayService.getUplinkPort(deviceId)); - if (port != null) { - OpenstackPort openstackPort = getOpenstackPort(ethernet.getSourceMAC(), - Ip4Address.valueOf(iPacket.getSourceAddress())); - l3EventExecutorService.execute(new OpenstackPnatHandler(rulePopulator, context, - portNum, openstackPort, port)); - - } else { - log.warn("There`s no external interface"); - } - - break; - } - } else if (ethernet.getEtherType() == Ethernet.TYPE_ARP) { - arpEventExecutorService.execute(() -> - openstackArpHandler.processArpPacketFromRouter(context, ethernet)); - } - } - - private int getPortNum(MacAddress sourceMac, int destinationAddress) { - int portNum = findUnusedPortNum(); - if (portNum == 0) { - clearPortNumMap(); - portNum = findUnusedPortNum(); - } - tpPortNumMap.put(portNum, sourceMac.toString().concat(COLON).concat(String.valueOf(destinationAddress))); - return portNum; - } - - private int findUnusedPortNum() { - for (int i = TP_PORT_MINIMUM_NUM; i < TP_PORT_MAXIMUM_NUM; i++) { - if (!tpPortNumMap.containsKey(i)) { - return i; - } - } - return 0; + private void unsetExternalConnection(OpenstackRouter osRouter, String osNetId) { + if (!osRouter.gatewayExternalInfo().isEnablePnat()) { + log.debug("Source NAT is disabled"); + return; } + // FIXME router interface is subnet specific, not network + OpenstackNetwork osNet = openstackService.network(osNetId); + removeExternalRules(osNet); } - private boolean checkGatewayNode(DeviceId deviceId) { - return gatewayService.getGatewayDeviceIds().contains(deviceId); + private void setRoutes(OpenstackRouter osRouter, Optional host) { + Set routableNets = routableNetworks(osRouter.id()); + if (routableNets.size() < 2) { + // no other subnet interface is connected to this router, do nothing + return; + } + + // FIXME router interface is subnet specific, not network + Set routableNetIds = routableNets.stream() + .map(OpenstackNetwork::id) + .collect(Collectors.toSet()); + + Set hosts = host.isPresent() ? ImmutableSet.of(host.get()) : + Tools.stream(hostService.getHosts()) + .filter(h -> routableNetIds.contains(h.annotations().value(NETWORK_ID))) + .collect(Collectors.toSet()); + + hosts.stream().forEach(h -> populateRoutingRules(h, routableNets)); } - private void clearPortNumMap() { - tpPortNumMap.entrySet().forEach(e -> { - if (System.currentTimeMillis() - e.getValue().creationTime() > PNAT_PORT_EXPIRE_TIME) { - tpPortNumMap.remove(e.getKey()); - } + private void unsetRoutes(OpenstackRouter osRouter, OpenstackNetwork osNet) { + // FIXME router interface is subnet specific, not network + Set routableNets = routableNetworks(osRouter.id()); + Tools.stream(hostService.getHosts()) + .filter(h -> Objects.equals( + h.annotations().value(NETWORK_ID), osNet.id())) + .forEach(h -> removeRoutingRules(h, routableNets)); + + routableNets.stream().forEach(n -> { + Tools.stream(hostService.getHosts()) + .filter(h -> Objects.equals( + h.annotations().value(NETWORK_ID), + n.id())) + .forEach(h -> removeRoutingRules(h, ImmutableSet.of(osNet))); + log.debug("Removed between {} to {}", n.name(), osNet.name()); }); } - private Optional getExternalPort(DeviceId deviceId, String interfaceName) { - return deviceService.getPorts(deviceId) - .stream() - .filter(p -> p.annotations().value(PORT_NAME).equals(interfaceName)) - .findAny(); - } - - private void checkExternalConnection(OpenstackRouter router, - Collection interfaces) { - checkNotNull(router, "Router can not be null"); - checkNotNull(interfaces, "routerInterfaces can not be null"); - Ip4Address externalIp = router.gatewayExternalInfo().externalFixedIps() - .values().stream().findFirst().orElse(null); - if ((externalIp == null) || (!router.gatewayExternalInfo().isEnablePnat())) { - log.debug("Not satisfied to set pnat configuration"); - return; - } - interfaces.forEach(this::initiateL3Rule); - } - - private Optional getRouterfromExternalIp(Ip4Address externalIp) { - return getExternalRouter(true) - .stream() - .filter(r -> r.gatewayExternalInfo() - .externalFixedIps() - .values() - .stream() - .findAny() - .get() - .equals(externalIp)) - .findAny(); - } - - private void initiateL3Rule(OpenstackRouterInterface routerInterface) { - long vni = Long.parseLong(openstackService.network(openstackService - .port(routerInterface.portId()).networkId()).segmentId()); - rulePopulator.populateExternalRules(vni); - } - - private Collection getOpenstackRouterInterface(OpenstackRouter router) { - List interfaces = Lists.newArrayList(); - openstackService.ports() - .stream() - .filter(p -> p.deviceOwner().equals(DEVICE_OWNER_ROUTER_INTERFACE) - && p.deviceId().equals(router.id())) - .forEach(p -> interfaces.add(portToRouterInterface(p))); - return interfaces; - } - - private OpenstackRouter getOpenstackRouter(String id) { + private OpenstackRouter openstackRouter(String routerId) { return openstackService.routers().stream().filter(r -> - r.id().equals(id)).iterator().next(); + r.id().equals(routerId)).iterator().next(); } - private OpenstackPort getOpenstackPort(MacAddress sourceMac, Ip4Address ip4Address) { - OpenstackPort openstackPort = openstackService.ports().stream() - .filter(p -> p.macAddress().equals(sourceMac)).iterator().next(); - return openstackPort.fixedIps().values().stream().filter(ip -> - ip.equals(ip4Address)).count() > 0 ? openstackPort : null; - } - - private OpenstackFloatingIP getOpenstackFloatingIp(Ip4Address vmIp) { - Optional floatingIp = floatingIpMap.asJavaMap().values().stream() - .filter(f -> f.portId() != null && f.fixedIpAddress().equals(vmIp)) - .findAny(); - - if (floatingIp.isPresent()) { - return floatingIp.get(); - } - log.debug("There is no floating IP information for VM IP {}", vmIp); - - return null; - } - - private Optional getRouterInterfacePort(String networkId) { - - return openstackService.ports() - .stream() - .filter(p -> p.deviceOwner().equals(DEVICE_OWNER_ROUTER_INTERFACE) - && p.networkId().equals(networkId)) + private Optional routerIfacePort(String osNetId) { + // FIXME router interface is subnet specific, not network + return openstackService.ports().stream() + .filter(p -> p.deviceOwner().equals(DEVICE_OWNER_ROUTER_INTERFACE) && + p.networkId().equals(osNetId)) .findAny(); } - // TODO: Remove the function and the related codes when vRouter is running on different ONOS instance. - private void registerFloatingIpToHostService(OpenstackFloatingIP openstackFloatingIp, Action action) { + private Set routableNetworks(String osRouterId) { + // FIXME router interface is subnet specific, not network + return openstackService.ports().stream() + .filter(p -> p.deviceOwner().equals(DEVICE_OWNER_ROUTER_INTERFACE) && + p.deviceId().equals(osRouterId)) + .map(p -> openstackService.network(p.networkId())) + .collect(Collectors.toSet()); + } - Optional hostOptional = hostService.getHostsByIp(openstackFloatingIp.fixedIpAddress()) - .stream() - .findFirst(); - if (!hostOptional.isPresent()) { - log.warn("No host with IP {} is registered and cannot add the floating IP. ", - openstackFloatingIp.floatingIpAddress()); + private void populateExternalRules(OpenstackNetwork osNet) { + populateCnodeToGateway(Long.valueOf(osNet.segmentId())); + populateGatewayToController(Long.valueOf(osNet.segmentId())); + } + + private void removeExternalRules(OpenstackNetwork osNet) { + TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder(); + sBuilder.matchEthType(Ethernet.TYPE_IPV4) + .matchTunnelId(Long.valueOf(osNet.segmentId())) + .matchEthDst(Constants.DEFAULT_GATEWAY_MAC); + + nodeService.completeNodes().stream().forEach(node -> { + ForwardingObjective.Flag flag = node.type().equals(GATEWAY) ? + ForwardingObjective.Flag.VERSATILE : + ForwardingObjective.Flag.SPECIFIC; + + RulePopulatorUtil.removeRule( + flowObjectiveService, + appId, + node.intBridge(), + sBuilder.build(), + flag, + ROUTING_RULE_PRIORITY); + }); + } + + private void populateRoutingRules(Host host, Set osNets) { + String osNetId = host.annotations().value(NETWORK_ID); + if (osNetId == null) { return; } - Host host = hostOptional.get(); - Set ipAddresses = Sets.newHashSet(); - if (action == Action.ASSOCIATE) { - ipAddresses.add(openstackFloatingIp.floatingIpAddress()); + DeviceId localDevice = host.location().deviceId(); + PortNumber localPort = host.location().port(); + if (!nodeService.dataIp(localDevice).isPresent()) { + log.warn("Failed to populate L3 rules"); + return; } - HostDescription hostDescription = - new DefaultHostDescription(host.mac(), host.vlan(), host.location(), ipAddresses, - (DefaultAnnotations) host.annotations()); + // TODO improve pipeline, do we have to install access rules between networks + // for every single VMs? + osNets.stream().filter(osNet -> !osNet.id().equals(osNetId)).forEach(osNet -> { + populateRoutingRulestoSameNode( + host.ipAddresses().stream().findFirst().get().getIp4Address(), + host.mac(), + localPort, localDevice, + Long.valueOf(osNet.segmentId())); - hostProviderService.hostDetected(host.id(), hostDescription, false); + nodeService.completeNodes().stream() + .filter(node -> node.type().equals(COMPUTE)) + .filter(node -> !node.intBridge().equals(localDevice)) + .forEach(node -> populateRoutingRulestoDifferentNode( + host.ipAddresses().stream().findFirst().get().getIp4Address(), + Long.valueOf(osNet.segmentId()), + node.intBridge(), + nodeService.dataIp(localDevice).get().getIp4Address())); + }); } - private class InternalHostListener implements HostListener { - - private void hostDetected(Host host) { - String portId = host.annotations().value(Constants.PORT_ID); - OpenstackPort openstackPort = openstackService.port(portId); - if (openstackPort == null) { - log.warn("No OpenstackPort information found from OpenStack for port ID {}", portId); - return; - } - - Optional routerPort = getRouterInterfacePort(openstackPort.networkId()); - if (routerPort.isPresent()) { - OpenstackRouterInterface routerInterface = portToRouterInterface(routerPort.get()); - l3EventExecutorService.execute(() -> - setL3Connection(getOpenstackRouter(routerInterface.id()), openstackPort)); - - } + private void removeRoutingRules(Host host, Set osNets) { + String osNetId = host.annotations().value(NETWORK_ID); + if (osNetId == null) { + return; } - private void hostRemoved(Host host) { - String portId = host.annotations().value(Constants.PORT_ID); - OpenstackPort openstackPort = openstackService.port(portId); - if (openstackPort == null) { - log.warn("No OpenstackPort information found from OpenStack for port ID {}", portId); - return; - } + osNets.stream().filter(osNet -> !osNet.id().equals(osNetId)).forEach(osNet -> { + TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder(); + sBuilder.matchEthType(Ethernet.TYPE_IPV4) + .matchIPDst(host.ipAddresses().stream().findFirst().get().toIpPrefix()) + .matchTunnelId(Long.valueOf(osNet.segmentId())); - Optional routerPort = getRouterInterfacePort(openstackPort.networkId()); - if (routerPort.isPresent()) { - OpenstackRouterInterface routerInterface = portToRouterInterface(routerPort.get()); - IpAddress ipAddress = host.ipAddresses().stream().findFirst().get(); - l3EventExecutorService.execute(() -> rulePopulator.removeL3Rules(ipAddress.getIp4Address(), - getL3ConnectionList(host.annotations().value(Constants.NETWORK_ID), - getOpenstackRouterInterface(getOpenstackRouter(routerInterface.id()))))); - } - } - - private boolean isValidHost(Host host) { - return !host.ipAddresses().isEmpty() && - host.annotations().value(Constants.VXLAN_ID) != null && - host.annotations().value(Constants.NETWORK_ID) != null && - host.annotations().value(Constants.TENANT_ID) != null && - host.annotations().value(Constants.PORT_ID) != null; - } - - @Override - public void event(HostEvent event) { - Host host = event.subject(); - if (!mastershipService.isLocalMaster(host.location().deviceId())) { - // do not allow to proceed without mastership - return; - } - - if (!isValidHost(host)) { - log.debug("Invalid host event, ignore it {}", host); - return; - } - - switch (event.type()) { - case HOST_UPDATED: - case HOST_ADDED: - l3EventExecutorService.execute(() -> hostDetected(host)); - break; - case HOST_REMOVED: - l3EventExecutorService.execute(() -> hostRemoved(host)); - break; - default: - break; - } - } + nodeService.completeNodes().stream() + .filter(node -> node.type().equals(COMPUTE)) + .forEach(node -> RulePopulatorUtil.removeRule( + flowObjectiveService, + appId, + node.intBridge(), + sBuilder.build(), + ForwardingObjective.Flag.SPECIFIC, + ROUTING_RULE_PRIORITY)); + }); + log.debug("Removed routing rule from {} to {}", host, osNets); } - private class InternalOpenstackNodeListener implements OpenstackNodeListener { + private void populateGatewayToController(long vni) { + TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder(); + TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder(); - private void nodeComplete() { + sBuilder.matchEthType(Ethernet.TYPE_IPV4) + .matchTunnelId(vni) + .matchEthDst(Constants.DEFAULT_GATEWAY_MAC); + tBuilder.setOutput(PortNumber.CONTROLLER); - rulePopulator = new OpenstackRoutingRulePopulator(appId, openstackService, flowObjectiveService, - deviceService, driverService, nodeService, gatewayService); - openstackIcmpHandler = new OpenstackIcmpHandler(packetService, deviceService, hostService, - openstackService, nodeService, gatewayService); - openstackArpHandler = new OpenstackRoutingArpHandler(packetService, openstackService, nodeService, - gatewayService); + ForwardingObjective fo = DefaultForwardingObjective.builder() + .withSelector(sBuilder.build()) + .withTreatment(tBuilder.build()) + .withFlag(ForwardingObjective.Flag.VERSATILE) + .withPriority(ROUTING_RULE_PRIORITY) + .fromApp(appId) + .add(); - // Packet handlers must be started AFTER all initialization processes. - packetService.addProcessor(internalPacketProcessor, PacketProcessor.director(1)); + gatewayService.getGatewayDeviceIds().stream() + .forEach(deviceId -> flowObjectiveService.forward(deviceId, fo)); + } - openstackIcmpHandler.requestPacket(appId); - openstackArpHandler.requestPacket(appId); + private void populateCnodeToGateway(long vni) { + nodeService.completeNodes().stream() + .filter(node -> node.type().equals(COMPUTE)) + .forEach(node -> populateRuleToGateway( + node.intBridge(), + gatewayService.getGatewayGroupId(node.intBridge()), + vni)); + } - openstackService.floatingIps().stream() - .forEach(f -> floatingIpMap.put(f.id(), f)); + private void populateRuleToGateway(DeviceId deviceId, GroupId groupId, long vni) { + TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder(); + TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder(); - reloadInitL3Rules(); + sBuilder.matchEthType(Ethernet.TYPE_IPV4) + .matchTunnelId(vni) + .matchEthDst(Constants.DEFAULT_GATEWAY_MAC); + + tBuilder.group(groupId); + ForwardingObjective fo = DefaultForwardingObjective.builder() + .withSelector(sBuilder.build()) + .withTreatment(tBuilder.build()) + .withFlag(ForwardingObjective.Flag.SPECIFIC) + .withPriority(ROUTING_RULE_PRIORITY) + .fromApp(appId) + .add(); + + flowObjectiveService.forward(deviceId, fo); + } + + private void populateRoutingRulestoDifferentNode(Ip4Address vmIp, long vni, + DeviceId deviceId, Ip4Address hostIp) { + TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder(); + TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder(); + + sBuilder.matchEthType(Ethernet.TYPE_IPV4) + .matchTunnelId(vni) + .matchIPDst(vmIp.toIpPrefix()); + tBuilder.extension(buildExtension(deviceService, deviceId, hostIp), deviceId) + .setOutput(nodeService.tunnelPort(deviceId).get()); + + ForwardingObjective fo = DefaultForwardingObjective.builder() + .withSelector(sBuilder.build()) + .withTreatment(tBuilder.build()) + .withPriority(ROUTING_RULE_PRIORITY) + .withFlag(ForwardingObjective.Flag.SPECIFIC) + .fromApp(appId) + .add(); + + flowObjectiveService.forward(deviceId, fo); + } + + private void populateRoutingRulestoSameNode(Ip4Address vmIp, MacAddress vmMac, + PortNumber port, DeviceId deviceId, long vni) { + TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder(); + TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder(); + + sBuilder.matchEthType(Ethernet.TYPE_IPV4) + .matchIPDst(vmIp.toIpPrefix()) + .matchTunnelId(vni); + + tBuilder.setEthDst(vmMac) + .setOutput(port); + + ForwardingObjective fo = DefaultForwardingObjective.builder() + .withSelector(sBuilder.build()) + .withTreatment(tBuilder.build()) + .withPriority(ROUTING_RULE_PRIORITY) + .withFlag(ForwardingObjective.Flag.SPECIFIC) + .fromApp(appId) + .add(); + + flowObjectiveService.forward(deviceId, fo); + } + + private void reloadRoutingRules() { + eventExecutor.execute(() -> openstackService.ports().stream() + .filter(osPort -> osPort.deviceOwner().equals(DEVICE_OWNER_ROUTER_INTERFACE)) + .forEach(osPort -> { + OpenstackRouter osRouter = openstackRouter(osPort.deviceId()); + setRoutes(osRouter, Optional.empty()); + if (osRouter.gatewayExternalInfo().externalFixedIps().size() > 0) { + setExternalConnection(osRouter, osPort.networkId()); + } + })); + } + + @Override + protected void hostDetected(Host host) { + String osNetId = host.annotations().value(NETWORK_ID); + Optional routerIface = routerIfacePort(osNetId); + if (!routerIface.isPresent()) { + return; } + eventExecutor.execute(() -> setRoutes( + openstackRouter(routerIface.get().deviceId()), + Optional.of(host))); + } + + @Override + protected void hostRemoved(Host host) { + String osNetId = host.annotations().value(NETWORK_ID); + Optional routerIface = routerIfacePort(osNetId); + if (!routerIface.isPresent()) { + return; + } + Set routableNets = routableNetworks(routerIface.get().deviceId()); + eventExecutor.execute(() -> removeRoutingRules(host, routableNets)); + } + + private class InternalNodeListener implements OpenstackNodeListener { @Override public void event(OpenstackNodeEvent event) { @@ -730,28 +481,22 @@ public class OpenstackRoutingManager implements OpenstackRoutingService { switch (event.type()) { case COMPLETE: log.info("COMPLETE node {} detected", node.hostname()); - l3EventExecutorService.execute(() -> nodeComplete()); + if (node.type() == GATEWAY) { + GatewayNode gnode = GatewayNode.builder() + .gatewayDeviceId(node.intBridge()) + .dataIpAddress(node.dataIp().getIp4Address()) + .uplinkIntf(node.externalPortName().get()) + .build(); + gatewayService.addGatewayNode(gnode); + } + eventExecutor.execute(OpenstackRoutingManager.this::reloadRoutingRules); break; + case INIT: + case DEVICE_CREATED: case INCOMPLETE: - break; default: break; } } } - - private class InternalHostProvider extends AbstractProvider implements HostProvider { - - /** - * Creates a provider with the supplier identifier. - */ - protected InternalHostProvider() { - super(PID); - } - - @Override - public void triggerProbe(Host host) { - // nothing to do - } - } } diff --git a/apps/openstacknetworking/routing/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackRoutingRulePopulator.java b/apps/openstacknetworking/routing/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackRoutingRulePopulator.java deleted file mode 100644 index 875cf98cda..0000000000 --- a/apps/openstacknetworking/routing/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackRoutingRulePopulator.java +++ /dev/null @@ -1,693 +0,0 @@ -/* - * Copyright 2016-present 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.openstacknetworking.routing; - -import com.google.common.collect.Lists; -import org.onlab.packet.Ethernet; -import org.onlab.packet.IPv4; -import org.onlab.packet.Ip4Address; -import org.onlab.packet.IpAddress; -import org.onlab.packet.IpPrefix; -import org.onlab.packet.MacAddress; -import org.onlab.packet.TCP; -import org.onlab.packet.TpPort; -import org.onlab.packet.UDP; -import org.onosproject.core.ApplicationId; -import org.onosproject.core.GroupId; -import org.onosproject.net.Device; -import org.onosproject.net.DeviceId; -import org.onosproject.net.Host; -import org.onosproject.net.Port; -import org.onosproject.net.PortNumber; -import org.onosproject.net.behaviour.ExtensionTreatmentResolver; -import org.onosproject.net.device.DeviceService; -import org.onosproject.net.driver.DefaultDriverData; -import org.onosproject.net.driver.DefaultDriverHandler; -import org.onosproject.net.driver.Driver; -import org.onosproject.net.driver.DriverHandler; -import org.onosproject.net.driver.DriverService; -import org.onosproject.net.flow.DefaultTrafficSelector; -import org.onosproject.net.flow.DefaultTrafficTreatment; -import org.onosproject.net.flow.TrafficSelector; -import org.onosproject.net.flow.TrafficTreatment; -import org.onosproject.net.flow.instructions.ExtensionPropertyException; -import org.onosproject.net.flow.instructions.ExtensionTreatment; -import org.onosproject.net.flow.instructions.ExtensionTreatmentType; -import org.onosproject.net.flowobjective.DefaultForwardingObjective; -import org.onosproject.net.flowobjective.FlowObjectiveService; -import org.onosproject.net.flowobjective.ForwardingObjective; -import org.onosproject.net.packet.InboundPacket; -import org.onosproject.openstackinterface.OpenstackInterfaceService; -import org.onosproject.openstackinterface.OpenstackPort; -import org.onosproject.openstackinterface.OpenstackRouterInterface; -import org.onosproject.openstackinterface.OpenstackSubnet; -import org.onosproject.openstackinterface.OpenstackFloatingIP; -import org.onosproject.openstacknetworking.Constants; -import org.onosproject.openstacknetworking.OpenstackRoutingService; -import org.onosproject.scalablegateway.api.ScalableGatewayService; -import org.onosproject.openstacknode.OpenstackNode; -import org.onosproject.openstacknode.OpenstackNodeService; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.List; -import java.util.Optional; -import java.util.stream.StreamSupport; - -import static com.google.common.base.Preconditions.checkNotNull; -import static org.onlab.osgi.DefaultServiceDirectory.getService; -import static org.onosproject.net.AnnotationKeys.PORT_NAME; - -/** - * Populates Routing Flow Rules. - */ -public class OpenstackRoutingRulePopulator { - - private final Logger log = LoggerFactory.getLogger(getClass()); - - private final ApplicationId appId; - private final FlowObjectiveService flowObjectiveService; - private final OpenstackInterfaceService openstackService; - private final DeviceService deviceService; - private final DriverService driverService; - private final ScalableGatewayService gatewayService; - private final OpenstackNodeService nodeService; - - private static final String PORTNAME_PREFIX_TUNNEL = "vxlan"; - private static final String PORTNAME_PREFIX_VM = "tap"; - - private static final String PORTNOTNULL = "Port can not be null"; - private static final String DEVICENOTNULL = "Device can not be null"; - private static final String TUNNEL_DESTINATION = "tunnelDst"; - private static final int ROUTING_RULE_PRIORITY = 25000; - private static final int FLOATING_RULE_PRIORITY = 42000; - private static final int PNAT_RULE_PRIORITY = 26000; - private static final int PNAT_TIMEOUT = 120; - private static final int PREFIX_LENGTH = 32; - - private InboundPacket inboundPacket; - private OpenstackPort openstackPort; - private int portNum; - private MacAddress externalInterface; - private MacAddress externalRouter; - - /** - * The constructor of openstackRoutingRulePopulator. - * - * @param appId Caller`s appId - * @param openstackService Opestack REST request handler - * @param flowObjectiveService FlowObjectiveService - * @param deviceService DeviceService - * @param driverService DriverService - * @param nodeService openstack node service - * @param gatewayService scalable gateway service - */ - public OpenstackRoutingRulePopulator(ApplicationId appId, - OpenstackInterfaceService openstackService, - FlowObjectiveService flowObjectiveService, - DeviceService deviceService, - DriverService driverService, - OpenstackNodeService nodeService, - ScalableGatewayService gatewayService) { - this.appId = appId; - this.flowObjectiveService = flowObjectiveService; - this.openstackService = checkNotNull(openstackService); - this.deviceService = deviceService; - this.driverService = driverService; - this.gatewayService = gatewayService; - this.nodeService = nodeService; - } - - /** - * Populates flow rules for Pnat configurations. - * - * @param inboundPacket Packet-in event packet - * @param openstackPort Target VM information - * @param portNum Pnat port number - * @param externalIp external ip address - * @param externalInterfaceMacAddress Gateway external interface macaddress - * @param externalRouterMacAddress Outer(physical) router`s macaddress - */ - public void populatePnatFlowRules(InboundPacket inboundPacket, OpenstackPort openstackPort, int portNum, - Ip4Address externalIp, MacAddress externalInterfaceMacAddress, - MacAddress externalRouterMacAddress) { - this.inboundPacket = inboundPacket; - this.openstackPort = openstackPort; - this.portNum = portNum; - this.externalInterface = externalInterfaceMacAddress; - this.externalRouter = externalRouterMacAddress; - - long vni = getVni(openstackPort.networkId()); - - populatePnatIncomingFlowRules(vni, externalIp); - populatePnatOutgoingFlowRules(vni, externalIp); - } - - private void populatePnatOutgoingFlowRules(long vni, Ip4Address externalIp) { - IPv4 iPacket = (IPv4) inboundPacket.parsed().getPayload(); - - TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder(); - sBuilder.matchEthType(Ethernet.TYPE_IPV4) - .matchIPProtocol(iPacket.getProtocol()) - .matchTunnelId(vni) - .matchIPSrc(IpPrefix.valueOf(iPacket.getSourceAddress(), 32)) - .matchIPDst(IpPrefix.valueOf(iPacket.getDestinationAddress(), 32)); - - TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder(); - - switch (iPacket.getProtocol()) { - case IPv4.PROTOCOL_TCP: - TCP tcpPacket = (TCP) iPacket.getPayload(); - sBuilder.matchTcpSrc(TpPort.tpPort(tcpPacket.getSourcePort())) - .matchTcpDst(TpPort.tpPort(tcpPacket.getDestinationPort())); - tBuilder.setTcpSrc(TpPort.tpPort(portNum)); - break; - case IPv4.PROTOCOL_UDP: - UDP udpPacket = (UDP) iPacket.getPayload(); - sBuilder.matchUdpSrc(TpPort.tpPort(udpPacket.getSourcePort())) - .matchUdpDst(TpPort.tpPort(udpPacket.getDestinationPort())); - tBuilder.setUdpSrc(TpPort.tpPort(portNum)); - break; - default: - log.debug("Unsupported IPv4 protocol {}"); - break; - } - - tBuilder.setIpSrc(externalIp); - gatewayService.getGatewayNodes().forEach(node -> { - tBuilder.setOutput(gatewayService.getUplinkPort(node.getGatewayDeviceId())); - ForwardingObjective fo = DefaultForwardingObjective.builder() - .withSelector(sBuilder.build()) - .withTreatment(tBuilder.build()) - .withFlag(ForwardingObjective.Flag.VERSATILE) - .withPriority(PNAT_RULE_PRIORITY) - .makeTemporary(PNAT_TIMEOUT) - .fromApp(appId) - .add(); - - flowObjectiveService.forward(node.getGatewayDeviceId(), fo); - }); - - } - - private Port getPortOfExternalInterface() { - return deviceService.getPorts(getGatewayNode().id()).stream() - .filter(p -> p.annotations().value(PORT_NAME) - .equals(org.onosproject.openstacknode.Constants.PATCH_INTG_BRIDGE)) - .findAny().orElse(null); - } - - - private void populatePnatIncomingFlowRules(long vni, Ip4Address externalIp) { - IPv4 iPacket = (IPv4) inboundPacket.parsed().getPayload(); - - TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder(); - sBuilder.matchEthType(Ethernet.TYPE_IPV4) - .matchIPProtocol(iPacket.getProtocol()) - .matchIPDst(IpPrefix.valueOf(externalIp, 32)) - .matchIPSrc(IpPrefix.valueOf(iPacket.getDestinationAddress(), 32)); - - TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder(); - tBuilder.setTunnelId(vni) - .setEthDst(inboundPacket.parsed().getSourceMAC()) - .setIpDst(IpAddress.valueOf(iPacket.getSourceAddress())); - - switch (iPacket.getProtocol()) { - case IPv4.PROTOCOL_TCP: - TCP tcpPacket = (TCP) iPacket.getPayload(); - sBuilder.matchTcpSrc(TpPort.tpPort(tcpPacket.getDestinationPort())) - .matchTcpDst(TpPort.tpPort(portNum)); - tBuilder.setTcpDst(TpPort.tpPort(tcpPacket.getSourcePort())); - break; - case IPv4.PROTOCOL_UDP: - UDP udpPacket = (UDP) iPacket.getPayload(); - sBuilder.matchUdpSrc(TpPort.tpPort(udpPacket.getDestinationPort())) - .matchUdpDst(TpPort.tpPort(portNum)); - tBuilder.setUdpDst(TpPort.tpPort(udpPacket.getSourcePort())); - break; - default: - break; - } - - getGatewayNodeList().forEach(node -> { - DeviceId deviceId = node.id(); - tBuilder.extension(buildNiciraExtenstion(deviceId, - getHostIpfromOpenstackPort(openstackPort).getIp4Address()), deviceId) - .setOutput(getTunnelPort(deviceId)); - - ForwardingObjective fo = DefaultForwardingObjective.builder() - .withSelector(sBuilder.build()) - .withTreatment(tBuilder.build()) - .withFlag(ForwardingObjective.Flag.VERSATILE) - .withPriority(PNAT_RULE_PRIORITY) - .makeTemporary(PNAT_TIMEOUT) - .fromApp(appId) - .add(); - - flowObjectiveService.forward(deviceId, fo); - }); - } - - private List getGatewayNodeList() { - List devices = Lists.newArrayList(); - gatewayService.getGatewayDeviceIds().forEach(deviceId -> - devices.add(checkNotNull(deviceService.getDevice(deviceId)))); - return devices; - } - - private IpAddress getHostIpfromOpenstackPort(OpenstackPort openstackPort) { - Device device = getDevicefromOpenstackPort(openstackPort); - - Optional ipAddress = nodeService.dataIp(device.id()); - if (!ipAddress.isPresent()) { - log.warn("No IP address found for device {}", device.id()); - return null; - } - - return ipAddress.get(); - } - - private Device getDevicefromOpenstackPort(OpenstackPort openstackPort) { - String openstackPortName = PORTNAME_PREFIX_VM + openstackPort.id().substring(0, 11); - Device device = StreamSupport.stream(deviceService.getDevices().spliterator(), false) - .filter(d -> findPortinDevice(d.id(), openstackPortName)) - .iterator() - .next(); - checkNotNull(device, DEVICENOTNULL); - return device; - } - - private boolean findPortinDevice(DeviceId deviceId, String openstackPortName) { - Port port = deviceService.getPorts(deviceId) - .stream() - .filter(p -> p.isEnabled() && p.annotations().value(PORT_NAME).equals(openstackPortName)) - .findAny() - .orElse(null); - return port != null; - } - - /** - * Builds Nicira extension for tagging remoteIp of vxlan. - * - * @param deviceId Device Id of vxlan source device - * @param hostIp Remote Ip of vxlan destination device - * @return NiciraExtension Treatment - */ - public ExtensionTreatment buildNiciraExtenstion(DeviceId deviceId, Ip4Address hostIp) { - Driver driver = driverService.getDriver(deviceId); - DriverHandler driverHandler = new DefaultDriverHandler(new DefaultDriverData(driver, deviceId)); - ExtensionTreatmentResolver resolver = driverHandler.behaviour(ExtensionTreatmentResolver.class); - - ExtensionTreatment extensionInstruction = - resolver.getExtensionInstruction( - ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_SET_TUNNEL_DST.type()); - - try { - extensionInstruction.setPropertyValue(TUNNEL_DESTINATION, hostIp); - } catch (ExtensionPropertyException e) { - log.error("Error setting Nicira extension setting {}", e); - } - - return extensionInstruction; - } - - /** - * Returns port number of vxlan tunnel. - * - * @param deviceId Target Device Id - * @return PortNumber - */ - public PortNumber getTunnelPort(DeviceId deviceId) { - Port port = deviceService.getPorts(deviceId).stream() - .filter(p -> p.annotations().value(PORT_NAME).equals(PORTNAME_PREFIX_TUNNEL)) - .findAny().orElse(null); - - if (port == null) { - log.error("No TunnelPort was created."); - return null; - } - return port.number(); - - } - - /** - * Populates flow rules from openstackComputeNode to GatewayNode. - * - * @param vni Target network - */ - public void populateExternalRules(long vni) { - - // 1. computeNode to gateway - populateComputeNodeRules(vni); - // 2. gatewayNode to controller - populateRuleGatewaytoController(vni); - } - - private void populateRuleGatewaytoController(long vni) { - TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder(); - TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder(); - - sBuilder.matchEthType(Ethernet.TYPE_IPV4) - .matchTunnelId(vni) - .matchEthDst(Constants.GATEWAY_MAC); - tBuilder.setOutput(PortNumber.CONTROLLER); - - ForwardingObjective fo = DefaultForwardingObjective.builder() - .withSelector(sBuilder.build()) - .withTreatment(tBuilder.build()) - .withFlag(ForwardingObjective.Flag.VERSATILE) - .withPriority(ROUTING_RULE_PRIORITY) - .fromApp(appId) - .add(); - - getGatewayNodeList().forEach(device -> flowObjectiveService.forward(device.id(), fo)); - } - - private void populateComputeNodeRules(long vni) { - StreamSupport.stream(deviceService.getDevices().spliterator(), false) - .filter(d -> isTypeOf(d.id(), OpenstackNodeService.NodeType.COMPUTE)) - .forEach(d -> populateRuleToGatewayBySgw(d.id(), - gatewayService.getGatewayGroupId(d.id()), vni)); - } - - private void populateRuleToGatewayBySgw(DeviceId deviceId, GroupId groupId, long vni) { - TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder(); - TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder(); - - sBuilder.matchEthType(Ethernet.TYPE_IPV4) - .matchTunnelId(vni) - .matchEthDst(Constants.GATEWAY_MAC); - - tBuilder.group(groupId); - - ForwardingObjective fo = DefaultForwardingObjective.builder() - .withSelector(sBuilder.build()) - .withTreatment(tBuilder.build()) - .withFlag(ForwardingObjective.Flag.SPECIFIC) - .withPriority(ROUTING_RULE_PRIORITY) - .fromApp(appId) - .add(); - - flowObjectiveService.forward(deviceId, fo); - } - - private Device getGatewayNode() { - - // TODO Return the correct gateway node - Optional gwNode = nodeService.nodes().stream() - .filter(n -> n.type().equals(OpenstackNodeService.NodeType.GATEWAY)) - .findFirst(); - - if (!gwNode.isPresent()) { - log.warn("No Gateway is defined."); - return null; - } - - return deviceService.getDevice(gwNode.get().intBridge()); - } - - private boolean isTypeOf(DeviceId deviceId, OpenstackNodeService.NodeType type) { - - Optional node = nodeService.nodes().stream() - .filter(n -> n.intBridge().equals(deviceId) || - (n.routerBridge().isPresent() && n.routerBridge().get().equals(deviceId))) - .filter(n -> n.type().equals(type)) - .findFirst(); - - if (node.isPresent()) { - return true; - } - - return false; - } - - private long getVni(String netId) { - return Long.parseLong(openstackService.network(netId).segmentId()); - } - - /** - * Remove flow rules for external connection. - * - * @param routerInterface Corresponding routerInterface - */ - public void removeExternalRules(OpenstackRouterInterface routerInterface) { - OpenstackSubnet openstackSubnet = openstackService.subnet(routerInterface.subnetId()); - TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder(); - sBuilder.matchEthType(Ethernet.TYPE_IPV4) - .matchTunnelId(getVni(openstackSubnet.networkId())) - .matchEthDst(Constants.GATEWAY_MAC); - - StreamSupport.stream(deviceService.getDevices().spliterator(), false) - .forEach(d -> { - ForwardingObjective.Flag flag = isTypeOf(d.id(), OpenstackNodeService.NodeType.GATEWAY) ? - ForwardingObjective.Flag.VERSATILE : - ForwardingObjective.Flag.SPECIFIC; - removeRule(d.id(), sBuilder, flag, ROUTING_RULE_PRIORITY); - }); - - } - - private void removeRule(DeviceId deviceId, TrafficSelector.Builder sBuilder, - ForwardingObjective.Flag flag, int priority) { - TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder(); - - ForwardingObjective fo = DefaultForwardingObjective.builder() - .withSelector(sBuilder.build()) - .withTreatment(tBuilder.build()) - .withFlag(flag) - .withPriority(priority) - .fromApp(appId) - .remove(); - - flowObjectiveService.forward(deviceId, fo); - } - - /** - * Populates flow rules for floatingIp configuration. - * - * @param floatingIP Corresponding floating ip information - */ - public void populateFloatingIpRules(OpenstackFloatingIP floatingIP) { - OpenstackPort port = openstackService.port(floatingIP.portId()); - //1. incoming rules - populateFloatingIpIncomingRules(floatingIP, port); - //2. outgoing rules - populateFloatingIpOutgoingRules(floatingIP, port); - } - - private void populateFloatingIpIncomingRules(OpenstackFloatingIP floatingIP, OpenstackPort port) { - TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder(); - TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder(); - - sBuilder.matchEthType(Ethernet.TYPE_IPV4) - .matchIPDst(IpPrefix.valueOf(floatingIP.floatingIpAddress(), PREFIX_LENGTH)); - - DeviceId gatewayDeviceId = DeviceId.deviceId(port.deviceId()); - Optional ipAddress = nodeService.dataIp(gatewayDeviceId); - if (!ipAddress.isPresent()) { - log.warn("No IP address found for device {}", port.deviceId()); - return; - } - tBuilder.setEthSrc(Constants.GATEWAY_MAC) - .setEthDst(port.macAddress()) - .setIpDst(floatingIP.fixedIpAddress()) - .setTunnelId(getVni(port.networkId())) - .extension(buildNiciraExtenstion(gatewayDeviceId, - ipAddress.get().getIp4Address()), gatewayDeviceId) - .setOutput(getTunnelPort(gatewayDeviceId)); - - ForwardingObjective fo = DefaultForwardingObjective.builder() - .withSelector(sBuilder.build()) - .withTreatment(tBuilder.build()) - .withFlag(ForwardingObjective.Flag.VERSATILE) - .withPriority(FLOATING_RULE_PRIORITY) - .fromApp(appId) - .add(); - - flowObjectiveService.forward(getGatewayNode().id(), fo); - } - - private void populateFloatingIpOutgoingRules(OpenstackFloatingIP floatingIP, OpenstackPort port) { - TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder(); - TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder(); - - sBuilder.matchEthType(Ethernet.TYPE_IPV4) - .matchTunnelId(getVni(port.networkId())) - .matchIPSrc(IpPrefix.valueOf(floatingIP.fixedIpAddress(), 32)); - - getGatewayNodeList().forEach(device -> { - DeviceId deviceId = device.id(); - tBuilder.setIpSrc(floatingIP.floatingIpAddress()) - .setEthSrc(Constants.GW_EXT_INT_MAC) - .setEthDst(Constants.PHY_ROUTER_MAC) - .setOutput(getExternalPortNum(deviceId)); - - ForwardingObjective fo = DefaultForwardingObjective.builder() - .withSelector(sBuilder.build()) - .withTreatment(tBuilder.build()) - .withFlag(ForwardingObjective.Flag.VERSATILE) - .withPriority(FLOATING_RULE_PRIORITY) - .fromApp(appId) - .add(); - - flowObjectiveService.forward(deviceId, fo); - }); - } - - private PortNumber getExternalPortNum(DeviceId deviceId) { - return checkNotNull(gatewayService.getUplinkPort(deviceId), PORTNOTNULL); - } - - /** - * Removes flow rules for floating ip configuration. - * - * @param floatingIp Corresponding floating ip information - * @param host host information for vm to remove - */ - public void removeFloatingIpRules(OpenstackFloatingIP floatingIp, Host host) { - TrafficSelector.Builder sOutgoingBuilder = DefaultTrafficSelector.builder(); - TrafficSelector.Builder sIncomingBuilder = DefaultTrafficSelector.builder(); - - // XXX FloatingIp.tenant_id() == host.vxlan_id ??? - sOutgoingBuilder.matchEthType(Ethernet.TYPE_IPV4) - .matchTunnelId(Integer.parseInt(host.annotations().value(Constants.VXLAN_ID))) - .matchIPSrc(IpPrefix.valueOf(floatingIp.fixedIpAddress(), PREFIX_LENGTH)); - - sIncomingBuilder.matchEthType(Ethernet.TYPE_IPV4) - .matchIPDst(IpPrefix.valueOf(floatingIp.floatingIpAddress(), PREFIX_LENGTH)); - - getGatewayNodeList().forEach(device -> { - removeRule(device.id(), sOutgoingBuilder, ForwardingObjective.Flag.VERSATILE, FLOATING_RULE_PRIORITY); - removeRule(device.id(), sIncomingBuilder, ForwardingObjective.Flag.VERSATILE, FLOATING_RULE_PRIORITY); - }); - } - - /** - * Populates L3 rules for east to west traffic. - * - * @param openstackPort target VM - * @param targetList target openstackRouterInterfaces - */ - public void populateL3Rules(OpenstackPort openstackPort, List targetList) { - Device device = getDevicefromOpenstackPort(openstackPort); - Port port = getPortFromOpenstackPort(device, openstackPort); - Ip4Address vmIp = openstackPort.fixedIps().values().iterator().next(); - - if (port == null) { - return; - } - - targetList.forEach(routerInterface -> { - long vni = getVni(openstackService.port(routerInterface.portId()).networkId()); - - if (vmIp == null) { - return; - } - - populateL3RulestoSameNode(vmIp, openstackPort, port, device, vni); - - deviceService.getAvailableDevices().forEach(d -> { - if (!d.equals(device) && !d.equals(getGatewayNode())) { - populateL3RulestoDifferentNode(vmIp, vni, d.id(), - getHostIpfromOpenstackPort(openstackPort).getIp4Address()); - } - }); - }); - } - - private void populateL3RulestoDifferentNode(Ip4Address vmIp, long vni, DeviceId deviceId, Ip4Address hostIp) { - TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder(); - TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder(); - - sBuilder.matchEthType(Ethernet.TYPE_IPV4) - .matchTunnelId(vni) - .matchIPDst(vmIp.toIpPrefix()); - tBuilder.extension(buildNiciraExtenstion(deviceId, hostIp), deviceId) - .setOutput(getTunnelPort(deviceId)); - - ForwardingObjective fo = DefaultForwardingObjective.builder() - .withSelector(sBuilder.build()) - .withTreatment(tBuilder.build()) - .withPriority(ROUTING_RULE_PRIORITY) - .withFlag(ForwardingObjective.Flag.SPECIFIC) - .fromApp(appId) - .add(); - - flowObjectiveService.forward(deviceId, fo); - } - - private void populateL3RulestoSameNode(Ip4Address vmIp, OpenstackPort p, Port port, Device device, long vni) { - TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder(); - TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder(); - - sBuilder.matchEthType(Ethernet.TYPE_IPV4) - .matchIPDst(vmIp.toIpPrefix()) - .matchTunnelId(vni); - - tBuilder.setEthDst(p.macAddress()) - .setOutput(port.number()); - - ForwardingObjective fo = DefaultForwardingObjective.builder() - .withSelector(sBuilder.build()) - .withTreatment(tBuilder.build()) - .withPriority(ROUTING_RULE_PRIORITY) - .withFlag(ForwardingObjective.Flag.SPECIFIC) - .fromApp(appId) - .add(); - - flowObjectiveService.forward(device.id(), fo); - } - - private Port getPortFromOpenstackPort(Device device, OpenstackPort p) { - String openstackPortName = PORTNAME_PREFIX_VM + p.id().substring(0, 11); - return deviceService.getPorts(device.id()) - .stream() - .filter(pt -> pt.annotations().value(PORT_NAME).equals(openstackPortName)) - .findAny() - .orElse(null); - } - - /** - * Removes L3 rules for routerInterface events. - * - * @param vmIp Corresponding Vm ip - * @param routerInterfaces Corresponding routerInterfaces - */ - public void removeL3Rules(Ip4Address vmIp, List routerInterfaces) { - if (vmIp == null) { - return; - } - - OpenstackRoutingService routingService = getService(OpenstackRoutingService.class); - - deviceService.getAvailableDevices().forEach(d -> { - if (isTypeOf(d.id(), OpenstackNodeService.NodeType.COMPUTE)) { - routerInterfaces.forEach(routerInterface -> { - String networkId = routingService.networkIdForRouterInterface(routerInterface.portId()); - long vni = getVni(networkId); - - TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder(); - - sBuilder.matchEthType(Ethernet.TYPE_IPV4) - .matchIPDst(vmIp.toIpPrefix()) - .matchTunnelId(vni); - - removeRule(d.id(), sBuilder, ForwardingObjective.Flag.SPECIFIC, ROUTING_RULE_PRIORITY); - }); - } - }); - } -} diff --git a/apps/openstacknetworking/routing/src/main/java/org/onosproject/openstacknetworking/routing/package-info.java b/apps/openstacknetworking/routing/src/main/java/org/onosproject/openstacknetworking/routing/package-info.java index 78d05a975f..67fd1a56ab 100644 --- a/apps/openstacknetworking/routing/src/main/java/org/onosproject/openstacknetworking/routing/package-info.java +++ b/apps/openstacknetworking/routing/src/main/java/org/onosproject/openstacknetworking/routing/package-info.java @@ -15,6 +15,8 @@ */ /** - * Application for OpenstackRouting. + * Implements OpenStack L3 service plugin, which routes packets between subnets, + * forwards packets from internal networks to external ones, and accesses instances + * from external networks through floating IPs. */ -package org.onosproject.openstacknetworking.routing; \ No newline at end of file +package org.onosproject.openstacknetworking.routing; diff --git a/apps/openstacknetworking/switching/src/main/java/org/onosproject/openstacknetworking/switching/OpenstackSecurityGroupRulePopulator.java b/apps/openstacknetworking/switching/src/main/java/org/onosproject/openstacknetworking/switching/OpenstackSecurityGroupManager.java similarity index 97% rename from apps/openstacknetworking/switching/src/main/java/org/onosproject/openstacknetworking/switching/OpenstackSecurityGroupRulePopulator.java rename to apps/openstacknetworking/switching/src/main/java/org/onosproject/openstacknetworking/switching/OpenstackSecurityGroupManager.java index 82a5bacb35..e06b7fac61 100644 --- a/apps/openstacknetworking/switching/src/main/java/org/onosproject/openstacknetworking/switching/OpenstackSecurityGroupRulePopulator.java +++ b/apps/openstacknetworking/switching/src/main/java/org/onosproject/openstacknetworking/switching/OpenstackSecurityGroupManager.java @@ -30,6 +30,7 @@ import org.onlab.packet.Ip4Address; import org.onlab.packet.IpPrefix; import org.onlab.packet.TpPort; import org.onlab.util.Tools; +import org.onosproject.core.ApplicationId; import org.onosproject.net.DeviceId; import org.onosproject.net.Host; import org.onosproject.net.flow.DefaultTrafficSelector; @@ -43,6 +44,7 @@ import org.onosproject.openstackinterface.OpenstackPort; import org.onosproject.openstackinterface.OpenstackSecurityGroup; import org.onosproject.openstackinterface.OpenstackSecurityGroupRule; import org.onosproject.openstacknetworking.OpenstackSecurityGroupService; +import org.onosproject.openstacknetworking.AbstractVmHandler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -60,7 +62,7 @@ import static org.onosproject.openstacknetworking.Constants.*; */ @Component(immediate = true) @Service -public class OpenstackSecurityGroupRulePopulator extends AbstractVmHandler +public class OpenstackSecurityGroupManager extends AbstractVmHandler implements OpenstackSecurityGroupService { private final Logger log = LoggerFactory.getLogger(getClass()); @@ -77,10 +79,12 @@ public class OpenstackSecurityGroupRulePopulator extends AbstractVmHandler private static final String ETHTYPE_IPV4 = "IPV4"; private final Map> securityGroupRuleMap = Maps.newConcurrentMap(); + private ApplicationId appId; @Activate protected void activate() { super.activate(); + appId = coreService.registerApplication(SWITCHING_APP_ID); } @Deactivate @@ -96,7 +100,7 @@ public class OpenstackSecurityGroupRulePopulator extends AbstractVmHandler Optional host = getVmByPortId(osPort.id()); if (!host.isPresent()) { - log.warn("No host found with {}", osPort); + log.debug("No host found with {}", osPort.id()); return; } eventExecutor.execute(() -> updateSecurityGroupRules(host.get(), true)); diff --git a/apps/openstacknetworking/switching/src/main/java/org/onosproject/openstacknetworking/switching/OpenstackArpHandler.java b/apps/openstacknetworking/switching/src/main/java/org/onosproject/openstacknetworking/switching/OpenstackSwitchingArpHandler.java similarity index 96% rename from apps/openstacknetworking/switching/src/main/java/org/onosproject/openstacknetworking/switching/OpenstackArpHandler.java rename to apps/openstacknetworking/switching/src/main/java/org/onosproject/openstacknetworking/switching/OpenstackSwitchingArpHandler.java index c31bfd9900..c35371fb9f 100644 --- a/apps/openstacknetworking/switching/src/main/java/org/onosproject/openstacknetworking/switching/OpenstackArpHandler.java +++ b/apps/openstacknetworking/switching/src/main/java/org/onosproject/openstacknetworking/switching/OpenstackSwitchingArpHandler.java @@ -40,6 +40,7 @@ import org.onosproject.net.packet.PacketService; import org.onosproject.openstackinterface.OpenstackInterfaceService; import org.onosproject.openstackinterface.OpenstackNetwork; import org.onosproject.openstackinterface.OpenstackPort; +import org.onosproject.openstacknetworking.AbstractVmHandler; import org.osgi.service.component.ComponentContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -54,12 +55,11 @@ import static org.onosproject.openstacknetworking.Constants.*; * Handles ARP packet from VMs. */ @Component(immediate = true) -public final class OpenstackArpHandler extends AbstractVmHandler { +public final class OpenstackSwitchingArpHandler extends AbstractVmHandler { private final Logger log = LoggerFactory.getLogger(getClass()); private static final String GATEWAY_MAC = "gatewayMac"; - private static final String DEFAULT_GATEWAY_MAC = "1f:1f:1f:1f:1f:1f"; @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected PacketService packetService; @@ -67,9 +67,9 @@ public final class OpenstackArpHandler extends AbstractVmHandler { @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected OpenstackInterfaceService openstackService; - @Property(name = GATEWAY_MAC, value = DEFAULT_GATEWAY_MAC, + @Property(name = GATEWAY_MAC, value = DEFAULT_GATEWAY_MAC_STR, label = "Fake MAC address for virtual network subnet gateway") - private String gatewayMac = DEFAULT_GATEWAY_MAC; + private String gatewayMac = DEFAULT_GATEWAY_MAC_STR; private final InternalPacketProcessor packetProcessor = new InternalPacketProcessor(); private final Set gateways = Sets.newConcurrentHashSet(); diff --git a/apps/openstacknetworking/switching/src/main/java/org/onosproject/openstacknetworking/switching/OpenstackSwitchingHostManager.java b/apps/openstacknetworking/switching/src/main/java/org/onosproject/openstacknetworking/switching/OpenstackSwitchingHostManager.java new file mode 100644 index 0000000000..2d6621a4c1 --- /dev/null +++ b/apps/openstacknetworking/switching/src/main/java/org/onosproject/openstacknetworking/switching/OpenstackSwitchingHostManager.java @@ -0,0 +1,318 @@ +/* + * Copyright 2016-present 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.openstacknetworking.switching; + +import com.google.common.base.Strings; +import com.google.common.collect.Sets; +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.Reference; +import org.apache.felix.scr.annotations.ReferenceCardinality; +import org.apache.felix.scr.annotations.Service; +import org.onlab.packet.Ip4Address; +import org.onlab.packet.IpPrefix; +import org.onlab.packet.VlanId; +import org.onosproject.core.CoreService; +import org.onosproject.dhcp.DhcpService; +import org.onosproject.dhcp.IpAssignment; +import org.onosproject.mastership.MastershipService; +import org.onosproject.net.ConnectPoint; +import org.onosproject.net.DefaultAnnotations; +import org.onosproject.net.Device; +import org.onosproject.net.Host; +import org.onosproject.net.HostId; +import org.onosproject.net.HostLocation; +import org.onosproject.net.Port; +import org.onosproject.net.device.DeviceEvent; +import org.onosproject.net.device.DeviceListener; +import org.onosproject.net.device.DeviceService; +import org.onosproject.net.host.DefaultHostDescription; +import org.onosproject.net.host.HostDescription; +import org.onosproject.net.host.HostProvider; +import org.onosproject.net.host.HostProviderRegistry; +import org.onosproject.net.host.HostProviderService; +import org.onosproject.net.host.HostService; +import org.onosproject.net.provider.AbstractProvider; +import org.onosproject.net.provider.ProviderId; +import org.onosproject.openstackinterface.OpenstackInterfaceService; +import org.onosproject.openstackinterface.OpenstackNetwork; +import org.onosproject.openstackinterface.OpenstackPort; +import org.onosproject.openstackinterface.OpenstackSubnet; +import org.onosproject.openstacknode.OpenstackNode; +import org.onosproject.openstacknode.OpenstackNodeEvent; +import org.onosproject.openstacknode.OpenstackNodeListener; +import org.onosproject.openstacknode.OpenstackNodeService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Date; +import java.util.Map; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import static org.onlab.util.Tools.groupedThreads; +import static org.onosproject.dhcp.IpAssignment.AssignmentStatus.Option_RangeNotEnforced; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static org.onosproject.net.AnnotationKeys.PORT_NAME; +import static org.onosproject.openstacknetworking.Constants.*; + +@Service +@Component(immediate = true) +public final class OpenstackSwitchingHostManager extends AbstractProvider + implements HostProvider { + + private final Logger log = LoggerFactory.getLogger(getClass()); + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected CoreService coreService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected DeviceService deviceService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected HostProviderRegistry hostProviderRegistry; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected DhcpService dhcpService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected HostService hostService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected MastershipService mastershipService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected OpenstackInterfaceService openstackService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected OpenstackNodeService openstackNodeService; + + private final ExecutorService deviceEventExecutor = + Executors.newSingleThreadExecutor(groupedThreads("onos/openstackswitching", "device-event")); + private final ExecutorService configEventExecutor = + Executors.newSingleThreadExecutor(groupedThreads("onos/openstackswitching", "config-event")); + private final InternalDeviceListener internalDeviceListener = new InternalDeviceListener(); + private final InternalOpenstackNodeListener internalNodeListener = new InternalOpenstackNodeListener(); + + private HostProviderService hostProvider; + + /** + * Creates OpenStack switching host provider. + */ + public OpenstackSwitchingHostManager() { + super(new ProviderId("host", SWITCHING_APP_ID)); + } + + @Activate + protected void activate() { + coreService.registerApplication(SWITCHING_APP_ID); + deviceService.addListener(internalDeviceListener); + openstackNodeService.addListener(internalNodeListener); + hostProvider = hostProviderRegistry.register(this); + + log.info("Started"); + } + + @Deactivate + protected void deactivate() { + hostProviderRegistry.unregister(this); + deviceService.removeListener(internalDeviceListener); + openstackNodeService.removeListener(internalNodeListener); + + deviceEventExecutor.shutdown(); + configEventExecutor.shutdown(); + + log.info("Stopped"); + } + + @Override + public void triggerProbe(Host host) { + // no probe is required + } + + private void processPortAdded(Port port) { + // TODO check the node state is COMPLETE + OpenstackPort osPort = openstackService.port(port); + if (osPort == null) { + log.warn("Failed to get OpenStack port for {}", port); + return; + } + + OpenstackNetwork osNet = openstackService.network(osPort.networkId()); + if (osNet == null) { + log.warn("Failed to get OpenStack network {}", + osPort.networkId()); + return; + } + + registerDhcpInfo(osPort); + ConnectPoint connectPoint = new ConnectPoint(port.element().id(), port.number()); + // TODO remove gateway IP from host annotation + Map.Entry fixedIp = osPort.fixedIps().entrySet().stream().findFirst().get(); + + // Added CREATE_TIME intentionally to trigger HOST_UPDATED event for the + // existing instances. + DefaultAnnotations.Builder annotations = DefaultAnnotations.builder() + .set(NETWORK_ID, osPort.networkId()) + .set(SUBNET_ID, fixedIp.getKey()) + .set(PORT_ID, osPort.id()) + .set(VXLAN_ID, osNet.segmentId()) + .set(TENANT_ID, osNet.tenantId()) + // TODO remove gateway IP from host annotation + .set(GATEWAY_IP, fixedIp.getValue().toString()) + .set(CREATE_TIME, String.valueOf(System.currentTimeMillis())); + + HostDescription hostDesc = new DefaultHostDescription( + osPort.macAddress(), + VlanId.NONE, + new HostLocation(connectPoint, System.currentTimeMillis()), + Sets.newHashSet(osPort.fixedIps().values()), + annotations.build()); + + HostId hostId = HostId.hostId(osPort.macAddress()); + hostProvider.hostDetected(hostId, hostDesc, false); + } + + private void processPortRemoved(Port port) { + ConnectPoint connectPoint = new ConnectPoint(port.element().id(), port.number()); + removeHosts(connectPoint); + } + + private void removeHosts(ConnectPoint connectPoint) { + hostService.getConnectedHosts(connectPoint).stream() + .forEach(host -> { + dhcpService.removeStaticMapping(host.mac()); + hostProvider.hostVanished(host.id()); + }); + } + + private void registerDhcpInfo(OpenstackPort openstackPort) { + checkNotNull(openstackPort); + checkArgument(!openstackPort.fixedIps().isEmpty()); + + OpenstackSubnet openstackSubnet = openstackService.subnets().stream() + .filter(n -> n.networkId().equals(openstackPort.networkId())) + .findFirst().orElse(null); + if (openstackSubnet == null) { + log.warn("Failed to find subnet for {}", openstackPort); + return; + } + + Ip4Address ipAddress = openstackPort.fixedIps().values().stream().findFirst().get(); + IpPrefix subnetPrefix = IpPrefix.valueOf(openstackSubnet.cidr()); + Ip4Address broadcast = Ip4Address.makeMaskedAddress( + ipAddress, + subnetPrefix.prefixLength()); + + // TODO: supports multiple DNS servers + Ip4Address domainServer = openstackSubnet.dnsNameservers().isEmpty() ? + DNS_SERVER_IP : openstackSubnet.dnsNameservers().get(0); + + IpAssignment ipAssignment = IpAssignment.builder() + .ipAddress(ipAddress) + .leasePeriod(DHCP_INFINITE_LEASE) + .timestamp(new Date()) + .subnetMask(Ip4Address.makeMaskPrefix(subnetPrefix.prefixLength())) + .broadcast(broadcast) + .domainServer(domainServer) + .assignmentStatus(Option_RangeNotEnforced) + .routerAddress(Ip4Address.valueOf(openstackSubnet.gatewayIp())) + .build(); + + dhcpService.setStaticMapping(openstackPort.macAddress(), ipAssignment); + } + + private class InternalDeviceListener implements DeviceListener { + + @Override + public void event(DeviceEvent event) { + Device device = event.subject(); + if (!mastershipService.isLocalMaster(device.id())) { + // do not allow to proceed without mastership + return; + } + + Port port = event.port(); + if (port == null) { + return; + } + + String portName = port.annotations().value(PORT_NAME); + if (Strings.isNullOrEmpty(portName) || + !portName.startsWith(PORT_NAME_PREFIX_VM)) { + // handles VM connected port event only + return; + } + + switch (event.type()) { + case PORT_UPDATED: + if (!event.port().isEnabled()) { + deviceEventExecutor.execute(() -> processPortRemoved(event.port())); + } + break; + case PORT_ADDED: + deviceEventExecutor.execute(() -> processPortAdded(event.port())); + break; + default: + break; + } + } + } + + private class InternalOpenstackNodeListener implements OpenstackNodeListener { + + @Override + public void event(OpenstackNodeEvent event) { + OpenstackNode node = event.node(); + // TODO check leadership of the node and make only the leader process + + switch (event.type()) { + case COMPLETE: + log.info("COMPLETE node {} detected", node.hostname()); + + // adds existing VMs running on the complete state node + deviceService.getPorts(node.intBridge()).stream() + .filter(port -> port.annotations().value(PORT_NAME) + .startsWith(PORT_NAME_PREFIX_VM) && + port.isEnabled()) + .forEach(port -> { + deviceEventExecutor.execute(() -> processPortAdded(port)); + log.info("VM is detected on {}", port); + }); + + // removes stale VMs + hostService.getHosts().forEach(host -> { + if (deviceService.getPort(host.location().deviceId(), + host.location().port()) == null) { + deviceEventExecutor.execute(() -> removeHosts(host.location())); + log.info("Removed stale VM {}", host.location()); + } + }); + break; + case INCOMPLETE: + log.warn("{} is changed to INCOMPLETE state", node); + break; + case INIT: + case DEVICE_CREATED: + default: + break; + } + } + } +} diff --git a/apps/openstacknetworking/switching/src/main/java/org/onosproject/openstacknetworking/switching/OpenstackSwitchingManager.java b/apps/openstacknetworking/switching/src/main/java/org/onosproject/openstacknetworking/switching/OpenstackSwitchingManager.java index 55fce2c99b..59b41ff814 100644 --- a/apps/openstacknetworking/switching/src/main/java/org/onosproject/openstacknetworking/switching/OpenstackSwitchingManager.java +++ b/apps/openstacknetworking/switching/src/main/java/org/onosproject/openstacknetworking/switching/OpenstackSwitchingManager.java @@ -1,319 +1,273 @@ /* - * Copyright 2016-present 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. - */ +* Copyright 2016-present 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.openstacknetworking.switching; -import com.google.common.base.Strings; -import com.google.common.collect.Sets; 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.Reference; import org.apache.felix.scr.annotations.ReferenceCardinality; -import org.apache.felix.scr.annotations.Service; +import org.onlab.packet.Ethernet; import org.onlab.packet.Ip4Address; -import org.onlab.packet.IpPrefix; -import org.onlab.packet.VlanId; -import org.onosproject.core.CoreService; -import org.onosproject.dhcp.DhcpService; -import org.onosproject.dhcp.IpAssignment; -import org.onosproject.mastership.MastershipService; -import org.onosproject.net.ConnectPoint; -import org.onosproject.net.DefaultAnnotations; -import org.onosproject.net.Device; +import org.onlab.packet.IpAddress; +import org.onosproject.core.ApplicationId; +import org.onosproject.net.DeviceId; import org.onosproject.net.Host; -import org.onosproject.net.HostId; -import org.onosproject.net.HostLocation; -import org.onosproject.net.Port; -import org.onosproject.net.device.DeviceEvent; -import org.onosproject.net.device.DeviceListener; +import org.onosproject.net.PortNumber; import org.onosproject.net.device.DeviceService; -import org.onosproject.net.host.DefaultHostDescription; -import org.onosproject.net.host.HostDescription; -import org.onosproject.net.host.HostProvider; -import org.onosproject.net.host.HostProviderRegistry; -import org.onosproject.net.host.HostProviderService; -import org.onosproject.net.host.HostService; -import org.onosproject.net.provider.AbstractProvider; -import org.onosproject.net.provider.ProviderId; -import org.onosproject.openstackinterface.OpenstackInterfaceService; -import org.onosproject.openstackinterface.OpenstackNetwork; -import org.onosproject.openstackinterface.OpenstackPort; -import org.onosproject.openstackinterface.OpenstackSubnet; -import org.onosproject.openstacknode.OpenstackNode; -import org.onosproject.openstacknode.OpenstackNodeEvent; -import org.onosproject.openstacknode.OpenstackNodeListener; +import org.onosproject.net.flow.DefaultTrafficSelector; +import org.onosproject.net.flow.DefaultTrafficTreatment; +import org.onosproject.net.flow.TrafficSelector; +import org.onosproject.net.flow.TrafficTreatment; +import org.onosproject.net.flowobjective.DefaultForwardingObjective; +import org.onosproject.net.flowobjective.FlowObjectiveService; +import org.onosproject.net.flowobjective.ForwardingObjective; +import org.onosproject.openstacknetworking.AbstractVmHandler; import org.onosproject.openstacknode.OpenstackNodeService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.Date; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; +import java.util.Objects; +import java.util.Optional; -import static org.onlab.util.Tools.groupedThreads; -import static org.onosproject.dhcp.IpAssignment.AssignmentStatus.Option_RangeNotEnforced; - -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; -import static org.onosproject.net.AnnotationKeys.PORT_NAME; import static org.onosproject.openstacknetworking.Constants.*; +import static org.onosproject.openstacknetworking.RulePopulatorUtil.buildExtension; -@Service -@Component(immediate = true) /** - * Populates forwarding rules for VMs created by Openstack. + * Populates switching flow rules. */ -public final class OpenstackSwitchingManager extends AbstractProvider - implements HostProvider { +@Component(immediate = true) +public final class OpenstackSwitchingManager extends AbstractVmHandler { private final Logger log = LoggerFactory.getLogger(getClass()); - @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) - protected CoreService coreService; - @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected DeviceService deviceService; @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) - protected HostProviderRegistry hostProviderRegistry; + protected FlowObjectiveService flowObjectiveService; @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) - protected DhcpService dhcpService; + protected OpenstackNodeService nodeService; - @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) - protected HostService hostService; - - @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) - protected MastershipService mastershipService; - - @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) - protected OpenstackInterfaceService openstackService; - - @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) - protected OpenstackNodeService openstackNodeService; - - private final ExecutorService deviceEventExecutor = - Executors.newSingleThreadExecutor(groupedThreads("onos/openstackswitching", "device-event")); - private final ExecutorService configEventExecutor = - Executors.newSingleThreadExecutor(groupedThreads("onos/openstackswitching", "config-event")); - private final InternalDeviceListener internalDeviceListener = new InternalDeviceListener(); - private final InternalOpenstackNodeListener internalNodeListener = new InternalOpenstackNodeListener(); - - private HostProviderService hostProvider; - - /** - * Creates OpenStack switching host provider. - */ - public OpenstackSwitchingManager() { - super(new ProviderId("host", APP_ID)); - } + private ApplicationId appId; @Activate protected void activate() { - coreService.registerApplication(APP_ID); - deviceService.addListener(internalDeviceListener); - openstackNodeService.addListener(internalNodeListener); - hostProvider = hostProviderRegistry.register(this); - - log.info("Started"); + super.activate(); + appId = coreService.registerApplication(SWITCHING_APP_ID); } @Deactivate protected void deactivate() { - hostProviderRegistry.unregister(this); - deviceService.removeListener(internalDeviceListener); - openstackNodeService.removeListener(internalNodeListener); + super.deactivate(); + } - deviceEventExecutor.shutdown(); - configEventExecutor.shutdown(); + private void populateSwitchingRules(Host host) { + populateFlowRulesForTunnelTag(host); + populateFlowRulesForTrafficToSameCnode(host); + populateFlowRulesForTrafficToDifferentCnode(host); - log.info("Stopped"); + log.debug("Populated switching rule for {}", host); + } + + private void populateFlowRulesForTunnelTag(Host host) { + TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder(); + TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder(); + + sBuilder.matchEthType(Ethernet.TYPE_IPV4) + .matchInPort(host.location().port()); + + tBuilder.setTunnelId(Long.valueOf(getVni(host))); + + ForwardingObjective fo = DefaultForwardingObjective.builder() + .withSelector(sBuilder.build()) + .withTreatment(tBuilder.build()) + .withPriority(TUNNELTAG_RULE_PRIORITY) + .withFlag(ForwardingObjective.Flag.SPECIFIC) + .fromApp(appId) + .add(); + + flowObjectiveService.forward(host.location().deviceId(), fo); + } + + private void populateFlowRulesForTrafficToSameCnode(Host host) { + //For L2 Switching Case + TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder(); + TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder(); + + sBuilder.matchEthType(Ethernet.TYPE_IPV4) + .matchIPDst(getIp(host).toIpPrefix()) + .matchTunnelId(Long.valueOf(getVni(host))); + + tBuilder.setOutput(host.location().port()); + + ForwardingObjective fo = DefaultForwardingObjective.builder() + .withSelector(sBuilder.build()) + .withTreatment(tBuilder.build()) + .withPriority(SWITCHING_RULE_PRIORITY) + .withFlag(ForwardingObjective.Flag.SPECIFIC) + .fromApp(appId) + .add(); + + flowObjectiveService.forward(host.location().deviceId(), fo); + } + + private void populateFlowRulesForTrafficToDifferentCnode(Host host) { + Ip4Address localVmIp = getIp(host); + DeviceId localDeviceId = host.location().deviceId(); + Optional localDataIp = nodeService.dataIp(localDeviceId); + + if (!localDataIp.isPresent()) { + log.debug("Failed to get data IP for device {}", + host.location().deviceId()); + return; + } + + String vni = getVni(host); + getVmsInDifferentCnode(host).forEach(remoteVm -> { + Optional remoteDataIp = nodeService.dataIp(remoteVm.location().deviceId()); + if (remoteDataIp.isPresent()) { + setVxLanFlowRule(vni, + localDeviceId, + remoteDataIp.get().getIp4Address(), + getIp(remoteVm)); + + setVxLanFlowRule(vni, + remoteVm.location().deviceId(), + localDataIp.get().getIp4Address(), + localVmIp); + } + }); + } + + private void setVxLanFlowRule(String vni, DeviceId deviceId, Ip4Address remoteIp, + Ip4Address vmIp) { + Optional tunnelPort = nodeService.tunnelPort(deviceId); + if (!tunnelPort.isPresent()) { + log.warn("Failed to get tunnel port from {}", deviceId); + return; + } + + TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder(); + TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder(); + + sBuilder.matchEthType(Ethernet.TYPE_IPV4) + .matchTunnelId(Long.parseLong(vni)) + .matchIPDst(vmIp.toIpPrefix()); + tBuilder.extension(buildExtension(deviceService, deviceId, remoteIp), deviceId) + .setOutput(tunnelPort.get()); + + ForwardingObjective fo = DefaultForwardingObjective.builder() + .withSelector(sBuilder.build()) + .withTreatment(tBuilder.build()) + .withPriority(SWITCHING_RULE_PRIORITY) + .withFlag(ForwardingObjective.Flag.SPECIFIC) + .fromApp(appId) + .add(); + + flowObjectiveService.forward(deviceId, fo); + } + + private void removeSwitchingRules(Host host) { + removeFlowRuleForTunnelTag(host); + removeFlowRuleForVMsInSameCnode(host); + removeFlowRuleForVMsInDiffrentCnode(host); + + log.debug("Removed switching rule for {}", host); + } + + private void removeFlowRuleForTunnelTag(Host host) { + TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder(); + TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder(); + + sBuilder.matchEthType(Ethernet.TYPE_IPV4) + .matchInPort(host.location().port()); + + ForwardingObjective fo = DefaultForwardingObjective.builder() + .withSelector(sBuilder.build()) + .withTreatment(tBuilder.build()) + .withPriority(TUNNELTAG_RULE_PRIORITY) + .withFlag(ForwardingObjective.Flag.SPECIFIC) + .fromApp(appId) + .remove(); + + flowObjectiveService.forward(host.location().deviceId(), fo); + } + + private void removeFlowRuleForVMsInSameCnode(Host host) { + TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder(); + sBuilder.matchEthType(Ethernet.TYPE_IPV4) + .matchIPDst(getIp(host).toIpPrefix()) + .matchTunnelId(Long.valueOf(getVni(host))); + + ForwardingObjective fo = DefaultForwardingObjective.builder() + .withSelector(sBuilder.build()) + .withTreatment(DefaultTrafficTreatment.builder().build()) + .withFlag(ForwardingObjective.Flag.SPECIFIC) + .withPriority(SWITCHING_RULE_PRIORITY) + .fromApp(appId) + .remove(); + + flowObjectiveService.forward(host.location().deviceId(), fo); + } + + private void removeFlowRuleForVMsInDiffrentCnode(Host host) { + DeviceId deviceId = host.location().deviceId(); + final boolean anyPortRemainedInSameCnode = hostService.getConnectedHosts(deviceId) + .stream() + .filter(this::isValidHost) + .anyMatch(h -> Objects.equals(getVni(h), getVni(host))); + + getVmsInDifferentCnode(host).forEach(h -> { + removeVxLanFlowRule(h.location().deviceId(), getIp(host), getVni(host)); + if (!anyPortRemainedInSameCnode) { + removeVxLanFlowRule(deviceId, getIp(h), getVni(host)); + } + }); + } + + private void removeVxLanFlowRule(DeviceId deviceId, Ip4Address vmIp, String vni) { + TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder(); + + sBuilder.matchEthType(Ethernet.TYPE_IPV4) + .matchTunnelId(Long.valueOf(vni)) + .matchIPDst(vmIp.toIpPrefix()); + + ForwardingObjective fo = DefaultForwardingObjective.builder() + .withSelector(sBuilder.build()) + .withTreatment(DefaultTrafficTreatment.builder().build()) + .withFlag(ForwardingObjective.Flag.SPECIFIC) + .withPriority(SWITCHING_RULE_PRIORITY) + .fromApp(appId) + .remove(); + + flowObjectiveService.forward(deviceId, fo); } @Override - public void triggerProbe(Host host) { - // no probe is required + protected void hostDetected(Host host) { + populateSwitchingRules(host); + log.info("Added new virtual machine to switching service {}", host); } - private void processPortAdded(Port port) { - // TODO check the node state is COMPLETE - OpenstackPort osPort = openstackService.port(port); - if (osPort == null) { - log.warn("Failed to get OpenStack port for {}", port); - return; - } - - OpenstackNetwork osNet = openstackService.network(osPort.networkId()); - if (osNet == null) { - log.warn("Failed to get OpenStack network {}", - osPort.networkId()); - return; - } - - registerDhcpInfo(osPort); - ConnectPoint connectPoint = new ConnectPoint(port.element().id(), port.number()); - // TODO remove this and openstackPortInfo - String gatewayIp = osNet.subnets().stream().findFirst().get().gatewayIp(); - - // Added CREATE_TIME intentionally to trigger HOST_UPDATED event for the - // existing instances. - DefaultAnnotations.Builder annotations = DefaultAnnotations.builder() - .set(NETWORK_ID, osPort.networkId()) - .set(PORT_ID, osPort.id()) - .set(VXLAN_ID, osNet.segmentId()) - .set(TENANT_ID, osNet.tenantId()) - // TODO remove this and openstackPortInfo - .set(GATEWAY_IP, gatewayIp) - .set(CREATE_TIME, String.valueOf(System.currentTimeMillis())); - - HostDescription hostDesc = new DefaultHostDescription( - osPort.macAddress(), - VlanId.NONE, - new HostLocation(connectPoint, System.currentTimeMillis()), - Sets.newHashSet(osPort.fixedIps().values()), - annotations.build()); - - HostId hostId = HostId.hostId(osPort.macAddress()); - hostProvider.hostDetected(hostId, hostDesc, false); - } - - private void processPortRemoved(Port port) { - ConnectPoint connectPoint = new ConnectPoint(port.element().id(), port.number()); - removeHosts(connectPoint); - } - - private void removeHosts(ConnectPoint connectPoint) { - hostService.getConnectedHosts(connectPoint).stream() - .forEach(host -> { - dhcpService.removeStaticMapping(host.mac()); - hostProvider.hostVanished(host.id()); - }); - } - - private void registerDhcpInfo(OpenstackPort openstackPort) { - checkNotNull(openstackPort); - checkArgument(!openstackPort.fixedIps().isEmpty()); - - OpenstackSubnet openstackSubnet = openstackService.subnets().stream() - .filter(n -> n.networkId().equals(openstackPort.networkId())) - .findFirst().orElse(null); - if (openstackSubnet == null) { - log.warn("Failed to find subnet for {}", openstackPort); - return; - } - - Ip4Address ipAddress = openstackPort.fixedIps().values().stream().findFirst().get(); - IpPrefix subnetPrefix = IpPrefix.valueOf(openstackSubnet.cidr()); - Ip4Address broadcast = Ip4Address.makeMaskedAddress( - ipAddress, - subnetPrefix.prefixLength()); - - // TODO: supports multiple DNS servers - Ip4Address domainServer = openstackSubnet.dnsNameservers().isEmpty() ? - DNS_SERVER_IP : openstackSubnet.dnsNameservers().get(0); - - IpAssignment ipAssignment = IpAssignment.builder() - .ipAddress(ipAddress) - .leasePeriod(DHCP_INFINITE_LEASE) - .timestamp(new Date()) - .subnetMask(Ip4Address.makeMaskPrefix(subnetPrefix.prefixLength())) - .broadcast(broadcast) - .domainServer(domainServer) - .assignmentStatus(Option_RangeNotEnforced) - .routerAddress(Ip4Address.valueOf(openstackSubnet.gatewayIp())) - .build(); - - dhcpService.setStaticMapping(openstackPort.macAddress(), ipAssignment); - } - - private class InternalDeviceListener implements DeviceListener { - - @Override - public void event(DeviceEvent event) { - Device device = event.subject(); - if (!mastershipService.isLocalMaster(device.id())) { - // do not allow to proceed without mastership - return; - } - - Port port = event.port(); - if (port == null) { - return; - } - - String portName = port.annotations().value(PORT_NAME); - if (Strings.isNullOrEmpty(portName) || - !portName.startsWith(PORTNAME_PREFIX_VM)) { - // handles VM connected port event only - return; - } - - switch (event.type()) { - case PORT_UPDATED: - if (!event.port().isEnabled()) { - deviceEventExecutor.execute(() -> processPortRemoved(event.port())); - } - break; - case PORT_ADDED: - deviceEventExecutor.execute(() -> processPortAdded(event.port())); - break; - default: - break; - } - } - } - - private class InternalOpenstackNodeListener implements OpenstackNodeListener { - - @Override - public void event(OpenstackNodeEvent event) { - OpenstackNode node = event.node(); - // TODO check leadership of the node and make only the leader process - - switch (event.type()) { - case COMPLETE: - log.info("COMPLETE node {} detected", node.hostname()); - - // adds existing VMs running on the complete state node - deviceService.getPorts(node.intBridge()).stream() - .filter(port -> port.annotations().value(PORT_NAME) - .startsWith(PORTNAME_PREFIX_VM) && - port.isEnabled()) - .forEach(port -> { - deviceEventExecutor.execute(() -> processPortAdded(port)); - log.info("VM is detected on {}", port); - }); - - // removes stale VMs - hostService.getHosts().forEach(host -> { - if (deviceService.getPort(host.location().deviceId(), - host.location().port()) == null) { - deviceEventExecutor.execute(() -> removeHosts(host.location())); - log.info("Removed stale VM {}", host.location()); - } - }); - break; - case INCOMPLETE: - log.warn("{} is changed to INCOMPLETE state", node); - break; - case INIT: - case DEVICE_CREATED: - default: - break; - } - } + @Override + protected void hostRemoved(Host host) { + removeSwitchingRules(host); + log.info("Removed virtual machine from switching service {}", host); } } diff --git a/apps/openstacknetworking/switching/src/main/java/org/onosproject/openstacknetworking/switching/OpenstackSwitchingRulePopulator.java b/apps/openstacknetworking/switching/src/main/java/org/onosproject/openstacknetworking/switching/OpenstackSwitchingRulePopulator.java deleted file mode 100644 index b3707a8252..0000000000 --- a/apps/openstacknetworking/switching/src/main/java/org/onosproject/openstacknetworking/switching/OpenstackSwitchingRulePopulator.java +++ /dev/null @@ -1,293 +0,0 @@ -/* -* Copyright 2016-present 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.openstacknetworking.switching; - -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.Reference; -import org.apache.felix.scr.annotations.ReferenceCardinality; -import org.onlab.packet.Ethernet; -import org.onlab.packet.Ip4Address; -import org.onlab.packet.IpAddress; -import org.onosproject.net.Device; -import org.onosproject.net.DeviceId; -import org.onosproject.net.Host; -import org.onosproject.net.PortNumber; -import org.onosproject.net.behaviour.ExtensionTreatmentResolver; -import org.onosproject.net.device.DeviceService; -import org.onosproject.net.flow.DefaultTrafficSelector; -import org.onosproject.net.flow.DefaultTrafficTreatment; -import org.onosproject.net.flow.TrafficSelector; -import org.onosproject.net.flow.TrafficTreatment; -import org.onosproject.net.flow.instructions.ExtensionTreatment; -import org.onosproject.net.flow.instructions.ExtensionPropertyException; -import org.onosproject.net.flowobjective.DefaultForwardingObjective; -import org.onosproject.net.flowobjective.FlowObjectiveService; -import org.onosproject.net.flowobjective.ForwardingObjective; -import org.onosproject.openstacknode.OpenstackNodeService; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.Objects; -import java.util.Optional; - -import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_SET_TUNNEL_DST; -import static org.onosproject.openstacknetworking.Constants.*; - -/** - * Populates switching flow rules. - */ -@Component(immediate = true) -public final class OpenstackSwitchingRulePopulator extends AbstractVmHandler { - - private final Logger log = LoggerFactory.getLogger(getClass()); - - @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) - protected DeviceService deviceService; - - @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) - protected FlowObjectiveService flowObjectiveService; - - @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) - protected OpenstackNodeService nodeService; - - - private static final String TUNNEL_DST = "tunnelDst"; - - @Activate - protected void activate() { - super.activate(); - } - - @Deactivate - protected void deactivate() { - super.deactivate(); - } - - private void populateSwitchingRules(Host host) { - populateFlowRulesForTunnelTag(host); - populateFlowRulesForTrafficToSameCnode(host); - populateFlowRulesForTrafficToDifferentCnode(host); - - log.debug("Populated switching rule for {}", host); - } - - private void populateFlowRulesForTunnelTag(Host host) { - TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder(); - TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder(); - - sBuilder.matchEthType(Ethernet.TYPE_IPV4) - .matchInPort(host.location().port()); - - tBuilder.setTunnelId(Long.valueOf(getVni(host))); - - ForwardingObjective fo = DefaultForwardingObjective.builder() - .withSelector(sBuilder.build()) - .withTreatment(tBuilder.build()) - .withPriority(TUNNELTAG_RULE_PRIORITY) - .withFlag(ForwardingObjective.Flag.SPECIFIC) - .fromApp(appId) - .add(); - - flowObjectiveService.forward(host.location().deviceId(), fo); - } - - private void populateFlowRulesForTrafficToSameCnode(Host host) { - //For L2 Switching Case - TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder(); - TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder(); - - sBuilder.matchEthType(Ethernet.TYPE_IPV4) - .matchIPDst(getIp(host).toIpPrefix()) - .matchTunnelId(Long.valueOf(getVni(host))); - - tBuilder.setOutput(host.location().port()); - - ForwardingObjective fo = DefaultForwardingObjective.builder() - .withSelector(sBuilder.build()) - .withTreatment(tBuilder.build()) - .withPriority(SWITCHING_RULE_PRIORITY) - .withFlag(ForwardingObjective.Flag.SPECIFIC) - .fromApp(appId) - .add(); - - flowObjectiveService.forward(host.location().deviceId(), fo); - } - - private void populateFlowRulesForTrafficToDifferentCnode(Host host) { - Ip4Address localVmIp = getIp(host); - DeviceId localDeviceId = host.location().deviceId(); - Optional localDataIp = nodeService.dataIp(localDeviceId); - - if (!localDataIp.isPresent()) { - log.debug("Failed to get data IP for device {}", - host.location().deviceId()); - return; - } - - String vni = getVni(host); - getVmsInDifferentCnode(host).forEach(remoteVm -> { - Optional remoteDataIp = nodeService.dataIp(remoteVm.location().deviceId()); - if (remoteDataIp.isPresent()) { - setVxLanFlowRule(vni, - localDeviceId, - remoteDataIp.get().getIp4Address(), - getIp(remoteVm)); - - setVxLanFlowRule(vni, - remoteVm.location().deviceId(), - localDataIp.get().getIp4Address(), - localVmIp); - } - }); - } - - private void setVxLanFlowRule(String vni, DeviceId deviceId, Ip4Address remoteIp, - Ip4Address vmIp) { - Optional tunnelPort = nodeService.tunnelPort(deviceId); - if (!tunnelPort.isPresent()) { - log.warn("Failed to get tunnel port from {}", deviceId); - return; - } - - TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder(); - TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder(); - - sBuilder.matchEthType(Ethernet.TYPE_IPV4) - .matchTunnelId(Long.parseLong(vni)) - .matchIPDst(vmIp.toIpPrefix()); - tBuilder.extension(buildNiciraExtenstion(deviceId, remoteIp), deviceId) - .setOutput(tunnelPort.get()); - - ForwardingObjective fo = DefaultForwardingObjective.builder() - .withSelector(sBuilder.build()) - .withTreatment(tBuilder.build()) - .withPriority(SWITCHING_RULE_PRIORITY) - .withFlag(ForwardingObjective.Flag.SPECIFIC) - .fromApp(appId) - .add(); - - flowObjectiveService.forward(deviceId, fo); - } - - private void removeSwitchingRules(Host host) { - removeFlowRuleForTunnelTag(host); - removeFlowRuleForVMsInSameCnode(host); - removeFlowRuleForVMsInDiffrentCnode(host); - - log.debug("Removed switching rule for {}", host); - } - - private void removeFlowRuleForTunnelTag(Host host) { - TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder(); - TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder(); - - sBuilder.matchEthType(Ethernet.TYPE_IPV4) - .matchInPort(host.location().port()); - - ForwardingObjective fo = DefaultForwardingObjective.builder() - .withSelector(sBuilder.build()) - .withTreatment(tBuilder.build()) - .withPriority(TUNNELTAG_RULE_PRIORITY) - .withFlag(ForwardingObjective.Flag.SPECIFIC) - .fromApp(appId) - .remove(); - - flowObjectiveService.forward(host.location().deviceId(), fo); - } - - private void removeFlowRuleForVMsInSameCnode(Host host) { - TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder(); - sBuilder.matchEthType(Ethernet.TYPE_IPV4) - .matchIPDst(getIp(host).toIpPrefix()) - .matchTunnelId(Long.valueOf(getVni(host))); - - ForwardingObjective fo = DefaultForwardingObjective.builder() - .withSelector(sBuilder.build()) - .withTreatment(DefaultTrafficTreatment.builder().build()) - .withFlag(ForwardingObjective.Flag.SPECIFIC) - .withPriority(SWITCHING_RULE_PRIORITY) - .fromApp(appId) - .remove(); - - flowObjectiveService.forward(host.location().deviceId(), fo); - } - - private void removeFlowRuleForVMsInDiffrentCnode(Host host) { - DeviceId deviceId = host.location().deviceId(); - final boolean anyPortRemainedInSameCnode = hostService.getConnectedHosts(deviceId) - .stream() - .filter(this::isValidHost) - .anyMatch(h -> Objects.equals(getVni(h), getVni(host))); - - getVmsInDifferentCnode(host).forEach(h -> { - removeVxLanFlowRule(h.location().deviceId(), getIp(host), getVni(host)); - if (!anyPortRemainedInSameCnode) { - removeVxLanFlowRule(deviceId, getIp(h), getVni(host)); - } - }); - } - - private void removeVxLanFlowRule(DeviceId deviceId, Ip4Address vmIp, String vni) { - TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder(); - - sBuilder.matchEthType(Ethernet.TYPE_IPV4) - .matchTunnelId(Long.valueOf(vni)) - .matchIPDst(vmIp.toIpPrefix()); - - ForwardingObjective fo = DefaultForwardingObjective.builder() - .withSelector(sBuilder.build()) - .withTreatment(DefaultTrafficTreatment.builder().build()) - .withFlag(ForwardingObjective.Flag.SPECIFIC) - .withPriority(SWITCHING_RULE_PRIORITY) - .fromApp(appId) - .remove(); - - flowObjectiveService.forward(deviceId, fo); - } - - private ExtensionTreatment buildNiciraExtenstion(DeviceId deviceId, Ip4Address remoteIp) { - Device device = deviceService.getDevice(deviceId); - if (device != null && !device.is(ExtensionTreatmentResolver.class)) { - log.error("The extension treatment is not supported"); - return null; - } - - ExtensionTreatmentResolver resolver = device.as(ExtensionTreatmentResolver.class); - ExtensionTreatment treatment = resolver.getExtensionInstruction(NICIRA_SET_TUNNEL_DST.type()); - try { - treatment.setPropertyValue(TUNNEL_DST, remoteIp); - return treatment; - } catch (ExtensionPropertyException e) { - log.warn("Failed to get tunnelDst extension treatment for {}", deviceId); - return null; - } - } - - @Override - protected void hostDetected(Host host) { - populateSwitchingRules(host); - log.info("Added new virtual machine to switching service {}", host); - } - - @Override - protected void hostRemoved(Host host) { - removeSwitchingRules(host); - log.info("Removed virtual machine from switching service {}", host); - } -} diff --git a/apps/openstacknetworking/web/src/main/java/org/onosproject/openstacknetworking/web/OpensatckRouterWebResource.java b/apps/openstacknetworking/web/src/main/java/org/onosproject/openstacknetworking/web/OpensatckRouterWebResource.java index d73ff4f381..3d666742f1 100644 --- a/apps/openstacknetworking/web/src/main/java/org/onosproject/openstacknetworking/web/OpensatckRouterWebResource.java +++ b/apps/openstacknetworking/web/src/main/java/org/onosproject/openstacknetworking/web/OpensatckRouterWebResource.java @@ -126,7 +126,7 @@ public class OpensatckRouterWebResource extends AbstractWebResource { OpenstackRoutingService routingService = getService(OpenstackRoutingService.class); - routingService.updateRouterInterface(openstackRouterInterface); + routingService.addRouterInterface(openstackRouterInterface); log.debug("REST API AddRouterInterface is called from router {} portId: {}, subnetId: {}, tenantId: {}", openstackRouterInterface.id(), openstackRouterInterface.portId(), @@ -147,7 +147,7 @@ public class OpensatckRouterWebResource extends AbstractWebResource { checkNotNull(id); OpenstackRoutingService routingService = getService(OpenstackRoutingService.class); - routingService.deleteRouter(id); + routingService.removeRouter(id); log.debug("REST API DELETE routers is called {}", id); return Response.noContent().build(); diff --git a/apps/openstacknetworking/web/src/main/java/org/onosproject/openstacknetworking/web/OpenstackFloatingIpWebResource.java b/apps/openstacknetworking/web/src/main/java/org/onosproject/openstacknetworking/web/OpenstackFloatingIpWebResource.java index bd6c2c99cb..3382620d26 100644 --- a/apps/openstacknetworking/web/src/main/java/org/onosproject/openstacknetworking/web/OpenstackFloatingIpWebResource.java +++ b/apps/openstacknetworking/web/src/main/java/org/onosproject/openstacknetworking/web/OpenstackFloatingIpWebResource.java @@ -20,7 +20,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import org.onosproject.openstackinterface.OpenstackFloatingIP; import org.onosproject.openstackinterface.web.OpenstackFloatingIpCodec; -import org.onosproject.openstacknetworking.OpenstackRoutingService; +import org.onosproject.openstacknetworking.OpenstackFloatingIpService; import org.onosproject.rest.AbstractWebResource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -45,8 +45,7 @@ import static com.google.common.base.Preconditions.checkNotNull; public class OpenstackFloatingIpWebResource extends AbstractWebResource { private final Logger log = LoggerFactory.getLogger(getClass()); - private static final OpenstackFloatingIpCodec FLOATING_IP_CODEC - = new OpenstackFloatingIpCodec(); + private static final OpenstackFloatingIpCodec FLOATING_IP_CODEC = new OpenstackFloatingIpCodec(); /** * Create FloatingIP. @@ -64,20 +63,15 @@ public class OpenstackFloatingIpWebResource extends AbstractWebResource { ObjectMapper mapper = new ObjectMapper(); ObjectNode floatingIpNode = (ObjectNode) mapper.readTree(input); - OpenstackFloatingIP osFloatingIp = - FLOATING_IP_CODEC.decode(floatingIpNode, this); - - OpenstackRoutingService routingService = - getService(OpenstackRoutingService.class); - - routingService.createFloatingIP(osFloatingIp); + OpenstackFloatingIP osFloatingIp = FLOATING_IP_CODEC.decode(floatingIpNode, this); + OpenstackFloatingIpService floatingIpService = + getService(OpenstackFloatingIpService.class); + floatingIpService.createFloatingIp(osFloatingIp); log.debug("REST API CREATE floatingip called"); - return Response.status(Response.Status.OK).build(); } catch (Exception e) { log.error("createFloatingIp failed with {}", e.toString()); - return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(e.toString()) .build(); } @@ -102,20 +96,15 @@ public class OpenstackFloatingIpWebResource extends AbstractWebResource { ObjectMapper mapper = new ObjectMapper(); ObjectNode floatingIpNode = (ObjectNode) mapper.readTree(input); - OpenstackFloatingIP osFloatingIp = - FLOATING_IP_CODEC.decode(floatingIpNode, this); - - OpenstackRoutingService routingService = - getService(OpenstackRoutingService.class); - - routingService.updateFloatingIP(osFloatingIp); + OpenstackFloatingIP osFloatingIp = FLOATING_IP_CODEC.decode(floatingIpNode, this); + OpenstackFloatingIpService floatingIpService = + getService(OpenstackFloatingIpService.class); + floatingIpService.updateFloatingIp(osFloatingIp); log.debug("REST API UPDATE floatingip called {}", id); - return Response.status(Response.Status.OK).build(); } catch (Exception e) { log.error("updateFloatingIp failed with {}", e.toString()); - return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(e.toString()) .build(); } @@ -133,12 +122,11 @@ public class OpenstackFloatingIpWebResource extends AbstractWebResource { public Response deleteFloatingIp(@PathParam("id") String id) { checkNotNull(id); - OpenstackRoutingService routingService = - getService(OpenstackRoutingService.class); - routingService.deleteFloatingIP(id); + OpenstackFloatingIpService floatingIpService = + getService(OpenstackFloatingIpService.class); + floatingIpService.deleteFloatingIp(id); log.debug("REST API DELETE floatingip is called {}", id); - return Response.noContent().build(); } diff --git a/apps/openstacknetworking/web/src/main/java/org/onosproject/openstacknetworking/web/OpenstackPortWebResource.java b/apps/openstacknetworking/web/src/main/java/org/onosproject/openstacknetworking/web/OpenstackPortWebResource.java index 4dd2533786..87dd4a3ae1 100644 --- a/apps/openstacknetworking/web/src/main/java/org/onosproject/openstacknetworking/web/OpenstackPortWebResource.java +++ b/apps/openstacknetworking/web/src/main/java/org/onosproject/openstacknetworking/web/OpenstackPortWebResource.java @@ -79,7 +79,6 @@ public class OpenstackPortWebResource extends AbstractWebResource { sgService.updateSecurityGroup(osPort); return Response.status(Response.Status.OK).build(); - } catch (IOException e) { log.error("UpdatePort post process failed due to {}", e.getMessage()); diff --git a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/OpenstackNode.java b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/OpenstackNode.java index 42edd72e43..2d73a85f71 100644 --- a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/OpenstackNode.java +++ b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/OpenstackNode.java @@ -28,6 +28,7 @@ import java.util.Optional; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; +import static org.onosproject.openstacknode.Constants.PATCH_INTG_BRIDGE; import static org.onosproject.openstacknode.OpenstackNodeEvent.NodeState.INIT; /** @@ -182,6 +183,20 @@ public final class OpenstackNode { return DeviceId.deviceId("ovsdb:" + managementIp.toString()); } + /** + * Returns the name of the port connected to the external network. + * It returns valid value only if the node is gateway node. + * + * @return external port name + */ + public Optional externalPortName() { + if (type == NodeType.GATEWAY) { + return Optional.of(PATCH_INTG_BRIDGE); + } else { + return Optional.empty(); + } + } + @Override public boolean equals(Object obj) { if (this == obj) {