blob: 62f412ac0ec61c8a608221a21ce9c498506b7211 [file] [log] [blame]
/*
* Copyright 2015-2016 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.openstacknetworking.switching;
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.PacketService;
import org.onosproject.openstackinterface.OpenstackInterfaceService;
import org.onosproject.openstackinterface.OpenstackPort;
import org.onosproject.openstacknetworking.OpenstackPortInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.nio.ByteBuffer;
import java.util.Collection;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Handles ARP packet from VMs.
*/
public class OpenstackArpHandler {
private static Logger log = LoggerFactory
.getLogger(OpenstackArpHandler.class);
private static final MacAddress GATEWAY_MAC = MacAddress.valueOf("1f:1f:1f:1f:1f:1f");
private PacketService packetService;
private OpenstackInterfaceService openstackService;
private HostService hostService;
/**
* Returns OpenstackArpHandler reference.
*
* @param openstackService OpenstackNetworkingService reference
* @param packetService PacketService reference
* @param hostService host service
*/
public OpenstackArpHandler(OpenstackInterfaceService openstackService, PacketService packetService,
HostService hostService) {
this.openstackService = openstackService;
this.packetService = packetService;
this.hostService = hostService;
}
/**
* 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
* @param openstackPortInfoCollection collection of port information
*/
public void processPacketIn(InboundPacket pkt, Collection<OpenstackPortInfo> openstackPortInfoCollection) {
Ethernet ethRequest = pkt.parsed();
ARP arp = (ARP) ethRequest.getPayload();
if (arp.getOpCode() != ARP.OP_REQUEST) {
return;
}
IpAddress sourceIp = Ip4Address.valueOf(arp.getSenderProtocolAddress());
MacAddress srcMac = MacAddress.valueOf(arp.getSenderHardwareAddress());
OpenstackPortInfo portInfo = openstackPortInfoCollection.stream()
.filter(p -> p.ip().equals(sourceIp) && p.mac().equals(srcMac)).findFirst().orElse(null);
IpAddress targetIp = Ip4Address.valueOf(arp.getTargetProtocolAddress());
MacAddress dstMac;
if (targetIp.equals(portInfo == null ? null : portInfo.gatewayIP())) {
dstMac = GATEWAY_MAC;
} else {
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 = openstackService.ports()
.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;
}
}
}