blob: 079489e4e4d9e520e8fb1a5f35c39636edbf973a [file] [log] [blame]
Charles Chanff79dd92018-06-01 16:33:48 -07001/*
2 * Copyright 2018-present Open Networking Foundation
3 *
4 * 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
7 *
8 * 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.
15 */
16
17package org.onosproject.provider.hostprobing.impl;
18
Charles Chanff79dd92018-06-01 16:33:48 -070019import org.onlab.packet.ARP;
20import org.onlab.packet.Ethernet;
21import org.onlab.packet.IPv6;
22import org.onlab.packet.Ip4Address;
23import org.onlab.packet.Ip6Address;
24import org.onlab.packet.IpAddress;
25import org.onlab.packet.MacAddress;
26import org.onlab.packet.VlanId;
27import org.onlab.packet.ndp.NeighborSolicitation;
28import org.onosproject.mastership.MastershipService;
29import org.onosproject.net.ConnectPoint;
30import org.onosproject.net.Host;
31import org.onosproject.net.HostLocation;
pierventre4f52c462021-09-17 23:02:19 +020032import org.onosproject.net.Port;
33import org.onosproject.net.device.DeviceService;
Charles Chanff79dd92018-06-01 16:33:48 -070034import org.onosproject.net.flow.DefaultTrafficTreatment;
35import org.onosproject.net.flow.TrafficTreatment;
36import org.onosproject.net.host.HostProbe;
37import org.onosproject.net.host.HostProbingEvent;
38import org.onosproject.net.host.HostProbingProvider;
39import org.onosproject.net.host.HostProbingProviderRegistry;
40import org.onosproject.net.host.HostProbingProviderService;
41import org.onosproject.net.host.HostProvider;
42import org.onosproject.net.host.HostProviderRegistry;
43import org.onosproject.net.host.HostProviderService;
44import org.onosproject.net.host.ProbeMode;
45import org.onosproject.net.packet.DefaultOutboundPacket;
46import org.onosproject.net.packet.OutboundPacket;
47import org.onosproject.net.packet.PacketProcessor;
48import org.onosproject.net.packet.PacketService;
49import org.onosproject.net.provider.AbstractProvider;
50import org.onosproject.net.provider.ProviderId;
51import org.osgi.service.component.ComponentContext;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070052import org.osgi.service.component.annotations.Activate;
53import org.osgi.service.component.annotations.Component;
54import org.osgi.service.component.annotations.Deactivate;
55import org.osgi.service.component.annotations.Reference;
56import org.osgi.service.component.annotations.ReferenceCardinality;
Charles Chanff79dd92018-06-01 16:33:48 -070057import org.slf4j.Logger;
58
59import java.nio.ByteBuffer;
60import java.util.Optional;
61import java.util.concurrent.ExecutorService;
62import java.util.concurrent.ScheduledExecutorService;
63import java.util.concurrent.TimeUnit;
64
65import static java.util.concurrent.Executors.newScheduledThreadPool;
66import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
67import static org.onlab.util.Tools.groupedThreads;
68import static org.slf4j.LoggerFactory.getLogger;
69
70/**
71 * Provider which sends host location probes to discover or verify a host at specific location.
72 */
Ray Milkeyd84f89b2018-08-17 14:54:17 -070073@Component(immediate = true, service = { HostProvider.class, HostProbingProvider.class })
Charles Chanff79dd92018-06-01 16:33:48 -070074public class DefaultHostProbingProvider extends AbstractProvider implements HostProvider, HostProbingProvider {
75 private final Logger log = getLogger(getClass());
76
Ray Milkeyd84f89b2018-08-17 14:54:17 -070077 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Charles Chanff79dd92018-06-01 16:33:48 -070078 private HostProviderRegistry providerRegistry;
79
Ray Milkeyd84f89b2018-08-17 14:54:17 -070080 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Charles Chanff79dd92018-06-01 16:33:48 -070081 private HostProbingProviderRegistry hostProbingProviderRegistry;
82
Ray Milkeyd84f89b2018-08-17 14:54:17 -070083 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Charles Chanff79dd92018-06-01 16:33:48 -070084 private PacketService packetService;
85
Ray Milkeyd84f89b2018-08-17 14:54:17 -070086 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Charles Chanff79dd92018-06-01 16:33:48 -070087 private MastershipService mastershipService;
88
pierventre4f52c462021-09-17 23:02:19 +020089 @Reference(cardinality = ReferenceCardinality.MANDATORY)
90 private DeviceService deviceService;
91
Charles Chanff79dd92018-06-01 16:33:48 -070092 private HostProviderService providerService;
93 private HostProbingProviderService hostProbingProviderService;
94 private ExecutorService packetHandler;
95 private ExecutorService probeEventHandler;
96 private ScheduledExecutorService hostProber;
97
98 private final PacketProcessor packetProcessor = context ->
99 packetHandler.execute(() -> {
100 Ethernet eth = context.inPacket().parsed();
101 if (eth == null) {
102 return;
103 }
104 MacAddress srcMac = eth.getSourceMAC();
105 MacAddress destMac = eth.getDestinationMAC();
106 VlanId vlan = VlanId.vlanId(eth.getVlanID());
107 ConnectPoint heardOn = context.inPacket().receivedFrom();
108
109 // Receives a location probe. Invalid entry from the cache
110 if (destMac.isOnos() && !MacAddress.NONE.equals(destMac)) {
111 log.debug("Receives probe for {}/{} on {}", srcMac, vlan, heardOn);
112 hostProbingProviderService.removeProbingHost(destMac);
113 }
114 });
115
116 // TODO Make this configurable
117 private static final int PROBE_INIT_DELAY_MS = 1000;
118 private static final int DEFAULT_RETRY = 5;
119
120 /**
121 * Creates an OpenFlow host provider.
122 */
123 public DefaultHostProbingProvider() {
124 super(new ProviderId("hostprobing", "org.onosproject.provider.hostprobing"));
125 }
126
127 @Activate
128 public void activate(ComponentContext context) {
129 providerService = providerRegistry.register(this);
130 hostProbingProviderService = hostProbingProviderRegistry.register(this);
131
132 packetHandler = newSingleThreadScheduledExecutor(groupedThreads("onos/host-loc-provider",
133 "packet-handler", log));
134 probeEventHandler = newSingleThreadScheduledExecutor(groupedThreads("onos/host-loc-provider",
135 "probe-handler", log));
136 hostProber = newScheduledThreadPool(32, groupedThreads("onos/host-loc-probe", "%d", log));
137
138 packetService.addProcessor(packetProcessor, PacketProcessor.advisor(1));
139 }
140
141 @Deactivate
142 public void deactivate() {
143 providerRegistry.unregister(this);
144 hostProbingProviderRegistry.unregister(this);
145 providerService = null;
146
147 packetService.removeProcessor(packetProcessor);
148
149 packetHandler.shutdown();
150 probeEventHandler.shutdown();
151 hostProber.shutdown();
152 }
153
154 @Override
155 public void triggerProbe(Host host) {
156 // Not doing anything at this moment...
157 }
158
159 @Override
160 public void processEvent(HostProbingEvent event) {
161 probeEventHandler.execute(() -> {
162 log.debug("Receiving HostProbingEvent {}", event);
163 HostProbe hostProbe = event.subject();
164
165 switch (event.type()) {
166 case PROBE_REQUESTED:
167 // Do nothing
168 break;
169 case PROBE_TIMEOUT:
170 // Retry probe until PROBE_FAIL
171 // TODO Only retry DISCOVER probes
172 probeHostInternal(hostProbe, hostProbe.connectPoint(),
173 hostProbe.mode(), hostProbe.probeMac(), hostProbe.retry());
174 break;
175 case PROBE_FAIL:
176 // Remove this location if this is a verify probe.
177 if (hostProbe.mode() == ProbeMode.VERIFY) {
pierventre4f52c462021-09-17 23:02:19 +0200178 ConnectPoint oldConnectPoint = hostProbe.connectPoint();
179 if (!oldConnectPoint.port().hasName()) {
180 oldConnectPoint = translateSwitchPort(oldConnectPoint);
181 }
Charles Chanff79dd92018-06-01 16:33:48 -0700182 providerService.removeLocationFromHost(hostProbe.id(),
pierventre4f52c462021-09-17 23:02:19 +0200183 new HostLocation(oldConnectPoint, 0L));
Charles Chanff79dd92018-06-01 16:33:48 -0700184 }
185 break;
186 case PROBE_COMPLETED:
187 // Add this location if this is a discover probe.
188 if (hostProbe.mode() == ProbeMode.DISCOVER) {
pierventre4f52c462021-09-17 23:02:19 +0200189 ConnectPoint newConnectPoint = hostProbe.connectPoint();
190 if (!newConnectPoint.port().hasName()) {
191 newConnectPoint = translateSwitchPort(newConnectPoint);
192 }
193 providerService.addLocationToHost(hostProbe.id(),
194 new HostLocation(newConnectPoint, System.currentTimeMillis()));
Charles Chanff79dd92018-06-01 16:33:48 -0700195 }
196 break;
197 default:
198 log.warn("Unknown HostProbingEvent type: {}", event.type());
199 }
200 });
201 }
202
203 @Override
204 public void probeHost(Host host, ConnectPoint connectPoint, ProbeMode probeMode) {
205 probeHostInternal(host, connectPoint, probeMode, null, DEFAULT_RETRY);
206 }
207
208 // probeMac can be null if this is the very first probe and the mac is to-be-generated.
209 private void probeHostInternal(Host host, ConnectPoint connectPoint, ProbeMode probeMode,
210 MacAddress probeMac, int retry) {
211 if (!mastershipService.isLocalMaster(connectPoint.deviceId())) {
212 log.debug("Current node is not master of {}, abort probing {}", connectPoint.deviceId(), host);
213 return;
214 }
215
216 log.debug("probeHostInternal host={}, cp={}, mode={}, probeMac={}, retry={}", host, connectPoint,
217 probeMode, probeMac, retry);
218 Optional<IpAddress> ipOptional = host.ipAddresses().stream().findFirst();
219
220 if (ipOptional.isPresent()) {
221 probeMac = hostProbingProviderService.addProbingHost(host, connectPoint, probeMode, probeMac, retry);
222
223 IpAddress ip = ipOptional.get();
224 log.debug("Constructing {} probe for host {} with {}", probeMode, host.id(), ip);
225 Ethernet probe;
226 if (ip.isIp4()) {
227 probe = ARP.buildArpRequest(probeMac.toBytes(), Ip4Address.ZERO.toOctets(),
228 host.id().mac().toBytes(), ip.toOctets(),
229 host.id().mac().toBytes(), host.id().vlanId().toShort());
230 } else {
231 probe = NeighborSolicitation.buildNdpSolicit(
232 ip.getIp6Address(),
233 Ip6Address.valueOf(IPv6.getLinkLocalAddress(probeMac.toBytes())),
234 ip.getIp6Address(),
235 probeMac,
236 host.id().mac(),
237 host.id().vlanId());
238 }
239
240 // NOTE: delay the probe a little bit to wait for the store synchronization is done
241 hostProber.schedule(() ->
242 sendLocationProbe(probe, connectPoint), PROBE_INIT_DELAY_MS, TimeUnit.MILLISECONDS);
243 } else {
244 log.debug("Host {} has no IP address yet. Skip probing.", host);
245 }
246 }
247
248 /**
249 * Send the probe packet on given port.
250 *
251 * @param probe the probe packet
252 * @param connectPoint the port we want to probe
253 */
254 private void sendLocationProbe(Ethernet probe, ConnectPoint connectPoint) {
255 log.debug("Sending probe for host {} on location {} with probeMac {}",
256 probe.getDestinationMAC(), connectPoint, probe.getSourceMAC());
257 TrafficTreatment treatment = DefaultTrafficTreatment.builder().setOutput(connectPoint.port()).build();
258 OutboundPacket outboundPacket = new DefaultOutboundPacket(connectPoint.deviceId(),
259 treatment, ByteBuffer.wrap(probe.serialize()));
260 packetService.emit(outboundPacket);
261 }
pierventre4f52c462021-09-17 23:02:19 +0200262
263 /* Connect point generated from netcfg may not have port name
264 we use the device service as translation service */
265 private ConnectPoint translateSwitchPort(ConnectPoint connectPoint) {
266 Port devicePort = deviceService.getPort(connectPoint);
267 if (devicePort != null) {
268 return new ConnectPoint(connectPoint.deviceId(), devicePort.number());
269 }
270 return connectPoint;
271 }
rruigon21782d62019-04-29 10:00:04 +0200272}