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