blob: b047af2bf8eef0e6c2e93badf563b829a195be27 [file] [log] [blame]
Thomas Vachuska781d18b2014-10-27 10:31:25 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2014-present Open Networking Foundation
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
Charles Chan35a32322017-08-14 11:42:11 -070018import com.google.common.collect.Sets;
alshabibe1cf87d2014-10-17 09:23:50 -070019import org.apache.felix.scr.annotations.Activate;
20import org.apache.felix.scr.annotations.Component;
21import org.apache.felix.scr.annotations.Deactivate;
Thomas Vachuska33601602014-11-19 03:32:15 -080022import org.apache.felix.scr.annotations.Modified;
23import org.apache.felix.scr.annotations.Property;
alshabibe1cf87d2014-10-17 09:23:50 -070024import org.apache.felix.scr.annotations.Reference;
25import org.apache.felix.scr.annotations.ReferenceCardinality;
Charles Chan47933752017-11-30 15:37:50 -080026import org.apache.felix.scr.annotations.Service;
Jonathan Harte8600eb2015-01-12 10:30:45 -080027import org.onlab.packet.ARP;
Yi Tsengfcf5dce2017-07-26 14:30:41 -070028import org.onlab.packet.BasePacket;
Charles Chane6067892016-11-17 10:23:46 -080029import org.onlab.packet.DHCP;
Yi Tsengfcf5dce2017-07-26 14:30:41 -070030import org.onlab.packet.DHCP6;
Jonathan Harte8600eb2015-01-12 10:30:45 -080031import org.onlab.packet.Ethernet;
Pavlin Radoslavovd6612f92015-02-23 13:53:32 -080032import org.onlab.packet.ICMP6;
Thomas Vachuskaf845cf62015-03-24 10:13:09 -070033import org.onlab.packet.IPacket;
Charles Chane6067892016-11-17 10:23:46 -080034import org.onlab.packet.IPv4;
Jonathan Harte8600eb2015-01-12 10:30:45 -080035import org.onlab.packet.IPv6;
Charles Chan35a32322017-08-14 11:42:11 -070036import org.onlab.packet.Ip4Address;
Yi Tsengfcf5dce2017-07-26 14:30:41 -070037import org.onlab.packet.Ip6Address;
Jonathan Harte8600eb2015-01-12 10:30:45 -080038import org.onlab.packet.IpAddress;
Charles M.C. Chan956cb692015-04-26 18:49:39 +080039import org.onlab.packet.MacAddress;
Charles Chane6067892016-11-17 10:23:46 -080040import org.onlab.packet.UDP;
Jonathan Harte8600eb2015-01-12 10:30:45 -080041import org.onlab.packet.VlanId;
Yi Tsengfcf5dce2017-07-26 14:30:41 -070042import org.onlab.packet.dhcp.Dhcp6ClientIdOption;
43import org.onlab.packet.dhcp.Dhcp6IaAddressOption;
44import org.onlab.packet.dhcp.Dhcp6IaNaOption;
45import org.onlab.packet.dhcp.Dhcp6IaTaOption;
46import org.onlab.packet.dhcp.Dhcp6RelayOption;
Charles M.C. Chan956cb692015-04-26 18:49:39 +080047import org.onlab.packet.ipv6.IExtensionHeader;
Jonathan Harte8600eb2015-01-12 10:30:45 -080048import org.onlab.packet.ndp.NeighborAdvertisement;
49import org.onlab.packet.ndp.NeighborSolicitation;
Charles M.C. Chan441d7da2015-03-17 21:03:39 +080050import org.onlab.packet.ndp.RouterAdvertisement;
51import org.onlab.packet.ndp.RouterSolicitation;
Jian Lid9b5f552016-03-11 18:15:31 -080052import org.onlab.util.Tools;
Thomas Vachuska6519e6f2015-03-11 02:29:31 -070053import org.onosproject.cfg.ComponentConfigService;
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -080054import org.onosproject.core.ApplicationId;
55import org.onosproject.core.CoreService;
Charles Chan47933752017-11-30 15:37:50 -080056import org.onosproject.net.host.HostLocationProbingService;
Ray Milkeyfacf2862017-08-03 11:58:29 -070057import org.onosproject.net.intf.InterfaceService;
Brian O'Connorabafb502014-12-02 22:26:20 -080058import org.onosproject.net.ConnectPoint;
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -080059import org.onosproject.net.Device;
Charles Chan888e20a2017-05-01 15:44:23 -070060import org.onosproject.net.DeviceId;
Brian O'Connorabafb502014-12-02 22:26:20 -080061import org.onosproject.net.Host;
62import org.onosproject.net.HostId;
63import org.onosproject.net.HostLocation;
64import org.onosproject.net.device.DeviceEvent;
65import org.onosproject.net.device.DeviceListener;
66import org.onosproject.net.device.DeviceService;
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -080067import org.onosproject.net.flow.DefaultTrafficSelector;
sdn94b00152016-08-30 02:12:32 -070068import org.onosproject.net.flow.DefaultTrafficTreatment;
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -080069import org.onosproject.net.flow.TrafficSelector;
sdn94b00152016-08-30 02:12:32 -070070import org.onosproject.net.flow.TrafficTreatment;
Brian O'Connorabafb502014-12-02 22:26:20 -080071import org.onosproject.net.host.DefaultHostDescription;
72import org.onosproject.net.host.HostDescription;
73import org.onosproject.net.host.HostProvider;
74import org.onosproject.net.host.HostProviderRegistry;
75import org.onosproject.net.host.HostProviderService;
76import org.onosproject.net.host.HostService;
sdn94b00152016-08-30 02:12:32 -070077import org.onosproject.net.packet.DefaultOutboundPacket;
78import org.onosproject.net.packet.OutboundPacket;
Brian O'Connorabafb502014-12-02 22:26:20 -080079import org.onosproject.net.packet.PacketContext;
Jonathan Hart3cfce8e2015-01-14 16:43:27 -080080import org.onosproject.net.packet.PacketPriority;
Brian O'Connorabafb502014-12-02 22:26:20 -080081import org.onosproject.net.packet.PacketProcessor;
82import org.onosproject.net.packet.PacketService;
83import org.onosproject.net.provider.AbstractProvider;
84import org.onosproject.net.provider.ProviderId;
85import org.onosproject.net.topology.Topology;
86import org.onosproject.net.topology.TopologyService;
Thomas Vachuska33601602014-11-19 03:32:15 -080087import org.osgi.service.component.ComponentContext;
alshabibe1cf87d2014-10-17 09:23:50 -070088import org.slf4j.Logger;
89
sdn94b00152016-08-30 02:12:32 -070090import java.nio.ByteBuffer;
Thomas Vachuskaf845cf62015-03-24 10:13:09 -070091import java.util.Dictionary;
Yi Tseng7a38f9a2017-06-09 14:36:40 -070092import java.util.Objects;
93import java.util.Optional;
Madan Jampania3770c32015-12-11 12:07:41 -080094import java.util.concurrent.ExecutorService;
Charles Chanbb86b782017-09-13 16:14:22 -070095import java.util.concurrent.Executors;
96import java.util.concurrent.ScheduledExecutorService;
97import java.util.concurrent.TimeUnit;
Yi Tseng7a38f9a2017-06-09 14:36:40 -070098import java.util.stream.Stream;
Charles Chan35a32322017-08-14 11:42:11 -070099import java.util.Set;
Thomas Vachuskaf845cf62015-03-24 10:13:09 -0700100
Madan Jampania3770c32015-12-11 12:07:41 -0800101import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
102import static org.onlab.util.Tools.groupedThreads;
Thomas Vachuskaf845cf62015-03-24 10:13:09 -0700103import static org.slf4j.LoggerFactory.getLogger;
104
alshabibe1cf87d2014-10-17 09:23:50 -0700105/**
Thomas Vachuskaec9c7dd2015-09-03 18:30:04 -0700106 * Provider which uses an OpenFlow controller to detect network end-station
107 * hosts.
alshabibe1cf87d2014-10-17 09:23:50 -0700108 */
109@Component(immediate = true)
Charles Chan47933752017-11-30 15:37:50 -0800110@Service
111public class HostLocationProvider extends AbstractProvider implements HostProvider, HostLocationProbingService {
alshabibe1cf87d2014-10-17 09:23:50 -0700112 private final Logger log = getLogger(getClass());
113
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -0800114 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
115 protected CoreService coreService;
116
117 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
alshabibe1cf87d2014-10-17 09:23:50 -0700118 protected HostProviderRegistry providerRegistry;
119
120 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Pavlin Radoslavovd6612f92015-02-23 13:53:32 -0800121 protected PacketService packetService;
alshabibe1cf87d2014-10-17 09:23:50 -0700122
123 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
124 protected TopologyService topologyService;
125
Thomas Vachuska33601602014-11-19 03:32:15 -0800126 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
127 protected HostService hostService;
128
129 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
130 protected DeviceService deviceService;
131
Thomas Vachuska6519e6f2015-03-11 02:29:31 -0700132 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
133 protected ComponentConfigService cfgService;
134
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700135 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
136 protected InterfaceService interfaceService;
137
alshabibe1cf87d2014-10-17 09:23:50 -0700138 private HostProviderService providerService;
139
140 private final InternalHostProvider processor = new InternalHostProvider();
Thomas Vachuska33601602014-11-19 03:32:15 -0800141 private final DeviceListener deviceListener = new InternalDeviceListener();
142
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -0800143 private ApplicationId appId;
144
Thomas Vachuska33601602014-11-19 03:32:15 -0800145 @Property(name = "hostRemovalEnabled", boolValue = true,
146 label = "Enable host removal on port/device down events")
147 private boolean hostRemovalEnabled = true;
alshabibe1cf87d2014-10-17 09:23:50 -0700148
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700149 @Property(name = "requestArp", boolValue = true,
150 label = "Request ARP packets for neighbor discovery by the " +
Charles Chane6067892016-11-17 10:23:46 -0800151 "Host Location Provider; default is true")
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700152 private boolean requestArp = true;
Charles Chane6067892016-11-17 10:23:46 -0800153
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700154 @Property(name = "requestIpv6ND", boolValue = false,
155 label = "Requests IPv6 Neighbor Discovery by the " +
Thomas Vachuska27bee092015-06-23 19:03:10 -0700156 "Host Location Provider; default is false")
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700157 private boolean requestIpv6ND = false;
Charles Chane6067892016-11-17 10:23:46 -0800158
159 @Property(name = "useDhcp", boolValue = false,
Yi Tsengaa417a62017-09-08 17:22:51 -0700160 label = "Use DHCP to update IP address of the host; default is false")
Charles Chane6067892016-11-17 10:23:46 -0800161 private boolean useDhcp = false;
alshabibe1cf87d2014-10-17 09:23:50 -0700162
Yi Tsengfcf5dce2017-07-26 14:30:41 -0700163 @Property(name = "useDhcp6", boolValue = false,
Yi Tsengaa417a62017-09-08 17:22:51 -0700164 label = "Use DHCPv6 to update IP address of the host; default is false")
Yi Tsengfcf5dce2017-07-26 14:30:41 -0700165 private boolean useDhcp6 = false;
166
Jonathan Hart9af322d2016-01-06 17:42:04 -0800167 @Property(name = "requestInterceptsEnabled", boolValue = true,
168 label = "Enable requesting packet intercepts")
169 private boolean requestInterceptsEnabled = true;
170
Charles Chan35a32322017-08-14 11:42:11 -0700171 @Property(name = "multihomingEnabled", boolValue = false,
172 label = "Allow hosts to be multihomed")
173 private boolean multihomingEnabled = false;
Madan Jampania3770c32015-12-11 12:07:41 -0800174
Charles Chan47933752017-11-30 15:37:50 -0800175 private int probeInitDelayMs = 1000;
176
Charles Chan35a32322017-08-14 11:42:11 -0700177 protected ExecutorService eventHandler;
sdn94b00152016-08-30 02:12:32 -0700178
Charles Chan8277b6b2017-12-03 13:48:46 -0800179 private int probeDelayMs = 1000;
180
alshabibe1cf87d2014-10-17 09:23:50 -0700181 /**
182 * Creates an OpenFlow host provider.
183 */
184 public HostLocationProvider() {
Brian O'Connorabafb502014-12-02 22:26:20 -0800185 super(new ProviderId("of", "org.onosproject.provider.host"));
alshabibe1cf87d2014-10-17 09:23:50 -0700186 }
187
188 @Activate
Thomas Vachuska33601602014-11-19 03:32:15 -0800189 public void activate(ComponentContext context) {
Thomas Vachuska6519e6f2015-03-11 02:29:31 -0700190 cfgService.registerProperties(getClass());
191 appId = coreService.registerApplication("org.onosproject.provider.host");
Jonathan Hart9af322d2016-01-06 17:42:04 -0800192 eventHandler = newSingleThreadScheduledExecutor(
HIGUCHI Yutad9e01052016-04-14 09:31:42 -0700193 groupedThreads("onos/host-loc-provider", "event-handler", log));
alshabibe1cf87d2014-10-17 09:23:50 -0700194 providerService = providerRegistry.register(this);
Brian O'Connor3b783262015-07-29 17:49:24 -0700195 packetService.addProcessor(processor, PacketProcessor.advisor(1));
Thomas Vachuska33601602014-11-19 03:32:15 -0800196 deviceService.addListener(deviceListener);
Jonathan Hart9af322d2016-01-06 17:42:04 -0800197
198 modified(context);
Jonathan Hart3cfce8e2015-01-14 16:43:27 -0800199
Charles M.C. Chane148de82015-05-06 12:38:21 +0800200 log.info("Started with Application ID {}", appId.id());
201 }
202
203 @Deactivate
204 public void deactivate() {
Charles M.C. Chane148de82015-05-06 12:38:21 +0800205 cfgService.unregisterProperties(getClass(), false);
Thomas Vachuska27bee092015-06-23 19:03:10 -0700206
207 withdrawIntercepts();
208
Charles M.C. Chane148de82015-05-06 12:38:21 +0800209 providerRegistry.unregister(this);
210 packetService.removeProcessor(processor);
211 deviceService.removeListener(deviceListener);
Madan Jampania3770c32015-12-11 12:07:41 -0800212 eventHandler.shutdown();
Charles M.C. Chane148de82015-05-06 12:38:21 +0800213 providerService = null;
214 log.info("Stopped");
215 }
216
217 @Modified
218 public void modified(ComponentContext context) {
Charles M.C. Chane148de82015-05-06 12:38:21 +0800219 readComponentConfiguration(context);
Jonathan Hart9af322d2016-01-06 17:42:04 -0800220
221 if (requestInterceptsEnabled) {
222 requestIntercepts();
223 } else {
224 withdrawIntercepts();
225 }
Charles M.C. Chane148de82015-05-06 12:38:21 +0800226 }
227
228 /**
Thomas Vachuska27bee092015-06-23 19:03:10 -0700229 * Request packet intercepts.
Charles M.C. Chane148de82015-05-06 12:38:21 +0800230 */
Thomas Vachuska27bee092015-06-23 19:03:10 -0700231 private void requestIntercepts() {
Charles Chane6067892016-11-17 10:23:46 -0800232 // Use ARP
233 TrafficSelector arpSelector = DefaultTrafficSelector.builder()
234 .matchEthType(Ethernet.TYPE_ARP)
235 .build();
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700236 if (requestArp) {
Charles Chane6067892016-11-17 10:23:46 -0800237 packetService.requestPackets(arpSelector, PacketPriority.CONTROL, appId);
Thomas Vachuska27bee092015-06-23 19:03:10 -0700238 } else {
Charles Chane6067892016-11-17 10:23:46 -0800239 packetService.cancelPackets(arpSelector, PacketPriority.CONTROL, appId);
Pavlin Radoslavov93b606b2015-02-25 17:28:39 -0800240 }
Thomas Vachuska27bee092015-06-23 19:03:10 -0700241
Charles Chane6067892016-11-17 10:23:46 -0800242 // Use IPv6 Neighbor Discovery
243 TrafficSelector ipv6NsSelector = DefaultTrafficSelector.builder()
244 .matchEthType(Ethernet.TYPE_IPV6)
245 .matchIPProtocol(IPv6.PROTOCOL_ICMP6)
246 .matchIcmpv6Type(ICMP6.NEIGHBOR_SOLICITATION)
247 .build();
248 TrafficSelector ipv6NaSelector = DefaultTrafficSelector.builder()
249 .matchEthType(Ethernet.TYPE_IPV6)
250 .matchIPProtocol(IPv6.PROTOCOL_ICMP6)
251 .matchIcmpv6Type(ICMP6.NEIGHBOR_ADVERTISEMENT)
252 .build();
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700253 if (requestIpv6ND) {
Charles Chane6067892016-11-17 10:23:46 -0800254 packetService.requestPackets(ipv6NsSelector, PacketPriority.CONTROL, appId);
255 packetService.requestPackets(ipv6NaSelector, PacketPriority.CONTROL, appId);
Thomas Vachuska27bee092015-06-23 19:03:10 -0700256 } else {
Charles Chane6067892016-11-17 10:23:46 -0800257 packetService.cancelPackets(ipv6NsSelector, PacketPriority.CONTROL, appId);
258 packetService.cancelPackets(ipv6NaSelector, PacketPriority.CONTROL, appId);
259 }
Thomas Vachuska27bee092015-06-23 19:03:10 -0700260 }
261
262 /**
263 * Withdraw packet intercepts.
264 */
265 private void withdrawIntercepts() {
266 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
267 selector.matchEthType(Ethernet.TYPE_ARP);
Aaron Kruglikov07a923d2015-07-03 13:30:57 -0700268 packetService.cancelPackets(selector.build(), PacketPriority.CONTROL, appId);
Thomas Vachuska27bee092015-06-23 19:03:10 -0700269
270 // IPv6 Neighbor Solicitation packet.
271 selector.matchEthType(Ethernet.TYPE_IPV6);
272 selector.matchIPProtocol(IPv6.PROTOCOL_ICMP6);
273 selector.matchIcmpv6Type(ICMP6.NEIGHBOR_SOLICITATION);
274 packetService.cancelPackets(selector.build(), PacketPriority.CONTROL, appId);
275
276 // IPv6 Neighbor Advertisement packet.
277 selector.matchIcmpv6Type(ICMP6.NEIGHBOR_ADVERTISEMENT);
278 packetService.cancelPackets(selector.build(), PacketPriority.CONTROL, appId);
Pavlin Radoslavov93b606b2015-02-25 17:28:39 -0800279 }
280
281 /**
282 * Extracts properties from the component configuration context.
283 *
284 * @param context the component context
285 */
286 private void readComponentConfiguration(ComponentContext context) {
287 Dictionary<?, ?> properties = context.getProperties();
288 Boolean flag;
289
Jian Lid9b5f552016-03-11 18:15:31 -0800290 flag = Tools.isPropertyEnabled(properties, "hostRemovalEnabled");
Pavlin Radoslavov93b606b2015-02-25 17:28:39 -0800291 if (flag == null) {
292 log.info("Host removal on port/device down events is not configured, " +
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700293 "using current value of {}", hostRemovalEnabled);
Pavlin Radoslavov93b606b2015-02-25 17:28:39 -0800294 } else {
295 hostRemovalEnabled = flag;
296 log.info("Configured. Host removal on port/device down events is {}",
297 hostRemovalEnabled ? "enabled" : "disabled");
Thomas Vachuska33601602014-11-19 03:32:15 -0800298 }
Pavlin Radoslavov93b606b2015-02-25 17:28:39 -0800299
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700300 flag = Tools.isPropertyEnabled(properties, "requestArp");
Charles Chane6067892016-11-17 10:23:46 -0800301 if (flag == null) {
302 log.info("Using ARP is not configured, " +
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700303 "using current value of {}", requestArp);
Charles Chane6067892016-11-17 10:23:46 -0800304 } else {
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700305 requestArp = flag;
Charles Chane6067892016-11-17 10:23:46 -0800306 log.info("Configured. Using ARP is {}",
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700307 requestArp ? "enabled" : "disabled");
Charles Chane6067892016-11-17 10:23:46 -0800308 }
309
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700310 flag = Tools.isPropertyEnabled(properties, "requestIpv6ND");
Pavlin Radoslavov93b606b2015-02-25 17:28:39 -0800311 if (flag == null) {
312 log.info("Using IPv6 Neighbor Discovery is not configured, " +
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700313 "using current value of {}", requestIpv6ND);
Pavlin Radoslavov93b606b2015-02-25 17:28:39 -0800314 } else {
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700315 requestIpv6ND = flag;
Pavlin Radoslavov93b606b2015-02-25 17:28:39 -0800316 log.info("Configured. Using IPv6 Neighbor Discovery is {}",
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700317 requestIpv6ND ? "enabled" : "disabled");
Charles Chane6067892016-11-17 10:23:46 -0800318 }
319
320 flag = Tools.isPropertyEnabled(properties, "useDhcp");
321 if (flag == null) {
322 log.info("Using DHCP is not configured, " +
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700323 "using current value of {}", useDhcp);
Charles Chane6067892016-11-17 10:23:46 -0800324 } else {
325 useDhcp = flag;
326 log.info("Configured. Using DHCP is {}",
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700327 useDhcp ? "enabled" : "disabled");
Pavlin Radoslavov93b606b2015-02-25 17:28:39 -0800328 }
Jonathan Hart9af322d2016-01-06 17:42:04 -0800329
Jian Lid9b5f552016-03-11 18:15:31 -0800330 flag = Tools.isPropertyEnabled(properties, "requestInterceptsEnabled");
Jonathan Hart9af322d2016-01-06 17:42:04 -0800331 if (flag == null) {
332 log.info("Request intercepts is not configured, " +
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700333 "using current value of {}", requestInterceptsEnabled);
Jonathan Hart9af322d2016-01-06 17:42:04 -0800334 } else {
335 requestInterceptsEnabled = flag;
336 log.info("Configured. Request intercepts is {}",
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700337 requestInterceptsEnabled ? "enabled" : "disabled");
Jonathan Hart9af322d2016-01-06 17:42:04 -0800338 }
Charles Chan35a32322017-08-14 11:42:11 -0700339
340 flag = Tools.isPropertyEnabled(properties, "multihomingEnabled");
341 if (flag == null) {
342 log.info("Multihoming is not configured, " +
343 "using current value of {}", multihomingEnabled);
344 } else {
345 multihomingEnabled = flag;
346 log.info("Configured. Multihoming is {}",
347 multihomingEnabled ? "enabled" : "disabled");
348 }
Charles Chan47933752017-11-30 15:37:50 -0800349 }
Charles Chan35a32322017-08-14 11:42:11 -0700350
Charles Chan47933752017-11-30 15:37:50 -0800351 @Override
352 public void probeHostLocation(Host host, ConnectPoint connectPoint, ProbeMode probeMode) {
353 host.ipAddresses().stream().findFirst().ifPresent(ip -> {
354 MacAddress probeMac = providerService.addPendingHostLocation(host.id(), connectPoint, probeMode);
355 log.debug("Constructing {} probe for host {} with {}", probeMode, host.id(), ip);
356 Ethernet probe;
357 if (ip.isIp4()) {
358 probe = ARP.buildArpRequest(probeMac.toBytes(), Ip4Address.ZERO.toOctets(),
359 host.id().mac().toBytes(), ip.toOctets(),
360 host.id().mac().toBytes(), host.id().vlanId().toShort());
361 } else {
362 probe = NeighborSolicitation.buildNdpSolicit(
363 ip.getIp6Address(),
364 Ip6Address.valueOf(IPv6.getLinkLocalAddress(probeMac.toBytes())),
365 ip.getIp6Address(),
366 probeMac,
367 host.id().mac(),
368 host.id().vlanId());
369 }
370
371 // NOTE: delay the probe a little bit to wait for the store synchronization is done
372 ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
373 executorService.schedule(() ->
374 sendLocationProbe(probe, connectPoint), probeInitDelayMs, TimeUnit.MILLISECONDS);
375 });
376 }
377
378 /**
379 * Send the probe packet on given port.
380 *
381 * @param probe the probe packet
382 * @param connectPoint the port we want to probe
383 */
384 private void sendLocationProbe(Ethernet probe, ConnectPoint connectPoint) {
385 log.info("Sending probe for host {} on location {} with probeMac {}",
386 probe.getDestinationMAC(), connectPoint, probe.getSourceMAC());
387 TrafficTreatment treatment = DefaultTrafficTreatment.builder().setOutput(connectPoint.port()).build();
388 OutboundPacket outboundPacket = new DefaultOutboundPacket(connectPoint.deviceId(),
389 treatment, ByteBuffer.wrap(probe.serialize()));
390 packetService.emit(outboundPacket);
Pavlin Radoslavov93b606b2015-02-25 17:28:39 -0800391 }
392
alshabibe1cf87d2014-10-17 09:23:50 -0700393 @Override
394 public void triggerProbe(Host host) {
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800395 //log.info("Triggering probe on device {} ", host);
Jonathan Hartf353e402016-09-23 09:27:36 -0700396
397 // FIXME Disabling host probing for now, because sending packets from a
398 // broadcast MAC address caused problems when two ONOS networks were
399 // interconnected. Host probing should take into account the interface
400 // configuration when determining which source address to use.
401
402 //MastershipRole role = deviceService.getRole(host.location().deviceId());
403 //if (role.equals(MastershipRole.MASTER)) {
404 // host.ipAddresses().forEach(ip -> {
405 // sendProbe(host, ip);
406 // });
407 //} else {
408 // log.info("not the master, master will probe {}");
409 //}
sdn94b00152016-08-30 02:12:32 -0700410 }
411
412 private void sendProbe(Host host, IpAddress targetIp) {
413 Ethernet probePacket = null;
414 if (targetIp.isIp4()) {
415 // IPv4: Use ARP
416 probePacket = buildArpRequest(targetIp, host);
417 } else {
418 // IPv6: Use Neighbor Discovery
Jonathan Hartf353e402016-09-23 09:27:36 -0700419 //TODO need to implement ndp probe
sdn94b00152016-08-30 02:12:32 -0700420 log.info("Triggering probe on device {} ", host);
Ray Milkey74e59132018-01-17 15:24:52 -0800421 return;
sdn94b00152016-08-30 02:12:32 -0700422 }
423
424 TrafficTreatment treatment = DefaultTrafficTreatment.builder().setOutput(host.location().port()).build();
425
426 OutboundPacket outboundPacket = new DefaultOutboundPacket(host.location().deviceId(), treatment,
427 ByteBuffer.wrap(probePacket.serialize()));
428
429 packetService.emit(outboundPacket);
430 }
431
Charles Chan888e20a2017-05-01 15:44:23 -0700432 // This method is using source ip as 0.0.0.0 , to receive the reply even from the sub net hosts.
sdn94b00152016-08-30 02:12:32 -0700433 private Ethernet buildArpRequest(IpAddress targetIp, Host host) {
Charles Chan35a32322017-08-14 11:42:11 -0700434 return ARP.buildArpRequest(MacAddress.BROADCAST.toBytes(), Ip4Address.ZERO.toOctets(),
435 MacAddress.BROADCAST.toBytes(), targetIp.toOctets(),
436 MacAddress.BROADCAST.toBytes(), VlanId.NONE.toShort());
alshabibe1cf87d2014-10-17 09:23:50 -0700437 }
438
439 private class InternalHostProvider implements PacketProcessor {
Charles M.C. Chan956cb692015-04-26 18:49:39 +0800440 /**
Charles Chan888e20a2017-05-01 15:44:23 -0700441 * Create or update host information.
442 * Will not update IP if IP is null, all zero or self-assigned.
Charles M.C. Chan956cb692015-04-26 18:49:39 +0800443 *
Thomas Vachuska27bee092015-06-23 19:03:10 -0700444 * @param hid host ID
445 * @param mac source Mac address
Charles M.C. Chan956cb692015-04-26 18:49:39 +0800446 * @param vlan VLAN ID
447 * @param hloc host location
Charles Chan888e20a2017-05-01 15:44:23 -0700448 * @param ip source IP address or null if not updating
Charles M.C. Chan956cb692015-04-26 18:49:39 +0800449 */
Charles Chan888e20a2017-05-01 15:44:23 -0700450 private void createOrUpdateHost(HostId hid, MacAddress mac,
451 VlanId vlan, HostLocation hloc,
452 IpAddress ip) {
Charles Chan35a32322017-08-14 11:42:11 -0700453 Set<HostLocation> newLocations = Sets.newHashSet(hloc);
454
455 if (multihomingEnabled) {
456 Host existingHost = hostService.getHost(hid);
457 if (existingHost != null) {
458 Set<HostLocation> prevLocations = existingHost.locations();
Charles Chan35a32322017-08-14 11:42:11 -0700459
Charles Chan653e2ac2017-11-22 14:49:55 -0800460 if (prevLocations.stream().noneMatch(loc -> loc.deviceId().equals(hloc.deviceId()))) {
461 // New location is on a device that we haven't seen before
462 // Could be a dual-home host. Append new location and send out the probe
463 newLocations.addAll(prevLocations);
Charles Chan47933752017-11-30 15:37:50 -0800464 prevLocations.forEach(prevLocation ->
465 probeHostLocation(existingHost, prevLocation, ProbeMode.VERIFY));
Charles Chan653e2ac2017-11-22 14:49:55 -0800466 } else {
467 // Move within the same switch
468 // Simply replace old location that is on the same device
469 prevLocations.stream().filter(loc -> !loc.deviceId().equals(hloc.deviceId()))
470 .forEach(newLocations::add);
Charles Chan35a32322017-08-14 11:42:11 -0700471 }
472 }
473 }
474
Charles Chan888e20a2017-05-01 15:44:23 -0700475 HostDescription desc = ip == null || ip.isZero() || ip.isSelfAssigned() ?
Charles Chan35a32322017-08-14 11:42:11 -0700476 new DefaultHostDescription(mac, vlan, newLocations, Sets.newHashSet(), false) :
477 new DefaultHostDescription(mac, vlan, newLocations, Sets.newHashSet(ip), false);
Sahil Lele3a0cdd52015-07-21 14:16:31 -0700478 try {
Ray Milkeydc083442016-02-22 11:27:57 -0800479 providerService.hostDetected(hid, desc, false);
Sahil Lele3a0cdd52015-07-21 14:16:31 -0700480 } catch (IllegalStateException e) {
481 log.debug("Host {} suppressed", hid);
482 }
Charles M.C. Chan956cb692015-04-26 18:49:39 +0800483 }
alshabibe1cf87d2014-10-17 09:23:50 -0700484
Charles Chane6067892016-11-17 10:23:46 -0800485 /**
Charles Chan888e20a2017-05-01 15:44:23 -0700486 * Updates IP address for an existing host.
Charles Chane6067892016-11-17 10:23:46 -0800487 *
488 * @param hid host ID
489 * @param ip IP address
490 */
Charles Chan888e20a2017-05-01 15:44:23 -0700491 private void updateHostIp(HostId hid, IpAddress ip) {
Charles Chane6067892016-11-17 10:23:46 -0800492 Host host = hostService.getHost(hid);
493 if (host == null) {
Charles Chan35a32322017-08-14 11:42:11 -0700494 log.warn("Fail to update IP for {}. Host does not exist", hid);
Charles Chane6067892016-11-17 10:23:46 -0800495 return;
496 }
497
Charles Chan888e20a2017-05-01 15:44:23 -0700498 HostDescription desc = new DefaultHostDescription(hid.mac(), hid.vlanId(),
Charles Chan35a32322017-08-14 11:42:11 -0700499 host.locations(), Sets.newHashSet(ip), false);
Charles Chane6067892016-11-17 10:23:46 -0800500 try {
501 providerService.hostDetected(hid, desc, false);
502 } catch (IllegalStateException e) {
503 log.debug("Host {} suppressed", hid);
504 }
505 }
506
alshabibe1cf87d2014-10-17 09:23:50 -0700507 @Override
508 public void process(PacketContext context) {
Yi Tseng5ba756c2018-02-16 14:41:16 -0800509 // FIXME: MWC-2018 HACK - ignore packet ins as we rely on the the netcfg host config
510 // But segmentrouting needs this provider
511 }
512
513 public void processPacketIn(PacketContext context) {
alshabib4a179dc2014-10-17 17:17:01 -0700514 if (context == null) {
515 return;
516 }
alshabibe1cf87d2014-10-17 09:23:50 -0700517
Thomas Vachuskaf845cf62015-03-24 10:13:09 -0700518 Ethernet eth = context.inPacket().parsed();
Jonathan Harte8600eb2015-01-12 10:30:45 -0800519 if (eth == null) {
520 return;
521 }
Jonathan Hartf353e402016-09-23 09:27:36 -0700522
Charles M.C. Chan956cb692015-04-26 18:49:39 +0800523 MacAddress srcMac = eth.getSourceMAC();
Jonathan Hartf353e402016-09-23 09:27:36 -0700524 if (srcMac.isBroadcast() || srcMac.isMulticast()) {
525 return;
526 }
Jonathan Harte8600eb2015-01-12 10:30:45 -0800527
alshabibe1cf87d2014-10-17 09:23:50 -0700528 VlanId vlan = VlanId.vlanId(eth.getVlanID());
529 ConnectPoint heardOn = context.inPacket().receivedFrom();
530
Thomas Vachuskaf845cf62015-03-24 10:13:09 -0700531 // If this arrived on control port, bail out.
532 if (heardOn.port().isLogical()) {
533 return;
534 }
535
alshabibe1cf87d2014-10-17 09:23:50 -0700536 // If this is not an edge port, bail out.
537 Topology topology = topologyService.currentTopology();
538 if (topologyService.isInfrastructure(topology, heardOn)) {
539 return;
540 }
541
Thomas Vachuskaec9c7dd2015-09-03 18:30:04 -0700542 HostLocation hloc = new HostLocation(heardOn, System.currentTimeMillis());
alshabibe1cf87d2014-10-17 09:23:50 -0700543 HostId hid = HostId.hostId(eth.getSourceMAC(), vlan);
Charles Chan35a32322017-08-14 11:42:11 -0700544 MacAddress destMac = eth.getDestinationMAC();
545
546 // Receives a location probe. Invalid entry from the cache
547 if (multihomingEnabled && destMac.isOnos() && !MacAddress.NONE.equals(destMac)) {
548 log.info("Receives probe for {}/{} on {}", srcMac, vlan, heardOn);
549 providerService.removePendingHostLocation(destMac);
550 return;
551 }
alshabibe1cf87d2014-10-17 09:23:50 -0700552
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800553 // ARP: possible new hosts, update both location and IP
alshabibe1cf87d2014-10-17 09:23:50 -0700554 if (eth.getEtherType() == Ethernet.TYPE_ARP) {
555 ARP arp = (ARP) eth.getPayload();
Pavlin Radoslavovd6612f92015-02-23 13:53:32 -0800556 IpAddress ip = IpAddress.valueOf(IpAddress.Version.INET,
557 arp.getSenderProtocolAddress());
Charles Chan888e20a2017-05-01 15:44:23 -0700558 createOrUpdateHost(hid, srcMac, vlan, hloc, ip);
alshabibe1cf87d2014-10-17 09:23:50 -0700559
Charles Chan5d5e0622015-09-25 13:00:06 -0700560 // IPv4: update location only
alshabibe1cf87d2014-10-17 09:23:50 -0700561 } else if (eth.getEtherType() == Ethernet.TYPE_IPV4) {
Yi Tsengaa417a62017-09-08 17:22:51 -0700562 // Update host location
563 createOrUpdateHost(hid, srcMac, vlan, hloc, null);
564 if (useDhcp) {
565 DHCP dhcp = findDhcp(eth).orElse(null);
566 // DHCP ACK: additionally update IP of DHCP client
567 if (dhcp != null && dhcp.getPacketType().equals(DHCP.MsgType.DHCPACK)) {
568 MacAddress hostMac = MacAddress.valueOf(dhcp.getClientHardwareAddress());
569 VlanId hostVlan = VlanId.vlanId(eth.getVlanID());
570 HostId hostId = HostId.hostId(hostMac, hostVlan);
571 updateHostIp(hostId, IpAddress.valueOf(dhcp.getYourIPAddress()));
Yi Tsengcfa637a2017-07-26 17:24:10 -0700572 }
Charles Chane6067892016-11-17 10:23:46 -0800573 }
Charles Chan5d5e0622015-09-25 13:00:06 -0700574 // NeighborAdvertisement and NeighborSolicitation: possible
575 // new hosts, update both location and IP.
576 //
577 // IPv6: update location only
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800578 } else if (eth.getEtherType() == Ethernet.TYPE_IPV6) {
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800579 IPv6 ipv6 = (IPv6) eth.getPayload();
Charles M.C. Chan956cb692015-04-26 18:49:39 +0800580 IpAddress ip = IpAddress.valueOf(IpAddress.Version.INET6,
Thomas Vachuska27bee092015-06-23 19:03:10 -0700581 ipv6.getSourceAddress());
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800582
Charles M.C. Chan956cb692015-04-26 18:49:39 +0800583 // skip extension headers
584 IPacket pkt = ipv6;
585 while (pkt.getPayload() != null &&
586 pkt.getPayload() instanceof IExtensionHeader) {
587 pkt = pkt.getPayload();
588 }
Charles M.C. Chan9148d2d2015-04-27 03:36:39 +0800589 pkt = pkt.getPayload();
Yi Tsengfcf5dce2017-07-26 14:30:41 -0700590
591 // DHCPv6 protocol
592 DHCP6 dhcp6 = findDhcp6(pkt).orElse(null);
593 if (dhcp6 != null && useDhcp6) {
594 createOrUpdateHost(hid, srcMac, vlan, hloc, null);
595 handleDhcp6(dhcp6, vlan);
596 return;
597 }
598
Charles M.C. Chan9148d2d2015-04-27 03:36:39 +0800599 if (pkt != null && pkt instanceof ICMP6) {
Yi Tsengfcf5dce2017-07-26 14:30:41 -0700600 // Neighbor Discovery Protocol
Charles M.C. Chan956cb692015-04-26 18:49:39 +0800601 pkt = pkt.getPayload();
Charles Chan178b0f52017-10-08 23:51:22 -0400602 if (pkt != null) {
603 // RouterSolicitation, RouterAdvertisement
604 if (pkt instanceof RouterAdvertisement || pkt instanceof RouterSolicitation) {
Charles M.C. Chan441d7da2015-03-17 21:03:39 +0800605 return;
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800606 }
Charles Chan178b0f52017-10-08 23:51:22 -0400607 if (pkt instanceof NeighborSolicitation || pkt instanceof NeighborAdvertisement) {
608 // Duplicate Address Detection
609 if (ip.isZero()) {
610 return;
611 }
612 // NeighborSolicitation, NeighborAdvertisement
613 createOrUpdateHost(hid, srcMac, vlan, hloc, ip);
614
615 // Also learn from the target address of NeighborAdvertisement
616 if (pkt instanceof NeighborAdvertisement) {
617 NeighborAdvertisement na = (NeighborAdvertisement) pkt;
618 Ip6Address targetAddr = Ip6Address.valueOf(na.getTargetAddress());
619 createOrUpdateHost(hid, srcMac, vlan, hloc, targetAddr);
620 }
621 return;
622 }
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800623 }
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800624 }
Charles M.C. Chan956cb692015-04-26 18:49:39 +0800625
Yi Tsengaa417a62017-09-08 17:22:51 -0700626 // multicast, exclude DHCPv6
627 if (eth.isMulticast() && dhcp6 == null) {
Charles M.C. Chan956cb692015-04-26 18:49:39 +0800628 return;
629 }
630
631 // normal IPv6 packets
Charles Chan888e20a2017-05-01 15:44:23 -0700632 createOrUpdateHost(hid, srcMac, vlan, hloc, null);
alshabibe1cf87d2014-10-17 09:23:50 -0700633 }
634 }
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700635
Yi Tsengfcf5dce2017-07-26 14:30:41 -0700636 /**
637 * Handles DHCPv6 packet, if message type is ACK, update IP address
638 * according to DHCPv6 payload (IA Address option).
639 *
640 * @param dhcp6 the DHCPv6 payload
641 * @param vlanId the vlan of this packet
642 */
643 private void handleDhcp6(DHCP6 dhcp6, VlanId vlanId) {
644 // extract the relay message if exist
645 while (dhcp6 != null && DHCP6.RELAY_MSG_TYPES.contains(dhcp6.getMsgType())) {
646 dhcp6 = dhcp6.getOptions().stream()
647 .filter(opt -> opt instanceof Dhcp6RelayOption)
648 .map(BasePacket::getPayload)
649 .map(pld -> (DHCP6) pld)
650 .findFirst()
651 .orElse(null);
652 }
653
654 if (dhcp6 == null) {
655 // Can't find dhcp payload
656 log.warn("Can't find dhcp payload from relay message");
657 return;
658 }
659
660 if (dhcp6.getMsgType() != DHCP6.MsgType.REPLY.value()) {
661 // Update IP address only when we received REPLY message
662 return;
663 }
664 Optional<Dhcp6ClientIdOption> clientIdOption = dhcp6.getOptions()
665 .stream()
666 .filter(opt -> opt instanceof Dhcp6ClientIdOption)
667 .map(opt -> (Dhcp6ClientIdOption) opt)
668 .findFirst();
669
670 if (!clientIdOption.isPresent()) {
671 // invalid DHCPv6 option
672 log.warn("Can't find client ID from DHCPv6 {}", dhcp6);
673 return;
674 }
675
676 byte[] linkLayerAddr = clientIdOption.get().getDuid().getLinkLayerAddress();
677 if (linkLayerAddr == null || linkLayerAddr.length != 6) {
678 // No any mac address found
679 log.warn("Can't find client mac from option {}", clientIdOption);
680 return;
681 }
682 MacAddress clientMac = MacAddress.valueOf(linkLayerAddr);
683
684 // Extract IPv6 address from IA NA ot IA TA option
685 Optional<Dhcp6IaNaOption> iaNaOption = dhcp6.getOptions()
686 .stream()
687 .filter(opt -> opt instanceof Dhcp6IaNaOption)
688 .map(opt -> (Dhcp6IaNaOption) opt)
689 .findFirst();
690 Optional<Dhcp6IaTaOption> iaTaOption = dhcp6.getOptions()
691 .stream()
692 .filter(opt -> opt instanceof Dhcp6IaTaOption)
693 .map(opt -> (Dhcp6IaTaOption) opt)
694 .findFirst();
695 Optional<Dhcp6IaAddressOption> iaAddressOption;
696 if (iaNaOption.isPresent()) {
697 iaAddressOption = iaNaOption.get().getOptions().stream()
698 .filter(opt -> opt instanceof Dhcp6IaAddressOption)
699 .map(opt -> (Dhcp6IaAddressOption) opt)
700 .findFirst();
701 } else if (iaTaOption.isPresent()) {
702 iaAddressOption = iaTaOption.get().getOptions().stream()
703 .filter(opt -> opt instanceof Dhcp6IaAddressOption)
704 .map(opt -> (Dhcp6IaAddressOption) opt)
705 .findFirst();
706 } else {
707 iaAddressOption = Optional.empty();
708 }
709 if (iaAddressOption.isPresent()) {
710 Ip6Address ip = iaAddressOption.get().getIp6Address();
711 HostId hostId = HostId.hostId(clientMac, vlanId);
712 updateHostIp(hostId, ip);
713 } else {
714 log.warn("Can't find IPv6 address from DHCPv6 {}", dhcp6);
715 }
716 }
717
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700718 private Optional<DHCP> findDhcp(Ethernet eth) {
719 IPacket pkt = eth.getPayload();
720 return Stream.of(pkt)
721 .filter(Objects::nonNull)
722 .filter(p -> p instanceof IPv4)
723 .map(IPacket::getPayload)
724 .filter(Objects::nonNull)
725 .filter(p -> p instanceof UDP)
726 .map(IPacket::getPayload)
727 .filter(Objects::nonNull)
728 .filter(p -> p instanceof DHCP)
729 .map(p -> (DHCP) p)
730 .findFirst();
731 }
Yi Tsengfcf5dce2017-07-26 14:30:41 -0700732
733 private Optional<DHCP6> findDhcp6(IPacket pkt) {
734 return Stream.of(pkt)
735 .filter(Objects::nonNull)
736 .filter(p -> p instanceof UDP)
737 .map(IPacket::getPayload)
738 .filter(Objects::nonNull)
739 .filter(p -> p instanceof DHCP6)
740 .map(p -> (DHCP6) p)
741 .findFirst();
742 }
alshabibe1cf87d2014-10-17 09:23:50 -0700743 }
Thomas Vachuska33601602014-11-19 03:32:15 -0800744
745 // Auxiliary listener to device events.
746 private class InternalDeviceListener implements DeviceListener {
747 @Override
748 public void event(DeviceEvent event) {
Madan Jampania3770c32015-12-11 12:07:41 -0800749 eventHandler.execute(() -> handleEvent(event));
750 }
751
752 private void handleEvent(DeviceEvent event) {
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -0800753 Device device = event.subject();
754 switch (event.type()) {
Thomas Vachuska27bee092015-06-23 19:03:10 -0700755 case DEVICE_ADDED:
756 break;
757 case DEVICE_AVAILABILITY_CHANGED:
Charles Chan888e20a2017-05-01 15:44:23 -0700758 if (hostRemovalEnabled && !deviceService.isAvailable(device.id())) {
759 processDeviceDown(device.id());
Thomas Vachuska27bee092015-06-23 19:03:10 -0700760 }
761 break;
762 case DEVICE_SUSPENDED:
763 case DEVICE_UPDATED:
764 // Nothing to do?
765 break;
766 case DEVICE_REMOVED:
767 if (hostRemovalEnabled) {
Charles Chan888e20a2017-05-01 15:44:23 -0700768 processDeviceDown(device.id());
Thomas Vachuska27bee092015-06-23 19:03:10 -0700769 }
770 break;
771 case PORT_ADDED:
772 break;
773 case PORT_UPDATED:
Charles Chan888e20a2017-05-01 15:44:23 -0700774 if (hostRemovalEnabled && !event.port().isEnabled()) {
775 processPortDown(new ConnectPoint(device.id(), event.port().number()));
Thomas Vachuska27bee092015-06-23 19:03:10 -0700776 }
777 break;
778 case PORT_REMOVED:
779 // Nothing to do?
780 break;
781 default:
782 break;
Thomas Vachuska33601602014-11-19 03:32:15 -0800783 }
784 }
785 }
786
Charles Chan888e20a2017-05-01 15:44:23 -0700787 /**
788 * When a device goes down, update the location of affected hosts.
789 *
790 * @param deviceId the device that goes down
791 */
792 private void processDeviceDown(DeviceId deviceId) {
793 hostService.getConnectedHosts(deviceId).forEach(affectedHost -> affectedHost.locations().stream()
794 .filter(hostLocation -> hostLocation.deviceId().equals(deviceId))
795 .forEach(affectedLocation ->
796 providerService.removeLocationFromHost(affectedHost.id(), affectedLocation))
797 );
798 }
799
800 /**
801 * When a port goes down, update the location of affected hosts.
802 *
803 * @param connectPoint the port that goes down
804 */
805 private void processPortDown(ConnectPoint connectPoint) {
806 hostService.getConnectedHosts(connectPoint).forEach(affectedHost ->
807 providerService.removeLocationFromHost(affectedHost.id(), new HostLocation(connectPoint, 0L))
808 );
Thomas Vachuska33601602014-11-19 03:32:15 -0800809 }
810
alshabibe1cf87d2014-10-17 09:23:50 -0700811}