[ONOS-7444] Optimize SONA gw doesn't use vrouter app and quagga anymore
- Done: Deriving MAC address from external peer router and simple SNAT functionality
- Todo: SNAT, Floating IP-based routing
Change-Id: Ib1a5784a7304c44b28d7b2c9891b98fd13000db1
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/ExternalPeerRouter.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/ExternalPeerRouter.java
new file mode 100644
index 0000000..d42d337
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/ExternalPeerRouter.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * 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.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
+
+/**
+ * Representation of external peer router.
+ */
+public interface ExternalPeerRouter {
+ /**
+ * Returns external peer router ip address.
+ *
+ * @return ip address.
+ */
+ IpAddress externalPeerRouterIp();
+
+ /**
+ * Returns external peer router mac address.
+ *
+ * @return mac address
+ */
+ MacAddress externalPeerRouterMac();
+
+ /**
+ * Returns external peer router vlan id.
+ *
+ * @return vlan id
+ */
+ VlanId externalPeerRouterVlanId();
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackNetworkService.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackNetworkService.java
index 058bb9a..a297b83 100644
--- a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackNetworkService.java
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackNetworkService.java
@@ -15,9 +15,14 @@
*/
package org.onosproject.openstacknetworking.api;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
import org.onosproject.event.ListenerService;
+import org.openstack4j.model.network.ExternalGateway;
import org.openstack4j.model.network.Network;
import org.openstack4j.model.network.Port;
+import org.openstack4j.model.network.Router;
import org.openstack4j.model.network.Subnet;
import java.util.Set;
@@ -89,9 +94,74 @@
Set<Port> ports();
/**
- * Returns all OpenStack ports associated with the supplied network.
+ * Returns all OpenStack ports associated with supplied network.
+ *
* @param networkId network id
* @return set of ports
*/
Set<Port> ports(String networkId);
+
+ /**
+ * Derives external router mac address with supplied external gateway.
+ *
+ * @param externalGateway external gateway information
+ * @param router router which owns externalGateway
+ */
+ void deriveExternalPeerRouterMac(ExternalGateway externalGateway, Router router);
+
+ /**
+ * Deletes external router with supplied external gateway.
+ *
+ * @param externalGateway external gateway information
+ */
+ void deleteExternalPeerRouter(ExternalGateway externalGateway);
+
+ /**
+ * Updates external router mac address with supplied ip address.
+ *
+ * @param ipAddress ip address
+ * @param macAddress mac address
+ */
+ void updateExternalPeerRouterMac(IpAddress ipAddress, MacAddress macAddress);
+
+ /**
+ * Updates external router vlan id with supplied ip address.
+ *
+ * @param ipAddress ip address
+ * @param vlanId vlan id
+ */
+ void updateExternalPeerRouterVlan(IpAddress ipAddress, VlanId vlanId);
+
+ /**
+ * Updates external router ith supplied ip address, mac address, vlan id.
+ *
+ * @param ipAddress ip address
+ * @param macAddress mac address
+ * @param vlanId vlan id
+ */
+ void updateExternalPeerRouter(IpAddress ipAddress, MacAddress macAddress, VlanId vlanId);
+
+ /**
+ * Returns external router mac with supplied external gateway.
+ *
+ * @param externalGateway external gateway information
+ * @return mac address
+ */
+ MacAddress externalPeerRouterMac(ExternalGateway externalGateway);
+
+ /**
+ * Returns external peer router with supplied ip address.
+ *
+ * @param ipAddress ip address
+ * @return external peer router
+ */
+ ExternalPeerRouter externalPeerRouter(IpAddress ipAddress);
+
+ /**
+ * Returns external peer router list.
+ *
+ * @return external peer router list
+ */
+ Set<ExternalPeerRouter> externalPeerRouters();
+
}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/ExternalPeerRouterListCommand.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/ExternalPeerRouterListCommand.java
new file mode 100644
index 0000000..ebd949c
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/ExternalPeerRouterListCommand.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * 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 com.google.common.collect.Lists;
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.openstacknetworking.api.ExternalPeerRouter;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
+
+import java.util.List;
+
+/**
+ * Lists external peer router lists.
+ */
+@Command(scope = "onos", name = "openstack-peer-routers",
+ description = "Lists external peer router lists")
+public class ExternalPeerRouterListCommand extends AbstractShellCommand {
+
+ private static final String FORMAT = "%-20s%-20s%-20s";
+
+ @Override
+ protected void execute() {
+ OpenstackNetworkService service = AbstractShellCommand.get(OpenstackNetworkService.class);
+
+ print(FORMAT, "Router IP", "Mac Address", "VLAN ID");
+ List<ExternalPeerRouter> routers = Lists.newArrayList(service.externalPeerRouters());
+
+ for (ExternalPeerRouter router: routers) {
+ print(FORMAT, router.externalPeerRouterIp(),
+ router.externalPeerRouterMac().toString(),
+ router.externalPeerRouterVlanId());
+ }
+ }
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/IpAddressCompleter.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/IpAddressCompleter.java
new file mode 100644
index 0000000..4ae1dd4
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/IpAddressCompleter.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * 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.console.Completer;
+import org.apache.karaf.shell.console.completer.StringsCompleter;
+import org.onlab.packet.IpAddress;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.openstacknetworking.api.ExternalPeerRouter;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.stream.Collectors;
+
+/**
+ * IP Address Completer.
+ */
+public class IpAddressCompleter implements Completer {
+
+ @Override
+ public int complete(String buffer, int cursor, List<String> candidates) {
+ StringsCompleter delegate = new StringsCompleter();
+ OpenstackNetworkService osNetService = AbstractShellCommand.get(OpenstackNetworkService.class);
+ Set<IpAddress> set = osNetService.externalPeerRouters().stream()
+ .map(ExternalPeerRouter::externalPeerRouterIp)
+ .collect(Collectors.toSet());
+ SortedSet<String> strings = delegate.getStrings();
+
+ Iterator<IpAddress> it = set.iterator();
+
+ while (it.hasNext()) {
+ strings.add(it.next().toString());
+ }
+
+ return delegate.complete(buffer, cursor, candidates);
+
+ }
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/MacAddressCompleter.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/MacAddressCompleter.java
new file mode 100644
index 0000000..6e21de6
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/MacAddressCompleter.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * 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.console.Completer;
+import org.apache.karaf.shell.console.completer.StringsCompleter;
+import org.onlab.packet.MacAddress;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.openstacknetworking.api.ExternalPeerRouter;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.stream.Collectors;
+
+/**
+ * Mac Address Completer.
+ */
+public class MacAddressCompleter implements Completer {
+
+ @Override
+ public int complete(String buffer, int cursor, List<String> candidates) {
+ StringsCompleter delegate = new StringsCompleter();
+ OpenstackNetworkService osNetService = AbstractShellCommand.get(OpenstackNetworkService.class);
+ Set<MacAddress> set = osNetService.externalPeerRouters().stream()
+ .map(ExternalPeerRouter::externalPeerRouterMac)
+ .collect(Collectors.toSet());
+ SortedSet<String> strings = delegate.getStrings();
+
+ Iterator<MacAddress> it = set.iterator();
+
+ while (it.hasNext()) {
+ strings.add(it.next().toString());
+ }
+
+ return delegate.complete(buffer, cursor, candidates);
+
+ }
+ }
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/UpdateExternalPeerRouterCommand.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/UpdateExternalPeerRouterCommand.java
new file mode 100644
index 0000000..15a7555
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/UpdateExternalPeerRouterCommand.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * 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 com.google.common.collect.Lists;
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.openstacknetworking.api.ExternalPeerRouter;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
+
+import java.util.List;
+
+/**
+ * Updates external peer router.
+ */
+@Command(scope = "onos", name = "openstack-update-peer-router",
+ description = "Update external peer router")
+public class UpdateExternalPeerRouterCommand extends AbstractShellCommand {
+
+ @Argument(index = 0, name = "ip address", description = "ip address",
+ required = true, multiValued = false)
+ private String ipAddress = null;
+
+ @Argument(index = 1, name = "mac address", description = "mac address",
+ required = true, multiValued = false)
+ private String macAddress = null;
+
+ @Argument(index = 2, name = "vlan id", description = "vlan id",
+ required = true, multiValued = false)
+ private String vlanId = null;
+
+ private static final String FORMAT = "%-20s%-20s%-20s";
+ private static final String NO_ELEMENT = "There's no external peer router information with given ip address";
+ private static final String NONE = "None";
+
+ @Override
+ protected void execute() {
+ OpenstackNetworkService service = AbstractShellCommand.get(OpenstackNetworkService.class);
+
+ IpAddress externalPeerIpAddress = IpAddress.valueOf(
+ IpAddress.Version.INET, Ip4Address.valueOf(ipAddress).toOctets());
+
+ if (service.externalPeerRouters().isEmpty()) {
+ print(NO_ELEMENT);
+ return;
+ } else if (service.externalPeerRouters().stream()
+ .noneMatch(router -> router.externalPeerRouterIp().toString().equals(ipAddress))) {
+ print(NO_ELEMENT);
+ return;
+ }
+ try {
+ if (vlanId.equals(NONE)) {
+ service.updateExternalPeerRouter(externalPeerIpAddress,
+ MacAddress.valueOf(macAddress),
+ VlanId.NONE);
+
+ } else {
+ service.updateExternalPeerRouter(externalPeerIpAddress,
+ MacAddress.valueOf(macAddress),
+ VlanId.vlanId(vlanId));
+ }
+ } catch (IllegalArgumentException e) {
+ log.error("Exception occurred because of {}", e.toString());
+ }
+
+
+ print(FORMAT, "Router IP", "Mac Address", "VLAN ID");
+ List<ExternalPeerRouter> routers = Lists.newArrayList(service.externalPeerRouters());
+
+ for (ExternalPeerRouter router: routers) {
+ print(FORMAT, router.externalPeerRouterIp(),
+ router.externalPeerRouterMac().toString(),
+ router.externalPeerRouterVlanId());
+ }
+ }
+}
+
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/UpdateExternalPeerRouterVlanCommand.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/UpdateExternalPeerRouterVlanCommand.java
new file mode 100644
index 0000000..eed62e7
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/UpdateExternalPeerRouterVlanCommand.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * 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 com.google.common.collect.Lists;
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.VlanId;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.openstacknetworking.api.ExternalPeerRouter;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
+
+import java.util.List;
+
+/**
+ * Updates external peer router macc address.
+ */
+@Command(scope = "onos", name = "openstack-update-peer-router-vlan",
+ description = "Updates external peer router vlan")
+public class UpdateExternalPeerRouterVlanCommand extends AbstractShellCommand {
+ @Argument(index = 0, name = "ip address", description = "ip address",
+ required = true, multiValued = false)
+ private String ipAddress = null;
+
+ @Argument(index = 1, name = "vlan id", description = "vlan id",
+ required = true, multiValued = false)
+ private String vlanId = null;
+
+ private static final String FORMAT = "%-20s%-20s%-20s";
+ private static final String NO_ELEMENT = "There's no external peer router information with given ip address";
+ private static final String NONE = "None";
+
+ @Override
+ protected void execute() {
+ OpenstackNetworkService service = AbstractShellCommand.get(OpenstackNetworkService.class);
+
+ IpAddress externalPeerIpAddress = IpAddress.valueOf(
+ IpAddress.Version.INET, Ip4Address.valueOf(ipAddress).toOctets());
+
+ if (service.externalPeerRouters().isEmpty()) {
+ print(NO_ELEMENT);
+ return;
+ } else if (service.externalPeerRouters().stream()
+ .noneMatch(router -> router.externalPeerRouterIp().toString().equals(ipAddress))) {
+ print(NO_ELEMENT);
+ return;
+ }
+
+ try {
+ if (vlanId.equals(NONE)) {
+ service.updateExternalPeerRouterVlan(externalPeerIpAddress, VlanId.NONE);
+ } else {
+ service.updateExternalPeerRouterVlan(externalPeerIpAddress, VlanId.vlanId(vlanId));
+ }
+ } catch (IllegalArgumentException e) {
+ log.error("Exception occurred because of {}", e.toString());
+ }
+
+ print(FORMAT, "Router IP", "Mac Address", "VLAN ID");
+ List<ExternalPeerRouter> routers = Lists.newArrayList(service.externalPeerRouters());
+
+ for (ExternalPeerRouter router: routers) {
+ print(FORMAT, router.externalPeerRouterIp(),
+ router.externalPeerRouterMac().toString(),
+ router.externalPeerRouterVlanId());
+ }
+ }
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/VlanIdCompleter.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/VlanIdCompleter.java
new file mode 100644
index 0000000..0768859
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/VlanIdCompleter.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * 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.console.Completer;
+import org.apache.karaf.shell.console.completer.StringsCompleter;
+import org.onlab.packet.VlanId;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.openstacknetworking.api.ExternalPeerRouter;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.stream.Collectors;
+
+/**
+ * Vlan Id Completer.
+ */
+public class VlanIdCompleter implements Completer {
+
+ @Override
+ public int complete(String buffer, int cursor, List<String> candidates) {
+ StringsCompleter delegate = new StringsCompleter();
+ OpenstackNetworkService osNetService = AbstractShellCommand.get(OpenstackNetworkService.class);
+ Set<VlanId> set = osNetService.externalPeerRouters().stream()
+ .map(ExternalPeerRouter::externalPeerRouterVlanId)
+ .collect(Collectors.toSet());
+ SortedSet<String> strings = delegate.getStrings();
+
+ Iterator<VlanId> it = set.iterator();
+
+ while (it.hasNext()) {
+ strings.add(it.next().toString());
+ }
+
+ return delegate.complete(buffer, cursor, candidates);
+
+ }
+}
\ No newline at end of file
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/DefaultExternalPeerRouter.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/DefaultExternalPeerRouter.java
new file mode 100644
index 0000000..fb18502
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/DefaultExternalPeerRouter.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * 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.MoreObjects;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
+import org.onosproject.openstacknetworking.api.ExternalPeerRouter;
+
+import java.util.Objects;
+
+/**
+ * Implementation of external peer router.
+ */
+public class DefaultExternalPeerRouter implements ExternalPeerRouter {
+
+ private IpAddress externalPeerRouterIp;
+ private MacAddress externalPeerRouterMac;
+ private VlanId externalPeerRouterVlanId;
+
+ public DefaultExternalPeerRouter(IpAddress externalPeerRouterIp,
+ MacAddress externalPeerRouterMac,
+ VlanId externalPeerRouterVlanId) {
+ this.externalPeerRouterIp = externalPeerRouterIp;
+ this.externalPeerRouterMac = externalPeerRouterMac;
+ this.externalPeerRouterVlanId = externalPeerRouterVlanId;
+ }
+
+ @Override
+ public IpAddress externalPeerRouterIp() {
+ return this.externalPeerRouterIp;
+ }
+ @Override
+ public MacAddress externalPeerRouterMac() {
+ return this.externalPeerRouterMac;
+ }
+ @Override
+ public VlanId externalPeerRouterVlanId() {
+ return this.externalPeerRouterVlanId;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+
+ if (obj instanceof DefaultExternalPeerRouter) {
+ DefaultExternalPeerRouter that = (DefaultExternalPeerRouter) obj;
+ return Objects.equals(externalPeerRouterIp, that.externalPeerRouterIp) &&
+ Objects.equals(externalPeerRouterMac, that.externalPeerRouterMac) &&
+ Objects.equals(externalPeerRouterVlanId, that.externalPeerRouterVlanId);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(externalPeerRouterIp,
+ externalPeerRouterMac,
+ externalPeerRouterVlanId);
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(getClass())
+ .add("externalPeerRouterIp", externalPeerRouterIp)
+ .add("externalPeerRouterMac", externalPeerRouterMac)
+ .add("externalPeerRouterVlanId", externalPeerRouterVlanId)
+ .toString();
+ }
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/DistributedOpenstackNetworkStore.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/DistributedOpenstackNetworkStore.java
index 4442c9a..19650e3 100644
--- a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/DistributedOpenstackNetworkStore.java
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/DistributedOpenstackNetworkStore.java
@@ -285,7 +285,7 @@
public void event(MapEvent<String, Network> event) {
switch (event.type()) {
case UPDATE:
- log.debug("OpenStack network updated {}", event.newValue());
+ log.debug("OpenStack network updated");
eventExecutor.execute(() -> {
notifyDelegate(new OpenstackNetworkEvent(
OPENSTACK_NETWORK_UPDATED,
@@ -293,7 +293,7 @@
});
break;
case INSERT:
- log.debug("OpenStack network created {}", event.newValue());
+ log.debug("OpenStack network created");
eventExecutor.execute(() -> {
notifyDelegate(new OpenstackNetworkEvent(
OPENSTACK_NETWORK_CREATED,
@@ -301,7 +301,7 @@
});
break;
case REMOVE:
- log.debug("OpenStack network removed {}", event.oldValue());
+ log.debug("OpenStack network removed");
eventExecutor.execute(() -> {
notifyDelegate(new OpenstackNetworkEvent(
OPENSTACK_NETWORK_REMOVED,
@@ -321,7 +321,7 @@
public void event(MapEvent<String, Subnet> event) {
switch (event.type()) {
case UPDATE:
- log.debug("OpenStack subnet updated {}", event.newValue());
+ log.debug("OpenStack subnet updated");
eventExecutor.execute(() -> {
notifyDelegate(new OpenstackNetworkEvent(
OPENSTACK_SUBNET_UPDATED,
@@ -330,7 +330,7 @@
});
break;
case INSERT:
- log.debug("OpenStack subnet created {}", event.newValue());
+ log.debug("OpenStack subnet created");
eventExecutor.execute(() -> {
notifyDelegate(new OpenstackNetworkEvent(
OPENSTACK_SUBNET_CREATED,
@@ -339,7 +339,7 @@
});
break;
case REMOVE:
- log.debug("OpenStack subnet removed {}", event.oldValue());
+ log.debug("OpenStack subnet removed");
eventExecutor.execute(() -> {
notifyDelegate(new OpenstackNetworkEvent(
OPENSTACK_SUBNET_REMOVED,
@@ -360,7 +360,7 @@
public void event(MapEvent<String, Port> event) {
switch (event.type()) {
case UPDATE:
- log.debug("OpenStack port updated {}", event.newValue());
+ log.debug("OpenStack port updated");
eventExecutor.execute(() -> {
Port oldPort = event.oldValue().value();
Port newPort = event.newValue().value();
@@ -371,7 +371,7 @@
});
break;
case INSERT:
- log.debug("OpenStack port created {}", event.newValue());
+ log.debug("OpenStack port created");
eventExecutor.execute(() -> {
notifyDelegate(new OpenstackNetworkEvent(
OPENSTACK_PORT_CREATED,
@@ -380,7 +380,7 @@
});
break;
case REMOVE:
- log.debug("OpenStack port removed {}", event.oldValue());
+ log.debug("OpenStack port removed");
eventExecutor.execute(() -> {
notifyDelegate(new OpenstackNetworkEvent(
OPENSTACK_PORT_REMOVED,
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/DistributedOpenstackRouterStore.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/DistributedOpenstackRouterStore.java
index 0397e8a..62fc7ac 100644
--- a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/DistributedOpenstackRouterStore.java
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/DistributedOpenstackRouterStore.java
@@ -25,6 +25,7 @@
import org.onlab.util.KryoNamespace;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
import org.onosproject.openstacknetworking.api.OpenstackRouterEvent;
import org.onosproject.openstacknetworking.api.OpenstackRouterStore;
import org.onosproject.openstacknetworking.api.OpenstackRouterStoreDelegate;
@@ -94,6 +95,9 @@
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected StorageService storageService;
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected OpenstackNetworkService osNetworkService;
+
private final ExecutorService eventExecutor = newSingleThreadExecutor(
groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
private final MapEventListener<String, Router> routerMapListener = new OpenstackRouterMapListener();
@@ -271,7 +275,7 @@
public void event(MapEvent<String, Router> event) {
switch (event.type()) {
case UPDATE:
- log.debug("OpenStack router updated {}", event.newValue());
+ log.debug("OpenStack router updated");
eventExecutor.execute(() -> {
notifyDelegate(new OpenstackRouterEvent(
OPENSTACK_ROUTER_UPDATED,
@@ -280,7 +284,7 @@
});
break;
case INSERT:
- log.debug("OpenStack router created {}", event.newValue());
+ log.debug("OpenStack router created");
eventExecutor.execute(() -> {
notifyDelegate(new OpenstackRouterEvent(
OPENSTACK_ROUTER_CREATED,
@@ -288,7 +292,7 @@
});
break;
case REMOVE:
- log.debug("OpenStack router removed {}", event.oldValue());
+ log.debug("OpenStack router removed");
eventExecutor.execute(() -> {
notifyDelegate(new OpenstackRouterEvent(
OPENSTACK_ROUTER_REMOVED,
@@ -324,7 +328,7 @@
public void event(MapEvent<String, RouterInterface> event) {
switch (event.type()) {
case UPDATE:
- log.debug("OpenStack router interface updated {}", event.newValue());
+ log.debug("OpenStack router interface updated");
eventExecutor.execute(() -> {
notifyDelegate(new OpenstackRouterEvent(
OPENSTACK_ROUTER_INTERFACE_UPDATED,
@@ -333,7 +337,7 @@
});
break;
case INSERT:
- log.debug("OpenStack router interface created {}", event.newValue());
+ log.debug("OpenStack router interface created");
eventExecutor.execute(() -> {
notifyDelegate(new OpenstackRouterEvent(
OPENSTACK_ROUTER_INTERFACE_ADDED,
@@ -342,7 +346,7 @@
});
break;
case REMOVE:
- log.debug("OpenStack router interface removed {}", event.oldValue());
+ log.debug("OpenStack router interface removed");
eventExecutor.execute(() -> {
notifyDelegate(new OpenstackRouterEvent(
OPENSTACK_ROUTER_INTERFACE_REMOVED,
@@ -363,7 +367,7 @@
public void event(MapEvent<String, NetFloatingIP> event) {
switch (event.type()) {
case UPDATE:
- log.debug("OpenStack floating IP updated {}", event.newValue());
+ log.debug("OpenStack floating IP updated");
eventExecutor.execute(() -> {
Router osRouter = Strings.isNullOrEmpty(
event.newValue().value().getRouterId()) ?
@@ -377,7 +381,7 @@
});
break;
case INSERT:
- log.debug("OpenStack floating IP created {}", event.newValue());
+ log.debug("OpenStack floating IP created");
eventExecutor.execute(() -> {
Router osRouter = Strings.isNullOrEmpty(
event.newValue().value().getRouterId()) ?
@@ -390,7 +394,7 @@
});
break;
case REMOVE:
- log.debug("OpenStack floating IP removed {}", event.oldValue());
+ log.debug("OpenStack floating IP removed");
eventExecutor.execute(() -> {
Router osRouter = Strings.isNullOrEmpty(
event.oldValue().value().getRouterId()) ?
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackNetworkManager.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackNetworkManager.java
index 6f10112..8d16bce 100644
--- a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackNetworkManager.java
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackNetworkManager.java
@@ -23,20 +23,45 @@
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.ARP;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
+import org.onlab.util.KryoNamespace;
+import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
import org.onosproject.event.ListenerRegistry;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.packet.DefaultOutboundPacket;
+import org.onosproject.net.packet.PacketService;
import org.onosproject.openstacknetworking.api.Constants;
+import org.onosproject.openstacknetworking.api.ExternalPeerRouter;
import org.onosproject.openstacknetworking.api.OpenstackNetworkAdminService;
import org.onosproject.openstacknetworking.api.OpenstackNetworkEvent;
import org.onosproject.openstacknetworking.api.OpenstackNetworkListener;
import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
import org.onosproject.openstacknetworking.api.OpenstackNetworkStore;
import org.onosproject.openstacknetworking.api.OpenstackNetworkStoreDelegate;
+import org.onosproject.openstacknode.api.OpenstackNode;
+import org.onosproject.openstacknode.api.OpenstackNodeService;
+import org.onosproject.store.serializers.KryoNamespaces;
+import org.onosproject.store.service.ConsistentMap;
+import org.onosproject.store.service.Serializer;
+import org.onosproject.store.service.StorageService;
+import org.onosproject.store.service.Versioned;
+import org.openstack4j.model.network.ExternalGateway;
+import org.openstack4j.model.network.IP;
import org.openstack4j.model.network.Network;
import org.openstack4j.model.network.Port;
+import org.openstack4j.model.network.Router;
import org.openstack4j.model.network.Subnet;
import org.slf4j.Logger;
+import java.nio.ByteBuffer;
+import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
@@ -51,6 +76,7 @@
* Provides implementation of administering and interfacing OpenStack network,
* subnet, and port.
*/
+
@Service
@Component(immediate = true)
public class OpenstackNetworkManager
@@ -77,21 +103,57 @@
private static final String ERR_NULL_PORT_ID = "OpenStack port ID cannot be null";
private static final String ERR_NULL_PORT_NET_ID = "OpenStack port network ID cannot be null";
+ private static final String ERR_NOT_FOUND = " does not exist";
private static final String ERR_IN_USE = " still in use";
+ private static final String ERR_DUPLICATE = " already exists";
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected CoreService coreService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected PacketService packetService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected DeviceService deviceService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected OpenstackNetworkStore osNetworkStore;
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected StorageService storageService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected OpenstackNodeService osNodeService;
+
+
private final OpenstackNetworkStoreDelegate delegate = new InternalNetworkStoreDelegate();
+ private ConsistentMap<String, ExternalPeerRouter> externalPeerRouterMap;
+
+ private static final KryoNamespace SERIALIZER_EXTERNAL_PEER_ROUTER_MAP = KryoNamespace.newBuilder()
+ .register(KryoNamespaces.API)
+ .register(ExternalPeerRouter.class)
+ .register(DefaultExternalPeerRouter.class)
+ .register(MacAddress.class)
+ .register(IpAddress.class)
+ .register(VlanId.class)
+ .build();
+
+ private ApplicationId appId;
+
+
@Activate
protected void activate() {
- coreService.registerApplication(Constants.OPENSTACK_NETWORKING_APP_ID);
+ appId = coreService.registerApplication(Constants.OPENSTACK_NETWORKING_APP_ID);
+
osNetworkStore.setDelegate(delegate);
log.info("Started");
+
+ externalPeerRouterMap = storageService.<String, ExternalPeerRouter>consistentMapBuilder()
+ .withSerializer(Serializer.using(SERIALIZER_EXTERNAL_PEER_ROUTER_MAP))
+ .withName("external-routermap")
+ .withApplicationId(appId)
+ .build();
}
@Deactivate
@@ -256,12 +318,12 @@
.stream()
.filter(p -> p.getId().contains(portName.substring(3)))
.findFirst();
- return osPort.isPresent() ? osPort.get() : null;
+ return osPort.orElse(null);
}
@Override
public Set<Port> ports() {
- return osNetworkStore.ports();
+ return ImmutableSet.copyOf(osNetworkStore.ports());
}
@Override
@@ -272,6 +334,175 @@
return ImmutableSet.copyOf(osPorts);
}
+ @Override
+ public ExternalPeerRouter externalPeerRouter(IpAddress ipAddress) {
+ if (externalPeerRouterMap.containsKey(ipAddress.toString())) {
+ return externalPeerRouterMap.get(ipAddress.toString()).value();
+ }
+ return null;
+ }
+
+ @Override
+ public void deriveExternalPeerRouterMac(ExternalGateway externalGateway, Router router) {
+ log.info("deriveExternalPeerRouterMac called");
+
+ IpAddress sourceIp = getExternalGatewaySourceIp(externalGateway, router);
+ IpAddress targetIp = getExternalPeerRouterIp(externalGateway);
+
+ if (sourceIp == null || targetIp == null) {
+ log.warn("Failed to derive external router mac address because source IP {} or target IP {} is null",
+ sourceIp, targetIp);
+ return;
+ }
+
+ if (externalPeerRouterMap.containsKey(targetIp.toString()) &&
+ !externalPeerRouterMap.get(
+ targetIp.toString()).value().externalPeerRouterMac().equals(MacAddress.NONE)) {
+ return;
+ }
+
+ MacAddress sourceMac = Constants.DEFAULT_GATEWAY_MAC;
+ Ethernet ethRequest = ARP.buildArpRequest(sourceMac.toBytes(),
+ sourceIp.toOctets(),
+ targetIp.toOctets(),
+ VlanId.NO_VID);
+
+ if (osNodeService.completeNodes(OpenstackNode.NodeType.GATEWAY).isEmpty()) {
+ log.warn("There's no complete gateway");
+ return;
+ }
+ OpenstackNode gatewayNode = osNodeService.completeNodes(OpenstackNode.NodeType.GATEWAY)
+ .stream()
+ .findFirst()
+ .orElse(null);
+
+ if (gatewayNode == null) {
+ return;
+ }
+
+ String upLinkPort = gatewayNode.uplinkPort();
+
+ org.onosproject.net.Port port = deviceService.getPorts(gatewayNode.intgBridge()).stream()
+ .filter(p -> Objects.equals(p.annotations().value(PORT_NAME), upLinkPort))
+ .findAny().orElse(null);
+
+ if (port == null) {
+ log.warn("There's no uplink port for gateway node {}", gatewayNode.toString());
+ return;
+ }
+
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+ .setOutput(port.number())
+ .build();
+
+ packetService.emit(new DefaultOutboundPacket(
+ gatewayNode.intgBridge(),
+ treatment,
+ ByteBuffer.wrap(ethRequest.serialize())));
+
+ externalPeerRouterMap.put(
+ targetIp.toString(), new DefaultExternalPeerRouter(targetIp, MacAddress.NONE, VlanId.NONE));
+
+ log.info("Initializes external peer router map with peer router IP {}", targetIp.toString());
+ }
+
+ @Override
+ public void deleteExternalPeerRouter(ExternalGateway externalGateway) {
+ IpAddress targetIp = getExternalPeerRouterIp(externalGateway);
+ if (targetIp == null) {
+ return;
+ }
+
+ if (externalPeerRouterMap.containsKey(targetIp.toString())) {
+ externalPeerRouterMap.remove(targetIp.toString());
+ }
+ }
+
+ private IpAddress getExternalGatewaySourceIp(ExternalGateway externalGateway, Router router) {
+ Port exGatewayPort = ports(externalGateway.getNetworkId())
+ .stream()
+ .filter(port -> Objects.equals(port.getDeviceId(), router.getId()))
+ .findAny().orElse(null);
+ if (exGatewayPort == null) {
+ log.warn("no external gateway port for router({})", router.getName());
+ return null;
+ }
+
+ IP ipAddress = exGatewayPort.getFixedIps().stream().findFirst().orElse(null);
+
+ return ipAddress == null ? null : IpAddress.valueOf(ipAddress.getIpAddress());
+ }
+
+ private IpAddress getExternalPeerRouterIp(ExternalGateway externalGateway) {
+ Optional<Subnet> externalSubnet = subnets(externalGateway.getNetworkId())
+ .stream()
+ .findFirst();
+
+ if (externalSubnet.isPresent()) {
+ return IpAddress.valueOf(externalSubnet.get().getGateway());
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public void updateExternalPeerRouterMac(IpAddress ipAddress, MacAddress macAddress) {
+ try {
+ externalPeerRouterMap.computeIfPresent(ipAddress.toString(), (id, existing) ->
+ new DefaultExternalPeerRouter(ipAddress, macAddress, existing.externalPeerRouterVlanId()));
+ } catch (Exception e) {
+ log.error("Exception occurred because of {}", e.toString());
+ }
+
+ log.info("Updated external peer router map {}",
+ externalPeerRouterMap.get(ipAddress.toString()).value().toString());
+ }
+
+
+ @Override
+ public void updateExternalPeerRouter(IpAddress ipAddress, MacAddress macAddress, VlanId vlanId) {
+ try {
+ externalPeerRouterMap.computeIfPresent(ipAddress.toString(), (id, existing) ->
+ new DefaultExternalPeerRouter(ipAddress, macAddress, vlanId));
+ } catch (Exception e) {
+ log.error("Exception occurred because of {}", e.toString());
+ }
+ }
+
+ @Override
+ public MacAddress externalPeerRouterMac(ExternalGateway externalGateway) {
+ IpAddress ipAddress = getExternalPeerRouterIp(externalGateway);
+
+ if (ipAddress == null) {
+ return null;
+ }
+ if (externalPeerRouterMap.containsKey(ipAddress.toString())) {
+ return externalPeerRouterMap.get(ipAddress.toString()).value().externalPeerRouterMac();
+ } else {
+ throw new NoSuchElementException();
+ }
+ }
+
+ @Override
+ public void updateExternalPeerRouterVlan(IpAddress ipAddress, VlanId vlanId) {
+
+ try {
+ externalPeerRouterMap.computeIfPresent(ipAddress.toString(), (id, existing) -> {
+ return new DefaultExternalPeerRouter(ipAddress, existing.externalPeerRouterMac(), vlanId);
+ });
+ } catch (Exception e) {
+ log.error("Exception occurred because of {}", e.toString());
+ }
+ }
+
+ @Override
+ public Set<ExternalPeerRouter> externalPeerRouters() {
+ Set<ExternalPeerRouter> externalPeerRouters = externalPeerRouterMap.values().stream()
+ .map(Versioned::value)
+ .collect(Collectors.toSet());
+ return ImmutableSet.copyOf(externalPeerRouters);
+ }
+
private boolean isNetworkInUse(String netId) {
return !subnets(netId).isEmpty() && !ports(netId).isEmpty();
}
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
index c527f9e..98fdbd9 100644
--- a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingArpHandler.java
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingArpHandler.java
@@ -26,6 +26,7 @@
import org.onlab.packet.IpAddress;
import org.onlab.packet.MacAddress;
import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
import org.onosproject.net.flow.DefaultTrafficTreatment;
import org.onosproject.net.flow.TrafficTreatment;
import org.onosproject.net.packet.DefaultOutboundPacket;
@@ -90,36 +91,50 @@
private void processArpPacket(PacketContext context, Ethernet ethernet) {
ARP arp = (ARP) ethernet.getPayload();
- if (arp.getOpCode() != ARP.OP_REQUEST) {
- return;
+ if (arp.getOpCode() == ARP.OP_REQUEST) {
+ if (log.isTraceEnabled()) {
+ log.trace("ARP request received from {} for {}",
+ Ip4Address.valueOf(arp.getSenderProtocolAddress()).toString(),
+ Ip4Address.valueOf(arp.getTargetProtocolAddress()).toString());
+ }
+
+ IpAddress targetIp = Ip4Address.valueOf(arp.getTargetProtocolAddress());
+ if (!isServiceIp(targetIp.getIp4Address())) {
+ log.trace("Unknown target ARP request for {}, ignore it", targetIp);
+ 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())));
+
+ context.block();
+ } else if (arp.getOpCode() == ARP.OP_REPLY) {
+ PortNumber receivedPortNum = context.inPacket().receivedFrom().port();
+ log.debug("ARP reply ip: {}, mac: {}",
+ Ip4Address.valueOf(arp.getSenderProtocolAddress()),
+ MacAddress.valueOf(arp.getSenderHardwareAddress()));
+ try {
+ if (receivedPortNum.equals(
+ osNodeService.node(context.inPacket().receivedFrom().deviceId()).uplinkPortNum())) {
+ osNetworkService.updateExternalPeerRouterMac(
+ Ip4Address.valueOf(arp.getSenderProtocolAddress()),
+ MacAddress.valueOf(arp.getSenderHardwareAddress()));
+ }
+ } catch (Exception e) {
+ log.error("Exception occurred because of {}", e.toString());
+ }
}
- if (log.isTraceEnabled()) {
- log.trace("ARP request received from {} for {}",
- Ip4Address.valueOf(arp.getSenderProtocolAddress()).toString(),
- Ip4Address.valueOf(arp.getTargetProtocolAddress()).toString());
- }
-
- IpAddress targetIp = Ip4Address.valueOf(arp.getTargetProtocolAddress());
- if (!isServiceIp(targetIp.getIp4Address())) {
- log.trace("Unknown target ARP request for {}, ignore it", targetIp);
- 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())));
-
- context.block();
}
private class InternalPacketProcessor implements PacketProcessor {
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingHandler.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingHandler.java
index dd2e887..0a915b7 100644
--- a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingHandler.java
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingHandler.java
@@ -38,7 +38,6 @@
import org.onosproject.cluster.NodeId;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
-import org.onosproject.core.GroupId;
import org.onosproject.mastership.MastershipService;
import org.onosproject.net.DeviceId;
import org.onosproject.net.PortNumber;
@@ -207,7 +206,6 @@
private void routerUpdated(Router osRouter) {
ExternalGateway exGateway = osRouter.getExternalGatewayInfo();
-
osRouterService.routerInterfaces(osRouter.getId()).forEach(iface -> {
Network network = osNetworkService.network(osNetworkService.subnet(iface.getSubnetId())
.getNetworkId());
@@ -215,10 +213,12 @@
});
if (exGateway == null) {
+ osNetworkService.deleteExternalPeerRouter(exGateway);
osRouterService.routerInterfaces(osRouter.getId()).forEach(iface -> {
setSourceNat(osRouter, iface, false);
});
} else {
+ osNetworkService.deriveExternalPeerRouterMac(exGateway, osRouter);
osRouterService.routerInterfaces(osRouter.getId()).forEach(iface -> {
setSourceNat(osRouter, iface, exGateway.isEnableSnat());
});
@@ -384,6 +384,12 @@
}
private void setGatewayIcmp(Subnet osSubnet, boolean install) {
+ OpenstackNode sourceNatGateway = osNodeService.completeNodes(GATEWAY).stream().findFirst().orElse(null);
+
+ if (sourceNatGateway == null) {
+ return;
+ }
+
if (Strings.isNullOrEmpty(osSubnet.getGateway())) {
// do nothing if no gateway is set
return;
@@ -397,7 +403,7 @@
.filter(cNode -> cNode.dataIp() != null)
.forEach(cNode -> setRulesToGatewayWithDstIp(
cNode,
- cNode.gatewayGroupId(NetworkMode.VXLAN),
+ sourceNatGateway,
network.getProviderSegID(),
IpAddress.valueOf(osSubnet.getGateway()),
NetworkMode.VXLAN,
@@ -408,7 +414,7 @@
.filter(cNode -> cNode.vlanPortNum() != null)
.forEach(cNode -> setRulesToGatewayWithDstIp(
cNode,
- cNode.gatewayGroupId(NetworkMode.VLAN),
+ sourceNatGateway,
network.getProviderSegID(),
IpAddress.valueOf(osSubnet.getGateway()),
NetworkMode.VLAN,
@@ -620,7 +626,11 @@
private void setRulesToGateway(OpenstackNode osNode, String segmentId, IpPrefix srcSubnet,
NetworkType networkType, boolean install) {
TrafficTreatment treatment;
- GroupId groupId;
+ OpenstackNode sourceNatGateway = osNodeService.completeNodes(GATEWAY).stream().findFirst().orElse(null);
+
+ if (sourceNatGateway == null) {
+ return;
+ }
TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
.matchEthType(Ethernet.TYPE_IPV4)
@@ -630,11 +640,9 @@
switch (networkType) {
case VXLAN:
sBuilder.matchTunnelId(Long.parseLong(segmentId));
- groupId = osNode.gatewayGroupId(NetworkMode.VXLAN);
break;
case VLAN:
sBuilder.matchVlanId(VlanId.vlanId(segmentId));
- groupId = osNode.gatewayGroupId(NetworkMode.VLAN);
break;
default:
final String error = String.format(
@@ -644,7 +652,12 @@
}
treatment = DefaultTrafficTreatment.builder()
- .group(groupId)
+ .extension(buildExtension(
+ deviceService,
+ osNode.intgBridge(),
+ sourceNatGateway.dataIp().getIp4Address()),
+ osNode.intgBridge())
+ .setOutput(osNode.tunnelPortNum())
.build();
osFlowRuleService.setRule(
@@ -685,7 +698,7 @@
install);
}
- private void setRulesToGatewayWithDstIp(OpenstackNode osNode, GroupId groupId,
+ private void setRulesToGatewayWithDstIp(OpenstackNode osNode, OpenstackNode sourceNatGateway,
String segmentId, IpAddress dstIp,
NetworkMode networkMode, boolean install) {
TrafficSelector selector;
@@ -704,7 +717,12 @@
}
TrafficTreatment treatment = DefaultTrafficTreatment.builder()
- .group(groupId)
+ .extension(buildExtension(
+ deviceService,
+ osNode.intgBridge(),
+ sourceNatGateway.dataIp().getIp4Address()),
+ osNode.intgBridge())
+ .setOutput(osNode.tunnelPortNum())
.build();
osFlowRuleService.setRule(
@@ -929,7 +947,9 @@
event.routerIface()));
break;
case OPENSTACK_ROUTER_GATEWAY_ADDED:
+ log.debug("Router external gateway {} added", event.externalGateway().getNetworkId());
case OPENSTACK_ROUTER_GATEWAY_REMOVED:
+ log.debug("Router external gateway {} removed", event.externalGateway().getNetworkId());
case OPENSTACK_FLOATING_IP_CREATED:
case OPENSTACK_FLOATING_IP_UPDATED:
case OPENSTACK_FLOATING_IP_REMOVED:
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingSnatHandler.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingSnatHandler.java
index a07cdef..7beb11d 100644
--- a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingSnatHandler.java
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingSnatHandler.java
@@ -24,6 +24,7 @@
import org.onlab.packet.IPv4;
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;
@@ -183,19 +184,25 @@
IpAddress srcIp = IpAddress.valueOf(iPacket.getSourceAddress());
Subnet srcSubnet = getSourceSubnet(srcInstPort, srcIp);
IpAddress externalGatewayIp = getExternalIp(srcSubnet);
+
if (externalGatewayIp == null) {
return;
}
+ MacAddress externalPeerRouterMac = getExternalPeerRouterMac(srcSubnet);
+ if (externalPeerRouterMac == null) {
+ return;
+ }
+
populateSnatFlowRules(context.inPacket(),
srcInstPort,
TpPort.tpPort(patPort),
- externalGatewayIp);
+ externalGatewayIp, externalPeerRouterMac);
packetOut(eth.duplicate(),
packetIn.receivedFrom().deviceId(),
patPort,
- externalGatewayIp);
+ externalGatewayIp, externalPeerRouterMac);
}
private Subnet getSourceSubnet(InstancePort instance, IpAddress srcIp) {
@@ -209,6 +216,32 @@
return osNetworkService.subnet(fixedIp.getSubnetId());
}
+ private MacAddress getExternalPeerRouterMac(Subnet srcSubnet) {
+ RouterInterface osRouterIface = osRouterService.routerInterfaces().stream()
+ .filter(i -> Objects.equals(i.getSubnetId(), srcSubnet.getId()))
+ .findAny().orElse(null);
+ if (osRouterIface == null) {
+ // this subnet is not connected to the router
+ log.trace(ERR_PACKETIN + "source subnet(ID:{}, CIDR:{}) has no router",
+ srcSubnet.getId(), srcSubnet.getCidr());
+ return null;
+ }
+
+ Router osRouter = osRouterService.router(osRouterIface.getId());
+ if (osRouter == null) {
+ return null;
+ }
+ if (osRouter.getExternalGatewayInfo() == null) {
+ // this router does not have external connectivity
+ log.trace(ERR_PACKETIN + "router({}) has no external gateway",
+ osRouter.getName());
+ return null;
+ }
+
+ ExternalGateway exGatewayInfo = osRouter.getExternalGatewayInfo();
+
+ return osNetworkService.externalPeerRouterMac(exGatewayInfo);
+ }
private IpAddress getExternalIp(Subnet srcSubnet) {
RouterInterface osRouterIface = osRouterService.routerInterfaces().stream()
.filter(i -> Objects.equals(i.getSubnetId(), srcSubnet.getId()))
@@ -251,7 +284,7 @@
}
private void populateSnatFlowRules(InboundPacket packetIn, InstancePort srcInstPort,
- TpPort patPort, IpAddress externalIp) {
+ TpPort patPort, IpAddress externalIp, MacAddress externalPeerRouterMac) {
Network osNet = osNetworkService.network(srcInstPort.networkId());
if (osNet == null) {
final String error = String.format(ERR_PACKETIN + "network %s not found",
@@ -269,13 +302,15 @@
setUpstreamRules(osNet.getProviderSegID(),
osNet.getNetworkType(),
externalIp,
+ externalPeerRouterMac,
patPort,
packetIn);
}
private void setDownstreamRules(InstancePort srcInstPort, String segmentId,
NetworkType networkType,
- IpAddress externalIp, TpPort patPort,
+ IpAddress externalIp,
+ TpPort patPort,
InboundPacket packetIn) {
IPv4 iPacket = (IPv4) packetIn.parsed().getPayload();
IpAddress internalIp = IpAddress.valueOf(iPacket.getSourceAddress());
@@ -357,7 +392,8 @@
}
private void setUpstreamRules(String segmentId, NetworkType networkType,
- IpAddress externalIp, TpPort patPort,
+ IpAddress externalIp, MacAddress externalPeerRouterMac,
+ TpPort patPort,
InboundPacket packetIn) {
IPv4 iPacket = (IPv4) packetIn.parsed().getPayload();
@@ -389,14 +425,14 @@
sBuilder.matchTcpSrc(TpPort.tpPort(tcpPacket.getSourcePort()))
.matchTcpDst(TpPort.tpPort(tcpPacket.getDestinationPort()));
tBuilder.setTcpSrc(patPort)
- .setEthDst(DEFAULT_EXTERNAL_ROUTER_MAC);
+ .setEthDst(externalPeerRouterMac);
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);
+ .setEthDst(externalPeerRouterMac);
break;
default:
log.debug("Unsupported IPv4 protocol {}");
@@ -407,7 +443,7 @@
osNodeService.completeNodes(GATEWAY).forEach(gNode -> {
TrafficTreatment.Builder tmpBuilder =
DefaultTrafficTreatment.builder(tBuilder.build());
- tmpBuilder.setOutput(gNode.patchPortNum());
+ tmpBuilder.setOutput(gNode.uplinkPortNum());
osFlowRuleService.setRule(
appId,
@@ -421,7 +457,7 @@
}
private void packetOut(Ethernet ethPacketIn, DeviceId srcDevice, int patPort,
- IpAddress externalIp) {
+ IpAddress externalIp, MacAddress externalPeerRouterMac) {
IPv4 iPacket = (IPv4) ethPacketIn.getPayload();
switch (iPacket.getProtocol()) {
case IPv4.PROTOCOL_TCP:
@@ -446,7 +482,7 @@
iPacket.setSourceAddress(externalIp.toString());
iPacket.resetChecksum();
iPacket.setParent(ethPacketIn);
- ethPacketIn.setDestinationMACAddress(DEFAULT_EXTERNAL_ROUTER_MAC);
+ ethPacketIn.setDestinationMACAddress(externalPeerRouterMac);
ethPacketIn.setPayload(iPacket);
ethPacketIn.resetChecksum();
@@ -458,7 +494,7 @@
}
TrafficTreatment treatment = DefaultTrafficTreatment.builder()
- .setOutput(srcNode.patchPortNum()).build();
+ .setOutput(srcNode.uplinkPortNum()).build();
packetService.emit(new DefaultOutboundPacket(
srcDevice,
treatment,
diff --git a/apps/openstacknetworking/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/apps/openstacknetworking/src/main/resources/OSGI-INF/blueprint/shell-config.xml
index 5b7cde6..179c3ff 100644
--- a/apps/openstacknetworking/src/main/resources/OSGI-INF/blueprint/shell-config.xml
+++ b/apps/openstacknetworking/src/main/resources/OSGI-INF/blueprint/shell-config.xml
@@ -42,5 +42,23 @@
<command>
<action class="org.onosproject.openstacknetworking.cli.OpenstackSyncRulesCommand"/>
</command>
+ <command>
+ <action class="org.onosproject.openstacknetworking.cli.ExternalPeerRouterListCommand"/>
+ </command>
+ <command>
+ <action class="org.onosproject.openstacknetworking.cli.UpdateExternalPeerRouterCommand"/>
+ <completers>
+ <ref component-id="ipAddressCompleter"/>
+ <ref component-id="macAddressCompleter"/>
+ <ref component-id="vlanIdCompleter"/>
+ </completers>
+ </command>
+ <command>
+ <action class="org.onosproject.openstacknetworking.cli.UpdateExternalPeerRouterVlanCommand"/>
+ </command>
</command-bundle>
+
+ <bean id="ipAddressCompleter" class="org.onosproject.openstacknetworking.cli.IpAddressCompleter"/>
+ <bean id="macAddressCompleter" class="org.onosproject.openstacknetworking.cli.MacAddressCompleter"/>
+ <bean id="vlanIdCompleter" class="org.onosproject.openstacknetworking.cli.VlanIdCompleter"/>
</blueprint>
diff --git a/apps/openstacknetworking/src/test/java/org/onosproject/openstacknetworking/impl/OpenstackNetworkManagerTest.java b/apps/openstacknetworking/src/test/java/org/onosproject/openstacknetworking/impl/OpenstackNetworkManagerTest.java
index 15ec873..866aa55 100644
--- a/apps/openstacknetworking/src/test/java/org/onosproject/openstacknetworking/impl/OpenstackNetworkManagerTest.java
+++ b/apps/openstacknetworking/src/test/java/org/onosproject/openstacknetworking/impl/OpenstackNetworkManagerTest.java
@@ -15,18 +15,28 @@
*/
package org.onosproject.openstacknetworking.impl;
+import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
import com.google.common.util.concurrent.MoreExecutors;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.onlab.junit.TestUtils;
+import org.onosproject.cluster.ClusterServiceAdapter;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreServiceAdapter;
import org.onosproject.core.DefaultApplicationId;
import org.onosproject.event.Event;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.device.DeviceServiceAdapter;
+import org.onosproject.net.packet.PacketServiceAdapter;
import org.onosproject.openstacknetworking.api.OpenstackNetworkEvent;
import org.onosproject.openstacknetworking.api.OpenstackNetworkListener;
+import org.onosproject.openstacknode.api.OpenstackNode;
+import org.onosproject.openstacknode.api.OpenstackNodeAdminService;
+import org.onosproject.openstacknode.api.OpenstackNodeListener;
+import org.onosproject.openstacknode.api.OpenstackNodeService;
import org.onosproject.store.service.TestStorageService;
import org.openstack4j.model.network.Network;
import org.openstack4j.model.network.Port;
@@ -36,10 +46,23 @@
import org.openstack4j.openstack.networking.domain.NeutronSubnet;
import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
-import static org.onosproject.openstacknetworking.api.OpenstackNetworkEvent.Type.*;
+import static org.onosproject.openstacknetworking.api.OpenstackNetworkEvent.Type.OPENSTACK_NETWORK_CREATED;
+import static org.onosproject.openstacknetworking.api.OpenstackNetworkEvent.Type.OPENSTACK_NETWORK_REMOVED;
+import static org.onosproject.openstacknetworking.api.OpenstackNetworkEvent.Type.OPENSTACK_NETWORK_UPDATED;
+import static org.onosproject.openstacknetworking.api.OpenstackNetworkEvent.Type.OPENSTACK_PORT_CREATED;
+import static org.onosproject.openstacknetworking.api.OpenstackNetworkEvent.Type.OPENSTACK_PORT_REMOVED;
+import static org.onosproject.openstacknetworking.api.OpenstackNetworkEvent.Type.OPENSTACK_PORT_UPDATED;
+import static org.onosproject.openstacknetworking.api.OpenstackNetworkEvent.Type.OPENSTACK_SUBNET_CREATED;
+import static org.onosproject.openstacknetworking.api.OpenstackNetworkEvent.Type.OPENSTACK_SUBNET_REMOVED;
+import static org.onosproject.openstacknetworking.api.OpenstackNetworkEvent.Type.OPENSTACK_SUBNET_UPDATED;
+import static org.onosproject.openstacknode.api.NodeState.COMPLETE;
/**
* Unit tests for OpenStack network manager.
@@ -101,7 +124,12 @@
osNetworkStore.activate();
target = new OpenstackNetworkManager();
- target.coreService = new TestCoreService();
+ TestUtils.setField(target, "coreService", new TestCoreService());
+ TestUtils.setField(target, "packetService", new PacketServiceAdapter());
+ TestUtils.setField(target, "deviceService", new DeviceServiceAdapter());
+ TestUtils.setField(target, "storageService", new TestStorageService());
+ TestUtils.setField(target, "clusterService", new ClusterServiceAdapter());
+ TestUtils.setField(target, "openstacknodeService", new TestOpenstackNodeManager());
target.osNetworkStore = osNetworkStore;
target.addListener(testListener);
target.activate();
@@ -560,6 +588,75 @@
}
}
+ private static class TestOpenstackNodeManager implements OpenstackNodeService, OpenstackNodeAdminService {
+ Map<String, OpenstackNode> osNodeMap = Maps.newHashMap();
+ List<OpenstackNodeListener> listeners = Lists.newArrayList();
+
+ @Override
+ public Set<OpenstackNode> nodes() {
+ return ImmutableSet.copyOf(osNodeMap.values());
+ }
+
+ @Override
+ public Set<OpenstackNode> nodes(OpenstackNode.NodeType type) {
+ return osNodeMap.values().stream()
+ .filter(osNode -> osNode.type() == type)
+ .collect(Collectors.toSet());
+ }
+
+ @Override
+ public Set<OpenstackNode> completeNodes() {
+ return osNodeMap.values().stream()
+ .filter(osNode -> osNode.state() == COMPLETE)
+ .collect(Collectors.toSet());
+ }
+
+ @Override
+ public Set<OpenstackNode> completeNodes(OpenstackNode.NodeType type) {
+ return osNodeMap.values().stream()
+ .filter(osNode -> osNode.type() == type && osNode.state() == COMPLETE)
+ .collect(Collectors.toSet());
+ }
+
+ @Override
+ public OpenstackNode node(String hostname) {
+ return osNodeMap.get(hostname);
+ }
+
+ @Override
+ public OpenstackNode node(DeviceId deviceId) {
+ return osNodeMap.values().stream()
+ .filter(osNode -> Objects.equals(osNode.intgBridge(), deviceId) ||
+ Objects.equals(osNode.ovsdb(), deviceId))
+ .findFirst().orElse(null);
+ }
+
+ @Override
+ public void addListener(OpenstackNodeListener listener) {
+ listeners.add(listener);
+ }
+
+ @Override
+ public void removeListener(OpenstackNodeListener listener) {
+ listeners.remove(listener);
+ }
+
+ @Override
+ public void createNode(OpenstackNode osNode) {
+ osNodeMap.put(osNode.hostname(), osNode);
+ }
+
+ @Override
+ public void updateNode(OpenstackNode osNode) {
+ osNodeMap.put(osNode.hostname(), osNode);
+ }
+
+ @Override
+ public OpenstackNode removeNode(String hostname) {
+ return null;
+ }
+ }
+
private static class TestOpenstackNetworkListener implements OpenstackNetworkListener {
private List<OpenstackNetworkEvent> events = Lists.newArrayList();
diff --git a/apps/openstacknode/api/src/main/java/org/onosproject/openstacknode/api/OpenstackNode.java b/apps/openstacknode/api/src/main/java/org/onosproject/openstacknode/api/OpenstackNode.java
index f1e2bc5..2ecfefe 100644
--- a/apps/openstacknode/api/src/main/java/org/onosproject/openstacknode/api/OpenstackNode.java
+++ b/apps/openstacknode/api/src/main/java/org/onosproject/openstacknode/api/OpenstackNode.java
@@ -151,6 +151,13 @@
String uplinkPort();
/**
+ * Returns the uplink port number.
+ *
+ * @return uplink port number
+ */
+ PortNumber uplinkPortNum();
+
+ /**
* Returns new openstack node instance with given state.
*
* @param newState updated state
diff --git a/apps/openstacknode/app/src/main/java/org/onosproject/openstacknode/impl/DefaultOpenstackNode.java b/apps/openstacknode/app/src/main/java/org/onosproject/openstacknode/impl/DefaultOpenstackNode.java
index 6650be6..f208529 100644
--- a/apps/openstacknode/app/src/main/java/org/onosproject/openstacknode/impl/DefaultOpenstackNode.java
+++ b/apps/openstacknode/app/src/main/java/org/onosproject/openstacknode/impl/DefaultOpenstackNode.java
@@ -122,6 +122,21 @@
}
@Override
+ public PortNumber uplinkPortNum() {
+ if (uplinkPort == null) {
+ return null;
+ }
+
+ DeviceService deviceService = DefaultServiceDirectory.getService(DeviceService.class);
+ Port port = deviceService.getPorts(intgBridge).stream()
+ .filter(p -> p.isEnabled() &&
+ Objects.equals(p.annotations().value(PORT_NAME), uplinkPort))
+ .findAny().orElse(null);
+
+ return port != null ? port.number() : null;
+
+ }
+ @Override
public PortNumber tunnelPortNum() {
if (dataIp == null) {
return null;