diff --git a/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtn.java b/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtn.java index 127c7c99ad..5b3f8aa5bc 100644 --- a/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtn.java +++ b/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtn.java @@ -23,6 +23,7 @@ import org.apache.felix.scr.annotations.Deactivate; import org.apache.felix.scr.annotations.Reference; import org.apache.felix.scr.annotations.ReferenceCardinality; import org.apache.felix.scr.annotations.Service; +import org.onlab.packet.Ethernet; import org.onlab.packet.Ip4Address; import org.onlab.util.ItemNotFoundException; import org.onlab.packet.IpAddress; @@ -56,6 +57,9 @@ import org.onosproject.net.group.GroupService; 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; @@ -134,6 +138,9 @@ public class CordVtn implements CordVtnService { @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected FlowRuleService flowRuleService; + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected PacketService packetService; + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected OvsdbController controller; @@ -154,6 +161,7 @@ public class CordVtn implements CordVtnService { 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 @@ public class CordVtn implements CordVtnService { private ConsistentMap nodeStore; private Map hostNetMap = Maps.newHashMap(); private CordVtnRuleInstaller ruleInstaller; + private CordVtnArpProxy arpProxy; private enum NodeState { @@ -223,6 +232,10 @@ public class CordVtn implements CordVtnService { 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 @@ public class CordVtn implements CordVtnService { protected void deactivate() { deviceService.removeListener(deviceListener); hostService.removeListener(hostListener); + packetService.removeProcessor(packetProcessor); eventExecutor.shutdown(); nodeStore.clear(); @@ -920,17 +934,18 @@ public class CordVtn implements CordVtnService { 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 @@ public class CordVtn implements CordVtnService { 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/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtnArpProxy.java b/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtnArpProxy.java new file mode 100644 index 0000000000..4cef148830 --- /dev/null +++ b/apps/cordvtn/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 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(); + } +}