org.onosproject.net.neighbour.impl.NeighbourResolutionManager.java Source code

Java tutorial

Introduction

Here is the source code for org.onosproject.net.neighbour.impl.NeighbourResolutionManager.java

Source

/*
 * Copyright 2016-present Open Networking Foundation
 *
 * 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.net.neighbour.impl;

import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Multimaps;
import com.google.common.collect.SetMultimap;
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.Modified;
import org.apache.felix.scr.annotations.Property;
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.ICMP6;
import org.onlab.packet.IPv6;
import org.onlab.packet.IpAddress;
import org.onlab.packet.MacAddress;
import org.onlab.packet.VlanId;
import org.onlab.util.Tools;
import org.onosproject.cfg.ComponentConfigService;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
import org.onosproject.net.intf.Interface;
import org.onosproject.net.neighbour.NeighbourHandlerRegistration;
import org.onosproject.net.neighbour.NeighbourMessageActions;
import org.onosproject.net.neighbour.NeighbourMessageContext;
import org.onosproject.net.neighbour.NeighbourMessageHandler;
import org.onosproject.net.neighbour.NeighbourResolutionService;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.edge.EdgePortService;
import org.onosproject.net.flow.DefaultTrafficSelector;
import org.onosproject.net.flow.TrafficSelector;
import org.onosproject.net.host.HostService;
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.osgi.service.component.ComponentContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Collection;
import java.util.Dictionary;
import java.util.Iterator;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

import static com.google.common.base.Preconditions.checkNotNull;
import static org.onlab.packet.Ethernet.TYPE_ARP;
import static org.onlab.packet.Ethernet.TYPE_IPV6;
import static org.onlab.packet.ICMP6.NEIGHBOR_ADVERTISEMENT;
import static org.onlab.packet.ICMP6.NEIGHBOR_SOLICITATION;
import static org.onlab.packet.IPv6.PROTOCOL_ICMP6;
import static org.onosproject.net.packet.PacketPriority.CONTROL;

/**
 * Manages handlers for neighbour messages.
 */
@Service
@Component(immediate = true)
public class NeighbourResolutionManager implements NeighbourResolutionService {

    private final Logger log = LoggerFactory.getLogger(getClass());

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

    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
    protected HostService hostService;

    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
    protected EdgePortService edgeService;

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

    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
    protected ComponentConfigService componentConfigService;

    @Property(name = "arpEnabled", boolValue = true, label = "Enable Address resolution protocol")
    protected boolean arpEnabled = true;

    @Property(name = "ndpEnabled", boolValue = false, label = "Enable IPv6 neighbour discovery")
    protected boolean ndpEnabled = false;

    @Property(name = "requestInterceptsEnabled", boolValue = true, label = "Enable requesting packet intercepts")
    private boolean requestInterceptsEnabled = true;

    private static final String APP_NAME = "org.onosproject.neighbour";
    private ApplicationId appId;

    private final SetMultimap<ConnectPoint, NeighbourHandlerRegistration> packetHandlers = Multimaps
            .synchronizedSetMultimap(HashMultimap.create());

    private final InternalPacketProcessor processor = new InternalPacketProcessor();
    private NeighbourMessageActions actions;

    @Activate
    protected void activate(ComponentContext context) {
        appId = coreService.registerApplication(APP_NAME);

        componentConfigService.registerProperties(getClass());
        modified(context);

        actions = new DefaultNeighbourMessageActions(packetService, edgeService);

        packetService.addProcessor(processor, PacketProcessor.director(1));
    }

    @Deactivate
    protected void deactivate() {
        cancelPackets();
        packetService.removeProcessor(processor);
        componentConfigService.unregisterProperties(getClass(), false);
    }

    @Modified
    protected void modified(ComponentContext context) {
        Dictionary<?, ?> properties = context.getProperties();
        Boolean flag;

        flag = Tools.isPropertyEnabled(properties, "ndpEnabled");
        if (flag != null) {
            ndpEnabled = flag;
            log.info("IPv6 neighbor discovery is {}", ndpEnabled ? "enabled" : "disabled");
        }

        flag = Tools.isPropertyEnabled(properties, "arpEnabled");
        if (flag != null) {
            arpEnabled = flag;
            log.info("Address resolution protocol is {}", arpEnabled ? "enabled" : "disabled");
        }

        flag = Tools.isPropertyEnabled(properties, "requestInterceptsEnabled");
        if (flag == null) {
            log.info("Request intercepts is not configured, " + "using current value of {}",
                    requestInterceptsEnabled);
        } else {
            requestInterceptsEnabled = flag;
            log.info("Configured. Request intercepts is {}", requestInterceptsEnabled ? "enabled" : "disabled");
        }

        synchronized (packetHandlers) {
            if (!packetHandlers.isEmpty() && requestInterceptsEnabled) {
                requestPackets();
            } else {
                cancelPackets();
            }
        }
    }

