blob: d590570c4e7e4020770bf21c8d04d983accee7cd [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;
32import org.onosproject.net.flow.DefaultTrafficTreatment;
33import org.onosproject.net.flow.TrafficTreatment;
34import org.onosproject.net.host.HostProbe;
35import org.onosproject.net.host.HostProbingEvent;
36import org.onosproject.net.host.HostProbingProvider;
37import org.onosproject.net.host.HostProbingProviderRegistry;
38import org.onosproject.net.host.HostProbingProviderService;
39import org.onosproject.net.host.HostProvider;
40import org.onosproject.net.host.HostProviderRegistry;
41import org.onosproject.net.host.HostProviderService;
42import org.onosproject.net.host.ProbeMode;
43import org.onosproject.net.packet.DefaultOutboundPacket;
44import org.onosproject.net.packet.OutboundPacket;
45import org.onosproject.net.packet.PacketProcessor;
46import org.onosproject.net.packet.PacketService;
47import org.onosproject.net.provider.AbstractProvider;
48import org.onosproject.net.provider.ProviderId;
49import org.osgi.service.component.ComponentContext;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070050import org.osgi.service.component.annotations.Activate;
51import org.osgi.service.component.annotations.Component;
52import org.osgi.service.component.annotations.Deactivate;
53import org.osgi.service.component.annotations.Reference;
54import org.osgi.service.component.annotations.ReferenceCardinality;
Charles Chanff79dd92018-06-01 16:33:48 -070055import org.slf4j.Logger;
56
57import java.nio.ByteBuffer;
58import java.util.Optional;
59import java.util.concurrent.ExecutorService;
60import java.util.concurrent.ScheduledExecutorService;
61import java.util.concurrent.TimeUnit;
62
63import static java.util.concurrent.Executors.newScheduledThreadPool;
64import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
65import static org.onlab.util.Tools.groupedThreads;
66import static org.slf4j.LoggerFactory.getLogger;
67
68/**
69 * Provider which sends host location probes to discover or verify a host at specific location.
70 */
Ray Milkeyd84f89b2018-08-17 14:54:17 -070071@Component(immediate = true, service = { HostProvider.class, HostProbingProvider.class })
Charles Chanff79dd92018-06-01 16:33:48 -070072public class DefaultHostProbingProvider extends AbstractProvider implements HostProvider, HostProbingProvider {
73 private final Logger log = getLogger(getClass());
74
Ray Milkeyd84f89b2018-08-17 14:54:17 -070075 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Charles Chanff79dd92018-06-01 16:33:48 -070076 private HostProviderRegistry providerRegistry;
77
Ray Milkeyd84f89b2018-08-17 14:54:17 -070078 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Charles Chanff79dd92018-06-01 16:33:48 -070079 private HostProbingProviderRegistry hostProbingProviderRegistry;
80
Ray Milkeyd84f89b2018-08-17 14:54:17 -070081 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Charles Chanff79dd92018-06-01 16:33:48 -070082 private PacketService packetService;
83
Ray Milkeyd84f89b2018-08-17 14:54:17 -070084 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Charles Chanff79dd92018-06-01 16:33:48 -070085 private MastershipService mastershipService;
86
87 private HostProviderService providerService;
88 private HostProbingProviderService hostProbingProviderService;
89 private ExecutorService packetHandler;
90 private ExecutorService probeEventHandler;
91 private ScheduledExecutorService hostProber;
92
93 private final PacketProcessor packetProcessor = context ->
94 packetHandler.execute(() -> {
95 Ethernet eth = context.inPacket().parsed();
96 if (eth == null) {
97 return;
98 }
99 MacAddress srcMac = eth.getSourceMAC();
100 MacAddress destMac = eth.getDestinationMAC();
101 VlanId vlan = VlanId.vlanId(eth.getVlanID());
102 ConnectPoint heardOn = context.inPacket().receivedFrom();
103
104 // Receives a location probe. Invalid entry from the cache
105 if (destMac.isOnos() && !MacAddress.NONE.equals(destMac)) {
106 log.debug("Receives probe for {}/{} on {}", srcMac, vlan, heardOn);
107 hostProbingProviderService.removeProbingHost(destMac);
108 }
109 });
110
111 // TODO Make this configurable
112 private static final int PROBE_INIT_DELAY_MS = 1000;
113 private static final int DEFAULT_RETRY = 5;
114
115 /**
116 * Creates an OpenFlow host provider.
117 */
118 public DefaultHostProbingProvider() {
119 super(new ProviderId("hostprobing", "org.onosproject.provider.hostprobing"));
120 }
121
122 @Activate
123 public void activate(ComponentContext context) {
124 providerService = providerRegistry.register(this);
125 hostProbingProviderService = hostProbingProviderRegistry.register(this);
126
127 packetHandler = newSingleThreadScheduledExecutor(groupedThreads("onos/host-loc-provider",
128 "packet-handler", log));
129 probeEventHandler = newSingleThreadScheduledExecutor(groupedThreads("onos/host-loc-provider",
130 "probe-handler", log));
131 hostProber = newScheduledThreadPool(32, groupedThreads("onos/host-loc-probe", "%d", log));
132
133 packetService.addProcessor(packetProcessor, PacketProcessor.advisor(1));
134 }
135
136 @Deactivate
137 public void deactivate() {
138 providerRegistry.unregister(this);
139 hostProbingProviderRegistry.unregister(this);
140 providerService = null;
141
142 packetService.removeProcessor(packetProcessor);
143
144 packetHandler.shutdown();
145 probeEventHandler.shutdown();
146 hostProber.shutdown();
147 }
148
149 @Override
150 public void triggerProbe(Host host) {
151 // Not doing anything at this moment...
152 }
153
154 @Override
155 public void processEvent(HostProbingEvent event) {
156 probeEventHandler.execute(() -> {
157 log.debug("Receiving HostProbingEvent {}", event);
158 HostProbe hostProbe = event.subject();
159
160 switch (event.type()) {
161 case PROBE_REQUESTED:
162 // Do nothing
163 break;
164 case PROBE_TIMEOUT:
165 // Retry probe until PROBE_FAIL
166 // TODO Only retry DISCOVER probes
167 probeHostInternal(hostProbe, hostProbe.connectPoint(),
168 hostProbe.mode(), hostProbe.probeMac(), hostProbe.retry());
169 break;
170 case PROBE_FAIL:
171 // Remove this location if this is a verify probe.
172 if (hostProbe.mode() == ProbeMode.VERIFY) {
173 providerService.removeLocationFromHost(hostProbe.id(),
174 (HostLocation) hostProbe.connectPoint());
175 }
176 break;
177 case PROBE_COMPLETED:
178 // Add this location if this is a discover probe.
179 if (hostProbe.mode() == ProbeMode.DISCOVER) {
180 HostLocation newLocation = new HostLocation(hostProbe.connectPoint(),
181 System.currentTimeMillis());
182 providerService.addLocationToHost(hostProbe.id(), newLocation);
183 }
184 break;
185 default:
186 log.warn("Unknown HostProbingEvent type: {}", event.type());
187 }
188 });
189 }
190
191 @Override
192 public void probeHost(Host host, ConnectPoint connectPoint, ProbeMode probeMode) {
193 probeHostInternal(host, connectPoint, probeMode, null, DEFAULT_RETRY);
194 }
195
196 // probeMac can be null if this is the very first probe and the mac is to-be-generated.
197 private void probeHostInternal(Host host, ConnectPoint connectPoint, ProbeMode probeMode,
198 MacAddress probeMac, int retry) {
199 if (!mastershipService.isLocalMaster(connectPoint.deviceId())) {
200 log.debug("Current node is not master of {}, abort probing {}", connectPoint.deviceId(), host);
201 return;
202 }
203
204 log.debug("probeHostInternal host={}, cp={}, mode={}, probeMac={}, retry={}", host, connectPoint,
205 probeMode, probeMac, retry);
206 Optional<IpAddress> ipOptional = host.ipAddresses().stream().findFirst();
207
208 if (ipOptional.isPresent()) {
209 probeMac = hostProbingProviderService.addProbingHost(host, connectPoint, probeMode, probeMac, retry);
210
211 IpAddress ip = ipOptional.get();
212 log.debug("Constructing {} probe for host {} with {}", probeMode, host.id(), ip);
213 Ethernet probe;
214 if (ip.isIp4()) {
215 probe = ARP.buildArpRequest(probeMac.toBytes(), Ip4Address.ZERO.toOctets(),
216 host.id().mac().toBytes(), ip.toOctets(),
217 host.id().mac().toBytes(), host.id().vlanId().toShort());
218 } else {
219 probe = NeighborSolicitation.buildNdpSolicit(
220 ip.getIp6Address(),
221 Ip6Address.valueOf(IPv6.getLinkLocalAddress(probeMac.toBytes())),
222 ip.getIp6Address(),
223 probeMac,
224 host.id().mac(),
225 host.id().vlanId());
226 }
227
228 // NOTE: delay the probe a little bit to wait for the store synchronization is done
229 hostProber.schedule(() ->
230 sendLocationProbe(probe, connectPoint), PROBE_INIT_DELAY_MS, TimeUnit.MILLISECONDS);
231 } else {
232 log.debug("Host {} has no IP address yet. Skip probing.", host);
233 }
234 }
235
236 /**
237 * Send the probe packet on given port.
238 *
239 * @param probe the probe packet
240 * @param connectPoint the port we want to probe
241 */
242 private void sendLocationProbe(Ethernet probe, ConnectPoint connectPoint) {
243 log.debug("Sending probe for host {} on location {} with probeMac {}",
244 probe.getDestinationMAC(), connectPoint, probe.getSourceMAC());
245 TrafficTreatment treatment = DefaultTrafficTreatment.builder().setOutput(connectPoint.port()).build();
246 OutboundPacket outboundPacket = new DefaultOutboundPacket(connectPoint.deviceId(),
247 treatment, ByteBuffer.wrap(probe.serialize()));
248 packetService.emit(outboundPacket);
249 }
250}