blob: 445afad7db9d29f33133fd488ec955667d952cbe [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
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;
Yi Tsengfcf5dce2017-07-26 14:30:41 -070026import org.onlab.packet.BasePacket;
Charles Chane6067892016-11-17 10:23:46 -080027import org.onlab.packet.DHCP;
Yi Tsengfcf5dce2017-07-26 14:30:41 -070028import org.onlab.packet.DHCP6;
Jonathan Harte8600eb2015-01-12 10:30:45 -080029import org.onlab.packet.Ethernet;
Pavlin Radoslavovd6612f92015-02-23 13:53:32 -080030import org.onlab.packet.ICMP6;
Thomas Vachuskaf845cf62015-03-24 10:13:09 -070031import org.onlab.packet.IPacket;
Charles Chane6067892016-11-17 10:23:46 -080032import org.onlab.packet.IPv4;
Jonathan Harte8600eb2015-01-12 10:30:45 -080033import org.onlab.packet.IPv6;
Yi Tsengfcf5dce2017-07-26 14:30:41 -070034import org.onlab.packet.Ip6Address;
Jonathan Harte8600eb2015-01-12 10:30:45 -080035import org.onlab.packet.IpAddress;
Charles M.C. Chan956cb692015-04-26 18:49:39 +080036import org.onlab.packet.MacAddress;
Charles Chane6067892016-11-17 10:23:46 -080037import org.onlab.packet.TpPort;
38import org.onlab.packet.UDP;
Jonathan Harte8600eb2015-01-12 10:30:45 -080039import org.onlab.packet.VlanId;
Yi Tsengfcf5dce2017-07-26 14:30:41 -070040import org.onlab.packet.dhcp.Dhcp6ClientIdOption;
41import org.onlab.packet.dhcp.Dhcp6IaAddressOption;
42import org.onlab.packet.dhcp.Dhcp6IaNaOption;
43import org.onlab.packet.dhcp.Dhcp6IaTaOption;
44import org.onlab.packet.dhcp.Dhcp6RelayOption;
Charles M.C. Chan956cb692015-04-26 18:49:39 +080045import org.onlab.packet.ipv6.IExtensionHeader;
Jonathan Harte8600eb2015-01-12 10:30:45 -080046import org.onlab.packet.ndp.NeighborAdvertisement;
47import org.onlab.packet.ndp.NeighborSolicitation;
Charles M.C. Chan441d7da2015-03-17 21:03:39 +080048import org.onlab.packet.ndp.RouterAdvertisement;
49import org.onlab.packet.ndp.RouterSolicitation;
Jian Lid9b5f552016-03-11 18:15:31 -080050import org.onlab.util.Tools;
Thomas Vachuska6519e6f2015-03-11 02:29:31 -070051import org.onosproject.cfg.ComponentConfigService;
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -080052import org.onosproject.core.ApplicationId;
53import org.onosproject.core.CoreService;
Yi Tseng7a38f9a2017-06-09 14:36:40 -070054import org.onosproject.incubator.net.intf.InterfaceService;
Brian O'Connorabafb502014-12-02 22:26:20 -080055import org.onosproject.net.ConnectPoint;
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -080056import org.onosproject.net.Device;
Charles Chan888e20a2017-05-01 15:44:23 -070057import org.onosproject.net.DeviceId;
Brian O'Connorabafb502014-12-02 22:26:20 -080058import org.onosproject.net.Host;
59import org.onosproject.net.HostId;
60import org.onosproject.net.HostLocation;
61import org.onosproject.net.device.DeviceEvent;
62import org.onosproject.net.device.DeviceListener;
63import org.onosproject.net.device.DeviceService;
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -080064import org.onosproject.net.flow.DefaultTrafficSelector;
sdn94b00152016-08-30 02:12:32 -070065import org.onosproject.net.flow.DefaultTrafficTreatment;
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -080066import org.onosproject.net.flow.TrafficSelector;
sdn94b00152016-08-30 02:12:32 -070067import org.onosproject.net.flow.TrafficTreatment;
Brian O'Connorabafb502014-12-02 22:26:20 -080068import org.onosproject.net.host.DefaultHostDescription;
69import org.onosproject.net.host.HostDescription;
70import org.onosproject.net.host.HostProvider;
71import org.onosproject.net.host.HostProviderRegistry;
72import org.onosproject.net.host.HostProviderService;
73import org.onosproject.net.host.HostService;
sdn94b00152016-08-30 02:12:32 -070074import org.onosproject.net.packet.DefaultOutboundPacket;
75import org.onosproject.net.packet.OutboundPacket;
Brian O'Connorabafb502014-12-02 22:26:20 -080076import org.onosproject.net.packet.PacketContext;
Jonathan Hart3cfce8e2015-01-14 16:43:27 -080077import org.onosproject.net.packet.PacketPriority;
Brian O'Connorabafb502014-12-02 22:26:20 -080078import org.onosproject.net.packet.PacketProcessor;
79import org.onosproject.net.packet.PacketService;
80import org.onosproject.net.provider.AbstractProvider;
81import org.onosproject.net.provider.ProviderId;
82import org.onosproject.net.topology.Topology;
83import org.onosproject.net.topology.TopologyService;
Thomas Vachuska33601602014-11-19 03:32:15 -080084import org.osgi.service.component.ComponentContext;
alshabibe1cf87d2014-10-17 09:23:50 -070085import org.slf4j.Logger;
86
sdn94b00152016-08-30 02:12:32 -070087import java.nio.ByteBuffer;
Thomas Vachuskaf845cf62015-03-24 10:13:09 -070088import java.util.Dictionary;
Yi Tseng7a38f9a2017-06-09 14:36:40 -070089import java.util.Objects;
90import java.util.Optional;
Madan Jampania3770c32015-12-11 12:07:41 -080091import java.util.concurrent.ExecutorService;
Yi Tseng7a38f9a2017-06-09 14:36:40 -070092import java.util.stream.Stream;
Thomas Vachuskaf845cf62015-03-24 10:13:09 -070093
Madan Jampania3770c32015-12-11 12:07:41 -080094import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
95import static org.onlab.util.Tools.groupedThreads;
Thomas Vachuskaf845cf62015-03-24 10:13:09 -070096import static org.slf4j.LoggerFactory.getLogger;
97
alshabibe1cf87d2014-10-17 09:23:50 -070098/**
Thomas Vachuskaec9c7dd2015-09-03 18:30:04 -070099 * Provider which uses an OpenFlow controller to detect network end-station
100 * hosts.
alshabibe1cf87d2014-10-17 09:23:50 -0700101 */
102@Component(immediate = true)
103public class HostLocationProvider extends AbstractProvider implements HostProvider {
alshabibe1cf87d2014-10-17 09:23:50 -0700104 private final Logger log = getLogger(getClass());
105
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -0800106 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
107 protected CoreService coreService;
108
109 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
alshabibe1cf87d2014-10-17 09:23:50 -0700110 protected HostProviderRegistry providerRegistry;
111
112 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Pavlin Radoslavovd6612f92015-02-23 13:53:32 -0800113 protected PacketService packetService;
alshabibe1cf87d2014-10-17 09:23:50 -0700114
115 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
116 protected TopologyService topologyService;
117
Thomas Vachuska33601602014-11-19 03:32:15 -0800118 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
119 protected HostService hostService;
120
121 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
122 protected DeviceService deviceService;
123
Thomas Vachuska6519e6f2015-03-11 02:29:31 -0700124 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
125 protected ComponentConfigService cfgService;
126
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700127 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
128 protected InterfaceService interfaceService;
129
alshabibe1cf87d2014-10-17 09:23:50 -0700130 private HostProviderService providerService;
131
132 private final InternalHostProvider processor = new InternalHostProvider();
Thomas Vachuska33601602014-11-19 03:32:15 -0800133 private final DeviceListener deviceListener = new InternalDeviceListener();
134
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -0800135 private ApplicationId appId;
136
Thomas Vachuska33601602014-11-19 03:32:15 -0800137 @Property(name = "hostRemovalEnabled", boolValue = true,
138 label = "Enable host removal on port/device down events")
139 private boolean hostRemovalEnabled = true;
alshabibe1cf87d2014-10-17 09:23:50 -0700140
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700141 @Property(name = "requestArp", boolValue = true,
142 label = "Request ARP packets for neighbor discovery by the " +
Charles Chane6067892016-11-17 10:23:46 -0800143 "Host Location Provider; default is true")
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700144 private boolean requestArp = true;
Charles Chane6067892016-11-17 10:23:46 -0800145
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700146 @Property(name = "requestIpv6ND", boolValue = false,
147 label = "Requests IPv6 Neighbor Discovery by the " +
Thomas Vachuska27bee092015-06-23 19:03:10 -0700148 "Host Location Provider; default is false")
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700149 private boolean requestIpv6ND = false;
Charles Chane6067892016-11-17 10:23:46 -0800150
151 @Property(name = "useDhcp", boolValue = false,
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700152 label = "Use DHCP for neighbor discovery by the " +
Charles Chane6067892016-11-17 10:23:46 -0800153 "Host Location Provider; default is false")
154 private boolean useDhcp = false;
alshabibe1cf87d2014-10-17 09:23:50 -0700155
Yi Tsengfcf5dce2017-07-26 14:30:41 -0700156 @Property(name = "useDhcp6", boolValue = false,
157 label = "Use DHCPv6 for neighbor discovery by the " +
158 "Host Location Provider; default is false")
159 private boolean useDhcp6 = false;
160
Jonathan Hart9af322d2016-01-06 17:42:04 -0800161 @Property(name = "requestInterceptsEnabled", boolValue = true,
162 label = "Enable requesting packet intercepts")
163 private boolean requestInterceptsEnabled = true;
164
Madan Jampania3770c32015-12-11 12:07:41 -0800165 protected ExecutorService eventHandler;
166
sdn94b00152016-08-30 02:12:32 -0700167 private static final byte[] SENDER_ADDRESS = IpAddress.valueOf("0.0.0.0").toOctets();
168
alshabibe1cf87d2014-10-17 09:23:50 -0700169 /**
170 * Creates an OpenFlow host provider.
171 */
172 public HostLocationProvider() {
Brian O'Connorabafb502014-12-02 22:26:20 -0800173 super(new ProviderId("of", "org.onosproject.provider.host"));
alshabibe1cf87d2014-10-17 09:23:50 -0700174 }
175
176 @Activate
Thomas Vachuska33601602014-11-19 03:32:15 -0800177 public void activate(ComponentContext context) {
Thomas Vachuska6519e6f2015-03-11 02:29:31 -0700178 cfgService.registerProperties(getClass());
179 appId = coreService.registerApplication("org.onosproject.provider.host");
Jonathan Hart9af322d2016-01-06 17:42:04 -0800180 eventHandler = newSingleThreadScheduledExecutor(
HIGUCHI Yutad9e01052016-04-14 09:31:42 -0700181 groupedThreads("onos/host-loc-provider", "event-handler", log));
alshabibe1cf87d2014-10-17 09:23:50 -0700182 providerService = providerRegistry.register(this);
Brian O'Connor3b783262015-07-29 17:49:24 -0700183 packetService.addProcessor(processor, PacketProcessor.advisor(1));
Thomas Vachuska33601602014-11-19 03:32:15 -0800184 deviceService.addListener(deviceListener);
Jonathan Hart9af322d2016-01-06 17:42:04 -0800185
186 modified(context);
Jonathan Hart3cfce8e2015-01-14 16:43:27 -0800187
Charles M.C. Chane148de82015-05-06 12:38:21 +0800188 log.info("Started with Application ID {}", appId.id());
189 }
190
191 @Deactivate
192 public void deactivate() {
Charles M.C. Chane148de82015-05-06 12:38:21 +0800193 cfgService.unregisterProperties(getClass(), false);
Thomas Vachuska27bee092015-06-23 19:03:10 -0700194
195 withdrawIntercepts();
196
Charles M.C. Chane148de82015-05-06 12:38:21 +0800197 providerRegistry.unregister(this);
198 packetService.removeProcessor(processor);
199 deviceService.removeListener(deviceListener);
Madan Jampania3770c32015-12-11 12:07:41 -0800200 eventHandler.shutdown();
Charles M.C. Chane148de82015-05-06 12:38:21 +0800201 providerService = null;
202 log.info("Stopped");
203 }
204
205 @Modified
206 public void modified(ComponentContext context) {
Charles M.C. Chane148de82015-05-06 12:38:21 +0800207 readComponentConfiguration(context);
Jonathan Hart9af322d2016-01-06 17:42:04 -0800208
209 if (requestInterceptsEnabled) {
210 requestIntercepts();
211 } else {
212 withdrawIntercepts();
213 }
Charles M.C. Chane148de82015-05-06 12:38:21 +0800214 }
215
216 /**
Thomas Vachuska27bee092015-06-23 19:03:10 -0700217 * Request packet intercepts.
Charles M.C. Chane148de82015-05-06 12:38:21 +0800218 */
Thomas Vachuska27bee092015-06-23 19:03:10 -0700219 private void requestIntercepts() {
Charles Chane6067892016-11-17 10:23:46 -0800220 // Use ARP
221 TrafficSelector arpSelector = DefaultTrafficSelector.builder()
222 .matchEthType(Ethernet.TYPE_ARP)
223 .build();
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700224 if (requestArp) {
Charles Chane6067892016-11-17 10:23:46 -0800225 packetService.requestPackets(arpSelector, PacketPriority.CONTROL, appId);
Thomas Vachuska27bee092015-06-23 19:03:10 -0700226 } else {
Charles Chane6067892016-11-17 10:23:46 -0800227 packetService.cancelPackets(arpSelector, PacketPriority.CONTROL, appId);
Pavlin Radoslavov93b606b2015-02-25 17:28:39 -0800228 }
Thomas Vachuska27bee092015-06-23 19:03:10 -0700229
Charles Chane6067892016-11-17 10:23:46 -0800230 // Use IPv6 Neighbor Discovery
231 TrafficSelector ipv6NsSelector = DefaultTrafficSelector.builder()
232 .matchEthType(Ethernet.TYPE_IPV6)
233 .matchIPProtocol(IPv6.PROTOCOL_ICMP6)
234 .matchIcmpv6Type(ICMP6.NEIGHBOR_SOLICITATION)
235 .build();
236 TrafficSelector ipv6NaSelector = DefaultTrafficSelector.builder()
237 .matchEthType(Ethernet.TYPE_IPV6)
238 .matchIPProtocol(IPv6.PROTOCOL_ICMP6)
239 .matchIcmpv6Type(ICMP6.NEIGHBOR_ADVERTISEMENT)
240 .build();
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700241 if (requestIpv6ND) {
Charles Chane6067892016-11-17 10:23:46 -0800242 packetService.requestPackets(ipv6NsSelector, PacketPriority.CONTROL, appId);
243 packetService.requestPackets(ipv6NaSelector, PacketPriority.CONTROL, appId);
Thomas Vachuska27bee092015-06-23 19:03:10 -0700244 } else {
Charles Chane6067892016-11-17 10:23:46 -0800245 packetService.cancelPackets(ipv6NsSelector, PacketPriority.CONTROL, appId);
246 packetService.cancelPackets(ipv6NaSelector, PacketPriority.CONTROL, appId);
247 }
248
249 // Use DHCP
250 TrafficSelector dhcpServerSelector = DefaultTrafficSelector.builder()
251 .matchEthType(Ethernet.TYPE_IPV4)
252 .matchIPProtocol(IPv4.PROTOCOL_UDP)
253 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_SERVER_PORT))
254 .build();
255 TrafficSelector dhcpClientSelector = DefaultTrafficSelector.builder()
256 .matchEthType(Ethernet.TYPE_IPV4)
257 .matchIPProtocol(IPv4.PROTOCOL_UDP)
258 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_CLIENT_PORT))
259 .build();
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 }
Pavlin Radoslavov93b606b2015-02-25 17:28:39 -0800339 }
340
alshabibe1cf87d2014-10-17 09:23:50 -0700341 @Override
342 public void triggerProbe(Host host) {
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800343 //log.info("Triggering probe on device {} ", host);
Jonathan Hartf353e402016-09-23 09:27:36 -0700344
345 // FIXME Disabling host probing for now, because sending packets from a
346 // broadcast MAC address caused problems when two ONOS networks were
347 // interconnected. Host probing should take into account the interface
348 // configuration when determining which source address to use.
349
350 //MastershipRole role = deviceService.getRole(host.location().deviceId());
351 //if (role.equals(MastershipRole.MASTER)) {
352 // host.ipAddresses().forEach(ip -> {
353 // sendProbe(host, ip);
354 // });
355 //} else {
356 // log.info("not the master, master will probe {}");
357 //}
sdn94b00152016-08-30 02:12:32 -0700358 }
359
360 private void sendProbe(Host host, IpAddress targetIp) {
361 Ethernet probePacket = null;
362 if (targetIp.isIp4()) {
363 // IPv4: Use ARP
364 probePacket = buildArpRequest(targetIp, host);
365 } else {
366 // IPv6: Use Neighbor Discovery
Jonathan Hartf353e402016-09-23 09:27:36 -0700367 //TODO need to implement ndp probe
sdn94b00152016-08-30 02:12:32 -0700368 log.info("Triggering probe on device {} ", host);
369 }
370
371 TrafficTreatment treatment = DefaultTrafficTreatment.builder().setOutput(host.location().port()).build();
372
373 OutboundPacket outboundPacket = new DefaultOutboundPacket(host.location().deviceId(), treatment,
374 ByteBuffer.wrap(probePacket.serialize()));
375
376 packetService.emit(outboundPacket);
377 }
378
Charles Chan888e20a2017-05-01 15:44:23 -0700379 // 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 -0700380 private Ethernet buildArpRequest(IpAddress targetIp, Host host) {
381
382 ARP arp = new ARP();
383 arp.setHardwareType(ARP.HW_TYPE_ETHERNET)
384 .setHardwareAddressLength((byte) Ethernet.DATALAYER_ADDRESS_LENGTH)
385 .setProtocolType(ARP.PROTO_TYPE_IP)
386 .setProtocolAddressLength((byte) IpAddress.INET_BYTE_LENGTH)
387 .setOpCode(ARP.OP_REQUEST);
388
389 arp.setSenderHardwareAddress(MacAddress.BROADCAST.toBytes())
390 .setSenderProtocolAddress(SENDER_ADDRESS)
391 .setTargetHardwareAddress(MacAddress.BROADCAST.toBytes())
392 .setTargetProtocolAddress(targetIp.toOctets());
393
394 Ethernet ethernet = new Ethernet();
395 ethernet.setEtherType(Ethernet.TYPE_ARP)
396 .setDestinationMACAddress(MacAddress.BROADCAST)
397 .setSourceMACAddress(MacAddress.BROADCAST).setPayload(arp);
398
399 ethernet.setPad(true);
400 return ethernet;
alshabibe1cf87d2014-10-17 09:23:50 -0700401 }
402
403 private class InternalHostProvider implements PacketProcessor {
Charles M.C. Chan956cb692015-04-26 18:49:39 +0800404 /**
Charles Chan888e20a2017-05-01 15:44:23 -0700405 * Create or update host information.
406 * Will not update IP if IP is null, all zero or self-assigned.
Charles M.C. Chan956cb692015-04-26 18:49:39 +0800407 *
Thomas Vachuska27bee092015-06-23 19:03:10 -0700408 * @param hid host ID
409 * @param mac source Mac address
Charles M.C. Chan956cb692015-04-26 18:49:39 +0800410 * @param vlan VLAN ID
411 * @param hloc host location
Charles Chan888e20a2017-05-01 15:44:23 -0700412 * @param ip source IP address or null if not updating
Charles M.C. Chan956cb692015-04-26 18:49:39 +0800413 */
Charles Chan888e20a2017-05-01 15:44:23 -0700414 private void createOrUpdateHost(HostId hid, MacAddress mac,
415 VlanId vlan, HostLocation hloc,
416 IpAddress ip) {
417 HostDescription desc = ip == null || ip.isZero() || ip.isSelfAssigned() ?
Thomas Vachuskaec9c7dd2015-09-03 18:30:04 -0700418 new DefaultHostDescription(mac, vlan, hloc) :
419 new DefaultHostDescription(mac, vlan, hloc, ip);
Sahil Lele3a0cdd52015-07-21 14:16:31 -0700420 try {
Ray Milkeydc083442016-02-22 11:27:57 -0800421 providerService.hostDetected(hid, desc, false);
Sahil Lele3a0cdd52015-07-21 14:16:31 -0700422 } catch (IllegalStateException e) {
423 log.debug("Host {} suppressed", hid);
424 }
Charles M.C. Chan956cb692015-04-26 18:49:39 +0800425 }
alshabibe1cf87d2014-10-17 09:23:50 -0700426
Charles Chane6067892016-11-17 10:23:46 -0800427 /**
Charles Chan888e20a2017-05-01 15:44:23 -0700428 * Updates IP address for an existing host.
Charles Chane6067892016-11-17 10:23:46 -0800429 *
430 * @param hid host ID
431 * @param ip IP address
432 */
Charles Chan888e20a2017-05-01 15:44:23 -0700433 private void updateHostIp(HostId hid, IpAddress ip) {
Charles Chane6067892016-11-17 10:23:46 -0800434 Host host = hostService.getHost(hid);
435 if (host == null) {
436 log.debug("Fail to update IP for {}. Host does not exist");
437 return;
438 }
439
Charles Chan888e20a2017-05-01 15:44:23 -0700440 HostDescription desc = new DefaultHostDescription(hid.mac(), hid.vlanId(),
441 host.location(), ip);
Charles Chane6067892016-11-17 10:23:46 -0800442 try {
443 providerService.hostDetected(hid, desc, false);
444 } catch (IllegalStateException e) {
445 log.debug("Host {} suppressed", hid);
446 }
447 }
448
alshabibe1cf87d2014-10-17 09:23:50 -0700449 @Override
450 public void process(PacketContext context) {
alshabib4a179dc2014-10-17 17:17:01 -0700451 if (context == null) {
452 return;
453 }
alshabibe1cf87d2014-10-17 09:23:50 -0700454
Thomas Vachuskaf845cf62015-03-24 10:13:09 -0700455 Ethernet eth = context.inPacket().parsed();
Jonathan Harte8600eb2015-01-12 10:30:45 -0800456 if (eth == null) {
457 return;
458 }
Jonathan Hartf353e402016-09-23 09:27:36 -0700459
Charles M.C. Chan956cb692015-04-26 18:49:39 +0800460 MacAddress srcMac = eth.getSourceMAC();
Jonathan Hartf353e402016-09-23 09:27:36 -0700461 if (srcMac.isBroadcast() || srcMac.isMulticast()) {
462 return;
463 }
Jonathan Harte8600eb2015-01-12 10:30:45 -0800464
alshabibe1cf87d2014-10-17 09:23:50 -0700465 VlanId vlan = VlanId.vlanId(eth.getVlanID());
466 ConnectPoint heardOn = context.inPacket().receivedFrom();
467
Thomas Vachuskaf845cf62015-03-24 10:13:09 -0700468 // If this arrived on control port, bail out.
469 if (heardOn.port().isLogical()) {
470 return;
471 }
472
alshabibe1cf87d2014-10-17 09:23:50 -0700473 // If this is not an edge port, bail out.
474 Topology topology = topologyService.currentTopology();
475 if (topologyService.isInfrastructure(topology, heardOn)) {
476 return;
477 }
478
Thomas Vachuskaec9c7dd2015-09-03 18:30:04 -0700479 HostLocation hloc = new HostLocation(heardOn, System.currentTimeMillis());
alshabibe1cf87d2014-10-17 09:23:50 -0700480 HostId hid = HostId.hostId(eth.getSourceMAC(), vlan);
481
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800482 // ARP: possible new hosts, update both location and IP
alshabibe1cf87d2014-10-17 09:23:50 -0700483 if (eth.getEtherType() == Ethernet.TYPE_ARP) {
484 ARP arp = (ARP) eth.getPayload();
Pavlin Radoslavovd6612f92015-02-23 13:53:32 -0800485 IpAddress ip = IpAddress.valueOf(IpAddress.Version.INET,
486 arp.getSenderProtocolAddress());
Charles Chan888e20a2017-05-01 15:44:23 -0700487 createOrUpdateHost(hid, srcMac, vlan, hloc, ip);
alshabibe1cf87d2014-10-17 09:23:50 -0700488
Charles Chan5d5e0622015-09-25 13:00:06 -0700489 // IPv4: update location only
alshabibe1cf87d2014-10-17 09:23:50 -0700490 } else if (eth.getEtherType() == Ethernet.TYPE_IPV4) {
Yi Tsengcfa637a2017-07-26 17:24:10 -0700491 DHCP dhcp = findDhcp(eth).orElse(null);
492 if (dhcp != null) {
493 if (useDhcp) {
494 // learn host (server or client) MAC address
495 createOrUpdateHost(hid, srcMac, vlan, hloc, null);
496
497 // DHCP ACK: additionally update IP of DHCP client
498 if (dhcp.getPacketType().equals(DHCP.MsgType.DHCPACK)) {
499 MacAddress hostMac = MacAddress.valueOf(dhcp.getClientHardwareAddress());
500 VlanId hostVlan = VlanId.vlanId(eth.getVlanID());
501 HostId hostId = HostId.hostId(hostMac, hostVlan);
502 updateHostIp(hostId, IpAddress.valueOf(dhcp.getYourIPAddress()));
503 }
504 }
505 } else {
506 // learn host MAC address
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700507 createOrUpdateHost(hid, srcMac, vlan, hloc, null);
Charles Chane6067892016-11-17 10:23:46 -0800508 }
Charles Chan5d5e0622015-09-25 13:00:06 -0700509 //
510 // NeighborAdvertisement and NeighborSolicitation: possible
511 // new hosts, update both location and IP.
512 //
513 // IPv6: update location only
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800514 } else if (eth.getEtherType() == Ethernet.TYPE_IPV6) {
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800515 IPv6 ipv6 = (IPv6) eth.getPayload();
Charles M.C. Chan956cb692015-04-26 18:49:39 +0800516 IpAddress ip = IpAddress.valueOf(IpAddress.Version.INET6,
Thomas Vachuska27bee092015-06-23 19:03:10 -0700517 ipv6.getSourceAddress());
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800518
Charles M.C. Chan956cb692015-04-26 18:49:39 +0800519 // skip extension headers
520 IPacket pkt = ipv6;
521 while (pkt.getPayload() != null &&
522 pkt.getPayload() instanceof IExtensionHeader) {
523 pkt = pkt.getPayload();
524 }
Charles M.C. Chan9148d2d2015-04-27 03:36:39 +0800525 pkt = pkt.getPayload();
Yi Tsengfcf5dce2017-07-26 14:30:41 -0700526
527 // DHCPv6 protocol
528 DHCP6 dhcp6 = findDhcp6(pkt).orElse(null);
529 if (dhcp6 != null && useDhcp6) {
530 createOrUpdateHost(hid, srcMac, vlan, hloc, null);
531 handleDhcp6(dhcp6, vlan);
532 return;
533 }
534
Charles M.C. Chan9148d2d2015-04-27 03:36:39 +0800535 if (pkt != null && pkt instanceof ICMP6) {
Yi Tsengfcf5dce2017-07-26 14:30:41 -0700536 // Neighbor Discovery Protocol
Charles M.C. Chan956cb692015-04-26 18:49:39 +0800537 pkt = pkt.getPayload();
538 // RouterSolicitation, RouterAdvertisement
Charles M.C. Chan9148d2d2015-04-27 03:36:39 +0800539 if (pkt != null && (pkt instanceof RouterAdvertisement ||
Thomas Vachuska27bee092015-06-23 19:03:10 -0700540 pkt instanceof RouterSolicitation)) {
Charles M.C. Chan441d7da2015-03-17 21:03:39 +0800541 return;
542 }
Charles M.C. Chan9148d2d2015-04-27 03:36:39 +0800543 if (pkt != null && (pkt instanceof NeighborSolicitation ||
Thomas Vachuska27bee092015-06-23 19:03:10 -0700544 pkt instanceof NeighborAdvertisement)) {
Charles M.C. Chan956cb692015-04-26 18:49:39 +0800545 // Duplicate Address Detection
546 if (ip.isZero()) {
Charles M.C. Chan441d7da2015-03-17 21:03:39 +0800547 return;
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800548 }
Charles M.C. Chan956cb692015-04-26 18:49:39 +0800549 // NeighborSolicitation, NeighborAdvertisement
Charles Chan888e20a2017-05-01 15:44:23 -0700550 createOrUpdateHost(hid, srcMac, vlan, hloc, ip);
Charles M.C. Chan956cb692015-04-26 18:49:39 +0800551 return;
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800552 }
Charles M.C. Chan7fee36a2014-12-31 00:19:59 +0800553 }
Charles M.C. Chan956cb692015-04-26 18:49:39 +0800554
555 // multicast
556 if (eth.isMulticast()) {
557 return;
558 }
559
560 // normal IPv6 packets
Charles Chan888e20a2017-05-01 15:44:23 -0700561 createOrUpdateHost(hid, srcMac, vlan, hloc, null);
alshabibe1cf87d2014-10-17 09:23:50 -0700562 }
563 }
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700564
Yi Tsengfcf5dce2017-07-26 14:30:41 -0700565 /**
566 * Handles DHCPv6 packet, if message type is ACK, update IP address
567 * according to DHCPv6 payload (IA Address option).
568 *
569 * @param dhcp6 the DHCPv6 payload
570 * @param vlanId the vlan of this packet
571 */
572 private void handleDhcp6(DHCP6 dhcp6, VlanId vlanId) {
573 // extract the relay message if exist
574 while (dhcp6 != null && DHCP6.RELAY_MSG_TYPES.contains(dhcp6.getMsgType())) {
575 dhcp6 = dhcp6.getOptions().stream()
576 .filter(opt -> opt instanceof Dhcp6RelayOption)
577 .map(BasePacket::getPayload)
578 .map(pld -> (DHCP6) pld)
579 .findFirst()
580 .orElse(null);
581 }
582
583 if (dhcp6 == null) {
584 // Can't find dhcp payload
585 log.warn("Can't find dhcp payload from relay message");
586 return;
587 }
588
589 if (dhcp6.getMsgType() != DHCP6.MsgType.REPLY.value()) {
590 // Update IP address only when we received REPLY message
591 return;
592 }
593 Optional<Dhcp6ClientIdOption> clientIdOption = dhcp6.getOptions()
594 .stream()
595 .filter(opt -> opt instanceof Dhcp6ClientIdOption)
596 .map(opt -> (Dhcp6ClientIdOption) opt)
597 .findFirst();
598
599 if (!clientIdOption.isPresent()) {
600 // invalid DHCPv6 option
601 log.warn("Can't find client ID from DHCPv6 {}", dhcp6);
602 return;
603 }
604
605 byte[] linkLayerAddr = clientIdOption.get().getDuid().getLinkLayerAddress();
606 if (linkLayerAddr == null || linkLayerAddr.length != 6) {
607 // No any mac address found
608 log.warn("Can't find client mac from option {}", clientIdOption);
609 return;
610 }
611 MacAddress clientMac = MacAddress.valueOf(linkLayerAddr);
612
613 // Extract IPv6 address from IA NA ot IA TA option
614 Optional<Dhcp6IaNaOption> iaNaOption = dhcp6.getOptions()
615 .stream()
616 .filter(opt -> opt instanceof Dhcp6IaNaOption)
617 .map(opt -> (Dhcp6IaNaOption) opt)
618 .findFirst();
619 Optional<Dhcp6IaTaOption> iaTaOption = dhcp6.getOptions()
620 .stream()
621 .filter(opt -> opt instanceof Dhcp6IaTaOption)
622 .map(opt -> (Dhcp6IaTaOption) opt)
623 .findFirst();
624 Optional<Dhcp6IaAddressOption> iaAddressOption;
625 if (iaNaOption.isPresent()) {
626 iaAddressOption = iaNaOption.get().getOptions().stream()
627 .filter(opt -> opt instanceof Dhcp6IaAddressOption)
628 .map(opt -> (Dhcp6IaAddressOption) opt)
629 .findFirst();
630 } else if (iaTaOption.isPresent()) {
631 iaAddressOption = iaTaOption.get().getOptions().stream()
632 .filter(opt -> opt instanceof Dhcp6IaAddressOption)
633 .map(opt -> (Dhcp6IaAddressOption) opt)
634 .findFirst();
635 } else {
636 iaAddressOption = Optional.empty();
637 }
638 if (iaAddressOption.isPresent()) {
639 Ip6Address ip = iaAddressOption.get().getIp6Address();
640 HostId hostId = HostId.hostId(clientMac, vlanId);
641 updateHostIp(hostId, ip);
642 } else {
643 log.warn("Can't find IPv6 address from DHCPv6 {}", dhcp6);
644 }
645 }
646
Yi Tseng7a38f9a2017-06-09 14:36:40 -0700647 private Optional<DHCP> findDhcp(Ethernet eth) {
648 IPacket pkt = eth.getPayload();
649 return Stream.of(pkt)
650 .filter(Objects::nonNull)
651 .filter(p -> p instanceof IPv4)
652 .map(IPacket::getPayload)
653 .filter(Objects::nonNull)
654 .filter(p -> p instanceof UDP)
655 .map(IPacket::getPayload)
656 .filter(Objects::nonNull)
657 .filter(p -> p instanceof DHCP)
658 .map(p -> (DHCP) p)
659 .findFirst();
660 }
Yi Tsengfcf5dce2017-07-26 14:30:41 -0700661
662 private Optional<DHCP6> findDhcp6(IPacket pkt) {
663 return Stream.of(pkt)
664 .filter(Objects::nonNull)
665 .filter(p -> p instanceof UDP)
666 .map(IPacket::getPayload)
667 .filter(Objects::nonNull)
668 .filter(p -> p instanceof DHCP6)
669 .map(p -> (DHCP6) p)
670 .findFirst();
671 }
alshabibe1cf87d2014-10-17 09:23:50 -0700672 }
Thomas Vachuska33601602014-11-19 03:32:15 -0800673
674 // Auxiliary listener to device events.
675 private class InternalDeviceListener implements DeviceListener {
676 @Override
677 public void event(DeviceEvent event) {
Madan Jampania3770c32015-12-11 12:07:41 -0800678 eventHandler.execute(() -> handleEvent(event));
679 }
680
681 private void handleEvent(DeviceEvent event) {
Pavlin Radoslavovd36a74b2015-01-09 11:59:07 -0800682 Device device = event.subject();
683 switch (event.type()) {
Thomas Vachuska27bee092015-06-23 19:03:10 -0700684 case DEVICE_ADDED:
685 break;
686 case DEVICE_AVAILABILITY_CHANGED:
Charles Chan888e20a2017-05-01 15:44:23 -0700687 if (hostRemovalEnabled && !deviceService.isAvailable(device.id())) {
688 processDeviceDown(device.id());
Thomas Vachuska27bee092015-06-23 19:03:10 -0700689 }
690 break;
691 case DEVICE_SUSPENDED:
692 case DEVICE_UPDATED:
693 // Nothing to do?
694 break;
695 case DEVICE_REMOVED:
696 if (hostRemovalEnabled) {
Charles Chan888e20a2017-05-01 15:44:23 -0700697 processDeviceDown(device.id());
Thomas Vachuska27bee092015-06-23 19:03:10 -0700698 }
699 break;
700 case PORT_ADDED:
701 break;
702 case PORT_UPDATED:
Charles Chan888e20a2017-05-01 15:44:23 -0700703 if (hostRemovalEnabled && !event.port().isEnabled()) {
704 processPortDown(new ConnectPoint(device.id(), event.port().number()));
Thomas Vachuska27bee092015-06-23 19:03:10 -0700705 }
706 break;
707 case PORT_REMOVED:
708 // Nothing to do?
709 break;
710 default:
711 break;
Thomas Vachuska33601602014-11-19 03:32:15 -0800712 }
713 }
714 }
715
Charles Chan888e20a2017-05-01 15:44:23 -0700716 /**
717 * When a device goes down, update the location of affected hosts.
718 *
719 * @param deviceId the device that goes down
720 */
721 private void processDeviceDown(DeviceId deviceId) {
722 hostService.getConnectedHosts(deviceId).forEach(affectedHost -> affectedHost.locations().stream()
723 .filter(hostLocation -> hostLocation.deviceId().equals(deviceId))
724 .forEach(affectedLocation ->
725 providerService.removeLocationFromHost(affectedHost.id(), affectedLocation))
726 );
727 }
728
729 /**
730 * When a port goes down, update the location of affected hosts.
731 *
732 * @param connectPoint the port that goes down
733 */
734 private void processPortDown(ConnectPoint connectPoint) {
735 hostService.getConnectedHosts(connectPoint).forEach(affectedHost ->
736 providerService.removeLocationFromHost(affectedHost.id(), new HostLocation(connectPoint, 0L))
737 );
Thomas Vachuska33601602014-11-19 03:32:15 -0800738 }
739
alshabibe1cf87d2014-10-17 09:23:50 -0700740}