[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/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();
+ }
+}