blob: f6ae2c5eb65a42e849f5cb32d4c2dac3993ca82c [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;
Thomas Vachuska6519e6f2015-03-11 02:29:31 -070040import org.onosproject.cfg.ComponentConfigService;
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -080041import org.onosproject.core.ApplicationId;
42import org.onosproject.core.CoreService;
Brian O'Connorabafb502014-12-02 22:26:20 -080043import org.onosproject.net.ConnectPoint;
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -080044import org.onosproject.net.Device;
Brian O'Connorabafb502014-12-02 22:26:20 -080045import org.onosproject.net.Host;
46import org.onosproject.net.HostId;
47import org.onosproject.net.HostLocation;
48import org.onosproject.net.device.DeviceEvent;
49import org.onosproject.net.device.DeviceListener;
50import org.onosproject.net.device.DeviceService;
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -080051import org.onosproject.net.flow.DefaultTrafficSelector;
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -080052import org.onosproject.net.flow.TrafficSelector;
Brian O'Connorabafb502014-12-02 22:26:20 -080053import org.onosproject.net.host.DefaultHostDescription;
54import org.onosproject.net.host.HostDescription;
55import org.onosproject.net.host.HostProvider;
56import org.onosproject.net.host.HostProviderRegistry;
57import org.onosproject.net.host.HostProviderService;
58import org.onosproject.net.host.HostService;
59import org.onosproject.net.packet.PacketContext;
Jonathan Hart3cfce8e2015-01-14 16:43:27 -080060import org.onosproject.net.packet.PacketPriority;
Brian O'Connorabafb502014-12-02 22:26:20 -080061import org.onosproject.net.packet.PacketProcessor;
62import org.onosproject.net.packet.PacketService;
63import org.onosproject.net.provider.AbstractProvider;
64import org.onosproject.net.provider.ProviderId;
65import org.onosproject.net.topology.Topology;
66import org.onosproject.net.topology.TopologyService;
Thomas Vachuska33601602014-11-19 03:32:15 -080067import org.osgi.service.component.ComponentContext;
alshabibe1cf87d2014-10-17 09:23:50 -070068import org.slf4j.Logger;
69
alshabibe1cf87d2014-10-17 09:23:50 -070070/**
71 * Provider which uses an OpenFlow controller to detect network
72 * end-station hosts.
73 */
74@Component(immediate = true)
75public class HostLocationProvider extends AbstractProvider implements HostProvider {
76
77 private final Logger log = getLogger(getClass());
78
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -080079 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
80 protected CoreService coreService;
81
82 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
alshabibe1cf87d2014-10-17 09:23:50 -070083 protected HostProviderRegistry providerRegistry;
84
85 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Pavlin Radoslavovd6612f92015-02-23 13:53:32 -080086 protected PacketService packetService;
alshabibe1cf87d2014-10-17 09:23:50 -070087
88 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
89 protected TopologyService topologyService;
90
Thomas Vachuska33601602014-11-19 03:32:15 -080091 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
92 protected HostService hostService;
93
94 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
95 protected DeviceService deviceService;
96
Thomas Vachuska6519e6f2015-03-11 02:29:31 -070097 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
98 protected ComponentConfigService cfgService;
99
alshabibe1cf87d2014-10-17 09:23:50 -0700100 private HostProviderService providerService;
101
102 private final InternalHostProvider processor = new InternalHostProvider();
Thomas Vachuska33601602014-11-19 03:32:15 -0800103 private final DeviceListener deviceListener = new InternalDeviceListener();
104
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -0800105 private ApplicationId appId;
106
Thomas Vachuska33601602014-11-19 03:32:15 -0800107 @Property(name = "hostRemovalEnabled", boolValue = true,
108 label = "Enable host removal on port/device down events")
109 private boolean hostRemovalEnabled = true;
alshabibe1cf87d2014-10-17 09:23:50 -0700110
Pavlin Radoslavov93b606b2015-02-25 17:28:39 -0800111 @Property(name = "ipv6NeighborDiscovery", boolValue = false,
112 label = "Enable using IPv6 Neighbor Discovery by the " +
113 "Host Location Provider; default is false")
114 private boolean ipv6NeighborDiscovery = false;
alshabibe1cf87d2014-10-17 09:23:50 -0700115
116 /**
117 * Creates an OpenFlow host provider.
118 */
119 public HostLocationProvider() {
Brian O'Connorabafb502014-12-02 22:26:20 -0800120 super(new ProviderId("of", "org.onosproject.provider.host"));
alshabibe1cf87d2014-10-17 09:23:50 -0700121 }
122
123 @Activate
Thomas Vachuska33601602014-11-19 03:32:15 -0800124 public void activate(ComponentContext context) {
Thomas Vachuska6519e6f2015-03-11 02:29:31 -0700125 cfgService.registerProperties(getClass());
126 appId = coreService.registerApplication("org.onosproject.provider.host");
Pavlin Radoslavov93b606b2015-02-25 17:28:39 -0800127 readComponentConfiguration(context);
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -0800128
alshabibe1cf87d2014-10-17 09:23:50 -0700129 providerService = providerRegistry.register(this);
Pavlin Radoslavovd6612f92015-02-23 13:53:32 -0800130 packetService.addProcessor(processor, 1);
Thomas Vachuska33601602014-11-19 03:32:15 -0800131 deviceService.addListener(deviceListener);
Jonathan Hart3cfce8e2015-01-14 16:43:27 -0800132
133 TrafficSelector.Builder selectorBuilder =
134 DefaultTrafficSelector.builder();
135 selectorBuilder.matchEthType(Ethernet.TYPE_ARP);
Pavlin Radoslavovd6612f92015-02-23 13:53:32 -0800136 packetService.requestPackets(selectorBuilder.build(),
Jonathan Hart3cfce8e2015-01-14 16:43:27 -0800137 PacketPriority.CONTROL, appId);
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -0800138
Pavlin Radoslavov93b606b2015-02-25 17:28:39 -0800139 if (ipv6NeighborDiscovery) {
140 // IPv6 Neighbor Solicitation packet.
141 selectorBuilder = DefaultTrafficSelector.builder();
142 selectorBuilder.matchEthType(Ethernet.TYPE_IPV6);
143 selectorBuilder.matchIPProtocol(IPv6.PROTOCOL_ICMP6);
144 selectorBuilder.matchIcmpv6Type(ICMP6.NEIGHBOR_SOLICITATION);
145 packetService.requestPackets(selectorBuilder.build(),
146 PacketPriority.CONTROL, appId);
Pavlin Radoslavovd6612f92015-02-23 13:53:32 -0800147
Pavlin Radoslavov93b606b2015-02-25 17:28:39 -0800148 // IPv6 Neighbor Advertisement packet.
149 selectorBuilder = DefaultTrafficSelector.builder();
150 selectorBuilder.matchEthType(Ethernet.TYPE_IPV6);
151 selectorBuilder.matchIPProtocol(IPv6.PROTOCOL_ICMP6);
152 selectorBuilder.matchIcmpv6Type(ICMP6.NEIGHBOR_ADVERTISEMENT);
153 packetService.requestPackets(selectorBuilder.build(),
154 PacketPriority.CONTROL, appId);
155 }
Pavlin Radoslavovd6612f92015-02-23 13:53:32 -0800156
157 log.info("Started with Application ID {}", appId.id());
alshabibe1cf87d2014-10-17 09:23:50 -0700158 }
159
160 @Deactivate
161 public void deactivate() {
Thomas Vachuska6519e6f2015-03-11 02:29:31 -0700162 cfgService.unregisterProperties(getClass(), false);
alshabibe1cf87d2014-10-17 09:23:50 -0700163 providerRegistry.unregister(this);
Pavlin Radoslavovd6612f92015-02-23 13:53:32 -0800164 packetService.removeProcessor(processor);
alshabibbfc6b722014-11-29 12:53:48 -0800165 deviceService.removeListener(deviceListener);
alshabibe1cf87d2014-10-17 09:23:50 -0700166 providerService = null;
167 log.info("Stopped");
168 }
169
Thomas Vachuska33601602014-11-19 03:32:15 -0800170 @Modified
171 public void modified(ComponentContext context) {
Pavlin Radoslavov93b606b2015-02-25 17:28:39 -0800172 readComponentConfiguration(context);
173 }
174
175 /**
176 * Extracts properties from the component configuration context.
177 *
178 * @param context the component context
179 */
180 private void readComponentConfiguration(ComponentContext context) {
181 Dictionary<?, ?> properties = context.getProperties();
182 Boolean flag;
183
184 flag = isPropertyEnabled(properties, "hostRemovalEnabled");
185 if (flag == null) {
186 log.info("Host removal on port/device down events is not configured, " +
187 "using current value of {}", hostRemovalEnabled);
188 } else {
189 hostRemovalEnabled = flag;
190 log.info("Configured. Host removal on port/device down events is {}",
191 hostRemovalEnabled ? "enabled" : "disabled");
Thomas Vachuska33601602014-11-19 03:32:15 -0800192 }
Pavlin Radoslavov93b606b2015-02-25 17:28:39 -0800193
194 flag = isPropertyEnabled(properties, "ipv6NeighborDiscovery");
195 if (flag == null) {
196 log.info("Using IPv6 Neighbor Discovery is not configured, " +
197 "using current value of {}", ipv6NeighborDiscovery);
198 } else {
199 ipv6NeighborDiscovery = flag;
200 log.info("Configured. Using IPv6 Neighbor Discovery is {}",
201 ipv6NeighborDiscovery ? "enabled" : "disabled");
202 }
203 }
204
205 /**
206 * Check property name is defined and set to true.
207 *
208 * @param properties properties to be looked up
209 * @param propertyName the name of the property to look up
210 * @return value when the propertyName is defined or return null
211 */
212 private static Boolean isPropertyEnabled(Dictionary<?, ?> properties,
213 String propertyName) {
214 Boolean value = null;
215 try {
216 String s = (String) properties.get(propertyName);
217 value = isNullOrEmpty(s) ? null : s.trim().equals("true");
218 } catch (ClassCastException e) {
219 // No propertyName defined.
220 value = null;
221 }
222 return value;
Thomas Vachuska33601602014-11-19 03:32:15 -0800223 }
224
alshabibe1cf87d2014-10-17 09:23:50 -0700225 @Override
226 public void triggerProbe(Host host) {
227 log.info("Triggering probe on device {}", host);
228 }
229
230 private class InternalHostProvider implements PacketProcessor {
231
232 @Override
233 public void process(PacketContext context) {
alshabib4a179dc2014-10-17 17:17:01 -0700234 if (context == null) {
235 return;
236 }
alshabibe1cf87d2014-10-17 09:23:50 -0700237 Ethernet eth = context.inPacket().parsed();
238
Jonathan Harte8600eb2015-01-12 10:30:45 -0800239 if (eth == null) {
240 return;
241 }
242
alshabibe1cf87d2014-10-17 09:23:50 -0700243 VlanId vlan = VlanId.vlanId(eth.getVlanID());
244 ConnectPoint heardOn = context.inPacket().receivedFrom();
245
246 // If this is not an edge port, bail out.
247 Topology topology = topologyService.currentTopology();
248 if (topologyService.isInfrastructure(topology, heardOn)) {
249 return;
250 }
251
Pavlin Radoslavovd6612f92015-02-23 13:53:32 -0800252 HostLocation hloc =
253 new HostLocation(heardOn, System.currentTimeMillis());
alshabibe1cf87d2014-10-17 09:23:50 -0700254
255 HostId hid = HostId.hostId(eth.getSourceMAC(), vlan);
256
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800257 // ARP: possible new hosts, update both location and IP
alshabibe1cf87d2014-10-17 09:23:50 -0700258 if (eth.getEtherType() == Ethernet.TYPE_ARP) {
259 ARP arp = (ARP) eth.getPayload();
Pavlin Radoslavovd6612f92015-02-23 13:53:32 -0800260 IpAddress ip = IpAddress.valueOf(IpAddress.Version.INET,
261 arp.getSenderProtocolAddress());
alshabibe1cf87d2014-10-17 09:23:50 -0700262 HostDescription hdescr =
Pavlin Radoslavovd6612f92015-02-23 13:53:32 -0800263 new DefaultHostDescription(eth.getSourceMAC(), vlan,
264 hloc, ip);
alshabibe1cf87d2014-10-17 09:23:50 -0700265 providerService.hostDetected(hid, hdescr);
266
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800267 // IPv4: update location only
alshabibe1cf87d2014-10-17 09:23:50 -0700268 } else if (eth.getEtherType() == Ethernet.TYPE_IPV4) {
alshabibe1cf87d2014-10-17 09:23:50 -0700269 HostDescription hdescr =
Pavlin Radoslavovd6612f92015-02-23 13:53:32 -0800270 new DefaultHostDescription(eth.getSourceMAC(), vlan, hloc);
alshabibe1cf87d2014-10-17 09:23:50 -0700271 providerService.hostDetected(hid, hdescr);
272
Pavlin Radoslavovd6612f92015-02-23 13:53:32 -0800273 //
274 // NeighborAdvertisement and NeighborSolicitation: possible
275 // new hosts, update both location and IP.
276 //
277 // IPv6: update location only
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800278 } else if (eth.getEtherType() == Ethernet.TYPE_IPV6) {
279 IpAddress ip = null;
280 IPv6 ipv6 = (IPv6) eth.getPayload();
281
282 IPacket iPkt = ipv6;
283 while (iPkt != null) {
Pavlin Radoslavovd6612f92015-02-23 13:53:32 -0800284 if (iPkt instanceof NeighborAdvertisement ||
285 iPkt instanceof NeighborSolicitation) {
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800286 IpAddress sourceAddress =
Pavlin Radoslavovd6612f92015-02-23 13:53:32 -0800287 IpAddress.valueOf(IpAddress.Version.INET6,
288 ipv6.getSourceAddress());
289 // Ignore DAD packets, in which source address is zero
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800290 if (!sourceAddress.isZero()) {
291 ip = sourceAddress;
292 break;
293 }
294 }
295 iPkt = iPkt.getPayload();
296 }
297 HostDescription hdescr = (ip == null) ?
Pavlin Radoslavovd6612f92015-02-23 13:53:32 -0800298 new DefaultHostDescription(eth.getSourceMAC(), vlan,
299 hloc) :
300 new DefaultHostDescription(eth.getSourceMAC(), vlan,
301 hloc, ip);
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800302 providerService.hostDetected(hid, hdescr);
alshabibe1cf87d2014-10-17 09:23:50 -0700303 }
304 }
305 }
Thomas Vachuska33601602014-11-19 03:32:15 -0800306
307 // Auxiliary listener to device events.
308 private class InternalDeviceListener implements DeviceListener {
309 @Override
310 public void event(DeviceEvent event) {
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -0800311 Device device = event.subject();
312 switch (event.type()) {
313 case DEVICE_ADDED:
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -0800314 break;
315 case DEVICE_AVAILABILITY_CHANGED:
316 if (hostRemovalEnabled &&
317 !deviceService.isAvailable(device.id())) {
318 removeHosts(hostService.getConnectedHosts(device.id()));
Thomas Vachuska33601602014-11-19 03:32:15 -0800319 }
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -0800320 break;
321 case DEVICE_SUSPENDED:
322 case DEVICE_UPDATED:
323 // Nothing to do?
324 break;
325 case DEVICE_REMOVED:
326 if (hostRemovalEnabled) {
327 removeHosts(hostService.getConnectedHosts(device.id()));
328 }
329 break;
330 case PORT_ADDED:
331 break;
332 case PORT_UPDATED:
333 if (hostRemovalEnabled) {
334 ConnectPoint point =
335 new ConnectPoint(device.id(), event.port().number());
336 removeHosts(hostService.getConnectedHosts(point));
337 }
338 break;
339 case PORT_REMOVED:
340 // Nothing to do?
341 break;
342 default:
343 break;
Thomas Vachuska33601602014-11-19 03:32:15 -0800344 }
345 }
346 }
347
348 // Signals host vanish for all specified hosts.
349 private void removeHosts(Set<Host> hosts) {
350 for (Host host : hosts) {
351 providerService.hostVanished(host.id());
352 }
353 }
354
alshabibe1cf87d2014-10-17 09:23:50 -0700355}