CORD-416 Implemented ARP proxy for service IPs
Added ARP proxy which sends fake ARP reply for service IPs.
Change-Id: I0583ee994def2a429701c0375af5203bdfaa39c5
diff --git a/src/main/java/org/onosproject/cordvtn/CordVtn.java b/src/main/java/org/onosproject/cordvtn/CordVtn.java
index 127c7c9..5b3f8aa 100644
--- a/src/main/java/org/onosproject/cordvtn/CordVtn.java
+++ b/src/main/java/org/onosproject/cordvtn/CordVtn.java
@@ -23,6 +23,7 @@
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.apache.felix.scr.annotations.Service;
+import org.onlab.packet.Ethernet;
import org.onlab.packet.Ip4Address;
import org.onlab.util.ItemNotFoundException;
import org.onlab.packet.IpAddress;
@@ -56,6 +57,9 @@
import org.onosproject.net.host.HostEvent;
import org.onosproject.net.host.HostListener;
import org.onosproject.net.host.HostService;
+import org.onosproject.net.packet.PacketContext;
+import org.onosproject.net.packet.PacketProcessor;
+import org.onosproject.net.packet.PacketService;
import org.onosproject.openstackswitching.OpenstackNetwork;
import org.onosproject.openstackswitching.OpenstackPort;
import org.onosproject.openstackswitching.OpenstackSubnet;
@@ -135,6 +139,9 @@
protected FlowRuleService flowRuleService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected PacketService packetService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected OvsdbController controller;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
@@ -154,6 +161,7 @@
private final DeviceListener deviceListener = new InternalDeviceListener();
private final HostListener hostListener = new InternalHostListener();
+ private final PacketProcessor packetProcessor = new InternalPacketProcessor();
private final OvsdbHandler ovsdbHandler = new OvsdbHandler();
private final BridgeHandler bridgeHandler = new BridgeHandler();
@@ -163,6 +171,7 @@
private ConsistentMap<CordVtnNode, NodeState> nodeStore;
private Map<HostId, OpenstackNetwork> hostNetMap = Maps.newHashMap();
private CordVtnRuleInstaller ruleInstaller;
+ private CordVtnArpProxy arpProxy;
private enum NodeState {
@@ -223,6 +232,10 @@
mastershipService,
DEFAULT_TUNNEL);
+ arpProxy = new CordVtnArpProxy(appId, packetService);
+ packetService.addProcessor(packetProcessor, PacketProcessor.director(0));
+ arpProxy.requestPacket();
+
deviceService.addListener(deviceListener);
hostService.addListener(hostListener);
@@ -233,6 +246,7 @@
protected void deactivate() {
deviceService.removeListener(deviceListener);
hostService.removeListener(hostListener);
+ packetService.removeProcessor(packetProcessor);
eventExecutor.shutdown();
nodeStore.clear();
@@ -920,17 +934,18 @@
log.info("VM {} is detected", host.id());
hostNetMap.put(host.id(), vNet);
+ CordService service = getCordService(vNet);
+ if (service != null) {
+ // TODO check if the service needs an update on its group buckets after done CORD-433
+ ruleInstaller.updateServiceGroup(service);
+ arpProxy.addServiceIp(service.serviceIp());
+ }
+
ruleInstaller.populateBasicConnectionRules(
host,
hostIp,
checkNotNull(getRemoteIp(host.location().deviceId())).getIp4Address(),
vNet);
-
- CordService service = getCordService(vNet);
- // TODO check if the service needs an update on its group buckets after done CORD-433
- if (service != null) {
- ruleInstaller.updateServiceGroup(service);
- }
}
@Override
@@ -950,12 +965,37 @@
ruleInstaller.removeBasicConnectionRules(host);
CordService service = getCordService(vNet);
- // TODO check if the service needs an update on its group buckets after done CORD-433
if (service != null) {
+ // TODO check if the service needs an update on its group buckets after done CORD-433
ruleInstaller.updateServiceGroup(service);
+
+ if (getHostsWithOpenstackNetwork(vNet).isEmpty()) {
+ arpProxy.removeServiceIp(service.serviceIp());
+ }
}
hostNetMap.remove(host.id());
}
}
+
+ private class InternalPacketProcessor implements PacketProcessor {
+
+ @Override
+ public void process(PacketContext context) {
+ if (context.isHandled()) {
+ return;
+ }
+
+ Ethernet ethPacket = context.inPacket().parsed();
+ if (ethPacket == null) {
+ return;
+ }
+
+ if (ethPacket.getEtherType() != Ethernet.TYPE_ARP) {
+ return;
+ }
+
+ arpProxy.processArpPacket(context, ethPacket);
+ }
+ }
}
diff --git a/src/main/java/org/onosproject/cordvtn/CordVtnArpProxy.java b/src/main/java/org/onosproject/cordvtn/CordVtnArpProxy.java
new file mode 100644
index 0000000..4cef148
--- /dev/null
+++ b/src/main/java/org/onosproject/cordvtn/CordVtnArpProxy.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright 2014-2015 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.cordvtn;
+
+import com.google.common.collect.Sets;
+import org.onlab.packet.ARP;
+import org.onlab.packet.EthType;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.net.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.slf4j.Logger;
+
+import java.nio.ByteBuffer;
+import java.util.Optional;
+import java.util.Set;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Handles ARP requests for virtual network service IPs.
+ */
+public class CordVtnArpProxy {
+ protected final Logger log = getLogger(getClass());
+ // TODO make gateway MAC address configurable
+ private static final MacAddress DEFAULT_GATEWAY_MAC = MacAddress.valueOf("00:00:00:00:00:01");
+
+ private final ApplicationId appId;
+ private final PacketService packetService;
+
+ private Set<Ip4Address> serviceIPs = Sets.newHashSet();
+
+ /**
+ * Default constructor.
+ *
+ * @param appId application id
+ * @param packetService packet service
+ */
+ public CordVtnArpProxy(ApplicationId appId, PacketService packetService) {
+ this.appId = appId;
+ this.packetService = packetService;
+ }
+
+ /**
+ * Requests ARP packet.
+ */
+ public void requestPacket() {
+ TrafficSelector selector = DefaultTrafficSelector.builder()
+ .matchEthType(EthType.EtherType.ARP.ethType().toShort())
+ .build();
+
+ packetService.requestPackets(selector,
+ PacketPriority.CONTROL,
+ appId,
+ Optional.empty());
+ }
+
+ /**
+ * Cancels ARP packet.
+ */
+ public void cancelPacket() {
+ TrafficSelector selector = DefaultTrafficSelector.builder()
+ .matchEthType(EthType.EtherType.ARP.ethType().toShort())
+ .build();
+
+ packetService.cancelPackets(selector,
+ PacketPriority.CONTROL,
+ appId,
+ Optional.empty());
+ }
+
+ /**
+ * Adds a given service IP address to be served.
+ *
+ * @param serviceIp service ip
+ */
+ public void addServiceIp(IpAddress serviceIp) {
+ checkNotNull(serviceIp);
+ serviceIPs.add(serviceIp.getIp4Address());
+ }
+
+ /**
+ * Removes a given service IP address from this ARP proxy.
+ *
+ * @param serviceIp service ip
+ */
+ public void removeServiceIp(IpAddress serviceIp) {
+ checkNotNull(serviceIp);
+ serviceIPs.remove(serviceIp.getIp4Address());
+ }
+
+ /**
+ * Emits ARP reply with fake MAC address for a given ARP request.
+ * It only handles requests for the registered service IPs, and the other
+ * requests can be handled by other ARP handlers like openstackSwitching or
+ * proxyArp, for example.
+ *
+ * @param context packet context
+ * @param ethPacket ethernet packet
+ */
+ public void processArpPacket(PacketContext context, Ethernet ethPacket) {
+ ARP arpPacket = (ARP) ethPacket.getPayload();
+ Ip4Address targetIp = Ip4Address.valueOf(arpPacket.getTargetProtocolAddress());
+
+ if (arpPacket.getOpCode() != ARP.OP_REQUEST) {
+ return;
+ }
+
+ if (!serviceIPs.contains(targetIp)) {
+ return;
+ }
+
+ Ethernet ethReply = ARP.buildArpReply(
+ targetIp,
+ DEFAULT_GATEWAY_MAC,
+ ethPacket);
+
+ 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();
+ }
+}