    private void requestPackets() {
        if (arpEnabled) {
            packetService.requestPackets(buildArpSelector(), CONTROL, appId);
        } else {
            packetService.cancelPackets(buildArpSelector(), CONTROL, appId);
        }

        if (ndpEnabled) {
            packetService.requestPackets(buildNeighborSolicitationSelector(), CONTROL, appId);
            packetService.requestPackets(buildNeighborAdvertisementSelector(), CONTROL, appId);
        } else {
            packetService.cancelPackets(buildNeighborSolicitationSelector(), CONTROL, appId);
            packetService.cancelPackets(buildNeighborAdvertisementSelector(), CONTROL, appId);
        }
    }

    private void cancelPackets() {
        packetService.cancelPackets(buildArpSelector(), CONTROL, appId);
        packetService.cancelPackets(buildNeighborSolicitationSelector(), CONTROL, appId);
        packetService.cancelPackets(buildNeighborAdvertisementSelector(), CONTROL, appId);
    }

    private TrafficSelector buildArpSelector() {
        return DefaultTrafficSelector.builder().matchEthType(TYPE_ARP).build();
    }

    private TrafficSelector buildNeighborSolicitationSelector() {
        return DefaultTrafficSelector.builder().matchEthType(TYPE_IPV6).matchIPProtocol(PROTOCOL_ICMP6)
                .matchIcmpv6Type(NEIGHBOR_SOLICITATION).build();
    }

    private TrafficSelector buildNeighborAdvertisementSelector() {
        return DefaultTrafficSelector.builder().matchEthType(TYPE_IPV6).matchIPProtocol(PROTOCOL_ICMP6)
                .matchIcmpv6Type(NEIGHBOR_ADVERTISEMENT).build();
    }

    @Override
    public void registerNeighbourHandler(ConnectPoint connectPoint, NeighbourMessageHandler handler,
            ApplicationId appId) {
        register(connectPoint, new HandlerRegistration(handler, appId));
    }

    @Override
    public void registerNeighbourHandler(Interface intf, NeighbourMessageHandler handler, ApplicationId appId) {
        register(intf.connectPoint(), new HandlerRegistration(handler, intf, appId));
    }

    private void register(ConnectPoint connectPoint, HandlerRegistration registration) {
        synchronized (packetHandlers) {
            if (packetHandlers.isEmpty() && requestInterceptsEnabled) {
                requestPackets();
            }
            packetHandlers.put(connectPoint, registration);
        }
    }

    @Override
    public void unregisterNeighbourHandler(ConnectPoint connectPoint, NeighbourMessageHandler handler,
            ApplicationId appId) {
        unregister(connectPoint, new HandlerRegistration(handler, appId));
    }

    @Override
    public void unregisterNeighbourHandler(Interface intf, NeighbourMessageHandler handler, ApplicationId appId) {
        unregister(intf.connectPoint(), new HandlerRegistration(handler, intf, appId));
    }

    private void unregister(ConnectPoint connectPoint, HandlerRegistration registration) {
        synchronized (packetHandlers) {
            packetHandlers.remove(connectPoint, registration);

            if (packetHandlers.isEmpty()) {
                cancelPackets();
            }
        }
    }

    @Override
    public void unregisterNeighbourHandlers(ApplicationId appId) {
        synchronized (packetHandlers) {
            Iterator<NeighbourHandlerRegistration> it = packetHandlers.values().iterator();

            while (it.hasNext()) {
                NeighbourHandlerRegistration registration = it.next();
                if (registration.appId().equals(appId)) {
                    it.remove();
                }
            }

            if (packetHandlers.isEmpty()) {
                cancelPackets();
            }
        }
    }

    @Override
    public Map<ConnectPoint, Collection<NeighbourHandlerRegistration>> getHandlerRegistrations() {
        synchronized (packetHandlers) {
            return ImmutableMap.copyOf(Multimaps.asMap(packetHandlers));
        }
    }

    private void handlePacket(PacketContext context) {
        InboundPacket pkt = context.inPacket();
        Ethernet ethPkt = pkt.parsed();

        NeighbourMessageContext msgContext = DefaultNeighbourMessageContext.createContext(ethPkt,
                pkt.receivedFrom(), actions);

        if (msgContext == null) {
            return;
        }

        if (handleMessage(msgContext)) {
            context.block();
        }

    }

