2  * Copyright 2014-2015 Open Networking Laboratory
 
   4  * Licensed under the Apache License, Version 2.0 (the "License");
 
   5  * you may not use this file except in compliance with the License.
 
   6  * You may obtain a copy of the License at
 
   8  *     http://www.apache.org/licenses/LICENSE-2.0
 
  10  * Unless required by applicable law or agreed to in writing, software
 
  11  * distributed under the License is distributed on an "AS IS" BASIS,
 
  12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 
  13  * See the License for the specific language governing permissions and
 
  14  * limitations under the License.
 
  16 package org.onosproject.provider.host.impl;
 
  18 import org.apache.felix.scr.annotations.Activate;
 
  19 import org.apache.felix.scr.annotations.Component;
 
  20 import org.apache.felix.scr.annotations.Deactivate;
 
  21 import org.apache.felix.scr.annotations.Modified;
 
  22 import org.apache.felix.scr.annotations.Property;
 
  23 import org.apache.felix.scr.annotations.Reference;
 
  24 import org.apache.felix.scr.annotations.ReferenceCardinality;
 
  25 import org.onlab.packet.ARP;
 
  26 import org.onlab.packet.Ethernet;
 
  27 import org.onlab.packet.ICMP6;
 
  28 import org.onlab.packet.IPacket;
 
  29 import org.onlab.packet.IPv6;
 
  30 import org.onlab.packet.IpAddress;
 
  31 import org.onlab.packet.MacAddress;
 
  32 import org.onlab.packet.VlanId;
 
  33 import org.onlab.packet.ipv6.IExtensionHeader;
 
  34 import org.onlab.packet.ndp.NeighborAdvertisement;
 
  35 import org.onlab.packet.ndp.NeighborSolicitation;
 
  36 import org.onlab.packet.ndp.RouterAdvertisement;
 
  37 import org.onlab.packet.ndp.RouterSolicitation;
 
  38 import org.onosproject.cfg.ComponentConfigService;
 
  39 import org.onosproject.core.ApplicationId;
 
  40 import org.onosproject.core.CoreService;
 
  41 import org.onosproject.net.ConnectPoint;
 
  42 import org.onosproject.net.Device;
 
  43 import org.onosproject.net.Host;
 
  44 import org.onosproject.net.HostId;
 
  45 import org.onosproject.net.HostLocation;
 
  46 import org.onosproject.net.device.DeviceEvent;
 
  47 import org.onosproject.net.device.DeviceListener;
 
  48 import org.onosproject.net.device.DeviceService;
 
  49 import org.onosproject.net.flow.DefaultTrafficSelector;
 
  50 import org.onosproject.net.flow.TrafficSelector;
 
  51 import org.onosproject.net.host.DefaultHostDescription;
 
  52 import org.onosproject.net.host.HostDescription;
 
  53 import org.onosproject.net.host.HostProvider;
 
  54 import org.onosproject.net.host.HostProviderRegistry;
 
  55 import org.onosproject.net.host.HostProviderService;
 
  56 import org.onosproject.net.host.HostService;
 
  57 import org.onosproject.net.packet.PacketContext;
 
  58 import org.onosproject.net.packet.PacketPriority;
 
  59 import org.onosproject.net.packet.PacketProcessor;
 
  60 import org.onosproject.net.packet.PacketService;
 
  61 import org.onosproject.net.provider.AbstractProvider;
 
  62 import org.onosproject.net.provider.ProviderId;
 
  63 import org.onosproject.net.topology.Topology;
 
  64 import org.onosproject.net.topology.TopologyService;
 
  65 import org.osgi.service.component.ComponentContext;
 
  66 import org.slf4j.Logger;
 
  68 import java.util.Dictionary;
 
  71 import static com.google.common.base.Strings.isNullOrEmpty;
 
  72 import static org.slf4j.LoggerFactory.getLogger;
 
  75  * Provider which uses an OpenFlow controller to detect network end-station
 
  78 @Component(immediate = true)
 
  79 public class HostLocationProvider extends AbstractProvider implements HostProvider {
 
  81     private final Logger log = getLogger(getClass());
 
  83     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
 
  84     protected CoreService coreService;
 
  86     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
 
  87     protected HostProviderRegistry providerRegistry;
 
  89     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
 
  90     protected PacketService packetService;
 
  92     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
 
  93     protected TopologyService topologyService;
 
  95     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
 
  96     protected HostService hostService;
 
  98     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
 
  99     protected DeviceService deviceService;
 
 101     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
 
 102     protected ComponentConfigService cfgService;
 
 104     private HostProviderService providerService;
 
 106     private final InternalHostProvider processor = new InternalHostProvider();
 
 107     private final DeviceListener deviceListener = new InternalDeviceListener();
 
 109     private ApplicationId appId;
 
 111     @Property(name = "hostRemovalEnabled", boolValue = true,
 
 112             label = "Enable host removal on port/device down events")
 
 113     private boolean hostRemovalEnabled = true;
 
 115     @Property(name = "ipv6NeighborDiscovery", boolValue = false,
 
 116             label = "Enable using IPv6 Neighbor Discovery by the " +
 
 117                     "Host Location Provider; default is false")
 
 118     private boolean ipv6NeighborDiscovery = false;
 
 121      * Creates an OpenFlow host provider.
 
 123     public HostLocationProvider() {
 
 124         super(new ProviderId("of", "org.onosproject.provider.host"));
 
 128     public void activate(ComponentContext context) {
 
 129         cfgService.registerProperties(getClass());
 
 130         appId = coreService.registerApplication("org.onosproject.provider.host");
 
 132         providerService = providerRegistry.register(this);
 
 133         packetService.addProcessor(processor, PacketProcessor.advisor(1));
 
 134         deviceService.addListener(deviceListener);
 
 135         readComponentConfiguration(context);
 
 138         log.info("Started with Application ID {}", appId.id());
 
 142     public void deactivate() {
 
 143         cfgService.unregisterProperties(getClass(), false);
 
 145         withdrawIntercepts();
 
 147         providerRegistry.unregister(this);
 
 148         packetService.removeProcessor(processor);
 
 149         deviceService.removeListener(deviceListener);
 
 150         providerService = null;
 
 155     public void modified(ComponentContext context) {
 
 156         readComponentConfiguration(context);
 
 161      * Request packet intercepts.
 
 163     private void requestIntercepts() {
 
 164         TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
 
 165         selector.matchEthType(Ethernet.TYPE_ARP);
 
 166         packetService.requestPackets(selector.build(), PacketPriority.CONTROL, appId);
 
 168         // IPv6 Neighbor Solicitation packet.
 
 169         selector.matchEthType(Ethernet.TYPE_IPV6);
 
 170         selector.matchIPProtocol(IPv6.PROTOCOL_ICMP6);
 
 171         selector.matchIcmpv6Type(ICMP6.NEIGHBOR_SOLICITATION);
 
 172         if (ipv6NeighborDiscovery) {
 
 173             packetService.requestPackets(selector.build(), PacketPriority.CONTROL, appId);
 
 175             packetService.cancelPackets(selector.build(), PacketPriority.CONTROL, appId);
 
 178         // IPv6 Neighbor Advertisement packet.
 
 179         selector.matchIcmpv6Type(ICMP6.NEIGHBOR_ADVERTISEMENT);
 
 180         if (ipv6NeighborDiscovery) {
 
 181             packetService.requestPackets(selector.build(), PacketPriority.CONTROL, appId);
 
 183             packetService.cancelPackets(selector.build(), PacketPriority.CONTROL, appId);
 
 188      * Withdraw packet intercepts.
 
 190     private void withdrawIntercepts() {
 
 191         TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
 
 192         selector.matchEthType(Ethernet.TYPE_ARP);
 
 193         packetService.cancelPackets(selector.build(), PacketPriority.CONTROL, appId);
 
 195         // IPv6 Neighbor Solicitation packet.
 
 196         selector.matchEthType(Ethernet.TYPE_IPV6);
 
 197         selector.matchIPProtocol(IPv6.PROTOCOL_ICMP6);
 
 198         selector.matchIcmpv6Type(ICMP6.NEIGHBOR_SOLICITATION);
 
 199         packetService.cancelPackets(selector.build(), PacketPriority.CONTROL, appId);
 
 201         // IPv6 Neighbor Advertisement packet.
 
 202         selector.matchIcmpv6Type(ICMP6.NEIGHBOR_ADVERTISEMENT);
 
 203         packetService.cancelPackets(selector.build(), PacketPriority.CONTROL, appId);
 
 207      * Extracts properties from the component configuration context.
 
 209      * @param context the component context
 
 211     private void readComponentConfiguration(ComponentContext context) {
 
 212         Dictionary<?, ?> properties = context.getProperties();
 
 215         flag = isPropertyEnabled(properties, "hostRemovalEnabled");
 
 217             log.info("Host removal on port/device down events is not configured, " +
 
 218                              "using current value of {}", hostRemovalEnabled);
 
 220             hostRemovalEnabled = flag;
 
 221             log.info("Configured. Host removal on port/device down events is {}",
 
 222                      hostRemovalEnabled ? "enabled" : "disabled");
 
 225         flag = isPropertyEnabled(properties, "ipv6NeighborDiscovery");
 
 227             log.info("Using IPv6 Neighbor Discovery is not configured, " +
 
 228                              "using current value of {}", ipv6NeighborDiscovery);
 
 230             ipv6NeighborDiscovery = flag;
 
 231             log.info("Configured. Using IPv6 Neighbor Discovery is {}",
 
 232                      ipv6NeighborDiscovery ? "enabled" : "disabled");
 
 237      * Check property name is defined and set to true.
 
 239      * @param properties   properties to be looked up
 
 240      * @param propertyName the name of the property to look up
 
 241      * @return value when the propertyName is defined or return null
 
 243     private static Boolean isPropertyEnabled(Dictionary<?, ?> properties,
 
 244                                              String propertyName) {
 
 245         Boolean value = null;
 
 247             String s = (String) properties.get(propertyName);
 
 248             value = isNullOrEmpty(s) ? null : s.trim().equals("true");
 
 249         } catch (ClassCastException e) {
 
 250             // No propertyName defined.
 
 257     public void triggerProbe(Host host) {
 
 258         log.info("Triggering probe on device {}", host);
 
 261     private class InternalHostProvider implements PacketProcessor {
 
 263          * Update host location only.
 
 266          * @param mac  source Mac address
 
 267          * @param vlan VLAN ID
 
 268          * @param hloc host location
 
 270         private void updateLocation(HostId hid, MacAddress mac,
 
 271                                     VlanId vlan, HostLocation hloc) {
 
 272             HostDescription desc = new DefaultHostDescription(mac, vlan, hloc);
 
 274                 providerService.hostDetected(hid, desc);
 
 275             } catch (IllegalStateException e) {
 
 276                 log.debug("Host {} suppressed", hid);
 
 281          * Update host location and IP address.
 
 284          * @param mac  source Mac address
 
 285          * @param vlan VLAN ID
 
 286          * @param hloc host location
 
 287          * @param ip   source IP address
 
 289         private void updateLocationIP(HostId hid, MacAddress mac,
 
 290                                       VlanId vlan, HostLocation hloc,
 
 292             HostDescription desc = ip.isZero() || ip.isSelfAssigned() ?
 
 293                     new DefaultHostDescription(mac, vlan, hloc) :
 
 294                     new DefaultHostDescription(mac, vlan, hloc, ip);
 
 296                 providerService.hostDetected(hid, desc);
 
 297             } catch (IllegalStateException e) {
 
 298                 log.debug("Host {} suppressed", hid);
 
 303         public void process(PacketContext context) {
 
 304             if (context == null) {
 
 308             Ethernet eth = context.inPacket().parsed();
 
 312             MacAddress srcMac = eth.getSourceMAC();
 
 314             VlanId vlan = VlanId.vlanId(eth.getVlanID());
 
 315             ConnectPoint heardOn = context.inPacket().receivedFrom();
 
 317             // If this arrived on control port, bail out.
 
 318             if (heardOn.port().isLogical()) {
 
 322             // If this is not an edge port, bail out.
 
 323             Topology topology = topologyService.currentTopology();
 
 324             if (topologyService.isInfrastructure(topology, heardOn)) {
 
 328             HostLocation hloc = new HostLocation(heardOn, System.currentTimeMillis());
 
 329             HostId hid = HostId.hostId(eth.getSourceMAC(), vlan);
 
 331             // ARP: possible new hosts, update both location and IP
 
 332             if (eth.getEtherType() == Ethernet.TYPE_ARP) {
 
 333                 ARP arp = (ARP) eth.getPayload();
 
 334                 IpAddress ip = IpAddress.valueOf(IpAddress.Version.INET,
 
 335                                                  arp.getSenderProtocolAddress());
 
 336                 updateLocationIP(hid, srcMac, vlan, hloc, ip);
 
 338                 // IPv4: update location only
 
 339             } else if (eth.getEtherType() == Ethernet.TYPE_IPV4) {
 
 340                 updateLocation(hid, srcMac, vlan, hloc);
 
 343                 // NeighborAdvertisement and NeighborSolicitation: possible
 
 344                 // new hosts, update both location and IP.
 
 346                 // IPv6: update location only
 
 347             } else if (eth.getEtherType() == Ethernet.TYPE_IPV6) {
 
 348                 IPv6 ipv6 = (IPv6) eth.getPayload();
 
 349                 IpAddress ip = IpAddress.valueOf(IpAddress.Version.INET6,
 
 350                                                  ipv6.getSourceAddress());
 
 352                 // skip extension headers
 
 354                 while (pkt.getPayload() != null &&
 
 355                         pkt.getPayload() instanceof IExtensionHeader) {
 
 356                     pkt = pkt.getPayload();
 
 359                 // Neighbor Discovery Protocol
 
 360                 pkt = pkt.getPayload();
 
 361                 if (pkt != null && pkt instanceof ICMP6) {
 
 362                     pkt = pkt.getPayload();
 
 363                     // RouterSolicitation, RouterAdvertisement
 
 364                     if (pkt != null && (pkt instanceof RouterAdvertisement ||
 
 365                             pkt instanceof RouterSolicitation)) {
 
 368                     if (pkt != null && (pkt instanceof NeighborSolicitation ||
 
 369                             pkt instanceof NeighborAdvertisement)) {
 
 370                         // Duplicate Address Detection
 
 374                         // NeighborSolicitation, NeighborAdvertisement
 
 375                         updateLocationIP(hid, srcMac, vlan, hloc, ip);
 
 381                 if (eth.isMulticast()) {
 
 385                 // normal IPv6 packets
 
 386                 updateLocation(hid, srcMac, vlan, hloc);
 
 391     // Auxiliary listener to device events.
 
 392     private class InternalDeviceListener implements DeviceListener {
 
 394         public void event(DeviceEvent event) {
 
 395             Device device = event.subject();
 
 396             switch (event.type()) {
 
 399                 case DEVICE_AVAILABILITY_CHANGED:
 
 400                     if (hostRemovalEnabled &&
 
 401                             !deviceService.isAvailable(device.id())) {
 
 402                         removeHosts(hostService.getConnectedHosts(device.id()));
 
 405                 case DEVICE_SUSPENDED:
 
 410                     if (hostRemovalEnabled) {
 
 411                         removeHosts(hostService.getConnectedHosts(device.id()));
 
 417                     if (hostRemovalEnabled) {
 
 419                                 new ConnectPoint(device.id(), event.port().number());
 
 420                         removeHosts(hostService.getConnectedHosts(point));
 
 432     // Signals host vanish for all specified hosts.
 
 433     private void removeHosts(Set<Host> hosts) {
 
 434         for (Host host : hosts) {
 
 435             providerService.hostVanished(host.id());