ONOS-5182 Simplified OpenStack networking application structure
Change-Id: Ic7941f2c9a2febec4f24745278c4c305a3937097
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/Constants.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/Constants.java
new file mode 100644
index 0000000..4ba121a
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/Constants.java
@@ -0,0 +1,69 @@
+/*
+ * 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.api;
+
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.Ip4Prefix;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.MacAddress;
+
+/**
+ * Provides constants used in OpenStackSwitching.
+ */
+public final class Constants {
+
+ private Constants() {
+ }
+
+ public static final String SWITCHING_APP_ID = "org.onosproject.openstackswitching";
+ public static final String ROUTING_APP_ID = "org.onosproject.openstackrouting";
+
+ 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 String PORT_NAME_PREFIX_VM = "tap";
+ public static final String PORT_NAME_PREFIX_TUNNEL = "vxlan";
+
+ 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";
+ public static final String GATEWAY_IP = "gatewayIp";
+ public static final String CREATE_TIME = "createTime";
+
+ public static final int SWITCHING_RULE_PRIORITY = 30000;
+ public static final int TUNNELTAG_RULE_PRIORITY = 30000;
+ public static final int ACL_RULE_PRIORITY = 30000;
+ public static final int EW_ROUTING_RULE_PRIORITY = 28000;
+
+ public static final int GATEWAY_ICMP_PRIORITY = 43000;
+ public static final int ROUTING_RULE_PRIORITY = 25000;
+ public static final int FLOATING_RULE_FOR_TRAFFIC_FROM_VM_PRIORITY = 42000;
+ public static final int FLOATING_RULE_PRIORITY = 41000;
+ 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/src/main/java/org/onosproject/openstacknetworking/api/OpenstackFloatingIpService.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackFloatingIpService.java
new file mode 100644
index 0000000..9988a4e
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackFloatingIpService.java
@@ -0,0 +1,66 @@
+/*
+ * 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.api;
+
+import org.onosproject.net.Host;
+
+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);
+
+ /**
+ * Handles to purge data plane flow of existing VM.
+ *
+ * @param host VM Host information
+ */
+ void purgeVmFlow(Host host);
+
+ /**
+ * Handles to reinstall data plane flow of existing VM.
+ *
+ * @param host VM Host information
+ */
+ void reinstallVmFlow(Host host);
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackRoutingService.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackRoutingService.java
new file mode 100644
index 0000000..29e492e
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackRoutingService.java
@@ -0,0 +1,79 @@
+/*
+ * 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.api;
+
+import org.onosproject.net.Host;
+
+import org.onosproject.openstackinterface.OpenstackRouter;
+import org.onosproject.openstackinterface.OpenstackRouterInterface;
+
+/**
+ * Handles router update requests from OpenStack.
+ */
+public interface OpenstackRoutingService {
+
+ /**
+ * Handles the router create request from OpenStack.
+ *
+ * @param osRouter router information
+ */
+ void createRouter(OpenstackRouter osRouter);
+
+ /**
+ * 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 osRouter router information
+ */
+ void updateRouter(OpenstackRouter osRouter);
+
+ /**
+ * Handles the router remove request from OpenStack.
+ *
+ * @param osRouterId identifier of the router
+ */
+ void removeRouter(String osRouterId);
+
+ /**
+ * Handles router interface add request from OpenStack.
+ *
+ * @param osInterface router interface information
+ */
+ void addRouterInterface(OpenstackRouterInterface osInterface);
+
+ /**
+ * Handles router interface remove request from OpenStack.
+ *
+ * @param osInterface router interface information
+ */
+ void removeRouterInterface(OpenstackRouterInterface osInterface);
+
+ /**
+ * Handles to purge data plane flow of existing VM.
+ *
+ * @param host VM Host information
+ */
+ void purgeVmFlow(Host host);
+
+ /**
+ * Handles to reinstall data plane flow of existing VM.
+ *
+ * @param host VM Host information
+ */
+ void reinstallVmFlow(Host host);
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackSecurityGroupService.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackSecurityGroupService.java
new file mode 100644
index 0000000..1709c95
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackSecurityGroupService.java
@@ -0,0 +1,47 @@
+/*
+ * 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.api;
+
+import org.onosproject.net.Host;
+
+import org.onosproject.openstackinterface.OpenstackPort;
+
+/**
+ * Represents OpenstackSecurityGroupService Interface.
+ */
+public interface OpenstackSecurityGroupService {
+
+ /**
+ * Updates the flow rules for Security Group for the VM (OpenstackPort).
+ *
+ * @param osPort OpenstackPort information for the VM
+ */
+ void updateSecurityGroup(OpenstackPort osPort);
+
+ /**
+ * Handles to purge data plane flow of existing VM.
+ *
+ * @param host VM Host information
+ */
+ void purgeVmFlow(Host host);
+
+ /**
+ * Handles to reinstall data plane flow of existing VM.
+ *
+ * @param host VM Host information
+ */
+ void reinstallVmFlow(Host host);
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackSwitchingService.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackSwitchingService.java
new file mode 100644
index 0000000..dc1113e
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackSwitchingService.java
@@ -0,0 +1,37 @@
+/*
+ * 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.api;
+
+import org.onosproject.net.Host;
+
+/**
+ * Handles switch update requests from OpenStack and CLI.
+ */
+public interface OpenstackSwitchingService {
+ /**
+ * Handles to purge data plane flow of existing VM.
+ *
+ * @param host VM Host information
+ */
+ void purgeVmFlow(Host host);
+
+ /**
+ * Handles to reinstall data plane flow of existing VM.
+ *
+ * @param host VM Host information
+ */
+ void reinstallVmFlow(Host host);
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/package-info.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/package-info.java
new file mode 100644
index 0000000..0cd17c1
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+/**
+ * Application for OpenstackRouting.
+ */
+package org.onosproject.openstacknetworking.api;
\ No newline at end of file
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackInstancePurgeFlowsCommand.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackInstancePurgeFlowsCommand.java
new file mode 100644
index 0000000..b2f5b8d
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackInstancePurgeFlowsCommand.java
@@ -0,0 +1,90 @@
+/*
+ * 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.cli;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.apache.karaf.shell.commands.Option;
+
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.net.Host;
+import org.onosproject.net.HostId;
+import org.onosproject.net.host.HostService;
+
+import org.onosproject.openstacknetworking.api.OpenstackSwitchingService;
+import org.onosproject.openstacknetworking.api.OpenstackSecurityGroupService;
+import org.onosproject.openstacknetworking.api.OpenstackRoutingService;
+import org.onosproject.openstacknetworking.api.OpenstackFloatingIpService;
+
+import static org.onosproject.openstacknetworking.api.Constants.*;
+
+/**
+ * Purge Flows of OpenstackInstance Data Plane.
+ */
+
+@Command(scope = "onos", name = "openstack-purge-flows",
+ description = "Purge data plane flows of existing VM.")
+public class OpenstackInstancePurgeFlowsCommand extends AbstractShellCommand {
+
+ @Option(name = "-a", aliases = "--all",
+ description = "HostIDs are all existing VM",
+ required = false, multiValued = false)
+ private Boolean allhost = false;
+
+ @Argument(index = 0, name = "hostId", description = "HostID(s)",
+ required = false, multiValued = true)
+ private String[] hostids = null;
+
+ @Override
+ protected void execute() {
+ HostService hostService = AbstractShellCommand.get(HostService.class);
+
+ OpenstackSwitchingService switchingService = getService(OpenstackSwitchingService.class);
+ OpenstackSecurityGroupService sgService = getService(OpenstackSecurityGroupService.class);
+ OpenstackRoutingService routingService = getService(OpenstackRoutingService.class);
+ OpenstackFloatingIpService floatingIpService = getService(OpenstackFloatingIpService.class);
+
+ if (allhost) {
+ switchingService.purgeVmFlow(null);
+ sgService.purgeVmFlow(null);
+ routingService.purgeVmFlow(null);
+ floatingIpService.purgeVmFlow(null);
+
+ hostService.getHosts().forEach(host -> {
+ printHost(host);
+ });
+ } else if (hostids != null) {
+ for (String hostid : hostids) {
+ Host host = hostService.getHost(HostId.hostId(hostid));
+ if (host == null) {
+ continue;
+ }
+ switchingService.purgeVmFlow(host);
+ sgService.purgeVmFlow(host);
+ routingService.purgeVmFlow(host);
+ floatingIpService.purgeVmFlow(host);
+ printHost(host);
+ }
+ }
+ }
+
+ private void printHost(Host host) {
+ print("Purge data plane flows of VM(hostid=%s, vni=%s, ip=%s, mac=%s).",
+ host.id(), host.annotations().value(VXLAN_ID),
+ host.ipAddresses().stream().findFirst().get().getIp4Address(), host.mac());
+ }
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackInstanceReInstallFlowCommand.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackInstanceReInstallFlowCommand.java
new file mode 100644
index 0000000..eda334d
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackInstanceReInstallFlowCommand.java
@@ -0,0 +1,89 @@
+/*
+ * 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.cli;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.apache.karaf.shell.commands.Option;
+
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.net.Host;
+import org.onosproject.net.HostId;
+import org.onosproject.net.host.HostService;
+
+import org.onosproject.openstacknetworking.api.OpenstackSwitchingService;
+import org.onosproject.openstacknetworking.api.OpenstackSecurityGroupService;
+import org.onosproject.openstacknetworking.api.OpenstackRoutingService;
+import org.onosproject.openstacknetworking.api.OpenstackFloatingIpService;
+
+import static org.onosproject.openstacknetworking.api.Constants.*;
+
+/**
+ * Re-Install Flows of OpenstackInstance Data Plane.
+ */
+
+@Command(scope = "onos", name = "openstack-reinstall-flows",
+ description = "Re-install data plane flows of existing VM.")
+public class OpenstackInstanceReInstallFlowCommand extends AbstractShellCommand {
+
+ @Option(name = "-a", aliases = "--all",
+ description = "HostIDs are all existing VM", required = false, multiValued = false)
+ private Boolean allhost = false;
+
+ @Argument(index = 0, name = "hostId", description = "HostID(s)",
+ required = false, multiValued = true)
+ private String[] hostids = null;
+
+ @Override
+ protected void execute() {
+ HostService hostService = AbstractShellCommand.get(HostService.class);
+
+ OpenstackSwitchingService switchingService = getService(OpenstackSwitchingService.class);
+ OpenstackSecurityGroupService sgService = getService(OpenstackSecurityGroupService.class);
+ OpenstackRoutingService routingService = getService(OpenstackRoutingService.class);
+ OpenstackFloatingIpService floatingIpService = getService(OpenstackFloatingIpService.class);
+
+ if (allhost) {
+ hostService.getHosts().forEach(host -> {
+ switchingService.reinstallVmFlow(host);
+ sgService.reinstallVmFlow(host);
+ routingService.reinstallVmFlow(host);
+ floatingIpService.reinstallVmFlow(host);
+ printHost(host);
+
+ });
+ } else if (hostids != null) {
+ for (String hostid : hostids) {
+ Host host = hostService.getHost(HostId.hostId(hostid));
+ if (host == null) {
+ continue;
+ }
+ switchingService.reinstallVmFlow(host);
+ sgService.reinstallVmFlow(host);
+ routingService.reinstallVmFlow(host);
+ floatingIpService.reinstallVmFlow(host);
+ printHost(host);
+ }
+ }
+ }
+
+ private void printHost(Host host) {
+ print("Re-install data plane flows of VM(hostid=%s, vni=%s, ip=%s, mac=%s).",
+ host.id(), host.annotations().value(VXLAN_ID),
+ host.ipAddresses().stream().findFirst().get().getIp4Address(), host.mac());
+ }
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/package-info.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/package-info.java
new file mode 100644
index 0000000..92d0a2c
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015-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.
+ */
+
+/**
+ * CLI implementation for refresh/reprogram the data plane for the existing VM(OpenStackInstance).
+ */
+package org.onosproject.openstacknetworking.cli;
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/AbstractVmHandler.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/AbstractVmHandler.java
new file mode 100644
index 0000000..f37a323
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/AbstractVmHandler.java
@@ -0,0 +1,177 @@
+/*
+ * 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.impl;
+
+import org.onlab.osgi.DefaultServiceDirectory;
+import org.onlab.osgi.ServiceDirectory;
+import org.onlab.packet.Ip4Address;
+import org.onlab.util.Tools;
+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.openstackinterface.OpenstackInterfaceService;
+import org.onosproject.openstackinterface.OpenstackRouter;
+import org.onosproject.openstackinterface.OpenstackSubnet;
+import org.slf4j.Logger;
+
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.stream.Collectors;
+
+import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
+import static org.onlab.util.Tools.groupedThreads;
+import static org.onosproject.openstacknetworking.api.Constants.*;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Provides abstract virtual machine handler.
+ */
+public abstract class AbstractVmHandler {
+ protected final Logger log = getLogger(getClass());
+
+ protected final ExecutorService eventExecutor = newSingleThreadScheduledExecutor(
+ groupedThreads(this.getClass().getSimpleName(), "event-handler"));
+
+ protected CoreService coreService;
+ protected MastershipService mastershipService;
+ protected HostService hostService;
+ protected OpenstackInterfaceService openstackService;
+
+ protected HostListener hostListener = new InternalHostListener();
+
+ protected void activate() {
+ ServiceDirectory services = new DefaultServiceDirectory();
+ coreService = services.get(CoreService.class);
+ mastershipService = services.get(MastershipService.class);
+ openstackService = services.get(OpenstackInterfaceService.class);
+ hostService = services.get(HostService.class);
+ hostService.addListener(hostListener);
+
+ log.info("Started");
+ }
+
+ protected void deactivate() {
+ hostService.removeListener(hostListener);
+ eventExecutor.shutdown();
+
+ log.info("Stopped");
+ }
+
+ /**
+ * Performs any action when a host is detected.
+ *
+ * @param host detected host
+ */
+ protected abstract void hostDetected(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() &&
+ host.annotations().value(VXLAN_ID) != null &&
+ host.annotations().value(NETWORK_ID) != null &&
+ host.annotations().value(TENANT_ID) != null &&
+ host.annotations().value(PORT_ID) != null;
+ }
+
+ protected Set<Host> getVmsInDifferentCnode(Host host) {
+ return Tools.stream(hostService.getHosts())
+ .filter(h -> !h.location().deviceId().equals(host.location().deviceId()))
+ .filter(this::isValidHost)
+ .filter(h -> Objects.equals(getVni(h), getVni(host)))
+ .collect(Collectors.toSet());
+ }
+
+ protected Optional<Host> getVmByPortId(String portId) {
+ return Tools.stream(hostService.getHosts())
+ .filter(this::isValidHost)
+ .filter(host -> host.annotations().value(PORT_ID).equals(portId))
+ .findFirst();
+ }
+
+ protected Set<Host> getHosts(OpenstackSubnet osSubnet) {
+ return Tools.stream(hostService.getHosts())
+ .filter(host -> host.annotations().value(SUBNET_ID).equals(osSubnet.id()))
+ .collect(Collectors.toSet());
+ }
+
+ protected Optional<OpenstackRouter> getRouter(Host host) {
+ return openstackService.routers().stream()
+ .filter(router -> routableSubNets(router.id()).stream()
+ .filter(subnet -> subnet.id().equals(host.annotations().value(SUBNET_ID)))
+ .findAny().isPresent())
+ .findAny();
+ }
+
+ protected Set<OpenstackSubnet> routableSubNets(String osRouterId) {
+ return openstackService.ports().stream()
+ .filter(p -> p.deviceOwner().equals(DEVICE_OWNER_ROUTER_INTERFACE) &&
+ p.deviceId().equals(osRouterId))
+ .map(p -> openstackService.subnet(p.fixedIps().keySet().stream().findFirst().get()))
+ .collect(Collectors.toSet());
+ }
+
+ protected Ip4Address getIp(Host host) {
+ return host.ipAddresses().stream().findFirst().get().getIp4Address();
+ }
+
+ protected String getVni(Host host) {
+ return host.annotations().value(VXLAN_ID);
+ }
+
+ protected String getTenantId(Host host) {
+ return host.annotations().value(TENANT_ID);
+ }
+
+ private class InternalHostListener implements HostListener {
+
+ @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:
+ eventExecutor.execute(() -> hostDetected(host));
+ break;
+ case HOST_REMOVED:
+ eventExecutor.execute(() -> hostRemoved(host));
+ break;
+ default:
+ break;
+ }
+ }
+ }
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackFloatingIpManager.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackFloatingIpManager.java
new file mode 100644
index 0000000..602b0f7
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackFloatingIpManager.java
@@ -0,0 +1,404 @@
+/*
+ * 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.impl;
+
+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.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.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.openstackinterface.OpenstackInterfaceService;
+import org.onosproject.openstacknetworking.api.Constants;
+import org.onosproject.openstacknetworking.api.OpenstackFloatingIpService;
+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.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.api.Constants.*;
+import static org.onosproject.openstacknetworking.impl.RulePopulatorUtil.buildExtension;
+
+
+@Service
+@Component(immediate = true)
+public class OpenstackFloatingIpManager extends AbstractVmHandler 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;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected OpenstackInterfaceService openstackService;
+
+ 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<IpAddress, Host> floatingIpMap;
+
+ private ApplicationId appId;
+
+ @Activate
+ protected void activate() {
+ super.activate();
+ appId = coreService.registerApplication(ROUTING_APP_ID);
+ nodeService.addListener(nodeListener);
+ floatingIpMap = storageService.<IpAddress, Host>consistentMapBuilder()
+ .withSerializer(Serializer.using(FLOATING_IP_SERIALIZER.build()))
+ .withName("openstackrouting-floatingip")
+ .withApplicationId(appId)
+ .build();
+
+ log.info("Started");
+ }
+
+ @Deactivate
+ protected void deactivate() {
+ super.deactivate();
+ nodeService.removeListener(nodeListener);
+ log.info("Stopped");
+ }
+
+ @Override
+ protected void hostDetected(Host host) {
+ IpAddress hostIp = host.ipAddresses().stream().findFirst().get();
+ Optional<OpenstackFloatingIP> floatingIp = openstackService.floatingIps().stream()
+ .filter(fip -> fip.fixedIpAddress() != null && fip.fixedIpAddress().equals(hostIp))
+ .findFirst();
+ if (floatingIp.isPresent()) {
+ eventExecutor.execute(() -> associateFloatingIp(floatingIp.get()));
+ }
+ }
+
+ @Override
+ protected void hostRemoved(Host host) {
+ IpAddress hostIp = host.ipAddresses().stream().findFirst().get();
+ Optional<OpenstackFloatingIP> floatingIp = openstackService.floatingIps().stream()
+ .filter(fip -> fip.fixedIpAddress() != null && fip.fixedIpAddress().equals(hostIp))
+ .findFirst();
+ if (floatingIp.isPresent()) {
+ eventExecutor.execute(() -> disassociateFloatingIp(floatingIp.get()));
+ }
+ }
+
+ @Override
+ public void reinstallVmFlow(Host host) {
+ if (host == null) {
+ hostService.getHosts().forEach(h -> {
+ hostDetected(h);
+ log.info("Re-Install data plane flow of virtual machine {}", h);
+ });
+ } else {
+ hostDetected(host);
+ log.info("Re-Install data plane flow of virtual machine {}", host);
+ }
+ }
+
+ @Override
+ public void purgeVmFlow(Host host) {
+ if (host == null) {
+ hostService.getHosts().forEach(h -> {
+ hostRemoved(h);
+ log.info("Purge data plane flow of virtual machine {}", h);
+ });
+ } else {
+ hostRemoved(host);
+ log.info("Purge data plane flow of virtual machine {}", host);
+ }
+ }
+
+ @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<Host> 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<Host> 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<IpAddress> 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().forEach(deviceId -> {
+ TrafficSelector.Builder sForTrafficFromVmBuilder = DefaultTrafficSelector.builder()
+ .matchEthType(Ethernet.TYPE_IPV4)
+ .matchIPDst(floatingIp.toIpPrefix())
+ .matchInPort(nodeService.tunnelPort(deviceId).get());
+
+ RulePopulatorUtil.setRule(
+ flowObjectiveService,
+ appId,
+ deviceId,
+ sOutgoingBuilder.build(),
+ DefaultTrafficTreatment.builder().build(),
+ ForwardingObjective.Flag.VERSATILE,
+ FLOATING_RULE_PRIORITY, false);
+
+ RulePopulatorUtil.setRule(
+ flowObjectiveService,
+ appId,
+ deviceId,
+ sIncomingBuilder.build(),
+ DefaultTrafficTreatment.builder().build(),
+ ForwardingObjective.Flag.VERSATILE,
+ FLOATING_RULE_PRIORITY, false);
+
+ RulePopulatorUtil.setRule(
+ flowObjectiveService,
+ appId,
+ deviceId,
+ sForTrafficFromVmBuilder.build(),
+ DefaultTrafficTreatment.builder().build(),
+ ForwardingObjective.Flag.VERSATILE,
+ FLOATING_RULE_FOR_TRAFFIC_FROM_VM_PRIORITY, false);
+ });
+ }
+
+ private void populateFloatingIpIncomingRules(IpAddress floatingIp, Host associatedVm) {
+ DeviceId cnodeId = associatedVm.location().deviceId();
+ Optional<IpAddress> dataIp = nodeService.dataIp(cnodeId);
+ Optional<IpAddress> fixedIp = associatedVm.ipAddresses().stream().findFirst();
+
+ if (!fixedIp.isPresent() || !dataIp.isPresent()) {
+ log.warn("Failed to associate floating IP({})", floatingIp);
+ return;
+ }
+
+ TrafficSelector selectorForTrafficFromExternal = DefaultTrafficSelector.builder()
+ .matchEthType(Ethernet.TYPE_IPV4)
+ .matchIPDst(floatingIp.toIpPrefix())
+ .build();
+
+ gatewayService.getGatewayDeviceIds().forEach(gnodeId -> {
+ TrafficTreatment treatmentForTrafficFromExternal = 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, gnodeId, dataIp.get().getIp4Address()),
+ gnodeId)
+ .setOutput(nodeService.tunnelPort(gnodeId).get())
+ .build();
+
+ ForwardingObjective forwardingObjectiveForTrafficFromExternal = DefaultForwardingObjective.builder()
+ .withSelector(selectorForTrafficFromExternal)
+ .withTreatment(treatmentForTrafficFromExternal)
+ .withFlag(ForwardingObjective.Flag.VERSATILE)
+ .withPriority(FLOATING_RULE_PRIORITY)
+ .fromApp(appId)
+ .add();
+
+ flowObjectiveService.forward(gnodeId, forwardingObjectiveForTrafficFromExternal);
+
+
+ TrafficSelector selectorForTrafficFromVm = DefaultTrafficSelector.builder()
+ .matchEthType(Ethernet.TYPE_IPV4)
+ .matchIPDst(floatingIp.toIpPrefix())
+ .matchInPort(nodeService.tunnelPort(gnodeId).get())
+ .build();
+
+ TrafficTreatment treatmentForTrafficFromVm = 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, gnodeId, dataIp.get().getIp4Address()),
+ gnodeId)
+ .setOutput(PortNumber.IN_PORT)
+ .build();
+
+ ForwardingObjective forwardingObjectiveForTrafficFromVm = DefaultForwardingObjective.builder()
+ .withSelector(selectorForTrafficFromVm)
+ .withTreatment(treatmentForTrafficFromVm)
+ .withFlag(ForwardingObjective.Flag.VERSATILE)
+ .withPriority(FLOATING_RULE_FOR_TRAFFIC_FROM_VM_PRIORITY)
+ .fromApp(appId)
+ .add();
+
+ flowObjectiveService.forward(gnodeId, forwardingObjectiveForTrafficFromVm);
+
+ });
+ }
+
+ 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().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);
+ });
+ }
+
+ // 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:
+ reinstallVmFlow(null);
+ break;
+ case INIT:
+ case DEVICE_CREATED:
+ case INCOMPLETE:
+ default:
+ break;
+ }
+ }
+ }
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackIcmpHandler.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackIcmpHandler.java
new file mode 100644
index 0000000..d58062a
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackIcmpHandler.java
@@ -0,0 +1,345 @@
+/*
+ * 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.impl;
+
+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.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Host;
+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.api.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.GatewayNode;
+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 java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
+import static org.onlab.util.Tools.groupedThreads;
+import static org.onosproject.openstacknetworking.api.Constants.*;
+import static org.onosproject.openstacknode.OpenstackNodeService.NodeType.GATEWAY;
+import static org.slf4j.LoggerFactory.getLogger;
+
+
+/**
+ * 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;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected PacketService packetService;
+
+ @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<String, Host> icmpInfoMap = Maps.newHashMap();
+
+ 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");
+ }
+
+ @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();
+
+ gatewayService.getGatewayDeviceIds().forEach(gateway -> {
+ packetService.requestPackets(icmpSelector,
+ PacketPriority.CONTROL,
+ appId,
+ Optional.of(gateway));
+ log.debug("Requested ICMP packet on {}", gateway);
+ });
+ }
+
+ private void processIcmpPacket(PacketContext context, Ethernet ethernet) {
+ IPv4 ipPacket = (IPv4) ethernet.getPayload();
+ log.trace("Processing ICMP packet from ip {}, mac {}",
+ Ip4Address.valueOf(ipPacket.getSourceAddress()),
+ ethernet.getSourceMAC());
+
+ ICMP icmp = (ICMP) ipPacket.getPayload();
+ short icmpId = getIcmpId(icmp);
+
+ DeviceId srcDevice = context.inPacket().receivedFrom().deviceId();
+ switch (icmp.getIcmpType()) {
+ case ICMP.TYPE_ECHO_REQUEST:
+ Optional<Host> reqHost = hostService.getHostsByMac(ethernet.getSourceMAC())
+ .stream().findFirst();
+ if (!reqHost.isPresent()) {
+ log.warn("No host found for MAC {}", ethernet.getSourceMAC());
+ return;
+ }
+
+ // 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<Ip4Address> 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(ipPacket.getDestinationAddress()))
+ .concat(String.valueOf(ipPacket.getSourceAddress()));
+
+ processReplyFromExternal(ipPacket, icmpInfoMap.get(icmpInfoKey));
+ icmpInfoMap.remove(icmpInfoKey);
+ break;
+ default:
+ break;
+ }
+ }
+
+ // 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();
+
+ int destinationAddress = ipPacket.getSourceAddress();
+
+ ipPacket.setSourceAddress(ipPacket.getDestinationAddress())
+ .setDestinationAddress(destinationAddress)
+ .resetChecksum();
+
+ ipPacket.setPayload(icmpReq);
+ Ethernet icmpReply = new Ethernet();
+ icmpReply.setEtherType(Ethernet.TYPE_IPV4)
+ .setSourceMACAddress(Constants.DEFAULT_GATEWAY_MAC)
+ .setDestinationMACAddress(reqHost.mac())
+ .setPayload(ipPacket);
+
+ sendReply(icmpReply, reqHost);
+ }
+
+ 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)
+ .setSourceMACAddress(DEFAULT_GATEWAY_MAC)
+ .setDestinationMACAddress(DEFAULT_EXTERNAL_ROUTER_MAC)
+ .setPayload(ipPacket);
+
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+ .setOutput(gatewayService.getUplinkPort(srcDevice))
+ .build();
+
+ OutboundPacket packet = new DefaultOutboundPacket(
+ srcDevice,
+ treatment,
+ ByteBuffer.wrap(icmpRequestEth.serialize()));
+
+ packetService.emit(packet);
+ }
+
+ private void processReplyFromExternal(IPv4 ipPacket, Host dstHost) {
+ ICMP icmpReply = (ICMP) ipPacket.getPayload();
+ icmpReply.resetChecksum();
+
+ Ip4Address ipAddress = dstHost.ipAddresses().stream().findFirst().get().getIp4Address();
+ ipPacket.setDestinationAddress(ipAddress.toInt())
+ .resetChecksum();
+ ipPacket.setPayload(icmpReply);
+
+ Ethernet icmpResponseEth = new Ethernet();
+ icmpResponseEth.setEtherType(Ethernet.TYPE_IPV4)
+ .setSourceMACAddress(Constants.DEFAULT_GATEWAY_MAC)
+ .setDestinationMACAddress(dstHost.mac())
+ .setPayload(ipPacket);
+
+ sendReply(icmpResponseEth, dstHost);
+ }
+
+ private void sendReply(Ethernet icmpReply, Host dstHost) {
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+ .setOutput(dstHost.location().port())
+ .build();
+
+ OutboundPacket packet = new DefaultOutboundPacket(
+ dstHost.location().deviceId(),
+ treatment,
+ ByteBuffer.wrap(icmpReply.serialize()));
+
+ packetService.emit(packet);
+ }
+
+ private Optional<Ip4Address> getSrcNatIp(Host host) {
+ // TODO cache external gateway IP for each network because
+ // asking Neutron for every ICMP request is a bad idea
+ Optional<OpenstackPort> 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 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();
+ if (iPacket.getProtocol() == IPv4.PROTOCOL_ICMP) {
+ eventExecutor.execute(() -> processIcmpPacket(context, ethernet));
+ }
+ }
+ }
+
+ 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(() -> {
+ GatewayNode gnode = GatewayNode.builder()
+ .gatewayDeviceId(node.intBridge())
+ .dataIpAddress(node.dataIp().getIp4Address())
+ .uplinkIntf(node.externalPortName().get())
+ .build();
+ gatewayService.addGatewayNode(gnode);
+ requestPacket(appId);
+ });
+ }
+ break;
+ case INIT:
+ case DEVICE_CREATED:
+ case INCOMPLETE:
+ default:
+ break;
+ }
+ }
+ }
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackPnatHandler.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackPnatHandler.java
new file mode 100644
index 0000000..5a5eafc
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackPnatHandler.java
@@ -0,0 +1,433 @@
+/*
+ * 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.impl;
+
+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.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.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 java.nio.ByteBuffer;
+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.api.Constants.*;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Handle NAT packet processing for managing flow rules in openstack nodes.
+ */
+@Component(immediate = true)
+public class OpenstackPnatHandler {
+ private final Logger log = getLogger(getClass());
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected CoreService coreService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected PacketService packetService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected HostService hostService;
+
+ @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<Integer, String> tpPortNumMap;
+ private ApplicationId appId;
+
+ @Activate
+ protected void activate() {
+ appId = coreService.registerApplication(ROUTING_APP_ID);
+ tpPortNumMap = storageService.<Integer, String>consistentMapBuilder()
+ .withSerializer(Serializer.using(NUMBER_SERIALIZER.build()))
+ .withName("openstackrouting-tpportnum")
+ .withApplicationId(appId)
+ .build();
+
+ packetService.addProcessor(packetProcessor, PacketProcessor.director(1));
+ log.info("Started");
+ }
+
+ @Deactivate
+ protected void deactivate() {
+ packetService.removeProcessor(packetProcessor);
+ log.info("Stopped");
+ }
+
+ 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;
+ }
+
+ populatePnatFlowRules(context.inPacket(),
+ osPort,
+ TpPort.tpPort(srcPort),
+ externalGatewayIp);
+
+ packetOut((Ethernet) ethernet.clone(),
+ inboundPacket.receivedFrom().deviceId(),
+ srcPort,
+ externalGatewayIp);
+ }
+
+ private void packetOut(Ethernet ethernet, DeviceId deviceId, int portNum, Ip4Address externalIp) {
+ IPv4 iPacket = (IPv4) ethernet.getPayload();
+ TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
+
+ switch (iPacket.getProtocol()) {
+ case IPv4.PROTOCOL_TCP:
+ TCP tcpPacket = (TCP) iPacket.getPayload();
+ tcpPacket.setSourcePort(portNum);
+ tcpPacket.resetChecksum();
+ tcpPacket.setParent(iPacket);
+ iPacket.setPayload(tcpPacket);
+ break;
+ case IPv4.PROTOCOL_UDP:
+ UDP udpPacket = (UDP) iPacket.getPayload();
+ udpPacket.setSourcePort(portNum);
+ udpPacket.resetChecksum();
+ udpPacket.setParent(iPacket);
+ iPacket.setPayload(udpPacket);
+ break;
+ default:
+ log.trace("Temporally, this method can process UDP and TCP protocol.");
+ return;
+ }
+
+ iPacket.setSourceAddress(externalIp.toString());
+ iPacket.resetChecksum();
+ iPacket.setParent(ethernet);
+ ethernet.setDestinationMACAddress(DEFAULT_EXTERNAL_ROUTER_MAC);
+ ethernet.setPayload(iPacket);
+
+ treatment.setOutput(gatewayService.getUplinkPort(deviceId));
+ ethernet.resetChecksum();
+ 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<OpenstackPort> 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> 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().forEach(gateway -> {
+ TrafficTreatment.Builder tmpBuilder = DefaultTrafficTreatment.builder(tBuilder.build());
+ 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<Host> 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().forEach(deviceId -> {
+ DeviceId srcDeviceId = srcVm.get().location().deviceId();
+ TrafficTreatment.Builder tmpBuilder = DefaultTrafficTreatment.builder(tBuilder.build());
+ 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;
+ }
+ }
+ }
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingArpHandler.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingArpHandler.java
new file mode 100644
index 0000000..0bf93dc
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingArpHandler.java
@@ -0,0 +1,143 @@
+/*
+ * 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.impl;
+
+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.Ethernet;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+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.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.api.Constants;
+import org.slf4j.Logger;
+
+import java.nio.ByteBuffer;
+import java.util.concurrent.ExecutorService;
+
+import static java.util.concurrent.Executors.newSingleThreadExecutor;
+import static org.onlab.util.Tools.groupedThreads;
+import static org.onosproject.openstacknetworking.api.Constants.DEVICE_OWNER_FLOATING_IP;
+import static org.onosproject.openstacknetworking.api.Constants.DEVICE_OWNER_ROUTER_GATEWAY;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Handle ARP, ICMP and NAT packets from gateway nodes.
+ */
+@Component(immediate = true)
+public class OpenstackRoutingArpHandler {
+ private final Logger log = getLogger(getClass());
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected PacketService packetService;
+
+ @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");
+ }
+
+ @Deactivate
+ protected void deactivate() {
+ packetService.removeProcessor(packetProcessor);
+ log.info("Stopped");
+ }
+
+ private void processArpPacket(PacketContext context, Ethernet ethernet) {
+ ARP arp = (ARP) ethernet.getPayload();
+ log.trace("arpEvent called from {} to {}",
+ Ip4Address.valueOf(arp.getSenderProtocolAddress()).toString(),
+ Ip4Address.valueOf(arp.getTargetProtocolAddress()).toString());
+
+ if (arp.getOpCode() != ARP.OP_REQUEST) {
+ return;
+ }
+
+ IpAddress targetIp = Ip4Address.valueOf(arp.getTargetProtocolAddress());
+ if (getTargetMacForTargetIp(targetIp.getIp4Address()) == MacAddress.NONE) {
+ return;
+ }
+
+ MacAddress targetMac = Constants.DEFAULT_EXTERNAL_ROUTER_MAC;
+ Ethernet ethReply = ARP.buildArpReply(targetIp.getIp4Address(),
+ targetMac, ethernet);
+
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+ .setOutput(context.inPacket().receivedFrom().port())
+ .build();
+
+ packetService.emit(new DefaultOutboundPacket(
+ context.inPacket().receivedFrom().deviceId(),
+ treatment,
+ 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(DEVICE_OWNER_ROUTER_GATEWAY) ||
+ p.deviceOwner().equals(DEVICE_OWNER_FLOATING_IP))
+ .filter(p -> p.fixedIps().containsValue(targetIp.getIp4Address()))
+ .findAny().orElse(null);
+
+ return port == null ? MacAddress.NONE : port.macAddress();
+ }
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingManager.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingManager.java
new file mode 100644
index 0000000..2482333
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingManager.java
@@ -0,0 +1,482 @@
+/*
+ * 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.impl;
+
+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.IPv4;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
+import org.onlab.util.Tools;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.core.GroupId;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Host;
+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.flowobjective.FlowObjectiveService;
+import org.onosproject.net.flowobjective.ForwardingObjective;
+import org.onosproject.openstackinterface.OpenstackInterfaceService;
+import org.onosproject.openstackinterface.OpenstackNetwork;
+import org.onosproject.openstackinterface.OpenstackRouter;
+import org.onosproject.openstackinterface.OpenstackRouterInterface;
+import org.onosproject.openstackinterface.OpenstackSubnet;
+import org.onosproject.openstacknetworking.api.Constants;
+import org.onosproject.openstacknetworking.api.OpenstackRoutingService;
+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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.stream.Collectors;
+
+import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
+import static org.onlab.util.Tools.groupedThreads;
+import static org.onosproject.openstacknetworking.api.Constants.*;
+import static org.onosproject.openstacknetworking.impl.RulePopulatorUtil.buildExtension;
+import static org.onosproject.openstacknode.OpenstackNodeService.NodeType.COMPUTE;
+import static org.onosproject.openstacknode.OpenstackNodeService.NodeType.GATEWAY;
+
+@Component(immediate = true)
+@Service
+public class OpenstackRoutingManager extends AbstractVmHandler implements OpenstackRoutingService {
+
+ private final Logger log = LoggerFactory.getLogger(getClass());
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected FlowObjectiveService flowObjectiveService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected OpenstackInterfaceService openstackService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected OpenstackNodeService nodeService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected ScalableGatewayService gatewayService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected CoreService coreService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected DeviceService deviceService;
+
+ private final ExecutorService eventExecutor = newSingleThreadScheduledExecutor(
+ groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
+ private final InternalNodeListener nodeListener = new InternalNodeListener();
+
+ private ApplicationId appId;
+
+ @Activate
+ protected void activate() {
+ super.activate();
+ appId = coreService.registerApplication(ROUTING_APP_ID);
+ nodeService.addListener(nodeListener);
+ }
+
+ @Deactivate
+ protected void deactivate() {
+ super.deactivate();
+ nodeService.removeListener(nodeListener);
+ log.info("stopped");
+ }
+
+ @Override
+ protected void hostDetected(Host host) {
+ // Installs forwarding flow rules to VMs in different nodes and different subnets
+ // that are connected via a router
+ Optional<OpenstackRouter> routerOfTheHost = getRouter(host);
+
+ if (!routerOfTheHost.isPresent()) {
+ return;
+ }
+
+ routableSubNets(routerOfTheHost.get().id()).stream()
+ .filter(subnet -> !subnet.id().equals(host.annotations().value(SUBNET_ID)))
+ .forEach(subnet -> setForwardingRulesAmongHostsInDifferentCnodes(host, getHosts(subnet), true));
+ }
+
+ @Override
+ protected void hostRemoved(Host host) {
+ // Removes forwarding flow rules to VMs in different nodes and different subnets
+ // that are connected via a router
+ Optional<OpenstackRouter> routerOfTheHost = getRouter(host);
+ if (!routerOfTheHost.isPresent()) {
+ return;
+ }
+
+ routableSubNets(routerOfTheHost.get().id()).stream()
+ .filter(subnet -> !subnet.id().equals(host.annotations().value(SUBNET_ID)))
+ .forEach(subnet -> setForwardingRulesAmongHostsInDifferentCnodes(host, getHosts(subnet), false));
+ }
+
+ @Override
+ public void createRouter(OpenstackRouter osRouter) {
+ }
+
+ @Override
+ public void updateRouter(OpenstackRouter osRouter) {
+ if (osRouter.gatewayExternalInfo().externalFixedIps().size() > 0) {
+ routableSubNets(osRouter.id()).stream()
+ .forEach(subnet -> setExternalConnection(osRouter, subnet, true));
+
+ log.info("Connected external gateway {} to router {}",
+ osRouter.gatewayExternalInfo().externalFixedIps(),
+ osRouter.name());
+ } else {
+ routableSubNets(osRouter.id()).stream()
+ .forEach(subnet -> setExternalConnection(osRouter, subnet, false));
+
+ log.info("Disconnected external gateway from router {}",
+ osRouter.name());
+ }
+ }
+
+ @Override
+ public void removeRouter(String osRouterId) {
+ // Nothing to do
+ // All interfaces need to be removed before the router is removed,
+ // and all related flow rues are removed when the interfaces are removed.
+ }
+
+ @Override
+ public void addRouterInterface(OpenstackRouterInterface routerIfaceAdded) {
+ OpenstackRouter osRouter = openstackRouter(routerIfaceAdded.id());
+ OpenstackSubnet osSubnetAdded = openstackService.subnet(routerIfaceAdded.subnetId());
+ if (osRouter == null || osSubnetAdded == null) {
+ log.warn("Failed to add router interface {}", routerIfaceAdded);
+ return;
+ }
+ handleRouterInterfaces(osRouter, osSubnetAdded);
+ }
+
+ @Override
+ public void removeRouterInterface(OpenstackRouterInterface routerIface) {
+ OpenstackRouter osRouter = openstackService.router(routerIface.id());
+ OpenstackSubnet osSubnetRemoved = openstackService.subnet(routerIface.subnetId());
+ if (osRouter == null) {
+ log.warn("Failed to remove router interface {}", routerIface);
+ return;
+ }
+ handleRouterInterfacesRemoved(osRouter, osSubnetRemoved);
+
+ log.info("Disconnected {} from router {}", osSubnetRemoved.cidr(), osRouter.name());
+ }
+
+ private void handleRouterInterfaces(OpenstackRouter osRouter, OpenstackSubnet osSubnetAdded) {
+ OpenstackNetwork osNetworkAdded = openstackService.network(osSubnetAdded.networkId());
+ if (osNetworkAdded == null) { // in case of external network subnet
+ return;
+ }
+
+ // Sets flow rules for routing among subnets connected to a router.
+ setRoutesAmongSubnets(osRouter, osSubnetAdded, true);
+
+ // Sets flow rules for forwarding "packets going to external networks" to gateway nodes.
+ if (osRouter.gatewayExternalInfo().externalFixedIps().size() > 0) {
+ setExternalConnection(osRouter, osSubnetAdded, true);
+ }
+
+ // Sets flow rules to handle ping to the virtual gateway.
+ Ip4Address vGatewayIp = Ip4Address.valueOf(osSubnetAdded.gatewayIp());
+ gatewayService.getGatewayDeviceIds()
+ .forEach(deviceId -> setGatewayIcmpRule(vGatewayIp, deviceId, true));
+
+ // Sets east-west routing rules for VMs in different Cnode to Switching Table.
+ setForwardingRulesForEastWestRouting(osRouter, osSubnetAdded, true);
+
+ }
+
+ private void handleRouterInterfacesRemoved(OpenstackRouter osRouter, OpenstackSubnet osSubnetRemoved) {
+
+ // Removes flow rules for routing among subnets connected to a router.
+ setRoutesAmongSubnets(osRouter, osSubnetRemoved, false);
+
+ // Removes flow rules for forwarding "packets going to external networks" to gateway nodes.
+ if (osRouter.gatewayExternalInfo().externalFixedIps().size() > 0) {
+ setExternalConnection(osRouter, osSubnetRemoved, false);
+ }
+
+ // Removes flow rules to handle ping to the virtual gateway.
+ Ip4Address vGatewayIp = Ip4Address.valueOf(osSubnetRemoved.gatewayIp());
+ gatewayService.getGatewayDeviceIds()
+ .forEach(deviceId -> setGatewayIcmpRule(vGatewayIp, deviceId, false));
+
+ // Removes east-west routing rules for VMs in different Cnode to Switching Table.
+ setForwardingRulesForEastWestRouting(osRouter, osSubnetRemoved, false);
+
+ // Resets east-west routing rules for VMs in different Cnode to Switching Table.
+ routableSubNets(osRouter.id()).stream()
+ .forEach(subnet -> setForwardingRulesForEastWestRouting(osRouter, subnet, true));
+ }
+
+ private void setRoutesAmongSubnets(OpenstackRouter osRouter, OpenstackSubnet osSubnetAdded, boolean install) {
+ Set<OpenstackSubnet> routableSubNets = routableSubNets(osRouter.id());
+ if (routableSubNets.size() < 2) {
+ // no other subnet interface is connected to this router, do nothing
+ return;
+ }
+
+ Map<String, String> vniMap = new HashMap<>();
+ openstackService.networks().forEach(n -> vniMap.put(n.id(), n.segmentId()));
+
+ routableSubNets.stream()
+ .filter(subnet -> !subnet.id().equals(osSubnetAdded.id()))
+ .filter(subnet -> vniMap.get(subnet.networkId()) != null)
+ .forEach(subnet -> nodeService.completeNodes().stream()
+ .filter(node -> node.type().equals(COMPUTE))
+ .forEach(node -> {
+ setRoutingRules(node.intBridge(),
+ Integer.parseInt(vniMap.get(subnet.networkId())),
+ Integer.parseInt(vniMap.get(osSubnetAdded.networkId())),
+ subnet, osSubnetAdded, install);
+ setRoutingRules(node.intBridge(),
+ Integer.parseInt(vniMap.get(osSubnetAdded.networkId())),
+ Integer.parseInt(vniMap.get(subnet.networkId())),
+ osSubnetAdded, subnet, install);
+ }
+ ));
+ }
+
+ private void setRoutingRules(DeviceId deviceId, int srcVni, int dstVni,
+ OpenstackSubnet subnetSrc, OpenstackSubnet subnetDst, boolean install) {
+
+ TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
+ TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
+
+ sBuilder.matchEthType(Ethernet.TYPE_IPV4)
+ .matchTunnelId(srcVni)
+ .matchIPSrc(IpPrefix.valueOf(subnetSrc.cidr()))
+ .matchIPDst(IpPrefix.valueOf(subnetDst.cidr()));
+
+ tBuilder.setTunnelId(dstVni);
+
+ RulePopulatorUtil.setRule(flowObjectiveService, appId, deviceId, sBuilder.build(),
+ tBuilder.build(), ForwardingObjective.Flag.SPECIFIC, EW_ROUTING_RULE_PRIORITY, install);
+
+ // Flow rules for destination is in different subnet and different node,
+ // because VNI is converted to destination VNI in the source VM node.
+ sBuilder = DefaultTrafficSelector.builder();
+ tBuilder = DefaultTrafficTreatment.builder();
+
+ sBuilder.matchEthType(Ethernet.TYPE_IPV4)
+ .matchTunnelId(dstVni)
+ .matchIPSrc(IpPrefix.valueOf(subnetSrc.cidr()))
+ .matchIPDst(IpPrefix.valueOf(subnetDst.cidr()));
+
+ tBuilder.setTunnelId(dstVni);
+
+ RulePopulatorUtil.setRule(flowObjectiveService, appId, deviceId, sBuilder.build(),
+ tBuilder.build(), ForwardingObjective.Flag.SPECIFIC, EW_ROUTING_RULE_PRIORITY, install);
+ }
+
+ private void setExternalConnection(OpenstackRouter osRouter, OpenstackSubnet osSubNet, boolean install) {
+ if (!osRouter.gatewayExternalInfo().isEnablePnat()) {
+ log.debug("Source NAT is disabled");
+ return;
+ }
+
+ //OpenstackSubnet osSubNet = openstackService.subnet(osSubNetId);
+ OpenstackNetwork osNet = openstackService.network(osSubNet.networkId());
+
+ nodeService.completeNodes().stream()
+ .filter(node -> node.type().equals(COMPUTE))
+ .forEach(node -> setRulesToGateway(
+ node.intBridge(),
+ gatewayService.getGatewayGroupId(node.intBridge()),
+ Long.valueOf(osNet.segmentId()), osSubNet.cidr(), install));
+
+ // Is this for PNAT ??
+ setRulesForGatewayToController(Long.valueOf(osNet.segmentId()), osSubNet.cidr(), install);
+ }
+
+ private void setRulesToGateway(DeviceId deviceId, GroupId groupId, long vni, String cidr, boolean install) {
+ TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
+ TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
+
+ sBuilder.matchEthType(Ethernet.TYPE_IPV4)
+ .matchTunnelId(vni)
+ .matchIPSrc(IpPrefix.valueOf(cidr))
+ .matchEthDst(Constants.DEFAULT_GATEWAY_MAC);
+
+ tBuilder.group(groupId);
+
+ RulePopulatorUtil.setRule(flowObjectiveService, appId, deviceId, sBuilder.build(),
+ tBuilder.build(), ForwardingObjective.Flag.SPECIFIC, ROUTING_RULE_PRIORITY, install);
+ }
+
+ private void setRulesForGatewayToController(long vni, String subNetCidr, boolean install) {
+ TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
+ TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
+
+ sBuilder.matchEthType(Ethernet.TYPE_IPV4)
+ .matchTunnelId(vni)
+ .matchIPSrc(IpPrefix.valueOf(subNetCidr))
+ .matchEthDst(Constants.DEFAULT_GATEWAY_MAC);
+ tBuilder.setOutput(PortNumber.CONTROLLER);
+
+ gatewayService.getGatewayDeviceIds().forEach(deviceId ->
+ RulePopulatorUtil.setRule(flowObjectiveService, appId, deviceId, sBuilder.build(),
+ tBuilder.build(), ForwardingObjective.Flag.VERSATILE, ROUTING_RULE_PRIORITY, install));
+ }
+
+ private void setGatewayIcmpRule(Ip4Address gatewayIp, DeviceId deviceId, boolean install) {
+ TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
+ TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
+
+ sBuilder.matchEthType(Ethernet.TYPE_IPV4)
+ .matchIPProtocol(IPv4.PROTOCOL_ICMP)
+ .matchIPDst(gatewayIp.toIpPrefix());
+
+ tBuilder.setOutput(PortNumber.CONTROLLER);
+
+ RulePopulatorUtil.setRule(flowObjectiveService, appId, deviceId, sBuilder.build(),
+ tBuilder.build(), ForwardingObjective.Flag.VERSATILE, GATEWAY_ICMP_PRIORITY, install);
+ }
+
+ private void setForwardingRulesForEastWestRouting(OpenstackRouter router, OpenstackSubnet subnetAdded,
+ boolean install) {
+
+ Set<OpenstackSubnet> subnets = routableSubNets(router.id());
+
+ Set<Host> hosts = Tools.stream(hostService.getHosts())
+ .filter(h -> getVni(h).equals(openstackService.network(subnetAdded.networkId()).segmentId()))
+ .collect(Collectors.toSet());
+
+ subnets.stream()
+ .filter(subnet -> !subnet.id().equals(subnetAdded.id()))
+ .forEach(subnet -> getHosts(subnet)
+ .forEach(h -> setForwardingRulesAmongHostsInDifferentCnodes(h, hosts, install)));
+ }
+
+ private void setForwardingRulesAmongHostsInDifferentCnodes(Host host, Set<Host> remoteHosts, boolean install) {
+ Ip4Address localVmIp = getIp(host);
+ DeviceId localDeviceId = host.location().deviceId();
+ Optional<IpAddress> localDataIp = nodeService.dataIp(localDeviceId);
+
+ if (!localDataIp.isPresent()) {
+ log.debug("Failed to get data IP for device {}",
+ host.location().deviceId());
+ return;
+ }
+
+ remoteHosts.stream()
+ .filter(remoteHost -> !host.location().deviceId().equals(remoteHost.location().deviceId()))
+ .forEach(remoteVm -> {
+ Optional<IpAddress> remoteDataIp = nodeService.dataIp(remoteVm.location().deviceId());
+ if (remoteDataIp.isPresent()) {
+ setVxLanFlowRule(getVni(remoteVm),
+ localDeviceId,
+ remoteDataIp.get().getIp4Address(),
+ getIp(remoteVm), install);
+
+ setVxLanFlowRule(getVni(host),
+ remoteVm.location().deviceId(),
+ localDataIp.get().getIp4Address(),
+ localVmIp, install);
+ }
+ });
+ }
+
+ private void setVxLanFlowRule(String vni, DeviceId deviceId, Ip4Address remoteIp,
+ Ip4Address vmIp, boolean install) {
+ Optional<PortNumber> 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());
+
+ RulePopulatorUtil.setRule(flowObjectiveService, appId, deviceId, sBuilder.build(),
+ tBuilder.build(), ForwardingObjective.Flag.SPECIFIC, SWITCHING_RULE_PRIORITY, install);
+ }
+
+
+ private OpenstackRouter openstackRouter(String routerId) {
+ return openstackService.routers().stream().filter(r ->
+ r.id().equals(routerId)).iterator().next();
+ }
+
+ @Override
+ public void reinstallVmFlow(Host host) {
+ // TODO: implements later
+ }
+
+ @Override
+ public void purgeVmFlow(Host host) {
+ // TODO: implements later
+ }
+
+ private class InternalNodeListener implements OpenstackNodeListener {
+
+ @Override
+ public void event(OpenstackNodeEvent event) {
+ OpenstackNode node = event.node();
+
+ switch (event.type()) {
+ case COMPLETE:
+ case INCOMPLETE:
+ log.info("COMPLETE node {} detected", node.hostname());
+ eventExecutor.execute(() -> {
+ if (node.type() == GATEWAY) {
+ GatewayNode gnode = GatewayNode.builder()
+ .gatewayDeviceId(node.intBridge())
+ .dataIpAddress(node.dataIp().getIp4Address())
+ .uplinkIntf(node.externalPortName().get())
+ .build();
+ gatewayService.addGatewayNode(gnode);
+ }
+ });
+ openstackService.routers().stream()
+ .forEach(router -> routableSubNets(router.id()).stream()
+ .forEach(subnet -> handleRouterInterfaces(router, subnet)));
+ break;
+ case INIT:
+ case DEVICE_CREATED:
+ default:
+ break;
+ }
+ }
+ }
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSecurityGroupManager.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSecurityGroupManager.java
new file mode 100644
index 0000000..3f6f480
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSecurityGroupManager.java
@@ -0,0 +1,425 @@
+/*
+* 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.impl;
+
+import com.google.common.collect.Maps;
+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.IPv4;
+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;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flowobjective.DefaultForwardingObjective;
+import org.onosproject.net.flowobjective.FlowObjectiveService;
+import org.onosproject.net.flowobjective.ForwardingObjective;
+import org.onosproject.openstackinterface.OpenstackInterfaceService;
+import org.onosproject.openstackinterface.OpenstackPort;
+import org.onosproject.openstackinterface.OpenstackSecurityGroup;
+import org.onosproject.openstackinterface.OpenstackSecurityGroupRule;
+import org.onosproject.openstacknetworking.api.OpenstackSecurityGroupService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import static org.onosproject.openstacknetworking.api.Constants.*;
+
+/**
+ * Populates flows rules for Security Groups of VMs.
+ *
+ */
+@Component(immediate = true)
+@Service
+public class OpenstackSecurityGroupManager extends AbstractVmHandler
+ implements OpenstackSecurityGroupService {
+
+ private final Logger log = LoggerFactory.getLogger(getClass());
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected OpenstackInterfaceService openstackService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected FlowObjectiveService flowObjectiveService;
+
+ private static final String PROTO_ICMP = "ICMP";
+ private static final String PROTO_TCP = "TCP";
+ private static final String PROTO_UDP = "UDP";
+ private static final String ETHTYPE_IPV4 = "IPV4";
+
+ private final Map<Host, Set<SecurityGroupRule>> securityGroupRuleMap = Maps.newConcurrentMap();
+ private ApplicationId appId;
+
+ @Activate
+ protected void activate() {
+ super.activate();
+ appId = coreService.registerApplication(SWITCHING_APP_ID);
+ }
+
+ @Deactivate
+ protected void deactivate() {
+ super.deactivate();
+ }
+
+ @Override
+ public void updateSecurityGroup(OpenstackPort osPort) {
+ if (!osPort.status().equals(OpenstackPort.PortStatus.ACTIVE)) {
+ return;
+ }
+
+ Optional<Host> host = getVmByPortId(osPort.id());
+ if (!host.isPresent()) {
+ log.debug("No host found with {}", osPort.id());
+ return;
+ }
+ eventExecutor.execute(() -> updateSecurityGroupRules(host.get(), true));
+ }
+
+ /**
+ * Populates security group rules for all VMs in the supplied tenant ID.
+ * VMs in the same tenant tend to be engaged to each other by sharing the
+ * same security groups or setting the remote to another security group.
+ * To make the implementation simpler and robust, it tries to reinstall
+ * security group rules for all the VMs in the same tenant whenever a new
+ * VM is detected or port is updated.
+ *
+ * @param tenantId tenant id to update security group rules
+ */
+ private void populateSecurityGroupRules(String tenantId, boolean install) {
+ securityGroupRuleMap.entrySet().stream()
+ .filter(entry -> getTenantId(entry.getKey()).equals(tenantId))
+ .forEach(entry -> {
+ Host local = entry.getKey();
+ entry.getValue().forEach(sgRule -> {
+ setSecurityGroupRule(local.location().deviceId(),
+ sgRule.rule(),
+ getIp(local),
+ sgRule.remoteIp(), install);
+ });
+ });
+ log.debug("Updated security group rules for {}", tenantId);
+ }
+
+ private void setSecurityGroupRule(DeviceId deviceId, OpenstackSecurityGroupRule sgRule,
+ Ip4Address vmIp, IpPrefix remoteIp,
+ boolean install) {
+ ForwardingObjective.Builder foBuilder = buildFlowObjective(sgRule, vmIp, remoteIp);
+ if (foBuilder == null) {
+ return;
+ }
+
+ if (install) {
+ flowObjectiveService.forward(deviceId, foBuilder.add());
+ } else {
+ flowObjectiveService.forward(deviceId, foBuilder.remove());
+ }
+ }
+
+ private ForwardingObjective.Builder buildFlowObjective(OpenstackSecurityGroupRule sgRule,
+ Ip4Address vmIp,
+ IpPrefix remoteIp) {
+ if (remoteIp != null && remoteIp.equals(IpPrefix.valueOf(vmIp, 32))) {
+ // do nothing if the remote IP is my IP
+ return null;
+ }
+
+ TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
+ buildMatchs(sBuilder, sgRule, vmIp, remoteIp);
+
+ return DefaultForwardingObjective.builder()
+ .withSelector(sBuilder.build())
+ .withTreatment(DefaultTrafficTreatment.builder().build())
+ .withPriority(ACL_RULE_PRIORITY)
+ .withFlag(ForwardingObjective.Flag.SPECIFIC)
+ .fromApp(appId);
+ }
+
+ private void buildMatchs(TrafficSelector.Builder sBuilder, OpenstackSecurityGroupRule sgRule,
+ Ip4Address vmIp, IpPrefix remoteIp) {
+ buildMatchEthType(sBuilder, sgRule.ethertype());
+ buildMatchDirection(sBuilder, sgRule.direction(), vmIp);
+ buildMatchProto(sBuilder, sgRule.protocol());
+ buildMatchPort(sBuilder, sgRule.protocol(), sgRule.direction(),
+ sgRule.portRangeMax(), sgRule.portRangeMin());
+ buildMatchRemoteIp(sBuilder, remoteIp, sgRule.direction());
+ }
+
+ private void buildMatchDirection(TrafficSelector.Builder sBuilder,
+ OpenstackSecurityGroupRule.Direction direction,
+ Ip4Address vmIp) {
+ if (direction.equals(OpenstackSecurityGroupRule.Direction.EGRESS)) {
+ sBuilder.matchIPSrc(IpPrefix.valueOf(vmIp, 32));
+ } else {
+ sBuilder.matchIPDst(IpPrefix.valueOf(vmIp, 32));
+ }
+ }
+
+ private void buildMatchEthType(TrafficSelector.Builder sBuilder, String etherType) {
+ // Either IpSrc or IpDst (or both) is set by default, and we need to set EthType as IPv4.
+ sBuilder.matchEthType(Ethernet.TYPE_IPV4);
+ if (etherType != null && !Objects.equals(etherType, "null") &&
+ !etherType.toUpperCase().equals(ETHTYPE_IPV4)) {
+ log.debug("EthType {} is not supported yet in Security Group", etherType);
+ }
+ }
+
+ private void buildMatchRemoteIp(TrafficSelector.Builder sBuilder, IpPrefix remoteIpPrefix,
+ OpenstackSecurityGroupRule.Direction direction) {
+ if (remoteIpPrefix != null && !remoteIpPrefix.getIp4Prefix().equals(IP_PREFIX_ANY)) {
+ if (direction.equals(OpenstackSecurityGroupRule.Direction.EGRESS)) {
+ sBuilder.matchIPDst(remoteIpPrefix);
+ } else {
+ sBuilder.matchIPSrc(remoteIpPrefix);
+ }
+ }
+ }
+
+ private void buildMatchProto(TrafficSelector.Builder sBuilder, String protocol) {
+ if (protocol != null) {
+ switch (protocol.toUpperCase()) {
+ case PROTO_ICMP:
+ sBuilder.matchIPProtocol(IPv4.PROTOCOL_ICMP);
+ break;
+ case PROTO_TCP:
+ sBuilder.matchIPProtocol(IPv4.PROTOCOL_TCP);
+ break;
+ case PROTO_UDP:
+ sBuilder.matchIPProtocol(IPv4.PROTOCOL_UDP);
+ break;
+ default:
+ }
+ }
+ }
+
+ private void buildMatchPort(TrafficSelector.Builder sBuilder, String protocol,
+ OpenstackSecurityGroupRule.Direction direction,
+ int portMin, int portMax) {
+ if (portMin > 0 && portMax > 0 && portMin == portMax) {
+ if (protocol.toUpperCase().equals(PROTO_TCP)) {
+ if (direction.equals(OpenstackSecurityGroupRule.Direction.EGRESS)) {
+ sBuilder.matchTcpSrc(TpPort.tpPort(portMax));
+ } else {
+ sBuilder.matchTcpDst(TpPort.tpPort(portMax));
+ }
+ } else if (protocol.toUpperCase().equals(PROTO_UDP)) {
+ if (direction.equals(OpenstackSecurityGroupRule.Direction.EGRESS)) {
+ sBuilder.matchUdpSrc(TpPort.tpPort(portMax));
+ } else {
+ sBuilder.matchUdpDst(TpPort.tpPort(portMax));
+ }
+ }
+ }
+ }
+
+ private void updateSecurityGroupRulesMap(Host host) {
+ OpenstackPort osPort = openstackService.port(host.annotations().value(PORT_ID));
+ if (osPort == null) {
+ log.debug("Failed to get OpenStack port information for {}", host);
+ return;
+ }
+
+ Set<SecurityGroupRule> rules = Sets.newHashSet();
+ osPort.securityGroups().forEach(sgId -> {
+ OpenstackSecurityGroup osSecGroup = openstackService.securityGroup(sgId);
+ if (osSecGroup != null) {
+ osSecGroup.rules().forEach(rule -> rules.addAll(getSgRules(rule)));
+ } else {
+ // TODO handle the case that the security group removed
+ log.warn("Failed to get security group {}", sgId);
+ }
+ });
+ securityGroupRuleMap.put(host, rules);
+ }
+
+ /**
+ * Returns set of security group rules with individual remote IP by
+ * converting remote group to actual IP address.
+ *
+ * @param sgRule security group rule
+ * @return set of security group rules
+ */
+ private Set<SecurityGroupRule> getSgRules(OpenstackSecurityGroupRule sgRule) {
+ Set<SecurityGroupRule> sgRules = Sets.newHashSet();
+ if (sgRule.remoteGroupId() != null && !sgRule.remoteGroupId().equals("null")) {
+ sgRules = getRemoteIps(sgRule.tenantId(), sgRule.remoteGroupId())
+ .stream()
+ .map(remoteIp -> new SecurityGroupRule(sgRule, remoteIp))
+ .collect(Collectors.toSet());
+ } else {
+ sgRules.add(new SecurityGroupRule(sgRule, sgRule.remoteIpPrefix()));
+ }
+ return sgRules;
+ }
+
+ /**
+ * Returns a set of host IP addresses engaged with supplied security group ID.
+ * It only searches a VM in the same tenant boundary.
+ *
+ * @param tenantId tenant id
+ * @param sgId security group id
+ * @return set of ip addresses in ip prefix format
+ */
+ private Set<IpPrefix> getRemoteIps(String tenantId, String sgId) {
+ Set<IpPrefix> remoteIps = Sets.newHashSet();
+ securityGroupRuleMap.entrySet().stream()
+ .filter(entry -> Objects.equals(getTenantId(entry.getKey()), tenantId))
+ .forEach(entry -> {
+ if (entry.getValue().stream()
+ .anyMatch(rule -> rule.rule().secuityGroupId().equals(sgId))) {
+ remoteIps.add(IpPrefix.valueOf(getIp(entry.getKey()), 32));
+ }
+ });
+ return remoteIps;
+ }
+
+ private void updateSecurityGroupRules(Host host, boolean isHostAdded) {
+ String tenantId = getTenantId(host);
+ populateSecurityGroupRules(tenantId, false);
+
+ if (isHostAdded) {
+ updateSecurityGroupRulesMap(host);
+ } else {
+ securityGroupRuleMap.remove(host);
+ }
+
+ Tools.stream(hostService.getHosts())
+ .filter(h -> Objects.equals(getTenantId(h), getTenantId(host)))
+ .forEach(this::updateSecurityGroupRulesMap);
+
+ populateSecurityGroupRules(tenantId, true);
+ }
+
+ @Override
+ protected void hostDetected(Host host) {
+ updateSecurityGroupRules(host, true);
+ log.info("Applied security group rules for {}", host);
+ }
+
+ @Override
+ protected void hostRemoved(Host host) {
+ updateSecurityGroupRules(host, false);
+ log.info("Applied security group rules for {}", host);
+ }
+
+ @Override
+ public void reinstallVmFlow(Host host) {
+ if (host == null) {
+ hostService.getHosts().forEach(h -> {
+ updateSecurityGroupRules(h, true);
+ log.info("Re-Install data plane flow of virtual machine {}", h);
+ });
+ } else {
+ securityGroupRuleMap.entrySet().stream()
+ .filter(entry -> entry.getKey().id().equals(host.id()))
+ .forEach(entry -> {
+ Host local = entry.getKey();
+ entry.getValue().forEach(sgRule -> {
+ setSecurityGroupRule(local.location().deviceId(),
+ sgRule.rule(),
+ getIp(local),
+ sgRule.remoteIp(), true);
+ });
+ });
+ log.info("Re-Install data plane flow of virtual machine {}", host);
+ }
+ }
+
+ @Override
+ public void purgeVmFlow(Host host) {
+ if (host == null) {
+ securityGroupRuleMap.entrySet().stream()
+ .forEach(entry -> {
+ Host local = entry.getKey();
+ entry.getValue().forEach(sgRule -> {
+ setSecurityGroupRule(local.location().deviceId(),
+ sgRule.rule(),
+ getIp(local),
+ sgRule.remoteIp(), false);
+ });
+ log.info("Purge data plane flow of virtual machine {}", local);
+ });
+ } else {
+ securityGroupRuleMap.entrySet().stream()
+ .filter(entry -> entry.getKey().id().equals(host.id()))
+ .forEach(entry -> {
+ Host local = entry.getKey();
+ entry.getValue().forEach(sgRule -> {
+ setSecurityGroupRule(local.location().deviceId(),
+ sgRule.rule(),
+ getIp(local),
+ sgRule.remoteIp(), false);
+ });
+ });
+ log.info("Purge data plane flow of virtual machine {}", host);
+ }
+ }
+
+ private final class SecurityGroupRule {
+ private final OpenstackSecurityGroupRule rule;
+ private final IpPrefix remoteIp;
+
+ private SecurityGroupRule(OpenstackSecurityGroupRule rule, IpPrefix remoteIp) {
+ this.rule = rule;
+ this.remoteIp = remoteIp;
+ }
+
+ private OpenstackSecurityGroupRule rule() {
+ return rule;
+ }
+
+ private IpPrefix remoteIp() {
+ return remoteIp;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+
+ if (obj instanceof SecurityGroupRule) {
+ SecurityGroupRule that = (SecurityGroupRule) obj;
+ if (Objects.equals(rule, that.rule) &&
+ Objects.equals(remoteIp, that.remoteIp)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(rule, remoteIp);
+ }
+ }
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingArpHandler.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingArpHandler.java
new file mode 100644
index 0000000..b4ae36c
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingArpHandler.java
@@ -0,0 +1,219 @@
+/*
+* 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.impl;
+
+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.Modified;
+import org.apache.felix.scr.annotations.Property;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onlab.packet.ARP;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onlab.util.Tools;
+import org.onosproject.net.Host;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.packet.DefaultOutboundPacket;
+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.OpenstackNetwork;
+import org.onosproject.openstackinterface.OpenstackPort;
+import org.osgi.service.component.ComponentContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import java.nio.ByteBuffer;
+import java.util.Dictionary;
+import java.util.Set;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.openstacknetworking.api.Constants.*;
+
+/**
+ * Handles ARP packet from VMs.
+ */
+@Component(immediate = true)
+public final class OpenstackSwitchingArpHandler extends AbstractVmHandler {
+
+ private final Logger log = LoggerFactory.getLogger(getClass());
+
+ private static final String GATEWAY_MAC = "gatewayMac";
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected PacketService packetService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected OpenstackInterfaceService openstackService;
+
+ @Property(name = GATEWAY_MAC, value = DEFAULT_GATEWAY_MAC_STR,
+ label = "Fake MAC address for virtual network subnet gateway")
+ private String gatewayMac = DEFAULT_GATEWAY_MAC_STR;
+
+ private final InternalPacketProcessor packetProcessor = new InternalPacketProcessor();
+ private final Set<Ip4Address> gateways = Sets.newConcurrentHashSet();
+
+ @Activate
+ protected void activate() {
+ packetService.addProcessor(packetProcessor, PacketProcessor.director(0));
+ super.activate();
+ }
+
+ @Deactivate
+ protected void deactivate() {
+ packetService.removeProcessor(packetProcessor);
+ super.deactivate();
+ }
+
+ @Modified
+ protected void modified(ComponentContext context) {
+ Dictionary<?, ?> properties = context.getProperties();
+ String updatedMac;
+
+ updatedMac = Tools.get(properties, GATEWAY_MAC);
+ if (!Strings.isNullOrEmpty(updatedMac) && !updatedMac.equals(gatewayMac)) {
+ gatewayMac = updatedMac;
+ }
+
+ log.info("Modified");
+ }
+
+ /**
+ * Processes ARP request packets.
+ * It checks if the target IP is owned by a known host first and then ask to
+ * OpenStack if it's not. This ARP proxy does not support overlapping IP.
+ *
+ * @param context packet context
+ * @param ethPacket ethernet packet
+ */
+ private void processPacketIn(PacketContext context, Ethernet ethPacket) {
+ ARP arpPacket = (ARP) ethPacket.getPayload();
+ if (arpPacket.getOpCode() != ARP.OP_REQUEST) {
+ return;
+ }
+
+ Ip4Address targetIp = Ip4Address.valueOf(arpPacket.getTargetProtocolAddress());
+ MacAddress replyMac = gateways.contains(targetIp) ? MacAddress.valueOf(gatewayMac) :
+ getMacFromHostService(targetIp);
+ if (replyMac.equals(MacAddress.NONE)) {
+ replyMac = getMacFromOpenstack(targetIp);
+ }
+
+ if (replyMac == MacAddress.NONE) {
+ log.debug("Failed to find MAC address for {}", targetIp.toString());
+ return;
+ }
+
+ Ethernet ethReply = ARP.buildArpReply(
+ targetIp.getIp4Address(),
+ replyMac,
+ ethPacket);
+
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+ .setOutput(context.inPacket().receivedFrom().port())
+ .build();
+
+ packetService.emit(new DefaultOutboundPacket(
+ context.inPacket().receivedFrom().deviceId(),
+ treatment,
+ ByteBuffer.wrap(ethReply.serialize())));
+ }
+
+ /**
+ * Returns MAC address of a host with a given target IP address by asking to
+ * OpenStack. It does not support overlapping IP.
+ *
+ * @param targetIp target ip address
+ * @return mac address, or null if it fails to fetch the mac
+ */
+ private MacAddress getMacFromOpenstack(IpAddress targetIp) {
+ checkNotNull(targetIp);
+
+ OpenstackPort openstackPort = openstackService.ports()
+ .stream()
+ .filter(port -> port.fixedIps().containsValue(targetIp.getIp4Address()))
+ .findFirst()
+ .orElse(null);
+
+ if (openstackPort != null) {
+ log.debug("Found MAC from OpenStack for {}", targetIp.toString());
+ return openstackPort.macAddress();
+ } else {
+ return MacAddress.NONE;
+ }
+ }
+
+ /**
+ * Returns MAC address of a host with a given target IP address by asking to
+ * host service. It does not support overlapping IP.
+ *
+ * @param targetIp target ip
+ * @return mac address, or null if it fails to find the mac
+ */
+ private MacAddress getMacFromHostService(IpAddress targetIp) {
+ checkNotNull(targetIp);
+
+ Host host = hostService.getHostsByIp(targetIp)
+ .stream()
+ .findFirst()
+ .orElse(null);
+
+ if (host != null) {
+ log.debug("Found MAC from host service for {}", targetIp.toString());
+ return host.mac();
+ } else {
+ return MacAddress.NONE;
+ }
+ }
+
+ @Override
+ protected void hostDetected(Host host) {
+ OpenstackNetwork osNet = openstackService.network(host.annotations().value(NETWORK_ID));
+ if (osNet == null) {
+ log.warn("Failed to get OpenStack network for {}", host);
+ return;
+ }
+ osNet.subnets().forEach(subnet -> gateways.add(Ip4Address.valueOf(subnet.gatewayIp())));
+ }
+
+ @Override
+ protected void hostRemoved(Host host) {
+ // TODO remove subnet gateway from gateways if no hosts exists on that subnet
+ }
+
+ private class InternalPacketProcessor implements PacketProcessor {
+
+ @Override
+ public void process(PacketContext context) {
+ if (context.isHandled()) {
+ return;
+ }
+
+ Ethernet ethPacket = context.inPacket().parsed();
+ if (ethPacket == null || ethPacket.getEtherType() != Ethernet.TYPE_ARP) {
+ return;
+ }
+ processPacketIn(context, ethPacket);
+ }
+ }
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingHostManager.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingHostManager.java
new file mode 100644
index 0000000..99dd927
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingHostManager.java
@@ -0,0 +1,319 @@
+/*
+ * 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.impl;
+
+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.api.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;
+ }
+
+ Map.Entry<String, Ip4Address> fixedIp = osPort.fixedIps().entrySet().stream().findFirst().get();
+
+ OpenstackSubnet openstackSubnet = openstackService.subnets().stream()
+ .filter(n -> n.networkId().equals(osPort.networkId()) &&
+ n.id().equals(fixedIp.getKey()))
+ .findFirst().orElse(null);
+ if (openstackSubnet == null) {
+ log.warn("Failed to find subnet for {}", osPort);
+ return;
+ }
+
+ registerDhcpInfo(osPort, openstackSubnet);
+ ConnectPoint connectPoint = new ConnectPoint(port.element().id(), port.number());
+
+ 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, openstackSubnet.gatewayIp());
+
+ 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, OpenstackSubnet openstackSubnet) {
+ checkNotNull(openstackPort);
+ checkNotNull(openstackSubnet);
+ checkArgument(!openstackPort.fixedIps().isEmpty());
+
+ 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()));
+ } else if (event.port().isEnabled()) {
+ deviceEventExecutor.execute(() -> processPortAdded(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/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingManager.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingManager.java
new file mode 100644
index 0000000..d9efb3f
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingManager.java
@@ -0,0 +1,235 @@
+/*
+* 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.impl;
+
+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.IpAddress;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Host;
+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.flowobjective.FlowObjectiveService;
+import org.onosproject.net.flowobjective.ForwardingObjective;
+import org.onosproject.openstacknetworking.api.OpenstackSwitchingService;
+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.openstacknetworking.api.Constants.*;
+import static org.onosproject.openstacknetworking.impl.RulePopulatorUtil.buildExtension;
+
+
+/**
+ * Populates switching flow rules.
+ */
+@Service
+@Component(immediate = true)
+public final class OpenstackSwitchingManager extends AbstractVmHandler
+ implements OpenstackSwitchingService {
+
+ 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 ApplicationId appId;
+
+ @Activate
+ protected void activate() {
+ super.activate();
+ appId = coreService.registerApplication(SWITCHING_APP_ID);
+ }
+
+ @Deactivate
+ protected void deactivate() {
+ super.deactivate();
+ }
+
+ private void populateSwitchingRules(Host host) {
+ setFlowRulesForTunnelTag(host, true);
+ setFlowRulesForTrafficToSameCnode(host, true);
+ setFlowRulesForTrafficToDifferentCnode(host, true);
+
+ log.debug("Populated switching rule for {}", host);
+ }
+
+ private void removeSwitchingRules(Host host) {
+ setFlowRulesForTunnelTag(host, false);
+ setFlowRulesForTrafficToSameCnode(host, false);
+ removeFlowRuleForVMsInDiffrentCnode(host);
+
+ log.debug("Removed switching rule for {}", host);
+ }
+
+ private void setFlowRulesForTunnelTag(Host host, boolean install) {
+ 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)));
+
+ RulePopulatorUtil.setRule(flowObjectiveService, appId, host.location().deviceId(),
+ sBuilder.build(), tBuilder.build(), ForwardingObjective.Flag.SPECIFIC,
+ TUNNELTAG_RULE_PRIORITY, install);
+ }
+
+ private void setFlowRulesForTrafficToSameCnode(Host host, boolean install) {
+ //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)));
+
+ // Destination setting is required for routing cases.
+ // We do not believe the rule would not degrade the forwarding performance.
+ // But, if it does, we need to move the rule in a separate routing table.
+ tBuilder.setEthDst(host.mac())
+ .setOutput(host.location().port());
+
+ RulePopulatorUtil.setRule(flowObjectiveService, appId, host.location().deviceId(),
+ sBuilder.build(), tBuilder.build(), ForwardingObjective.Flag.SPECIFIC,
+ SWITCHING_RULE_PRIORITY, install);
+ }
+
+ private void setFlowRulesForTrafficToDifferentCnode(Host host, boolean install) {
+ Ip4Address localVmIp = getIp(host);
+ DeviceId localDeviceId = host.location().deviceId();
+ Optional<IpAddress> 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<IpAddress> remoteDataIp = nodeService.dataIp(remoteVm.location().deviceId());
+ if (remoteDataIp.isPresent()) {
+ setVxLanFlowRule(vni,
+ localDeviceId,
+ remoteDataIp.get().getIp4Address(),
+ getIp(remoteVm), install);
+
+ setVxLanFlowRule(vni,
+ remoteVm.location().deviceId(),
+ localDataIp.get().getIp4Address(),
+ localVmIp, install);
+ }
+ });
+ }
+
+ private void setVxLanFlowRule(String vni, DeviceId deviceId, Ip4Address remoteIp,
+ Ip4Address vmIp, boolean install) {
+ Optional<PortNumber> 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());
+
+ RulePopulatorUtil.setRule(flowObjectiveService, appId, deviceId,
+ sBuilder.build(), tBuilder.build(), ForwardingObjective.Flag.SPECIFIC,
+ SWITCHING_RULE_PRIORITY, install);
+ }
+
+ 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 -> {
+ setVxLanFlowRule(getVni(host), h.location().deviceId(), Ip4Address.valueOf(0), getIp(host), false);
+ if (!anyPortRemainedInSameCnode) {
+ setVxLanFlowRule(getVni(host), deviceId, Ip4Address.valueOf(0), getIp(h), false);
+ }
+ });
+ }
+
+ @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);
+ }
+
+ @Override
+ public void reinstallVmFlow(Host host) {
+ if (host == null) {
+ hostService.getHosts().forEach(h -> {
+ populateSwitchingRules(h);
+ log.info("Re-Install data plane flow of virtual machine {}", h);
+ });
+ } else {
+ populateSwitchingRules(host);
+ log.info("Re-Install data plane flow of virtual machine {}", host);
+ }
+ }
+
+ @Override
+ public void purgeVmFlow(Host host) {
+ if (host == null) {
+ hostService.getHosts().forEach(h -> {
+ removeSwitchingRules(h);
+ log.info("Purge data plane flow of virtual machine {}", h);
+ });
+ } else {
+ removeSwitchingRules(host);
+ log.info("Purge data plane flow of virtual machine {}", host);
+ }
+ }
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/RulePopulatorUtil.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/RulePopulatorUtil.java
new file mode 100644
index 0000000..3a77804
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/RulePopulatorUtil.java
@@ -0,0 +1,109 @@
+/*
+ * 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.impl;
+
+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.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.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;
+ }
+ }
+
+ /**
+ * Adds 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 treatment traffic treatment
+ * @param flag flag
+ * @param priority priority
+ * @param install populate flows if true, remove them otherwise
+ */
+ public static void setRule(FlowObjectiveService flowObjectiveService,
+ ApplicationId appId,
+ DeviceId deviceId,
+ TrafficSelector selector,
+ TrafficTreatment treatment,
+ ForwardingObjective.Flag flag,
+ int priority,
+ boolean install) {
+ ForwardingObjective.Builder foBuilder = DefaultForwardingObjective.builder()
+ .withSelector(selector)
+ .withTreatment(treatment)
+ .withFlag(flag)
+ .withPriority(priority)
+ .fromApp(appId);
+
+ if (install) {
+ flowObjectiveService.forward(deviceId, foBuilder.add());
+ } else {
+ flowObjectiveService.forward(deviceId, foBuilder.remove());
+ }
+ }
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/package-info.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/package-info.java
new file mode 100644
index 0000000..47cd773
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+
+/**
+ * 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.impl;
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/web/OpensatckRouterWebResource.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/web/OpensatckRouterWebResource.java
new file mode 100644
index 0000000..c39afd6
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/web/OpensatckRouterWebResource.java
@@ -0,0 +1,187 @@
+/*
+ * 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.web;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.onosproject.openstackinterface.OpenstackRouter;
+import org.onosproject.openstackinterface.OpenstackRouterInterface;
+import org.onosproject.openstackinterface.web.OpenstackRouterCodec;
+import org.onosproject.openstackinterface.web.OpenstackRouterInterfaceCodec;
+import org.onosproject.openstacknetworking.api.OpenstackRoutingService;
+import org.onosproject.rest.AbstractWebResource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import java.io.InputStream;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Handles REST API call of Neturon L3 plugin.
+ */
+
+@Path("routers")
+public class OpensatckRouterWebResource extends AbstractWebResource {
+ private final Logger log = LoggerFactory.getLogger(getClass());
+
+ private static final OpenstackRouterInterfaceCodec ROUTER_INTERFACE_CODEC
+ = new OpenstackRouterInterfaceCodec();
+ private static final OpenstackRouterCodec ROUTER_CODEC
+ = new OpenstackRouterCodec();
+
+ @POST
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response createRouter(InputStream input) {
+ checkNotNull(input);
+ try {
+ ObjectMapper mapper = new ObjectMapper();
+ ObjectNode routerNode = (ObjectNode) mapper.readTree(input);
+
+ OpenstackRouter openstackRouter
+ = ROUTER_CODEC.decode(routerNode, this);
+
+ OpenstackRoutingService routingService
+ = getService(OpenstackRoutingService.class);
+ routingService.createRouter(openstackRouter);
+
+ log.debug("REST API CREATE router is called {}", input.toString());
+ return Response.status(Response.Status.OK).build();
+ } catch (Exception e) {
+ log.error("Create Router failed because of exception {}",
+ e.toString());
+ return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(e.toString())
+ .build();
+ }
+ }
+
+ @PUT
+ @Path("{id}")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response updateRouter(@PathParam("id") String id, InputStream input) {
+ checkNotNull(input);
+ try {
+ ObjectMapper mapper = new ObjectMapper();
+ ObjectNode routerNode = (ObjectNode) mapper.readTree(input);
+
+ OpenstackRouter or = ROUTER_CODEC.decode(routerNode, this);
+
+ OpenstackRouter.Builder osBuilder = new OpenstackRouter.Builder()
+ .tenantId(or.tenantId())
+ .id(id)
+ .name(or.name())
+ .status(OpenstackRouter.RouterStatus.ACTIVE)
+ .adminStateUp(Boolean.valueOf(or.adminStateUp()))
+ .gatewayExternalInfo(or.gatewayExternalInfo());
+
+ OpenstackRoutingService routingService
+ = getService(OpenstackRoutingService.class);
+ routingService.updateRouter(osBuilder.build());
+
+ log.debug("REST API UPDATE router is called from router {}", input.toString());
+ return Response.status(Response.Status.OK).build();
+ } catch (Exception e) {
+ log.error("Updates Router failed because of exception {}",
+ e.toString());
+ return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(e.toString())
+ .build();
+ }
+ }
+
+ @PUT
+ @Path("{id}/add_router_interface")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response addRouterInterface(@PathParam("id") String id, InputStream input) {
+ checkNotNull(input);
+ try {
+ ObjectMapper mapper = new ObjectMapper();
+ ObjectNode routerIfNode = (ObjectNode) mapper.readTree(input);
+
+ OpenstackRouterInterface openstackRouterInterface
+ = ROUTER_INTERFACE_CODEC.decode(routerIfNode, this);
+
+ OpenstackRoutingService routingService
+ = getService(OpenstackRoutingService.class);
+ routingService.addRouterInterface(openstackRouterInterface);
+
+ log.debug("REST API AddRouterInterface is called from router {} portId: {}, subnetId: {}, tenantId: {}",
+ openstackRouterInterface.id(), openstackRouterInterface.portId(),
+ openstackRouterInterface.subnetId(), openstackRouterInterface.tenantId());
+
+ return Response.status(Response.Status.OK).build();
+ } catch (Exception e) {
+ log.error("AddRouterInterface failed because of exception {}",
+ e.toString());
+ return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(e.toString())
+ .build();
+ }
+ }
+
+ @DELETE
+ @Path("{id}")
+ @Consumes(MediaType.APPLICATION_JSON)
+ public Response deleteRouter(@PathParam("id") String id) {
+ checkNotNull(id);
+ OpenstackRoutingService routingService
+ = getService(OpenstackRoutingService.class);
+ routingService.removeRouter(id);
+
+ log.debug("REST API DELETE routers is called {}", id);
+ return Response.noContent().build();
+ }
+
+ @PUT
+ @Path("{id}/remove_router_interface")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response removeRouterInterface(@PathParam("id") String id, InputStream input) {
+ checkNotNull(id);
+ checkNotNull(input);
+ try {
+ ObjectMapper mapper = new ObjectMapper();
+ ObjectNode routerIfNode = (ObjectNode) mapper.readTree(input);
+
+ OpenstackRouterInterface openstackRouterInterface
+ = ROUTER_INTERFACE_CODEC.decode(routerIfNode, this);
+
+ OpenstackRoutingService routingService
+ = getService(OpenstackRoutingService.class);
+ routingService.removeRouterInterface(openstackRouterInterface);
+
+ log.debug("REST API RemoveRouterInterface is called from router {} portId: {}, subnetId: {}," +
+ "tenantId: {}", openstackRouterInterface.id(), openstackRouterInterface.portId(),
+ openstackRouterInterface.subnetId(), openstackRouterInterface.tenantId());
+
+ return Response.status(Response.Status.OK).build();
+ } catch (Exception e) {
+ log.error("RemoveRouterInterface failed because of exception {}",
+ e.toString());
+ return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(e.toString())
+ .build();
+ }
+ }
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/web/OpenstackFloatingIpWebResource.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/web/OpenstackFloatingIpWebResource.java
new file mode 100644
index 0000000..a6880fd
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/web/OpenstackFloatingIpWebResource.java
@@ -0,0 +1,133 @@
+/*
+ * 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.web;
+
+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.api.OpenstackFloatingIpService;
+import org.onosproject.rest.AbstractWebResource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import java.io.InputStream;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Handles REST API call of Neutron L3 plugin.
+ */
+@Path("floatingips")
+public class OpenstackFloatingIpWebResource extends AbstractWebResource {
+ private final Logger log = LoggerFactory.getLogger(getClass());
+
+ private static final OpenstackFloatingIpCodec FLOATING_IP_CODEC = new OpenstackFloatingIpCodec();
+
+ /**
+ * Create FloatingIP.
+ *
+ * @param input JSON data describing FloatingIP
+ * @return 200 OK
+ */
+ @POST
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response createFloatingIp(InputStream input) {
+ checkNotNull(input);
+
+ try {
+ ObjectMapper mapper = new ObjectMapper();
+ ObjectNode floatingIpNode = (ObjectNode) mapper.readTree(input);
+
+ 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();
+ }
+ }
+
+ /**
+ * Update FloatingIP.
+ *
+ * @param id FloatingIP identifier
+ * @param input JSON data describing FloatingIP
+ * @return 200 OK
+ */
+ @PUT
+ @Path("{id}")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response updateFloatingIp(@PathParam("id") String id, InputStream input) {
+ checkNotNull(id);
+ checkNotNull(input);
+
+ try {
+ ObjectMapper mapper = new ObjectMapper();
+ ObjectNode floatingIpNode = (ObjectNode) mapper.readTree(input);
+
+ 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();
+ }
+ }
+
+ /**
+ * Delete FloatingIP.
+ *
+ * @param id FloatingIP identifier
+ * @return 204 OK
+ */
+ @DELETE
+ @Path("{id}")
+ @Consumes(MediaType.APPLICATION_JSON)
+ public Response deleteFloatingIp(@PathParam("id") String id) {
+ checkNotNull(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/src/main/java/org/onosproject/openstacknetworking/web/OpenstackNetworkWebResource.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/web/OpenstackNetworkWebResource.java
new file mode 100644
index 0000000..c547b2e
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/web/OpenstackNetworkWebResource.java
@@ -0,0 +1,64 @@
+/*
+ * 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.web;
+
+import org.onosproject.rest.AbstractWebResource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import java.io.InputStream;
+
+/**
+ * Handles REST API call of Neutron ML2 plugin.
+ */
+@Path("networks")
+public class OpenstackNetworkWebResource extends AbstractWebResource {
+
+ private final Logger log = LoggerFactory.getLogger(getClass());
+
+ @POST
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response createNetwork(InputStream input) {
+ log.debug("REST API networks is called {}", input.toString());
+ return Response.status(Response.Status.OK).build();
+ }
+
+ @PUT
+ @Path("{id}")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response updateNetwork(InputStream input) {
+ log.debug("REST API networks is called {}", input.toString());
+ return Response.status(Response.Status.OK).build();
+ }
+
+ @DELETE
+ @Path("{id}")
+ @Consumes(MediaType.APPLICATION_JSON)
+ public Response deleteNetwork(InputStream input) {
+ log.debug("REST API networks is called {}", input.toString());
+ return Response.noContent().build();
+ }
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/web/OpenstackPortWebResource.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/web/OpenstackPortWebResource.java
new file mode 100644
index 0000000..7683b0c
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/web/OpenstackPortWebResource.java
@@ -0,0 +1,90 @@
+/*
+ * 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.web;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.onosproject.openstackinterface.OpenstackPort;
+import org.onosproject.openstackinterface.web.OpenstackPortCodec;
+import org.onosproject.openstacknetworking.api.OpenstackSecurityGroupService;
+import org.onosproject.rest.AbstractWebResource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import java.io.IOException;
+import java.io.InputStream;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Handles Rest API call from Neutron ML2 plugin.
+ */
+@Path("ports")
+public class OpenstackPortWebResource extends AbstractWebResource {
+ private final Logger log = LoggerFactory.getLogger(getClass());
+
+ private static final OpenstackPortCodec PORT_CODEC
+ = new OpenstackPortCodec();
+
+ @POST
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response createPorts(InputStream input) {
+ return Response.status(Response.Status.OK).build();
+ }
+
+ @Path("{portUUID}")
+ @DELETE
+ @Consumes(MediaType.APPLICATION_JSON)
+ public Response deletePorts(@PathParam("portUUID") String id) {
+ return Response.noContent().build();
+ }
+
+ @PUT
+ @Path("{id}")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response updatePorts(@PathParam("id") String id, InputStream input) {
+ checkNotNull(input);
+ checkNotNull(id);
+
+ try {
+ ObjectMapper mapper = new ObjectMapper();
+ ObjectNode portNode = (ObjectNode) mapper.readTree(input);
+ OpenstackPort osPort = PORT_CODEC.decode(portNode, this);
+
+ OpenstackSecurityGroupService sgService
+ = getService(OpenstackSecurityGroupService.class);
+ sgService.updateSecurityGroup(osPort);
+
+ return Response.status(Response.Status.OK).build();
+ } catch (IOException e) {
+ log.error("UpdatePort post process failed due to {}", e.getMessage());
+
+ return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(e.getMessage())
+ .build();
+ }
+ }
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/web/OpenstackSubnetWebResource.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/web/OpenstackSubnetWebResource.java
new file mode 100644
index 0000000..ca9f3ab
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/web/OpenstackSubnetWebResource.java
@@ -0,0 +1,64 @@
+/*
+ * 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.web;
+
+/**
+ * Handles Rest API call from Neutron ML2 plugin.
+ */
+import org.onosproject.rest.AbstractWebResource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import java.io.InputStream;
+
+@Path("subnets")
+public class OpenstackSubnetWebResource extends AbstractWebResource {
+ protected static final Logger log = LoggerFactory
+ .getLogger(OpenstackSubnetWebResource.class);
+
+ @POST
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response createSubnet(InputStream input) {
+ return Response.status(Response.Status.OK).build();
+ }
+
+
+ @PUT
+ @Path("{subnetId}")
+ @Produces(MediaType.APPLICATION_JSON)
+ @Consumes(MediaType.APPLICATION_JSON)
+ public Response updateSubnet(@PathParam("subnetId") String id,
+ final InputStream input) {
+ return Response.status(Response.Status.OK).build();
+ }
+
+ @DELETE
+ @Path("{subnetId}")
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response deleteSubnet(@PathParam("subnetId") String id) {
+ return Response.noContent().build();
+ }
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/web/package-info.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/web/package-info.java
new file mode 100644
index 0000000..9f1e043
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/web/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+/**
+ * OpenStack networking implementation.
+ */
+package org.onosproject.openstacknetworking.web;