    private boolean handleMessage(NeighbourMessageContext context) {
        Collection<NeighbourHandlerRegistration> handled;
        synchronized (packetHandlers) {
            handled = packetHandlers.get(context.inPort()).stream()
                    .filter(registration -> registration.intf() == null || matches(context, registration.intf()))
                    .collect(Collectors.toSet());
        }
        handled.forEach(registration -> registration.handler().handleMessage(context, hostService));

        return !handled.isEmpty();
    }

    /**
     * Checks that incoming packet matches the parameters of the interface.
     * This means that if the interface specifies a particular parameter
     * (VLAN, IP address, etc.) then the incoming packet should match those
     * parameters.
     *
     * @param context incoming message context
     * @param intf interface to check
     * @return true if the incoming message matches the interface, otherwise false
     */
    private boolean matches(NeighbourMessageContext context, Interface intf) {
        checkNotNull(context);
        checkNotNull(intf);

        boolean matches = true;
        // For non-broadcast packets, if the interface has a MAC address check that
        // the destination MAC address of the packet matches the interface MAC
        if (!context.dstMac().isBroadcast() && !intf.mac().equals(MacAddress.NONE)
                && !intf.mac().equals(context.dstMac())) {
            matches = false;
        }
        // If the interface has a VLAN, check that the packet's VLAN matches
        if (!intf.vlan().equals(VlanId.NONE) && !intf.vlan().equals(context.vlan())) {
            matches = false;
        }
        // If the interface has IP addresses, check that the packet's target IP
        // address matches one of the interface IP addresses
        if (!intf.ipAddressesList().isEmpty() && !hasIp(intf, context.target())) {
            matches = false;
        }

        return matches;
    }

    /**
     * Returns true if the interface has the given IP address.
     *
     * @param intf interface to check
     * @param ip IP address
     * @return true if the IP is configured on the interface, otherwise false
     */
    private boolean hasIp(Interface intf, IpAddress ip) {
        return intf.ipAddressesList().stream().anyMatch(intfAddress -> intfAddress.ipAddress().equals(ip));
    }

    /**
     * Stores a neighbour message handler registration.
     */
    private class HandlerRegistration implements NeighbourHandlerRegistration {
        private final Interface intf;
        private final NeighbourMessageHandler handler;
        private final ApplicationId appId;

        /**
         * Creates a new handler registration.
         *
         * @param handler neighbour message handler
         */
        public HandlerRegistration(NeighbourMessageHandler handler, ApplicationId appId) {
            this(handler, null, appId);
        }

        /**
         * Creates a new handler registration.
         *
         * @param handler neighbour message handler
         * @param intf interface
         */
        public HandlerRegistration(NeighbourMessageHandler handler, Interface intf, ApplicationId appId) {
            this.intf = intf;
            this.handler = handler;
            this.appId = appId;
        }

        @Override
        public Interface intf() {
            return intf;
        }

        @Override
        public NeighbourMessageHandler handler() {
            return handler;
        }

        @Override
        public ApplicationId appId() {
            return appId;
        }

        @Override
        public boolean equals(Object other) {
            if (this == other) {
                return true;
            }

            if (!(other instanceof HandlerRegistration)) {
                return false;
            }

            HandlerRegistration that = (HandlerRegistration) other;

            return Objects.equals(intf, that.intf) && Objects.equals(handler, that.handler)
                    && Objects.equals(appId, that.appId);
        }

        @Override
        public int hashCode() {
            return Objects.hash(intf, handler, appId);
        }
    }

    /**
     * Packet processor for incoming packets.
     */
    private class InternalPacketProcessor implements PacketProcessor {

        @Override
        public void process(PacketContext context) {
            // Stop processing if the packet has been handled, since we
            // can't do any more to it.
            if (context.isHandled()) {
                return;
            }

            InboundPacket pkt = context.inPacket();
            Ethernet ethPkt = pkt.parsed();
            if (ethPkt == null) {
                return;
            }

            if (ethPkt.getEtherType() == TYPE_ARP) {
                // handle ARP packets
                handlePacket(context);
            } else if (ethPkt.getEtherType() == TYPE_IPV6) {
                IPv6 ipv6 = (IPv6) ethPkt.getPayload();
                if (ipv6.getNextHeader() == IPv6.PROTOCOL_ICMP6) {
                    ICMP6 icmp6 = (ICMP6) ipv6.getPayload();
                    if (icmp6.getIcmpType() == NEIGHBOR_SOLICITATION
                            || icmp6.getIcmpType() == NEIGHBOR_ADVERTISEMENT) {
                        // handle ICMPv6 solicitations and advertisements (NDP)
                        handlePacket(context);
                    }
                }
            }
        }
    }
}