Hyunsun Moon
Committed by Gerrit Code Review

CORD-416 Implemented ARP proxy for service IPs

Added ARP proxy which sends fake ARP reply for service IPs.

Change-Id: I0583ee994def2a429701c0375af5203bdfaa39c5
......@@ -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;
......@@ -135,6 +139,9 @@ public class CordVtn implements CordVtnService {
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 @@ 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<CordVtnNode, NodeState> nodeStore;
private Map<HostId, OpenstackNetwork> 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);
}
}
}
......
/*
* 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();
}
}