VirtualPublicHosts.java 6.21 KB
/*
 * Copyright 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.virtualbng;

import static org.slf4j.LoggerFactory.getLogger;

import java.nio.ByteBuffer;

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.MacAddress;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
import org.onosproject.net.ConnectPoint;
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.InboundPacket;
import org.onosproject.net.packet.PacketContext;
import org.onosproject.net.packet.PacketPriority;
import org.onosproject.net.packet.PacketProcessor;
import org.onosproject.net.packet.PacketService;
import org.slf4j.Logger;

/**
 * When the upstream gateway which is outside local SDN network wants to send
 * packets to our local public IP addresses, it will send out ARP requests to
 * get the MAC address of each public IP address. Actually, there are no hosts
 * configured with those public IP addresses, so this class is to emulate the
 * behavior of the non-existed hosts and return ARP replies.
 * <p>
 * Since we will rewrite the destination MAC address in the switch before
 * traffic packets go to the destination, so the MAC address can be any number.
 * We manually configured a random MAC address for this purpose in the vBNG
 * configuration file.
 * </p>
 */
@Component(immediate = true)
public class VirtualPublicHosts {
    private final Logger log = getLogger(getClass());

    private static final String APP_NAME =
            "org.onosproject.virtualbng.VirtualPublicHosts";

    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
    protected CoreService coreService;

    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
    protected PacketService packetService;

    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
    protected VbngConfigurationService vbngConfigService;

    private ApplicationId appId;
    private ArpRequestProcessor processor = new ArpRequestProcessor();

    @Activate
    public void activate() {
        appId = coreService.registerApplication(APP_NAME);

        packetService.addProcessor(processor,
                                   PacketProcessor.ADVISOR_MAX + 6);
        requestIntercepts();
        log.info("vBNG virtual public hosts started");
    }

    @Deactivate
    public void deactivate() {
        withdrawIntercepts();
        packetService.removeProcessor(processor);
        processor = null;
        log.info("vBNG virtual public hosts Stopped");
    }

    /**
     * Request packet in via PacketService.
     */
    private void requestIntercepts() {
        TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
        // Only IPv4 is supported in current vBNG.
        selector.matchEthType(Ethernet.TYPE_ARP);
        packetService.requestPackets(selector.build(),
                                     PacketPriority.REACTIVE, appId);
    }

    /**
     * Cancel request for packet in via PacketService.
     */
    private void withdrawIntercepts() {
        TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
        // Only IPv4 is supported in current vBNG.
        selector.matchEthType(Ethernet.TYPE_ARP);
        packetService.cancelPackets(selector.build(),
                                    PacketPriority.REACTIVE, appId);
    }

    /**
     * This class filters out the ARP request packets, generates the ARP
     * reply packets, and emits those packets.
     */
    private class ArpRequestProcessor implements PacketProcessor {
        @Override
        public void process(PacketContext context) {

            InboundPacket pkt = context.inPacket();
            Ethernet ethPkt = pkt.parsed();

            // Only handle the ARP packets
            if (ethPkt == null || ethPkt.getEtherType() != Ethernet.TYPE_ARP) {
                return;
            }
            ARP arpPacket = (ARP) ethPkt.getPayload();
            // Only handle ARP request packets
            if (arpPacket.getOpCode() != ARP.OP_REQUEST) {
                return;
            }

            Ip4Address targetIpAddress = Ip4Address
                    .valueOf(arpPacket.getTargetProtocolAddress());

            // Only handle an ARP request when the target IP address inside is
            // an assigned public IP address
            if (!vbngConfigService.isAssignedPublicIpAddress(targetIpAddress)) {
                return;
            }

            MacAddress virtualHostMac =
                    vbngConfigService.getPublicFacingMac();
            if (virtualHostMac == null) {
                return;
            }

            ConnectPoint srcConnectPoint = pkt.receivedFrom();
            Ethernet eth = ARP.buildArpReply(targetIpAddress,
                                             virtualHostMac,
                                             ethPkt);

            TrafficTreatment.Builder builder =
                    DefaultTrafficTreatment.builder();
            builder.setOutput(srcConnectPoint.port());
            packetService.emit(new DefaultOutboundPacket(
                    srcConnectPoint.deviceId(),
                    builder.build(),
                    ByteBuffer.wrap(eth.serialize())));
        }
    }
}