blob: 2d48d206bca7b320125bc5a3e2a31ba65166bf3f [file] [log] [blame]
Thomas Vachuska781d18b2014-10-27 10:31:25 -07001/*
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07002 * Copyright 2014 Open Networking Laboratory
Thomas Vachuska781d18b2014-10-27 10:31:25 -07003 *
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07004 * 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
Thomas Vachuska781d18b2014-10-27 10:31:25 -07007 *
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07008 * http://www.apache.org/licenses/LICENSE-2.0
9 *
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.
Thomas Vachuska781d18b2014-10-27 10:31:25 -070015 */
Brian O'Connorabafb502014-12-02 22:26:20 -080016package org.onosproject.provider.host.impl;
alshabibe1cf87d2014-10-17 09:23:50 -070017
Pavlin Radoslavov93b606b2015-02-25 17:28:39 -080018import static com.google.common.base.Strings.isNullOrEmpty;
Jonathan Harte8600eb2015-01-12 10:30:45 -080019import static org.slf4j.LoggerFactory.getLogger;
20
21import java.util.Dictionary;
22import java.util.Set;
23
alshabibe1cf87d2014-10-17 09:23:50 -070024import org.apache.felix.scr.annotations.Activate;
25import org.apache.felix.scr.annotations.Component;
26import org.apache.felix.scr.annotations.Deactivate;
Thomas Vachuska33601602014-11-19 03:32:15 -080027import org.apache.felix.scr.annotations.Modified;
28import org.apache.felix.scr.annotations.Property;
alshabibe1cf87d2014-10-17 09:23:50 -070029import org.apache.felix.scr.annotations.Reference;
30import org.apache.felix.scr.annotations.ReferenceCardinality;
Jonathan Harte8600eb2015-01-12 10:30:45 -080031import org.onlab.packet.ARP;
32import org.onlab.packet.Ethernet;
33import org.onlab.packet.IPacket;
Pavlin Radoslavovd6612f92015-02-23 13:53:32 -080034import org.onlab.packet.ICMP6;
Jonathan Harte8600eb2015-01-12 10:30:45 -080035import org.onlab.packet.IPv6;
36import org.onlab.packet.IpAddress;
37import org.onlab.packet.VlanId;
38import org.onlab.packet.ndp.NeighborAdvertisement;
39import org.onlab.packet.ndp.NeighborSolicitation;
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -080040import org.onosproject.core.ApplicationId;
41import org.onosproject.core.CoreService;
Brian O'Connorabafb502014-12-02 22:26:20 -080042import org.onosproject.net.ConnectPoint;
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -080043import org.onosproject.net.Device;
Brian O'Connorabafb502014-12-02 22:26:20 -080044import org.onosproject.net.Host;
45import org.onosproject.net.HostId;
46import org.onosproject.net.HostLocation;
47import org.onosproject.net.device.DeviceEvent;
48import org.onosproject.net.device.DeviceListener;
49import org.onosproject.net.device.DeviceService;
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -080050import org.onosproject.net.flow.DefaultTrafficSelector;
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -080051import org.onosproject.net.flow.TrafficSelector;
Brian O'Connorabafb502014-12-02 22:26:20 -080052import org.onosproject.net.host.DefaultHostDescription;
53import org.onosproject.net.host.HostDescription;
54import org.onosproject.net.host.HostProvider;
55import org.onosproject.net.host.HostProviderRegistry;
56import org.onosproject.net.host.HostProviderService;
57import org.onosproject.net.host.HostService;
58import org.onosproject.net.packet.PacketContext;
Jonathan Hart3cfce8e2015-01-14 16:43:27 -080059import org.onosproject.net.packet.PacketPriority;
Brian O'Connorabafb502014-12-02 22:26:20 -080060import org.onosproject.net.packet.PacketProcessor;
61import org.onosproject.net.packet.PacketService;
62import org.onosproject.net.provider.AbstractProvider;
63import org.onosproject.net.provider.ProviderId;
64import org.onosproject.net.topology.Topology;
65import org.onosproject.net.topology.TopologyService;
Thomas Vachuska33601602014-11-19 03:32:15 -080066import org.osgi.service.component.ComponentContext;
alshabibe1cf87d2014-10-17 09:23:50 -070067import org.slf4j.Logger;
68
alshabibe1cf87d2014-10-17 09:23:50 -070069/**
70 * Provider which uses an OpenFlow controller to detect network
71 * end-station hosts.
72 */
73@Component(immediate = true)
74public class HostLocationProvider extends AbstractProvider implements HostProvider {
75
76 private final Logger log = getLogger(getClass());
77
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -080078 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
79 protected CoreService coreService;
80
81 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
alshabibe1cf87d2014-10-17 09:23:50 -070082 protected HostProviderRegistry providerRegistry;
83
84 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Pavlin Radoslavovd6612f92015-02-23 13:53:32 -080085 protected PacketService packetService;
alshabibe1cf87d2014-10-17 09:23:50 -070086
87 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
88 protected TopologyService topologyService;
89
Thomas Vachuska33601602014-11-19 03:32:15 -080090 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
91 protected HostService hostService;
92
93 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
94 protected DeviceService deviceService;
95
alshabibe1cf87d2014-10-17 09:23:50 -070096 private HostProviderService providerService;
97
98 private final InternalHostProvider processor = new InternalHostProvider();
Thomas Vachuska33601602014-11-19 03:32:15 -080099 private final DeviceListener deviceListener = new InternalDeviceListener();
100
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -0800101 private ApplicationId appId;
102
Thomas Vachuska33601602014-11-19 03:32:15 -0800103 @Property(name = "hostRemovalEnabled", boolValue = true,
104 label = "Enable host removal on port/device down events")
105 private boolean hostRemovalEnabled = true;
alshabibe1cf87d2014-10-17 09:23:50 -0700106
Pavlin Radoslavov93b606b2015-02-25 17:28:39 -0800107 @Property(name = "ipv6NeighborDiscovery", boolValue = false,
108 label = "Enable using IPv6 Neighbor Discovery by the " +
109 "Host Location Provider; default is false")
110 private boolean ipv6NeighborDiscovery = false;
alshabibe1cf87d2014-10-17 09:23:50 -0700111
112 /**
113 * Creates an OpenFlow host provider.
114 */
115 public HostLocationProvider() {
Brian O'Connorabafb502014-12-02 22:26:20 -0800116 super(new ProviderId("of", "org.onosproject.provider.host"));
alshabibe1cf87d2014-10-17 09:23:50 -0700117 }
118
119 @Activate
Thomas Vachuska33601602014-11-19 03:32:15 -0800120 public void activate(ComponentContext context) {
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -0800121 appId =
122 coreService.registerApplication("org.onosproject.provider.host");
Pavlin Radoslavov93b606b2015-02-25 17:28:39 -0800123 readComponentConfiguration(context);
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -0800124
alshabibe1cf87d2014-10-17 09:23:50 -0700125 providerService = providerRegistry.register(this);
Pavlin Radoslavovd6612f92015-02-23 13:53:32 -0800126 packetService.addProcessor(processor, 1);
Thomas Vachuska33601602014-11-19 03:32:15 -0800127 deviceService.addListener(deviceListener);
Jonathan Hart3cfce8e2015-01-14 16:43:27 -0800128
129 TrafficSelector.Builder selectorBuilder =
130 DefaultTrafficSelector.builder();
131 selectorBuilder.matchEthType(Ethernet.TYPE_ARP);
Pavlin Radoslavovd6612f92015-02-23 13:53:32 -0800132 packetService.requestPackets(selectorBuilder.build(),
Jonathan Hart3cfce8e2015-01-14 16:43:27 -0800133 PacketPriority.CONTROL, appId);
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -0800134
Pavlin Radoslavov93b606b2015-02-25 17:28:39 -0800135 if (ipv6NeighborDiscovery) {
136 // IPv6 Neighbor Solicitation packet.
137 selectorBuilder = DefaultTrafficSelector.builder();
138 selectorBuilder.matchEthType(Ethernet.TYPE_IPV6);
139 selectorBuilder.matchIPProtocol(IPv6.PROTOCOL_ICMP6);
140 selectorBuilder.matchIcmpv6Type(ICMP6.NEIGHBOR_SOLICITATION);
141 packetService.requestPackets(selectorBuilder.build(),
142 PacketPriority.CONTROL, appId);
Pavlin Radoslavovd6612f92015-02-23 13:53:32 -0800143
Pavlin Radoslavov93b606b2015-02-25 17:28:39 -0800144 // IPv6 Neighbor Advertisement packet.
145 selectorBuilder = DefaultTrafficSelector.builder();
146 selectorBuilder.matchEthType(Ethernet.TYPE_IPV6);
147 selectorBuilder.matchIPProtocol(IPv6.PROTOCOL_ICMP6);
148 selectorBuilder.matchIcmpv6Type(ICMP6.NEIGHBOR_ADVERTISEMENT);
149 packetService.requestPackets(selectorBuilder.build(),
150 PacketPriority.CONTROL, appId);
151 }
Pavlin Radoslavovd6612f92015-02-23 13:53:32 -0800152
153 log.info("Started with Application ID {}", appId.id());
alshabibe1cf87d2014-10-17 09:23:50 -0700154 }
155
156 @Deactivate
157 public void deactivate() {
158 providerRegistry.unregister(this);
Pavlin Radoslavovd6612f92015-02-23 13:53:32 -0800159 packetService.removeProcessor(processor);
alshabibbfc6b722014-11-29 12:53:48 -0800160 deviceService.removeListener(deviceListener);
alshabibe1cf87d2014-10-17 09:23:50 -0700161 providerService = null;
162 log.info("Stopped");
163 }
164
Thomas Vachuska33601602014-11-19 03:32:15 -0800165 @Modified
166 public void modified(ComponentContext context) {
Pavlin Radoslavov93b606b2015-02-25 17:28:39 -0800167 readComponentConfiguration(context);
168 }
169
170 /**
171 * Extracts properties from the component configuration context.
172 *
173 * @param context the component context
174 */
175 private void readComponentConfiguration(ComponentContext context) {
176 Dictionary<?, ?> properties = context.getProperties();
177 Boolean flag;
178
179 flag = isPropertyEnabled(properties, "hostRemovalEnabled");
180 if (flag == null) {
181 log.info("Host removal on port/device down events is not configured, " +
182 "using current value of {}", hostRemovalEnabled);
183 } else {
184 hostRemovalEnabled = flag;
185 log.info("Configured. Host removal on port/device down events is {}",
186 hostRemovalEnabled ? "enabled" : "disabled");
Thomas Vachuska33601602014-11-19 03:32:15 -0800187 }
Pavlin Radoslavov93b606b2015-02-25 17:28:39 -0800188
189 flag = isPropertyEnabled(properties, "ipv6NeighborDiscovery");
190 if (flag == null) {
191 log.info("Using IPv6 Neighbor Discovery is not configured, " +
192 "using current value of {}", ipv6NeighborDiscovery);
193 } else {
194 ipv6NeighborDiscovery = flag;
195 log.info("Configured. Using IPv6 Neighbor Discovery is {}",
196 ipv6NeighborDiscovery ? "enabled" : "disabled");
197 }
198 }
199
200 /**
201 * Check property name is defined and set to true.
202 *
203 * @param properties properties to be looked up
204 * @param propertyName the name of the property to look up
205 * @return value when the propertyName is defined or return null
206 */
207 private static Boolean isPropertyEnabled(Dictionary<?, ?> properties,
208 String propertyName) {
209 Boolean value = null;
210 try {
211 String s = (String) properties.get(propertyName);
212 value = isNullOrEmpty(s) ? null : s.trim().equals("true");
213 } catch (ClassCastException e) {
214 // No propertyName defined.
215 value = null;
216 }
217 return value;
Thomas Vachuska33601602014-11-19 03:32:15 -0800218 }
219
alshabibe1cf87d2014-10-17 09:23:50 -0700220 @Override
221 public void triggerProbe(Host host) {
222 log.info("Triggering probe on device {}", host);
223 }
224
225 private class InternalHostProvider implements PacketProcessor {
226
227 @Override
228 public void process(PacketContext context) {
alshabib4a179dc2014-10-17 17:17:01 -0700229 if (context == null) {
230 return;
231 }
alshabibe1cf87d2014-10-17 09:23:50 -0700232 Ethernet eth = context.inPacket().parsed();
233
Jonathan Harte8600eb2015-01-12 10:30:45 -0800234 if (eth == null) {
235 return;
236 }
237
alshabibe1cf87d2014-10-17 09:23:50 -0700238 VlanId vlan = VlanId.vlanId(eth.getVlanID());
239 ConnectPoint heardOn = context.inPacket().receivedFrom();
240
241 // If this is not an edge port, bail out.
242 Topology topology = topologyService.currentTopology();
243 if (topologyService.isInfrastructure(topology, heardOn)) {
244 return;
245 }
246
Pavlin Radoslavovd6612f92015-02-23 13:53:32 -0800247 HostLocation hloc =
248 new HostLocation(heardOn, System.currentTimeMillis());
alshabibe1cf87d2014-10-17 09:23:50 -0700249
250 HostId hid = HostId.hostId(eth.getSourceMAC(), vlan);
251
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800252 // ARP: possible new hosts, update both location and IP
alshabibe1cf87d2014-10-17 09:23:50 -0700253 if (eth.getEtherType() == Ethernet.TYPE_ARP) {
254 ARP arp = (ARP) eth.getPayload();
Pavlin Radoslavovd6612f92015-02-23 13:53:32 -0800255 IpAddress ip = IpAddress.valueOf(IpAddress.Version.INET,
256 arp.getSenderProtocolAddress());
alshabibe1cf87d2014-10-17 09:23:50 -0700257 HostDescription hdescr =
Pavlin Radoslavovd6612f92015-02-23 13:53:32 -0800258 new DefaultHostDescription(eth.getSourceMAC(), vlan,
259 hloc, ip);
alshabibe1cf87d2014-10-17 09:23:50 -0700260 providerService.hostDetected(hid, hdescr);
261
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800262 // IPv4: update location only
alshabibe1cf87d2014-10-17 09:23:50 -0700263 } else if (eth.getEtherType() == Ethernet.TYPE_IPV4) {
alshabibe1cf87d2014-10-17 09:23:50 -0700264 HostDescription hdescr =
Pavlin Radoslavovd6612f92015-02-23 13:53:32 -0800265 new DefaultHostDescription(eth.getSourceMAC(), vlan, hloc);
alshabibe1cf87d2014-10-17 09:23:50 -0700266 providerService.hostDetected(hid, hdescr);
267
Pavlin Radoslavovd6612f92015-02-23 13:53:32 -0800268 //
269 // NeighborAdvertisement and NeighborSolicitation: possible
270 // new hosts, update both location and IP.
271 //
272 // IPv6: update location only
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800273 } else if (eth.getEtherType() == Ethernet.TYPE_IPV6) {
274 IpAddress ip = null;
275 IPv6 ipv6 = (IPv6) eth.getPayload();
276
277 IPacket iPkt = ipv6;
278 while (iPkt != null) {
Pavlin Radoslavovd6612f92015-02-23 13:53:32 -0800279 if (iPkt instanceof NeighborAdvertisement ||
280 iPkt instanceof NeighborSolicitation) {
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800281 IpAddress sourceAddress =
Pavlin Radoslavovd6612f92015-02-23 13:53:32 -0800282 IpAddress.valueOf(IpAddress.Version.INET6,
283 ipv6.getSourceAddress());
284 // Ignore DAD packets, in which source address is zero
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800285 if (!sourceAddress.isZero()) {
286 ip = sourceAddress;
287 break;
288 }
289 }
290 iPkt = iPkt.getPayload();
291 }
292 HostDescription hdescr = (ip == null) ?
Pavlin Radoslavovd6612f92015-02-23 13:53:32 -0800293 new DefaultHostDescription(eth.getSourceMAC(), vlan,
294 hloc) :
295 new DefaultHostDescription(eth.getSourceMAC(), vlan,
296 hloc, ip);
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800297 providerService.hostDetected(hid, hdescr);
alshabibe1cf87d2014-10-17 09:23:50 -0700298 }
299 }
300 }
Thomas Vachuska33601602014-11-19 03:32:15 -0800301
302 // Auxiliary listener to device events.
303 private class InternalDeviceListener implements DeviceListener {
304 @Override
305 public void event(DeviceEvent event) {
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -0800306 Device device = event.subject();
307 switch (event.type()) {
308 case DEVICE_ADDED:
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -0800309 break;
310 case DEVICE_AVAILABILITY_CHANGED:
311 if (hostRemovalEnabled &&
312 !deviceService.isAvailable(device.id())) {
313 removeHosts(hostService.getConnectedHosts(device.id()));
Thomas Vachuska33601602014-11-19 03:32:15 -0800314 }
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -0800315 break;
316 case DEVICE_SUSPENDED:
317 case DEVICE_UPDATED:
318 // Nothing to do?
319 break;
320 case DEVICE_REMOVED:
321 if (hostRemovalEnabled) {
322 removeHosts(hostService.getConnectedHosts(device.id()));
323 }
324 break;
325 case PORT_ADDED:
326 break;
327 case PORT_UPDATED:
328 if (hostRemovalEnabled) {
329 ConnectPoint point =
330 new ConnectPoint(device.id(), event.port().number());
331 removeHosts(hostService.getConnectedHosts(point));
332 }
333 break;
334 case PORT_REMOVED:
335 // Nothing to do?
336 break;
337 default:
338 break;
Thomas Vachuska33601602014-11-19 03:32:15 -0800339 }
340 }
341 }
342
343 // Signals host vanish for all specified hosts.
344 private void removeHosts(Set<Host> hosts) {
345 for (Host host : hosts) {
346 providerService.hostVanished(host.id());
347 }
348 }
349
alshabibe1cf87d2014-10-17 09:23:50 -0700350}