/* * Copyright 2016-present 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.impl; import org.apache.felix.scr.annotations.Activate; import org.apache.felix.scr.annotations.Component; import org.apache.felix.scr.annotations.Deactivate; import org.apache.felix.scr.annotations.Reference; import org.apache.felix.scr.annotations.ReferenceCardinality; 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.flow.DefaultTrafficTreatment; import org.onosproject.net.flow.TrafficTreatment; import org.onosproject.net.packet.DefaultOutboundPacket; import org.onosproject.net.packet.InboundPacket; import org.onosproject.net.packet.PacketContext; import org.onosproject.net.packet.PacketProcessor; import org.onosproject.net.packet.PacketService; import org.onosproject.openstacknetworking.api.OpenstackNetworkService; import org.onosproject.openstacknetworking.api.Constants; import org.onosproject.openstacknode.OpenstackNodeService; import org.slf4j.Logger; import java.nio.ByteBuffer; import java.util.Objects; import java.util.concurrent.ExecutorService; import static java.util.concurrent.Executors.newSingleThreadExecutor; import static org.onlab.util.Tools.groupedThreads; import static org.slf4j.LoggerFactory.getLogger; /** * Handle ARP requests from gateway nodes. */ @Component(immediate = true) public class OpenstackRoutingArpHandler { private final Logger log = getLogger(getClass()); private static final String DEVICE_OWNER_ROUTER_GW = "network:router_gateway"; private static final String DEVICE_OWNER_FLOATING_IP = "network:floatingip"; @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected PacketService packetService; @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected OpenstackNetworkService osNetworkService; @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected OpenstackNodeService osNodeService; private final ExecutorService eventExecutor = newSingleThreadExecutor( groupedThreads(this.getClass().getSimpleName(), "event-handler", log)); private final InternalPacketProcessor packetProcessor = new InternalPacketProcessor(); @Activate protected void activate() { packetService.addProcessor(packetProcessor, PacketProcessor.director(1)); log.info("Started"); } @Deactivate protected void deactivate() { packetService.removeProcessor(packetProcessor); eventExecutor.shutdown(); log.info("Stopped"); } private void processArpPacket(PacketContext context, Ethernet ethernet) { ARP arp = (ARP) ethernet.getPayload(); if (arp.getOpCode() != ARP.OP_REQUEST) { return; } if (log.isTraceEnabled()) { log.trace("ARP request received from {} for {}", Ip4Address.valueOf(arp.getSenderProtocolAddress()).toString(), Ip4Address.valueOf(arp.getTargetProtocolAddress()).toString()); } IpAddress targetIp = Ip4Address.valueOf(arp.getTargetProtocolAddress()); if (!isServiceIp(targetIp.getIp4Address())) { log.trace("Unknown target ARP request for {}, ignore it", targetIp); return; } MacAddress targetMac = Constants.DEFAULT_EXTERNAL_ROUTER_MAC; Ethernet ethReply = ARP.buildArpReply(targetIp.getIp4Address(), targetMac, ethernet); 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(); } private class InternalPacketProcessor implements PacketProcessor { @Override public void process(PacketContext context) { if (context.isHandled()) { return; } else if (!osNodeService.gatewayDeviceIds().contains( context.inPacket().receivedFrom().deviceId())) { // return if the packet is not from gateway nodes return; } InboundPacket pkt = context.inPacket(); Ethernet ethernet = pkt.parsed(); if (ethernet != null && ethernet.getEtherType() == Ethernet.TYPE_ARP) { eventExecutor.execute(() -> processArpPacket(context, ethernet)); } } } private boolean isServiceIp(IpAddress targetIp) { // FIXME use floating IP and external gateway information of router instead // once openstack4j fixed return osNetworkService.ports().stream() .filter(osPort -> Objects.equals(osPort.getDeviceOwner(), DEVICE_OWNER_ROUTER_GW) || Objects.equals(osPort.getDeviceOwner(), DEVICE_OWNER_FLOATING_IP)) .flatMap(osPort -> osPort.getFixedIps().stream()) .anyMatch(ip -> IpAddress.valueOf(ip.getIpAddress()).equals(targetIp)); } }