blob: ed64bbcb0e2751168bca3f21ed6c16267c5dbdca [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
18import org.apache.felix.scr.annotations.Activate;
19import org.apache.felix.scr.annotations.Component;
20import org.apache.felix.scr.annotations.Deactivate;
Thomas Vachuska33601602014-11-19 03:32:15 -080021import org.apache.felix.scr.annotations.Modified;
22import org.apache.felix.scr.annotations.Property;
alshabibe1cf87d2014-10-17 09:23:50 -070023import org.apache.felix.scr.annotations.Reference;
24import org.apache.felix.scr.annotations.ReferenceCardinality;
Jonathan Harte8600eb2015-01-12 10:30:45 -080025import org.onlab.packet.ARP;
26import org.onlab.packet.Ethernet;
Pavlin Radoslavovd6612f92015-02-23 13:53:32 -080027import org.onlab.packet.ICMP6;
Thomas Vachuskaf845cf62015-03-24 10:13:09 -070028import org.onlab.packet.IPacket;
Jonathan Harte8600eb2015-01-12 10:30:45 -080029import org.onlab.packet.IPv6;
30import org.onlab.packet.IpAddress;
31import org.onlab.packet.VlanId;
32import org.onlab.packet.ndp.NeighborAdvertisement;
33import org.onlab.packet.ndp.NeighborSolicitation;
Charles M.C. Chan441d7da2015-03-17 21:03:39 +080034import org.onlab.packet.ndp.RouterAdvertisement;
35import org.onlab.packet.ndp.RouterSolicitation;
Thomas Vachuska6519e6f2015-03-11 02:29:31 -070036import org.onosproject.cfg.ComponentConfigService;
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -080037import org.onosproject.core.ApplicationId;
38import org.onosproject.core.CoreService;
Brian O'Connorabafb502014-12-02 22:26:20 -080039import org.onosproject.net.ConnectPoint;
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -080040import org.onosproject.net.Device;
Brian O'Connorabafb502014-12-02 22:26:20 -080041import org.onosproject.net.Host;
42import org.onosproject.net.HostId;
43import org.onosproject.net.HostLocation;
44import org.onosproject.net.device.DeviceEvent;
45import org.onosproject.net.device.DeviceListener;
46import org.onosproject.net.device.DeviceService;
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -080047import org.onosproject.net.flow.DefaultTrafficSelector;
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -080048import org.onosproject.net.flow.TrafficSelector;
Brian O'Connorabafb502014-12-02 22:26:20 -080049import org.onosproject.net.host.DefaultHostDescription;
50import org.onosproject.net.host.HostDescription;
51import org.onosproject.net.host.HostProvider;
52import org.onosproject.net.host.HostProviderRegistry;
53import org.onosproject.net.host.HostProviderService;
54import org.onosproject.net.host.HostService;
55import org.onosproject.net.packet.PacketContext;
Jonathan Hart3cfce8e2015-01-14 16:43:27 -080056import org.onosproject.net.packet.PacketPriority;
Brian O'Connorabafb502014-12-02 22:26:20 -080057import org.onosproject.net.packet.PacketProcessor;
58import org.onosproject.net.packet.PacketService;
59import org.onosproject.net.provider.AbstractProvider;
60import org.onosproject.net.provider.ProviderId;
61import org.onosproject.net.topology.Topology;
62import org.onosproject.net.topology.TopologyService;
Thomas Vachuska33601602014-11-19 03:32:15 -080063import org.osgi.service.component.ComponentContext;
alshabibe1cf87d2014-10-17 09:23:50 -070064import org.slf4j.Logger;
65
Thomas Vachuskaf845cf62015-03-24 10:13:09 -070066import java.util.Dictionary;
67import java.util.Set;
68
69import static com.google.common.base.Strings.isNullOrEmpty;
70import static org.slf4j.LoggerFactory.getLogger;
71
alshabibe1cf87d2014-10-17 09:23:50 -070072/**
73 * Provider which uses an OpenFlow controller to detect network
74 * end-station hosts.
75 */
76@Component(immediate = true)
77public class HostLocationProvider extends AbstractProvider implements HostProvider {
78
79 private final Logger log = getLogger(getClass());
80
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -080081 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
82 protected CoreService coreService;
83
84 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
alshabibe1cf87d2014-10-17 09:23:50 -070085 protected HostProviderRegistry providerRegistry;
86
87 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Pavlin Radoslavovd6612f92015-02-23 13:53:32 -080088 protected PacketService packetService;
alshabibe1cf87d2014-10-17 09:23:50 -070089
90 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
91 protected TopologyService topologyService;
92
Thomas Vachuska33601602014-11-19 03:32:15 -080093 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
94 protected HostService hostService;
95
96 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
97 protected DeviceService deviceService;
98
Thomas Vachuska6519e6f2015-03-11 02:29:31 -070099 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
100 protected ComponentConfigService cfgService;
101
alshabibe1cf87d2014-10-17 09:23:50 -0700102 private HostProviderService providerService;
103
104 private final InternalHostProvider processor = new InternalHostProvider();
Thomas Vachuska33601602014-11-19 03:32:15 -0800105 private final DeviceListener deviceListener = new InternalDeviceListener();
106
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -0800107 private ApplicationId appId;
108
Thomas Vachuska33601602014-11-19 03:32:15 -0800109 @Property(name = "hostRemovalEnabled", boolValue = true,
110 label = "Enable host removal on port/device down events")
111 private boolean hostRemovalEnabled = true;
alshabibe1cf87d2014-10-17 09:23:50 -0700112
Pavlin Radoslavov93b606b2015-02-25 17:28:39 -0800113 @Property(name = "ipv6NeighborDiscovery", boolValue = false,
114 label = "Enable using IPv6 Neighbor Discovery by the " +
115 "Host Location Provider; default is false")
116 private boolean ipv6NeighborDiscovery = false;
alshabibe1cf87d2014-10-17 09:23:50 -0700117
118 /**
119 * Creates an OpenFlow host provider.
120 */
121 public HostLocationProvider() {
Brian O'Connorabafb502014-12-02 22:26:20 -0800122 super(new ProviderId("of", "org.onosproject.provider.host"));
alshabibe1cf87d2014-10-17 09:23:50 -0700123 }
124
125 @Activate
Thomas Vachuska33601602014-11-19 03:32:15 -0800126 public void activate(ComponentContext context) {
Thomas Vachuska6519e6f2015-03-11 02:29:31 -0700127 cfgService.registerProperties(getClass());
128 appId = coreService.registerApplication("org.onosproject.provider.host");
Pavlin Radoslavov93b606b2015-02-25 17:28:39 -0800129 readComponentConfiguration(context);
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -0800130
alshabibe1cf87d2014-10-17 09:23:50 -0700131 providerService = providerRegistry.register(this);
Pavlin Radoslavovd6612f92015-02-23 13:53:32 -0800132 packetService.addProcessor(processor, 1);
Thomas Vachuska33601602014-11-19 03:32:15 -0800133 deviceService.addListener(deviceListener);
Jonathan Hart3cfce8e2015-01-14 16:43:27 -0800134
135 TrafficSelector.Builder selectorBuilder =
136 DefaultTrafficSelector.builder();
137 selectorBuilder.matchEthType(Ethernet.TYPE_ARP);
Pavlin Radoslavovd6612f92015-02-23 13:53:32 -0800138 packetService.requestPackets(selectorBuilder.build(),
Jonathan Hart3cfce8e2015-01-14 16:43:27 -0800139 PacketPriority.CONTROL, appId);
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -0800140
Pavlin Radoslavov93b606b2015-02-25 17:28:39 -0800141 if (ipv6NeighborDiscovery) {
142 // IPv6 Neighbor Solicitation packet.
143 selectorBuilder = DefaultTrafficSelector.builder();
144 selectorBuilder.matchEthType(Ethernet.TYPE_IPV6);
145 selectorBuilder.matchIPProtocol(IPv6.PROTOCOL_ICMP6);
146 selectorBuilder.matchIcmpv6Type(ICMP6.NEIGHBOR_SOLICITATION);
147 packetService.requestPackets(selectorBuilder.build(),
148 PacketPriority.CONTROL, appId);
Pavlin Radoslavovd6612f92015-02-23 13:53:32 -0800149
Pavlin Radoslavov93b606b2015-02-25 17:28:39 -0800150 // IPv6 Neighbor Advertisement packet.
151 selectorBuilder = DefaultTrafficSelector.builder();
152 selectorBuilder.matchEthType(Ethernet.TYPE_IPV6);
153 selectorBuilder.matchIPProtocol(IPv6.PROTOCOL_ICMP6);
154 selectorBuilder.matchIcmpv6Type(ICMP6.NEIGHBOR_ADVERTISEMENT);
155 packetService.requestPackets(selectorBuilder.build(),
156 PacketPriority.CONTROL, appId);
157 }
Pavlin Radoslavovd6612f92015-02-23 13:53:32 -0800158
159 log.info("Started with Application ID {}", appId.id());
alshabibe1cf87d2014-10-17 09:23:50 -0700160 }
161
162 @Deactivate
163 public void deactivate() {
Thomas Vachuska6519e6f2015-03-11 02:29:31 -0700164 cfgService.unregisterProperties(getClass(), false);
alshabibe1cf87d2014-10-17 09:23:50 -0700165 providerRegistry.unregister(this);
Pavlin Radoslavovd6612f92015-02-23 13:53:32 -0800166 packetService.removeProcessor(processor);
alshabibbfc6b722014-11-29 12:53:48 -0800167 deviceService.removeListener(deviceListener);
alshabibe1cf87d2014-10-17 09:23:50 -0700168 providerService = null;
169 log.info("Stopped");
170 }
171
Thomas Vachuska33601602014-11-19 03:32:15 -0800172 @Modified
173 public void modified(ComponentContext context) {
Pavlin Radoslavov93b606b2015-02-25 17:28:39 -0800174 readComponentConfiguration(context);
175 }
176
177 /**
178 * Extracts properties from the component configuration context.
179 *
180 * @param context the component context
181 */
182 private void readComponentConfiguration(ComponentContext context) {
183 Dictionary<?, ?> properties = context.getProperties();
184 Boolean flag;
185
186 flag = isPropertyEnabled(properties, "hostRemovalEnabled");
187 if (flag == null) {
188 log.info("Host removal on port/device down events is not configured, " +
189 "using current value of {}", hostRemovalEnabled);
190 } else {
191 hostRemovalEnabled = flag;
192 log.info("Configured. Host removal on port/device down events is {}",
193 hostRemovalEnabled ? "enabled" : "disabled");
Thomas Vachuska33601602014-11-19 03:32:15 -0800194 }
Pavlin Radoslavov93b606b2015-02-25 17:28:39 -0800195
196 flag = isPropertyEnabled(properties, "ipv6NeighborDiscovery");
197 if (flag == null) {
198 log.info("Using IPv6 Neighbor Discovery is not configured, " +
199 "using current value of {}", ipv6NeighborDiscovery);
200 } else {
201 ipv6NeighborDiscovery = flag;
202 log.info("Configured. Using IPv6 Neighbor Discovery is {}",
203 ipv6NeighborDiscovery ? "enabled" : "disabled");
204 }
205 }
206
207 /**
208 * Check property name is defined and set to true.
209 *
210 * @param properties properties to be looked up
211 * @param propertyName the name of the property to look up
212 * @return value when the propertyName is defined or return null
213 */
214 private static Boolean isPropertyEnabled(Dictionary<?, ?> properties,
215 String propertyName) {
216 Boolean value = null;
217 try {
218 String s = (String) properties.get(propertyName);
219 value = isNullOrEmpty(s) ? null : s.trim().equals("true");
220 } catch (ClassCastException e) {
221 // No propertyName defined.
222 value = null;
223 }
224 return value;
Thomas Vachuska33601602014-11-19 03:32:15 -0800225 }
226
alshabibe1cf87d2014-10-17 09:23:50 -0700227 @Override
228 public void triggerProbe(Host host) {
229 log.info("Triggering probe on device {}", host);
230 }
231
232 private class InternalHostProvider implements PacketProcessor {
233
234 @Override
235 public void process(PacketContext context) {
alshabib4a179dc2014-10-17 17:17:01 -0700236 if (context == null) {
237 return;
238 }
alshabibe1cf87d2014-10-17 09:23:50 -0700239
Thomas Vachuskaf845cf62015-03-24 10:13:09 -0700240 Ethernet eth = context.inPacket().parsed();
Jonathan Harte8600eb2015-01-12 10:30:45 -0800241 if (eth == null) {
242 return;
243 }
244
alshabibe1cf87d2014-10-17 09:23:50 -0700245 VlanId vlan = VlanId.vlanId(eth.getVlanID());
246 ConnectPoint heardOn = context.inPacket().receivedFrom();
247
Thomas Vachuskaf845cf62015-03-24 10:13:09 -0700248 // If this arrived on control port, bail out.
249 if (heardOn.port().isLogical()) {
250 return;
251 }
252
alshabibe1cf87d2014-10-17 09:23:50 -0700253 // If this is not an edge port, bail out.
254 Topology topology = topologyService.currentTopology();
255 if (topologyService.isInfrastructure(topology, heardOn)) {
256 return;
257 }
258
Pavlin Radoslavovd6612f92015-02-23 13:53:32 -0800259 HostLocation hloc =
260 new HostLocation(heardOn, System.currentTimeMillis());
alshabibe1cf87d2014-10-17 09:23:50 -0700261
262 HostId hid = HostId.hostId(eth.getSourceMAC(), vlan);
263
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800264 // ARP: possible new hosts, update both location and IP
alshabibe1cf87d2014-10-17 09:23:50 -0700265 if (eth.getEtherType() == Ethernet.TYPE_ARP) {
266 ARP arp = (ARP) eth.getPayload();
Pavlin Radoslavovd6612f92015-02-23 13:53:32 -0800267 IpAddress ip = IpAddress.valueOf(IpAddress.Version.INET,
268 arp.getSenderProtocolAddress());
alshabibe1cf87d2014-10-17 09:23:50 -0700269 HostDescription hdescr =
Pavlin Radoslavovd6612f92015-02-23 13:53:32 -0800270 new DefaultHostDescription(eth.getSourceMAC(), vlan,
271 hloc, ip);
alshabibe1cf87d2014-10-17 09:23:50 -0700272 providerService.hostDetected(hid, hdescr);
273
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800274 // IPv4: update location only
alshabibe1cf87d2014-10-17 09:23:50 -0700275 } else if (eth.getEtherType() == Ethernet.TYPE_IPV4) {
alshabibe1cf87d2014-10-17 09:23:50 -0700276 HostDescription hdescr =
Pavlin Radoslavovd6612f92015-02-23 13:53:32 -0800277 new DefaultHostDescription(eth.getSourceMAC(), vlan, hloc);
alshabibe1cf87d2014-10-17 09:23:50 -0700278 providerService.hostDetected(hid, hdescr);
279
Charles M.C. Chan441d7da2015-03-17 21:03:39 +0800280 //
281 // NeighborAdvertisement and NeighborSolicitation: possible
282 // new hosts, update both location and IP.
283 //
284 // IPv6: update location only
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800285 } else if (eth.getEtherType() == Ethernet.TYPE_IPV6) {
286 IpAddress ip = null;
287 IPv6 ipv6 = (IPv6) eth.getPayload();
288
289 IPacket iPkt = ipv6;
290 while (iPkt != null) {
Charles M.C. Chan441d7da2015-03-17 21:03:39 +0800291 // Ignore Router Solicitation and Advertisement
292 if (iPkt instanceof RouterAdvertisement ||
293 iPkt instanceof RouterSolicitation) {
294 return;
295 }
Pavlin Radoslavovd6612f92015-02-23 13:53:32 -0800296 if (iPkt instanceof NeighborAdvertisement ||
297 iPkt instanceof NeighborSolicitation) {
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800298 IpAddress sourceAddress =
Pavlin Radoslavovd6612f92015-02-23 13:53:32 -0800299 IpAddress.valueOf(IpAddress.Version.INET6,
300 ipv6.getSourceAddress());
301 // Ignore DAD packets, in which source address is zero
Charles M.C. Chan441d7da2015-03-17 21:03:39 +0800302 if (sourceAddress.isZero()) {
303 return;
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800304 }
Charles M.C. Chan441d7da2015-03-17 21:03:39 +0800305 ip = sourceAddress;
306 break;
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800307 }
308 iPkt = iPkt.getPayload();
309 }
310 HostDescription hdescr = (ip == null) ?
Pavlin Radoslavovd6612f92015-02-23 13:53:32 -0800311 new DefaultHostDescription(eth.getSourceMAC(), vlan,
312 hloc) :
313 new DefaultHostDescription(eth.getSourceMAC(), vlan,
314 hloc, ip);
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800315 providerService.hostDetected(hid, hdescr);
alshabibe1cf87d2014-10-17 09:23:50 -0700316 }
317 }
318 }
Thomas Vachuska33601602014-11-19 03:32:15 -0800319
320 // Auxiliary listener to device events.
321 private class InternalDeviceListener implements DeviceListener {
322 @Override
323 public void event(DeviceEvent event) {
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -0800324 Device device = event.subject();
325 switch (event.type()) {
326 case DEVICE_ADDED:
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -0800327 break;
328 case DEVICE_AVAILABILITY_CHANGED:
329 if (hostRemovalEnabled &&
330 !deviceService.isAvailable(device.id())) {
331 removeHosts(hostService.getConnectedHosts(device.id()));
Thomas Vachuska33601602014-11-19 03:32:15 -0800332 }
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -0800333 break;
334 case DEVICE_SUSPENDED:
335 case DEVICE_UPDATED:
336 // Nothing to do?
337 break;
338 case DEVICE_REMOVED:
339 if (hostRemovalEnabled) {
340 removeHosts(hostService.getConnectedHosts(device.id()));
341 }
342 break;
343 case PORT_ADDED:
344 break;
345 case PORT_UPDATED:
346 if (hostRemovalEnabled) {
347 ConnectPoint point =
348 new ConnectPoint(device.id(), event.port().number());
349 removeHosts(hostService.getConnectedHosts(point));
350 }
351 break;
352 case PORT_REMOVED:
353 // Nothing to do?
354 break;
355 default:
356 break;
Thomas Vachuska33601602014-11-19 03:32:15 -0800357 }
358 }
359 }
360
361 // Signals host vanish for all specified hosts.
362 private void removeHosts(Set<Host> hosts) {
363 for (Host host : hosts) {
364 providerService.hostVanished(host.id());
365 }
366 }
367
alshabibe1cf87d2014-10-17 09:23:50 -0700368}