Hyunsun Moon
Committed by Gerrit Code Review

Improved openstackSwitching ARP handler

Save REST calls by checking if the target IP is owned by a known host first.

Change-Id: Id1ac0e5e13d635b5216d50c7cafaed1179a7410e
......@@ -18,12 +18,14 @@ package org.onosproject.openstackswitching;
import org.onlab.packet.ARP;
import org.onlab.packet.Ethernet;
import org.onlab.packet.Ip4Address;
import org.onlab.packet.IpAddress;
import org.onlab.packet.MacAddress;
import org.onosproject.net.Host;
import org.onosproject.net.flow.DefaultTrafficTreatment;
import org.onosproject.net.flow.TrafficTreatment;
import org.onosproject.net.host.HostService;
import org.onosproject.net.packet.DefaultOutboundPacket;
import org.onosproject.net.packet.InboundPacket;
import org.onosproject.net.packet.OutboundPacket;
import org.onosproject.net.packet.PacketService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
......@@ -40,69 +42,106 @@ public class OpenstackArpHandler {
.getLogger(OpenstackArpHandler.class);
private PacketService packetService;
private OpenstackRestHandler restHandler;
private HostService hostService;
/**
* Returns OpenstackArpHandler reference.
*
* @param restHandler rest API handler reference
* @param packetService PacketService reference
* @param hostService host service
*/
public OpenstackArpHandler(OpenstackRestHandler restHandler, PacketService packetService) {
public OpenstackArpHandler(OpenstackRestHandler restHandler, PacketService packetService,
HostService hostService) {
this.restHandler = checkNotNull(restHandler);
this.packetService = packetService;
this.hostService = hostService;
}
/**
* Processes ARP packets.
* Processes ARP request packets.
* It checks if the target IP is owned by a known host first and then ask to
* OpenStack if it's not. This ARP proxy does not support overlapping IP.
*
* @param pkt ARP request packet
*/
public void processPacketIn(InboundPacket pkt) {
Ethernet ethernet = pkt.parsed();
ARP arp = (ARP) ethernet.getPayload();
if (arp.getOpCode() == ARP.OP_REQUEST) {
byte[] srcMacAddress = arp.getSenderHardwareAddress();
byte[] srcIPAddress = arp.getSenderProtocolAddress();
byte[] dstIPAddress = arp.getTargetProtocolAddress();
//Searches the Dst MAC Address based on openstackPortMap
MacAddress macAddress = null;
OpenstackPort openstackPort = restHandler.getPorts().stream().
filter(e -> e.fixedIps().containsValue(Ip4Address.valueOf(
dstIPAddress))).findAny().orElse(null);
if (openstackPort != null) {
macAddress = openstackPort.macAddress();
log.debug("Found MACAddress: {}", macAddress.toString());
} else {
return;
}
//Creates a response packet
ARP arpReply = new ARP();
arpReply.setOpCode(ARP.OP_REPLY)
.setHardwareAddressLength(arp.getHardwareAddressLength())
.setHardwareType(arp.getHardwareType())
.setProtocolAddressLength(arp.getProtocolAddressLength())
.setProtocolType(arp.getProtocolType())
.setSenderHardwareAddress(macAddress.toBytes())
.setSenderProtocolAddress(dstIPAddress)
.setTargetHardwareAddress(srcMacAddress)
.setTargetProtocolAddress(srcIPAddress);
//Sends a response packet
ethernet.setDestinationMACAddress(srcMacAddress)
.setSourceMACAddress(macAddress)
.setEtherType(Ethernet.TYPE_ARP)
.setPayload(arpReply);
TrafficTreatment.Builder builder = DefaultTrafficTreatment.builder();
builder.setOutput(pkt.receivedFrom().port());
OutboundPacket packet = new DefaultOutboundPacket(pkt.receivedFrom().deviceId(),
builder.build(), ByteBuffer.wrap(ethernet.serialize()));
packetService.emit(packet);
Ethernet ethRequest = pkt.parsed();
ARP arp = (ARP) ethRequest.getPayload();
if (arp.getOpCode() != ARP.OP_REQUEST) {
return;
}
IpAddress targetIp = Ip4Address.valueOf(arp.getTargetProtocolAddress());
MacAddress dstMac = getMacFromHostService(targetIp);
if (dstMac == null) {
dstMac = getMacFromOpenstack(targetIp);
}
if (dstMac == null) {
log.debug("Failed to find MAC address for {}", targetIp.toString());
return;
}
Ethernet ethReply = ARP.buildArpReply(targetIp.getIp4Address(),
dstMac,
ethRequest);
TrafficTreatment treatment = DefaultTrafficTreatment.builder()
.setOutput(pkt.receivedFrom().port())
.build();
packetService.emit(new DefaultOutboundPacket(
pkt.receivedFrom().deviceId(),
treatment,
ByteBuffer.wrap(ethReply.serialize())));
}
/**
* Returns MAC address of a host with a given target IP address by asking to
* OpenStack. It does not support overlapping IP.
*
* @param targetIp target ip address
* @return mac address, or null if it fails to fetch the mac
*/
private MacAddress getMacFromOpenstack(IpAddress targetIp) {
checkNotNull(targetIp);
OpenstackPort openstackPort = restHandler.getPorts()
.stream()
.filter(port -> port.fixedIps().containsValue(targetIp))
.findFirst()
.orElse(null);
if (openstackPort != null) {
log.debug("Found MAC from OpenStack for {}", targetIp.toString());
return openstackPort.macAddress();
} else {
return null;
}
}
/**
* Returns MAC address of a host with a given target IP address by asking to
* host service. It does not support overlapping IP.
*
* @param targetIp target ip
* @return mac address, or null if it fails to find the mac
*/
private MacAddress getMacFromHostService(IpAddress targetIp) {
checkNotNull(targetIp);
Host host = hostService.getHostsByIp(targetIp)
.stream()
.findFirst()
.orElse(null);
if (host != null) {
log.debug("Found MAC from host service for {}", targetIp.toString());
return host.mac();
} else {
return null;
}
}
}
......
......@@ -394,7 +394,7 @@ public class OpenstackSwitchingManager implements OpenstackSwitchingService {
InboundPacket pkt = context.inPacket();
Ethernet ethernet = pkt.parsed();
if (ethernet.getEtherType() == Ethernet.TYPE_ARP) {
if (ethernet != null && ethernet.getEtherType() == Ethernet.TYPE_ARP) {
arpHandler.processPacketIn(pkt);
}
}
......@@ -483,7 +483,7 @@ public class OpenstackSwitchingManager implements OpenstackSwitchingService {
}
doNotPushFlows = cfg.doNotPushFlows();
restHandler = new OpenstackRestHandler(cfg);
arpHandler = new OpenstackArpHandler(restHandler, packetService);
arpHandler = new OpenstackArpHandler(restHandler, packetService, hostService);
initializeFlowRules();
}
......