[ONOS-3946] Implement IcmpHandler for OpenstackRoutingService
- Process Icmp packet sent from Host to external network for OpenstackGateway Node
- Process Arp packet sent from physical router to gateway
Change-Id: Ifcde71a9ca10180682811c9e1bcf58f991b36443
diff --git a/apps/openstackinterface/app/src/main/java/org/onosproject/openstackinterface/web/OpenstackRouterCodec.java b/apps/openstackinterface/app/src/main/java/org/onosproject/openstackinterface/web/OpenstackRouterCodec.java
index 6a1750e..ae791b9 100644
--- a/apps/openstackinterface/app/src/main/java/org/onosproject/openstackinterface/web/OpenstackRouterCodec.java
+++ b/apps/openstackinterface/app/src/main/java/org/onosproject/openstackinterface/web/OpenstackRouterCodec.java
@@ -68,7 +68,6 @@
String tenantId = checkNotNull(routerInfo.path(TENANT_ID).asText());
String id = checkNotNull(routerInfo.path(ID).asText());
String name = checkNotNull(routerInfo.path(NAME).asText());
- String status = checkNotNull(routerInfo.path(STATUS).asText());
String adminStateUp = checkNotNull(routerInfo.path(ADMIN_STATE_UP).asText());
OpenstackExternalGateway.Builder osExtBuiler = new OpenstackExternalGateway.Builder();
@@ -98,7 +97,7 @@
.tenantId(tenantId)
.id(id)
.name(name)
- .status(OpenstackRouter.RouterStatus.valueOf(status))
+ .status(OpenstackRouter.RouterStatus.ACTIVE)
.adminStateUp(Boolean.valueOf(adminStateUp))
.gatewayExternalInfo(osExtBuiler.build());
diff --git a/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/OpenstackPortInfo.java b/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/OpenstackPortInfo.java
index 0ab8681..d23f5f3 100644
--- a/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/OpenstackPortInfo.java
+++ b/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/OpenstackPortInfo.java
@@ -27,12 +27,13 @@
/**
* Contains OpenstackPort Information.
*/
-public class OpenstackPortInfo {
+public final class OpenstackPortInfo {
private final Ip4Address hostIp;
private final MacAddress hostMac;
private final DeviceId deviceId;
private final long vni;
private final Ip4Address gatewayIP;
+ private final String networkId;
private final Collection<String> securityGroups;
/**
@@ -46,12 +47,13 @@
* @param securityGroups security group list
*/
public OpenstackPortInfo(Ip4Address hostIp, MacAddress hostMac, DeviceId deviceId, long vni,
- Ip4Address gatewayIP, Collection<String> securityGroups) {
+ Ip4Address gatewayIP, String networkId, Collection<String> securityGroups) {
this.hostIp = hostIp;
this.hostMac = hostMac;
this.deviceId = deviceId;
this.vni = vni;
this.gatewayIP = gatewayIP;
+ this.networkId = networkId;
this.securityGroups = securityGroups;
}
@@ -101,6 +103,15 @@
}
/**
+ * Returns network ID.
+ *
+ * @return network ID
+ */
+ public String networkId() {
+ return networkId;
+ }
+
+ /**
* Returns Security Group ID list.
*
* @return list of Security Group ID
@@ -129,11 +140,12 @@
private long vni;
private Ip4Address gatewayIP;
private Collection<String> securityGroups;
+ private String networkId;
/**
* Sets the IP address of the port.
*
- * @param gatewayIP
+ * @param gatewayIP gateway IP
* @return Builder reference
*/
public Builder setGatewayIP(Ip4Address gatewayIP) {
@@ -142,6 +154,17 @@
}
/**
+ * Sets the network ID.
+ *
+ * @param networkId network id
+ * @return Builder reference
+ */
+ public Builder setNetworkId(String networkId) {
+ this.networkId = checkNotNull(networkId, "networkId cannot be null");
+ return this;
+ }
+
+ /**
* Sets the host IP address of the port.
*
* @param hostIp host IP address
@@ -202,16 +225,7 @@
* @return OpenstackPortInfo reference
*/
public OpenstackPortInfo build() {
- return new OpenstackPortInfo(this);
+ return new OpenstackPortInfo(hostIp, hostMac, deviceId, vni, gatewayIP, networkId, securityGroups);
}
}
-
- private OpenstackPortInfo(Builder builder) {
- hostIp = builder.hostIp;
- hostMac = builder.hostMac;
- deviceId = builder.deviceId;
- vni = builder.vni;
- gatewayIP = builder.gatewayIP;
- securityGroups = builder.securityGroups;
- }
}
diff --git a/apps/openstacknetworking/network-cfg.json b/apps/openstacknetworking/network-cfg.json
index 170d482..142ec8a 100644
--- a/apps/openstacknetworking/network-cfg.json
+++ b/apps/openstacknetworking/network-cfg.json
@@ -1,40 +1,50 @@
{
"apps" : {
- "org.onosproject.openstackswitching" : {
- "openstackswitching" : {
- "do_not_push_flows" : "false",
- "neutron_server" : "http://192.168.56.103:9696/v2.0/",
- "keystone_server" : "http://192.168.56.103:5000/v2.0/",
- "user_name" : "admin",
- "password" : "nova",
- "physicalRouterMac" : "00:00:00:00:00:20",
+ "org.onosproject.openstackrouting" : {
+ "openstackrouting" : {
+ "physicalRouterMac" : "2a:a1:8a:89:dd:a4",
+ "gatewayBridgeId" : "of:0000000000000003",
+ "gatewayExternalInterfaceName" : "veth0",
+ "gatewayExternalInterfaceMac" : "be:15:c6:b0:df:9f"
+ }
+ },
+ "org.onosproject.openstacknode" : {
+ "openstacknode" : {
"nodes" : [
- {
- "hostname" : "compute-01",
- "ovsdbIp" : "192.168.56.102",
- "ovsdbPort" : "6640",
- "bridgeId" : "of:0000000000000001",
- "openstackNodeType" : "COMPUTENODE"
- },
- {
- "hostname" : "compute-02",
- "ovsdbIp" : "192.168.56.101",
- "ovsdbPort" : "6640",
- "bridgeId" : "of:0000000000000002",
- "openstackNodeType" : "COMPUTENODE"
- },
- {
- "hostname" : "network",
- "ovsdbIp" : "192.168.56.106",
- "ovsdbPort" : "6640",
- "bridgeId" : "of:0000000000000003",
- "openstackNodeType" : "GATEWAYNODE",
- "externalIfName" : "eth3",
- "externalIfMacAddress" : "00:00:00:00:00:11"
- }
+ {
+ "hostname" : "compute-01",
+ "ovsdbIp" : "192.168.56.112",
+ "ovsdbPort" : "6640",
+ "bridgeId" : "of:0000000000000001",
+ "openstackNodeType" : "COMPUTENODE"
+ },
+ {
+ "hostname" : "compute-02",
+ "ovsdbIp" : "192.168.56.113",
+ "ovsdbPort" : "6640",
+ "bridgeId" : "of:0000000000000002",
+ "openstackNodeType" : "COMPUTENODE"
+ },
+ {
+ "hostname" : "network",
+ "ovsdbIp" : "192.168.56.114",
+ "ovsdbPort" : "6640",
+ "bridgeId" : "of:0000000000000003",
+ "openstackNodeType" : "GATEWAYNODE",
+ "gatewayExternalInterfaceName" : "veth0",
+ "gatewayExternalInterfaceMac" : "be:15:c6:b0:df:9f"
+ }
]
- }
- }
+ }
+ },
+ "org.onosproject.openstackinterface" : {
+ "openstackinterface" : {
+ "neutron_server" : "http://192.168.56.111:9696/v2.0/",
+ "keystone_server" : "http://192.168.56.111:5000/v2.0/",
+ "user_name" : "admin",
+ "password" : "nova"
+ }
+ }
},
"devices" : {
"of:0000000000000001" : {
@@ -49,3 +59,4 @@
}
}
}
+
diff --git a/apps/openstacknetworking/openstackrouting/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackIcmpHandler.java b/apps/openstacknetworking/openstackrouting/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackIcmpHandler.java
index 3cc11f0..9d8bd71 100644
--- a/apps/openstacknetworking/openstackrouting/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackIcmpHandler.java
+++ b/apps/openstacknetworking/openstackrouting/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackIcmpHandler.java
@@ -5,7 +5,7 @@
* 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
+ * 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,
@@ -15,21 +15,260 @@
*/
package org.onosproject.openstacknetworking.routing;
+import com.google.common.collect.Maps;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.ICMP;
+import org.onlab.packet.IPv4;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.MacAddress;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Port;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.packet.DefaultOutboundPacket;
+import org.onosproject.net.packet.OutboundPacket;
import org.onosproject.net.packet.PacketContext;
+import org.onosproject.net.packet.PacketPriority;
+import org.onosproject.net.packet.PacketService;
+import org.onosproject.openstackinterface.OpenstackInterfaceService;
+import org.onosproject.openstackinterface.OpenstackPort;
+import org.onosproject.openstacknetworking.OpenstackPortInfo;
+import org.onosproject.openstacknetworking.OpenstackSwitchingService;
+import org.slf4j.Logger;
+
+import java.nio.ByteBuffer;
+import java.util.Map;
+import java.util.Optional;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
/**
- * Handle ICMP packet processing for Managing Flow Rules In Openstack Nodes.
+ * Handle ICMP packet sent from Openstack Gateway nodes.
*/
-public class OpenstackIcmpHandler implements Runnable {
+public class OpenstackIcmpHandler {
+ protected final Logger log = getLogger(getClass());
- volatile PacketContext context;
- private OpenstackRoutingRulePopulator rulePopulator;
- OpenstackIcmpHandler(OpenstackRoutingRulePopulator rulePopulator, PacketContext context) {
- this.context = context;
- this.rulePopulator = rulePopulator;
+ private final PacketService packetService;
+ private final DeviceService deviceService;
+ private final Map<String, OpenstackPortInfo> icmpInfoMap = Maps.newHashMap();
+ private final OpenstackSwitchingService openstackSwitchingService;
+ private final OpenstackInterfaceService openstackService;
+ private final OpenstackRoutingConfig config;
+ private static final MacAddress GATEWAY_MAC = MacAddress.valueOf("1f:1f:1f:1f:1f:1f");
+ private static final String NETWORK_ROUTER_INTERFACE = "network:router_interface";
+ private static final String PORTNAME = "portName";
+
+ /**
+ * Default constructor.
+ *
+ * @param packetService packet service
+ * @param deviceService device service
+ * @param openstackService openstackInterface service
+ * @param config openstackRoutingConfig
+ * @param openstackSwitchingService openstackSwitching service
+ */
+ OpenstackIcmpHandler(PacketService packetService, DeviceService deviceService,
+ OpenstackInterfaceService openstackService, OpenstackRoutingConfig config,
+ OpenstackSwitchingService openstackSwitchingService) {
+ this.packetService = packetService;
+ this.deviceService = deviceService;
+ this.openstackService = checkNotNull(openstackService);
+ this.config = checkNotNull(config);
+ this.openstackSwitchingService = checkNotNull(openstackSwitchingService);
}
- @Override
- public void run() {
+ /**
+ * Requests ICMP packet.
+ *
+ * @param appId Application Id
+ */
+ public void requestPacket(ApplicationId appId) {
+ TrafficSelector icmpSelector = DefaultTrafficSelector.builder()
+ .matchEthType(Ethernet.TYPE_IPV4)
+ .matchIPProtocol(IPv4.PROTOCOL_ICMP)
+ .build();
+
+ packetService.requestPackets(icmpSelector,
+ PacketPriority.CONTROL,
+ appId,
+ Optional.of(DeviceId.deviceId(config.gatewayBridgeId())));
}
-}
\ No newline at end of file
+
+ /**
+ * Handles ICMP packet.
+ *
+ * @param context packet context
+ * @param ethernet ethernet
+ */
+ public void processIcmpPacket(PacketContext context, Ethernet ethernet) {
+ checkNotNull(context, "context can not be null");
+ checkNotNull(ethernet, "ethernet can not be null");
+
+ IPv4 ipPacket = (IPv4) ethernet.getPayload();
+
+ log.debug("icmpEvent called from ip {}, mac {}", Ip4Address.valueOf(ipPacket.getSourceAddress()).toString(),
+ ethernet.getSourceMAC().toString());
+
+ ICMP icmp = (ICMP) ipPacket.getPayload();
+ short icmpId = getIcmpId(icmp);
+
+ DeviceId deviceId = context.inPacket().receivedFrom().deviceId();
+ if (icmp.getIcmpType() == ICMP.TYPE_ECHO_REQUEST) {
+ //TODO: Considers icmp between internal subnets which are belonged to the same router.
+
+ OpenstackPortInfo openstackPortInfo =
+ getOpenstackPortInfo(Ip4Address.valueOf(ipPacket.getSourceAddress()), ethernet.getSourceMAC());
+
+ checkNotNull(openstackPortInfo, "openstackPortInfo can not be null");
+
+ if (ipPacket.getDestinationAddress() == openstackPortInfo.gatewayIP().toInt()) {
+ processIcmpPacketSentToGateway(ipPacket, icmp, openstackPortInfo);
+ } else {
+ Ip4Address pNatIpAddress = pNatIpForPort(openstackPortInfo);
+ checkNotNull(pNatIpAddress, "pNatIpAddress can not be null");
+
+ sendRequestPacketToExt(ipPacket, icmp, deviceId, pNatIpAddress);
+
+ String icmpInfoKey = String.valueOf(icmpId)
+ .concat(String.valueOf(pNatIpAddress.toInt()))
+ .concat(String.valueOf(ipPacket.getDestinationAddress()));
+ icmpInfoMap.putIfAbsent(icmpInfoKey, openstackPortInfo);
+ }
+ } else if (icmp.getIcmpType() == ICMP.TYPE_ECHO_REPLY) {
+ String icmpInfoKey = String.valueOf(icmpId)
+ .concat(String.valueOf(ipPacket.getDestinationAddress()))
+ .concat(String.valueOf(ipPacket.getSourceAddress()));
+
+ processResponsePacketFromExternalToHost(ipPacket, icmp, icmpInfoMap.get(icmpInfoKey));
+
+ icmpInfoMap.remove(icmpInfoKey);
+ }
+ }
+
+ private void processIcmpPacketSentToGateway(IPv4 icmpRequestIpv4, ICMP icmpRequest,
+ OpenstackPortInfo openstackPortInfo) {
+ icmpRequest.setIcmpType(ICMP.TYPE_ECHO_REPLY)
+ .resetChecksum();
+
+ icmpRequestIpv4.setSourceAddress(icmpRequestIpv4.getDestinationAddress())
+ .setDestinationAddress(openstackPortInfo.ip().toInt())
+ .resetChecksum();
+
+ icmpRequestIpv4.setPayload(icmpRequest);
+
+ Ethernet icmpResponseEth = new Ethernet();
+
+ icmpResponseEth.setEtherType(Ethernet.TYPE_IPV4)
+ .setSourceMACAddress(GATEWAY_MAC)
+ .setDestinationMACAddress(openstackPortInfo.mac())
+ .setPayload(icmpRequestIpv4);
+
+ sendResponsePacketToHost(icmpResponseEth, openstackPortInfo);
+ }
+
+ private void sendRequestPacketToExt(IPv4 icmpRequestIpv4, ICMP icmpRequest, DeviceId deviceId,
+ Ip4Address pNatIpAddress) {
+ icmpRequest.resetChecksum();
+ icmpRequestIpv4.setSourceAddress(pNatIpAddress.toInt())
+ .resetChecksum();
+ icmpRequestIpv4.setPayload(icmpRequest);
+
+ Ethernet icmpRequestEth = new Ethernet();
+
+ icmpRequestEth.setEtherType(Ethernet.TYPE_IPV4)
+ .setSourceMACAddress(MacAddress.valueOf(config.gatewayExternalInterfaceMac()))
+ .setDestinationMACAddress(MacAddress.valueOf(config.physicalRouterMac()))
+ .setPayload(icmpRequestIpv4);
+
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+ .setOutput(getPortForAnnotationPortName(DeviceId.deviceId(config.gatewayBridgeId()),
+ config.gatewayExternalInterfaceName()))
+ .build();
+
+ OutboundPacket packet = new DefaultOutboundPacket(deviceId,
+ treatment, ByteBuffer.wrap(icmpRequestEth.serialize()));
+
+ packetService.emit(packet);
+ }
+
+ private void processResponsePacketFromExternalToHost(IPv4 icmpResponseIpv4, ICMP icmpResponse,
+ OpenstackPortInfo openstackPortInfo) {
+ icmpResponse.resetChecksum();
+
+ icmpResponseIpv4.setDestinationAddress(openstackPortInfo.ip().toInt())
+ .resetChecksum();
+ icmpResponseIpv4.setPayload(icmpResponse);
+
+ Ethernet icmpResponseEth = new Ethernet();
+
+ icmpResponseEth.setEtherType(Ethernet.TYPE_IPV4)
+ .setSourceMACAddress(GATEWAY_MAC)
+ .setDestinationMACAddress(openstackPortInfo.mac())
+ .setPayload(icmpResponseIpv4);
+
+ sendResponsePacketToHost(icmpResponseEth, openstackPortInfo);
+ }
+
+ private void sendResponsePacketToHost(Ethernet icmpResponseEth, OpenstackPortInfo openstackPortInfo) {
+ Map.Entry<String, OpenstackPortInfo> entry = openstackSwitchingService.openstackPortInfo().entrySet().stream()
+ .filter(e -> e.getValue().mac().equals(openstackPortInfo.mac()))
+ .findAny().orElse(null);
+
+ if (entry == null) {
+ return;
+ }
+
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+ .setOutput(getPortForAnnotationPortName(openstackPortInfo.deviceId(), entry.getKey()))
+ .build();
+
+ OutboundPacket packet = new DefaultOutboundPacket(openstackPortInfo.deviceId(),
+ treatment, ByteBuffer.wrap(icmpResponseEth.serialize()));
+
+ packetService.emit(packet);
+ }
+
+ private OpenstackPortInfo getOpenstackPortInfo(Ip4Address sourceIp, MacAddress sourceMac) {
+ checkNotNull(openstackSwitchingService.openstackPortInfo(), "openstackportinfo collection can not be null");
+
+ return openstackSwitchingService.openstackPortInfo().values()
+ .stream().filter(p -> p.ip().equals(sourceIp) && p.mac().equals(sourceMac))
+ .findAny().orElse(null);
+ }
+
+ private short getIcmpId(ICMP icmp) {
+ return ByteBuffer.wrap(icmp.serialize(), 4, 2).getShort();
+ }
+
+ private Ip4Address pNatIpForPort(OpenstackPortInfo openstackPortInfo) {
+
+ OpenstackPort openstackPort = openstackService.ports().stream()
+ .filter(p -> p.deviceOwner().equals(NETWORK_ROUTER_INTERFACE) &&
+ p.networkId().equals(openstackPortInfo.networkId()))
+ .findAny().orElse(null);
+
+ checkNotNull(openstackPort, "openstackPort can not be null");
+
+ return openstackService.router(openstackPort.deviceId())
+ .gatewayExternalInfo().externalFixedIps().values()
+ .stream().findAny().orElse(null);
+ }
+
+ private PortNumber getPortForAnnotationPortName(DeviceId deviceId, String match) {
+ Port port = deviceService.getPorts(deviceId).stream()
+ .filter(p -> p.annotations().value(PORTNAME).equals(match))
+ .findAny().orElse(null);
+
+ checkNotNull(port, "port cannot be null");
+
+ return port.number();
+ }
+}
diff --git a/apps/openstacknetworking/openstackrouting/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackRoutingArpHandler.java b/apps/openstacknetworking/openstackrouting/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackRoutingArpHandler.java
new file mode 100644
index 0000000..8ae7282
--- /dev/null
+++ b/apps/openstacknetworking/openstackrouting/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackRoutingArpHandler.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2016 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.openstacknetworking.routing;
+
+import org.onlab.packet.ARP;
+import org.onlab.packet.EthType;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.IPv4;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.packet.DefaultOutboundPacket;
+import org.onosproject.net.packet.PacketContext;
+import org.onosproject.net.packet.PacketPriority;
+import org.onosproject.net.packet.PacketService;
+import org.onosproject.openstackinterface.OpenstackInterfaceService;
+import org.onosproject.openstackinterface.OpenstackPort;
+import org.slf4j.Logger;
+
+import java.nio.ByteBuffer;
+import java.util.Optional;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Handle ARP packet sent from Openstack Gateway nodes.
+ */
+public class OpenstackRoutingArpHandler {
+ protected final Logger log = getLogger(getClass());
+
+ private final PacketService packetService;
+ private final OpenstackInterfaceService openstackService;
+ private final OpenstackRoutingConfig config;
+ private static final String NETWORK_ROUTER_GATEWAY = "network:router_gateway";
+
+ /**
+ * Default constructor.
+ *
+ * @param packetService packet service
+ * @param openstackService openstackInterface service
+ * @param config openstackRoutingConfig
+ */
+ OpenstackRoutingArpHandler(PacketService packetService, OpenstackInterfaceService openstackService,
+ OpenstackRoutingConfig config) {
+ this.packetService = packetService;
+ this.openstackService = checkNotNull(openstackService);
+ this.config = checkNotNull(config);
+ }
+
+ /**
+ * Requests ARP packet to GatewayNode.
+ *
+ * @param appId application id
+ */
+ public void requestPacket(ApplicationId appId) {
+
+ TrafficSelector arpSelector = DefaultTrafficSelector.builder()
+ .matchEthType(EthType.EtherType.ARP.ethType().toShort())
+ .build();
+
+ packetService.requestPackets(arpSelector,
+ PacketPriority.CONTROL,
+ appId,
+ Optional.of(DeviceId.deviceId(config.gatewayBridgeId())));
+ }
+
+ /**
+ * Handles ARP packet.
+ *
+ * @param context packet context
+ * @param ethernet ethernet
+ */
+ public void processArpPacketFromRouter(PacketContext context, Ethernet ethernet) {
+ checkNotNull(context, "context can not be null");
+ checkNotNull(ethernet, "ethernet can not be null");
+
+ log.info("arpEvent called from {} to {}",
+ Ip4Address.valueOf(((IPv4) ethernet.getPayload()).getSourceAddress()).toString(),
+ Ip4Address.valueOf(((IPv4) ethernet.getPayload()).getDestinationAddress()).toString());
+ ARP arp = (ARP) ethernet.getPayload();
+
+ if (arp.getOpCode() != ARP.OP_REQUEST) {
+ return;
+ }
+
+ IpAddress targetIp = Ip4Address.valueOf(arp.getTargetProtocolAddress());
+ MacAddress targetMac = getTargetMacForTargetIp(targetIp.getIp4Address());
+
+ if (targetMac == MacAddress.NONE) {
+ return;
+ }
+
+ 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 MacAddress getTargetMacForTargetIp(Ip4Address targetIp) {
+ OpenstackPort port = openstackService.ports().stream()
+ .filter(p -> p.deviceOwner().equals(NETWORK_ROUTER_GATEWAY))
+ .filter(p -> p.fixedIps().containsValue(targetIp.getIp4Address()))
+ .findAny().orElse(null);
+
+ if (port == null) {
+ return MacAddress.NONE;
+ }
+ return port.macAddress();
+ }
+}
diff --git a/apps/openstacknetworking/openstackrouting/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackRoutingConfig.java b/apps/openstacknetworking/openstackrouting/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackRoutingConfig.java
new file mode 100644
index 0000000..0656a94
--- /dev/null
+++ b/apps/openstacknetworking/openstackrouting/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackRoutingConfig.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2016 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.openstacknetworking.routing;
+
+import org.onosproject.core.ApplicationId;
+import org.onosproject.net.config.Config;
+import org.slf4j.Logger;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Configuration object for OpenstackRouting service.
+ */
+public class OpenstackRoutingConfig extends Config<ApplicationId> {
+ protected final Logger log = getLogger(getClass());
+
+ public static final String PHYSICAL_ROUTER_MAC = "physicalRouterMac";
+ public static final String GATEWAY_BRIDGE_ID = "gatewayBridgeId";
+ public static final String GATEWAY_EXTERNAL_INTERFACE_NAME = "gatewayExternalInterfaceName";
+ public static final String GATEWAY_EXTERNAL_INTERFACE_MAC = "gatewayExternalInterfaceMac";
+
+ /**
+ * Returns physical router mac.
+ *
+ * @return physical router mac
+ */
+ public String physicalRouterMac() {
+ return this.get("physicalRouterMac", "");
+ }
+
+ /**
+ * Returns gateway's bridge id.
+ *
+ * @return bridge id
+ */
+ public String gatewayBridgeId() {
+ return this.get("gatewayBridgeId", "");
+ }
+
+ /**
+ * Returns gateway's external interface name.
+ *
+ * @return external interface name
+ */
+ public String gatewayExternalInterfaceName() {
+ return this.get("gatewayExternalInterfaceName", "");
+ }
+
+ /**
+ * Returns gateway's external interface mac.
+ *
+ * @return external interface mac
+ */
+ public String gatewayExternalInterfaceMac() {
+ return this.get("gatewayExternalInterfaceMac", "");
+ }
+}
diff --git a/apps/openstacknetworking/openstackrouting/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackRoutingManager.java b/apps/openstacknetworking/openstackrouting/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackRoutingManager.java
index b1c0a74..2ab19ac 100644
--- a/apps/openstacknetworking/openstackrouting/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackRoutingManager.java
+++ b/apps/openstacknetworking/openstackrouting/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackRoutingManager.java
@@ -32,6 +32,12 @@
import org.onosproject.core.CoreService;
import org.onosproject.net.DeviceId;
import org.onosproject.net.Port;
+import org.onosproject.net.config.ConfigFactory;
+import org.onosproject.net.config.NetworkConfigEvent;
+import org.onosproject.net.config.NetworkConfigListener;
+import org.onosproject.net.config.NetworkConfigRegistry;
+import org.onosproject.net.config.NetworkConfigService;
+import org.onosproject.net.config.basics.SubjectFactories;
import org.onosproject.net.device.DeviceService;
import org.onosproject.net.driver.DriverService;
import org.onosproject.net.flowobjective.FlowObjectiveService;
@@ -45,6 +51,7 @@
import org.onosproject.openstackinterface.OpenstackRouter;
import org.onosproject.openstackinterface.OpenstackRouterInterface;
import org.onosproject.openstacknetworking.OpenstackRoutingService;
+import org.onosproject.openstacknetworking.OpenstackSwitchingService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -80,24 +87,44 @@
protected OpenstackInterfaceService openstackService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected OpenstackSwitchingService openstackSwitchingService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected FlowObjectiveService flowObjectiveService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected DriverService driverService;
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected NetworkConfigService configService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected NetworkConfigRegistry configRegistry;
+
+
+
private ApplicationId appId;
private Map<String, OpenstackRouterInterface> routerInterfaceMap = Maps.newHashMap();
private Map<Integer, String> portNumMap = initPortNumMap();
private static final String APP_ID = "org.onosproject.openstackrouting";
private static final String PORT_NAME = "portName";
private static final String DEVICE_OWNER_ROUTER_INTERFACE = "network:router_interface";
+ private final ConfigFactory configFactory =
+ new ConfigFactory(SubjectFactories.APP_SUBJECT_FACTORY, OpenstackRoutingConfig.class, "openstackrouting") {
+ @Override
+ public OpenstackRoutingConfig createConfig() {
+ return new OpenstackRoutingConfig();
+ }
+ };
+ private final NetworkConfigListener configListener = new InternalConfigListener();
- // TODO: This will be replaced to get the information from openstackswitchingservice.
- private static final String EXTERNAL_INTERFACE_NAME = "veth0";
+ private OpenstackRoutingConfig config;
+ private static final int PNAT_PORT_NUM_START = 1024;
+ private static final int PNAT_PORT_NUM_END = 65535;
private Map<Integer, String> initPortNumMap() {
Map<Integer, String> map = Maps.newHashMap();
- for (int i = 1024; i < 65535; i++) {
+ for (int i = PNAT_PORT_NUM_START; i < PNAT_PORT_NUM_END; i++) {
map.put(i, "");
}
return map;
@@ -108,17 +135,30 @@
Executors.newSingleThreadExecutor(groupedThreads("onos/openstackrouting", "L3-event"));
private ExecutorService icmpEventExecutorService =
Executors.newSingleThreadExecutor(groupedThreads("onos/openstackrouting", "icmp-event"));
+ private ExecutorService arpEventExecutorService =
+ Executors.newSingleThreadExecutor(groupedThreads("onos/openstackrouting", "arp-event"));
+ private OpenstackIcmpHandler openstackIcmpHandler;
+ private OpenstackRoutingArpHandler openstackArpHandler;
@Activate
protected void activate() {
appId = coreService.registerApplication(APP_ID);
packetService.addProcessor(internalPacketProcessor, PacketProcessor.director(1));
+ configRegistry.registerConfigFactory(configFactory);
+ configService.addListener(configListener);
+
+ readConfiguration();
+
log.info("onos-openstackrouting started");
}
@Deactivate
protected void deactivate() {
packetService.removeProcessor(internalPacketProcessor);
+ l3EventExecutorService.shutdown();
+ icmpEventExecutorService.shutdown();
+ arpEventExecutorService.shutdown();
+
log.info("onos-openstackrouting stopped");
}
@@ -155,16 +195,16 @@
@Override
public void updateRouterInterface(OpenstackRouterInterface routerInterface) {
- routerInterfaceMap.putIfAbsent(routerInterface.portId(), routerInterface);
+ routerInterfaceMap.putIfAbsent(routerInterface.id(), routerInterface);
List<OpenstackRouterInterface> routerInterfaces = Lists.newArrayList();
routerInterfaces.add(routerInterface);
- checkExternalConnection(getOpenstackRouter(routerInterface.portId()), routerInterfaces);
+ checkExternalConnection(getOpenstackRouter(routerInterface.id()), routerInterfaces);
}
@Override
public void removeRouterInterface(OpenstackRouterInterface routerInterface) {
OpenstackRoutingRulePopulator rulePopulator = new OpenstackRoutingRulePopulator(appId,
- openstackService, flowObjectiveService, deviceService, driverService);
+ openstackService, flowObjectiveService, deviceService, driverService, config);
rulePopulator.removeExternalRules(routerInterface);
routerInterfaceMap.remove(routerInterface.portId());
}
@@ -181,10 +221,10 @@
private OpenstackRouterInterface portToRouterInterface(OpenstackPort p) {
OpenstackRouterInterface.Builder osBuilder = new OpenstackRouterInterface.Builder()
- .id(checkNotNull(p.id()))
+ .id(checkNotNull(p.deviceId()))
.tenantId(checkNotNull(openstackService.network(p.networkId()).tenantId()))
.subnetId(checkNotNull(p.fixedIps().keySet().stream().findFirst().orElse(null)).toString())
- .portId(checkNotNull(p.deviceId()));
+ .portId(checkNotNull(p.id()));
return osBuilder.build();
}
@@ -196,19 +236,26 @@
if (context.isHandled()) {
return;
+ } else if (!context.inPacket().receivedFrom().deviceId().toString()
+ .equals(config.gatewayBridgeId())) {
+ return;
}
InboundPacket pkt = context.inPacket();
Ethernet ethernet = pkt.parsed();
- if (ethernet != null && ethernet.getEtherType() == Ethernet.TYPE_IPV4) {
+ //TODO: Considers IPv6 later.
+ if (ethernet == null) {
+ return;
+ } else if (ethernet.getEtherType() == Ethernet.TYPE_IPV4) {
IPv4 iPacket = (IPv4) ethernet.getPayload();
OpenstackRoutingRulePopulator rulePopulator = new OpenstackRoutingRulePopulator(appId,
- openstackService, flowObjectiveService, deviceService,
- driverService);
+ openstackService, flowObjectiveService, deviceService, driverService, config);
switch (iPacket.getProtocol()) {
case IPv4.PROTOCOL_ICMP:
- icmpEventExecutorService.execute(new OpenstackIcmpHandler(rulePopulator, context));
+
+ icmpEventExecutorService.submit(() ->
+ openstackIcmpHandler.processIcmpPacket(context, ethernet));
break;
case IPv4.PROTOCOL_UDP:
// don't process DHCP
@@ -219,7 +266,8 @@
}
default:
int portNum = getPortNum(ethernet.getSourceMAC(), iPacket.getDestinationAddress());
- Port port = getExternalPort(pkt.receivedFrom().deviceId(), EXTERNAL_INTERFACE_NAME);
+ Port port =
+ getExternalPort(pkt.receivedFrom().deviceId(), config.gatewayExternalInterfaceName());
if (port == null) {
log.warn("There`s no external interface");
break;
@@ -230,7 +278,9 @@
portNum, openstackPort, port));
break;
}
-
+ } else if (ethernet.getEtherType() == Ethernet.TYPE_ARP) {
+ arpEventExecutorService.submit(() ->
+ openstackArpHandler.processArpPacketFromRouter(context, ethernet));
}
}
@@ -265,9 +315,9 @@
private void initiateL3Rule(OpenstackRouter router, OpenstackRouterInterface routerInterface) {
long vni = Long.parseLong(openstackService.network(openstackService
- .port(routerInterface.id()).networkId()).segmentId());
+ .port(routerInterface.portId()).networkId()).segmentId());
OpenstackRoutingRulePopulator rulePopulator = new OpenstackRoutingRulePopulator(appId,
- openstackService, flowObjectiveService, deviceService, driverService);
+ openstackService, flowObjectiveService, deviceService, driverService, config);
rulePopulator.populateExternalRules(vni, router, routerInterface);
}
@@ -288,4 +338,50 @@
.equals(ip4Address) ? openstackPort : null;
}
+ private void readConfiguration() {
+ config = configService.getConfig(appId, OpenstackRoutingConfig.class);
+ if (config == null) {
+ log.error("No configuration found");
+ return;
+ }
+
+ checkNotNull(config.physicalRouterMac());
+ checkNotNull(config.gatewayBridgeId());
+ checkNotNull(config.gatewayExternalInterfaceMac());
+ checkNotNull(config.gatewayExternalInterfaceName());
+
+ log.debug("Configured info: {}, {}, {}, {}", config.physicalRouterMac(), config.gatewayBridgeId(),
+ config.gatewayExternalInterfaceMac(), config.gatewayExternalInterfaceName());
+
+ reloadInitL3Rules();
+
+ openstackIcmpHandler = new OpenstackIcmpHandler(packetService, deviceService,
+ openstackService, config, openstackSwitchingService);
+ openstackArpHandler = new OpenstackRoutingArpHandler(packetService, openstackService, config);
+
+ openstackIcmpHandler.requestPacket(appId);
+ openstackArpHandler.requestPacket(appId);
+
+ log.info("OpenstackRouting configured");
+ }
+
+ private class InternalConfigListener implements NetworkConfigListener {
+
+ @Override
+ public void event(NetworkConfigEvent event) {
+ if (!event.configClass().equals(OpenstackRoutingConfig.class)) {
+ return;
+ }
+
+ switch (event.type()) {
+ case CONFIG_ADDED:
+ case CONFIG_UPDATED:
+ l3EventExecutorService.execute(OpenstackRoutingManager.this::readConfiguration);
+ break;
+ default:
+ log.debug("Unsupported event type {}", event.type().toString());
+ break;
+ }
+ }
+ }
}
diff --git a/apps/openstacknetworking/openstackrouting/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackRoutingRulePopulator.java b/apps/openstacknetworking/openstackrouting/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackRoutingRulePopulator.java
index b226001..ab1d360 100644
--- a/apps/openstacknetworking/openstackrouting/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackRoutingRulePopulator.java
+++ b/apps/openstacknetworking/openstackrouting/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackRoutingRulePopulator.java
@@ -71,6 +71,7 @@
private final OpenstackInterfaceService openstackService;
private final DeviceService deviceService;
private final DriverService driverService;
+ private final OpenstackRoutingConfig config;
private static final String PORTNAME_PREFIX_VM = "tap";
private static final String PORTNAME_PREFIX_ROUTER = "qr";
@@ -93,9 +94,6 @@
private OpenstackRouter router;
private OpenstackRouterInterface routerInterface;
- // TODO: This will be replaced to get the information from openstackswitchingservice.
- private static final String EXTERNAL_INTERFACE_NAME = "veth0";
-
/**
* The constructor of openstackRoutingRulePopulator.
*
@@ -104,20 +102,23 @@
* @param flowObjectiveService FlowObjectiveService
* @param deviceService DeviceService
* @param driverService DriverService
+ * @param config OpenstackRoutingConfig
*/
public OpenstackRoutingRulePopulator(ApplicationId appId, OpenstackInterfaceService openstackService,
- FlowObjectiveService flowObjectiveService,
- DeviceService deviceService, DriverService driverService) {
+ FlowObjectiveService flowObjectiveService, DeviceService deviceService,
+ DriverService driverService, OpenstackRoutingConfig config) {
this.appId = appId;
this.flowObjectiveService = flowObjectiveService;
- this.openstackService = openstackService;
+ this.openstackService = checkNotNull(openstackService);
this.deviceService = deviceService;
this.driverService = driverService;
+ this.config = config;
}
/**
* Populates flow rules for Pnat configurations.
- * @param inboundPacket Packet-in event packet
+ *
+ * @param inboundPacket Packet-in event packet
* @param openstackPort Target VM information
* @param portNum Pnat port number
* @param externalIp external ip address
@@ -168,6 +169,7 @@
tBuilder.setUdpSrc(TpPort.tpPort(portNum));
break;
default:
+ log.debug("Unsupported IPv4 protocol {}");
break;
}
@@ -188,7 +190,7 @@
private Port getPortNumOfExternalInterface() {
return deviceService.getPorts(inboundPacket.receivedFrom().deviceId()).stream()
- .filter(p -> p.annotations().value(PORTNAME).equals(EXTERNAL_INTERFACE_NAME))
+ .filter(p -> p.annotations().value(PORTNAME).equals(config.gatewayExternalInterfaceName()))
.findAny().orElse(null);
}
@@ -239,7 +241,15 @@
flowObjectiveService.forward(inboundPacket.receivedFrom().deviceId(), fo);
}
- private ExtensionTreatment buildNiciraExtenstion(DeviceId id, Ip4Address hostIp) {
+ /**
+ * Returns NiciraExtension treatment.
+ *
+ * @param id device id
+ * @param hostIp host ip
+ * @return NiciraExtension treatment
+ */
+
+ public ExtensionTreatment buildNiciraExtenstion(DeviceId id, Ip4Address hostIp) {
Driver driver = driverService.getDriver(id);
DriverHandler driverHandler = new DefaultDriverHandler(new DefaultDriverData(driver, id));
ExtensionTreatmentResolver resolver = driverHandler.behaviour(ExtensionTreatmentResolver.class);
@@ -257,7 +267,13 @@
return extensionInstruction;
}
- private PortNumber getTunnelPort(DeviceId deviceId) {
+ /**
+ * Returns port number of vxlan tunnel.
+ *
+ * @param deviceId device id
+ * @return port number of vxlan tunnel
+ */
+ public PortNumber getTunnelPort(DeviceId deviceId) {
Port port = deviceService.getPorts(deviceId).stream()
.filter(p -> p.annotations().value(PORTNAME).equals(PORTNAME_PREFIX_TUNNEL))
.findAny().orElse(null);
@@ -343,16 +359,11 @@
}
private Device getGatewayNode() {
- return checkNotNull(StreamSupport.stream(deviceService.getAvailableDevices().spliterator(), false)
- .filter(d -> checkGatewayNode(d.id()))
- .findAny()
- .orElse(null));
+ return checkNotNull(deviceService.getDevice(DeviceId.deviceId(config.gatewayBridgeId())));
}
private boolean checkGatewayNode(DeviceId deviceId) {
- return !deviceService.getPorts(deviceId).stream().anyMatch(port ->
- port.annotations().value(PORTNAME).startsWith(PORTNAME_PREFIX_ROUTER) ||
- port.annotations().value(PORTNAME).startsWith(PORTNAME_PREFIX_VM));
+ return deviceId.toString().equals(config.gatewayBridgeId());
}
private long getVni(OpenstackPort openstackPort) {
diff --git a/apps/openstacknetworking/openstackswitching/src/main/java/org/onosproject/openstacknetworking/switching/OpenstackSwitchingManager.java b/apps/openstacknetworking/openstackswitching/src/main/java/org/onosproject/openstacknetworking/switching/OpenstackSwitchingManager.java
index 2c88b3a..7cc974f 100644
--- a/apps/openstacknetworking/openstackswitching/src/main/java/org/onosproject/openstacknetworking/switching/OpenstackSwitchingManager.java
+++ b/apps/openstacknetworking/openstackswitching/src/main/java/org/onosproject/openstacknetworking/switching/OpenstackSwitchingManager.java
@@ -94,6 +94,7 @@
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected DriverService driverService;
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected OpenstackInterfaceService openstackService;
public static final String PORTNAME_PREFIX_VM = "tap";
@@ -126,6 +127,7 @@
packetService.addProcessor(internalPacketProcessor, PacketProcessor.director(1));
deviceService.addListener(internalDeviceListener);
hostService.addListener(internalHostListener);
+ arpHandler = new OpenstackArpHandler(openstackService, packetService, hostService);
arpHandler = new OpenstackArpHandler(openstackService, packetService, hostService);
sgRulePopulator = new OpenstackSecurityGroupRulePopulator(appId, openstackService, flowObjectiveService);
@@ -204,10 +206,12 @@
@Override
public void createNetwork(OpenstackNetwork openstackNetwork) {
+ //TODO
}
@Override
public void createSubnet(OpenstackSubnet openstackSubnet) {
+ //TODO
}
@Override
@@ -296,9 +300,17 @@
private void updatePortMap(DeviceId deviceId, String portName, Collection<OpenstackNetwork> networks,
Collection<OpenstackSubnet> subnets, OpenstackPort openstackPort) {
- long vni = Long.parseLong(networks.stream()
+ long vni;
+ OpenstackNetwork openstackNetwork = networks.stream()
.filter(n -> n.id().equals(openstackPort.networkId()))
- .findAny().orElse(null).segmentId());
+ .findAny().orElse(null);
+ if (openstackNetwork != null) {
+ vni = Long.parseLong(openstackNetwork.segmentId());
+ } else {
+ log.debug("updatePortMap failed because there's no OpenstackNetwork matches {}", openstackPort.networkId());
+ return;
+ }
+
OpenstackSubnet openstackSubnet = subnets.stream()
.filter(n -> n.networkId().equals(openstackPort.networkId()))
@@ -312,6 +324,7 @@
.setHostMac(openstackPort.macAddress())
.setVni(vni)
.setGatewayIP(gatewayIPAddress)
+ .setNetworkId(openstackPort.networkId())
.setSecurityGroups(openstackPort.securityGroups());
openstackPortInfoMap.put(portName, portBuilder.build());
@@ -321,7 +334,6 @@
securityGroupMap.put(sgId, openstackService.getSecurityGroup(sgId));
}
});
-
}
private void processHostRemoved(Host host) {
@@ -440,6 +452,7 @@
processPortRemoved((Device) deviceEvent.subject(), deviceEvent.port());
break;
default:
+ log.debug("Unsupported deviceEvent type {}", deviceEvent.type().toString());
break;
}
} else if (event instanceof HostEvent) {
@@ -450,6 +463,7 @@
processHostRemoved((Host) hostEvent.subject());
break;
default:
+ log.debug("Unsupported hostEvent type {}", hostEvent.type().toString());
break;
}
}