blob: 31dd0a94e1d585ae9f65827904d53862b72abb2b [file] [log] [blame]
Rusty Eddy4ae5aa82015-12-15 12:58:27 -08001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2016-present Open Networking Foundation
Rusty Eddy4ae5aa82015-12-15 12:58:27 -08003 *
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 */
16package org.onosproject.pim.impl;
17
Jonathan Hart5af5f142016-01-28 18:45:27 -080018import com.google.common.collect.ImmutableSet;
Rusty Eddy4ae5aa82015-12-15 12:58:27 -080019import com.google.common.collect.Maps;
Jonathan Hart54119bb2016-02-06 18:48:27 -080020import org.onlab.util.SafeRecurringTask;
Rusty Eddy4ae5aa82015-12-15 12:58:27 -080021import org.onosproject.net.ConnectPoint;
Jonathan Hart00cddda2016-02-16 10:30:37 -080022import org.onosproject.net.Host;
Jonathan Hart5af5f142016-01-28 18:45:27 -080023import org.onosproject.net.config.ConfigFactory;
24import org.onosproject.net.config.NetworkConfigEvent;
25import org.onosproject.net.config.NetworkConfigListener;
26import org.onosproject.net.config.NetworkConfigRegistry;
27import org.onosproject.net.config.basics.SubjectFactories;
Jonathan Hart00cddda2016-02-16 10:30:37 -080028import org.onosproject.net.host.HostService;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070029import org.onosproject.net.intf.Interface;
30import org.onosproject.net.intf.InterfaceEvent;
31import org.onosproject.net.intf.InterfaceListener;
32import org.onosproject.net.intf.InterfaceService;
Jonathan Hart00cddda2016-02-16 10:30:37 -080033import org.onosproject.net.mcast.McastEvent;
34import org.onosproject.net.mcast.McastListener;
35import org.onosproject.net.mcast.McastRoute;
36import org.onosproject.net.mcast.MulticastRouteService;
Jonathan Hart36fd31e2016-01-28 15:55:31 -080037import org.onosproject.net.packet.PacketService;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070038import org.onosproject.routeservice.Route;
39import org.onosproject.routeservice.RouteService;
40import org.osgi.service.component.annotations.Activate;
41import org.osgi.service.component.annotations.Component;
42import org.osgi.service.component.annotations.Deactivate;
43import org.osgi.service.component.annotations.Reference;
44import org.osgi.service.component.annotations.ReferenceCardinality;
Rusty Eddy4ae5aa82015-12-15 12:58:27 -080045import org.slf4j.Logger;
Jonathan Hart5af5f142016-01-28 18:45:27 -080046
Rusty Eddy4ae5aa82015-12-15 12:58:27 -080047import java.util.Map;
Jonathan Hart5af5f142016-01-28 18:45:27 -080048import java.util.Set;
Rusty Eddy4d5a92f2016-01-25 17:12:14 -080049import java.util.concurrent.Executors;
50import java.util.concurrent.ScheduledExecutorService;
51import java.util.concurrent.TimeUnit;
Rusty Eddy4ae5aa82015-12-15 12:58:27 -080052
53import static org.slf4j.LoggerFactory.getLogger;
54
55/**
56 * Manages PIMInterfaces.
57 *
58 * TODO: Do we need to add a ServiceListener?
59 */
Ray Milkeyd84f89b2018-08-17 14:54:17 -070060@Component(immediate = true, service = PimInterfaceService.class)
Jonathan Hartfbfe2a82016-03-29 11:36:33 -070061public class PimInterfaceManager implements PimInterfaceService {
Rusty Eddy4ae5aa82015-12-15 12:58:27 -080062
63 private final Logger log = getLogger(getClass());
64
Jonathan Hart5af5f142016-01-28 18:45:27 -080065 private static final Class<PimInterfaceConfig> PIM_INTERFACE_CONFIG_CLASS = PimInterfaceConfig.class;
66 private static final String PIM_INTERFACE_CONFIG_KEY = "pimInterface";
Rusty Eddy4ae5aa82015-12-15 12:58:27 -080067
Jonathan Hart6be70952016-02-12 21:11:26 -080068 public static final int DEFAULT_HELLO_INTERVAL = 30; // seconds
69
70 private static final int DEFAULT_TASK_PERIOD_MS = 250;
Jonathan Hart54119bb2016-02-06 18:48:27 -080071
72 // Create a Scheduled Executor service for recurring tasks
73 private final ScheduledExecutorService scheduledExecutorService =
Rusty Eddy4d5a92f2016-01-25 17:12:14 -080074 Executors.newScheduledThreadPool(1);
75
Jonathan Hart6be70952016-02-12 21:11:26 -080076 private final long initialHelloDelay = 1000;
Rusty Eddy4d5a92f2016-01-25 17:12:14 -080077
Jonathan Hart6be70952016-02-12 21:11:26 -080078 private final long pimHelloPeriod = DEFAULT_TASK_PERIOD_MS;
Rusty Eddy4ae5aa82015-12-15 12:58:27 -080079
Jonathan Hart6be70952016-02-12 21:11:26 -080080 private final int timeoutTaskPeriod = DEFAULT_TASK_PERIOD_MS;
Jonathan Hart54119bb2016-02-06 18:48:27 -080081
Jonathan Hart00cddda2016-02-16 10:30:37 -080082 private final int joinTaskPeriod = 10000;
83
Ray Milkeyd84f89b2018-08-17 14:54:17 -070084 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jonathan Hart36fd31e2016-01-28 15:55:31 -080085 protected PacketService packetService;
86
Ray Milkeyd84f89b2018-08-17 14:54:17 -070087 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jonathan Hart5af5f142016-01-28 18:45:27 -080088 protected NetworkConfigRegistry networkConfig;
89
Ray Milkeyd84f89b2018-08-17 14:54:17 -070090 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jonathan Hart5af5f142016-01-28 18:45:27 -080091 protected InterfaceService interfaceService;
92
Ray Milkeyd84f89b2018-08-17 14:54:17 -070093 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jonathan Hart00cddda2016-02-16 10:30:37 -080094 protected HostService hostService;
95
Ray Milkeyd84f89b2018-08-17 14:54:17 -070096 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jonathan Hart00cddda2016-02-16 10:30:37 -080097 protected MulticastRouteService multicastRouteService;
98
Ray Milkeyd84f89b2018-08-17 14:54:17 -070099 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jonathan Hart92ca5d32016-04-14 18:05:52 -0700100 protected RouteService unicastRouteService;
Jonathan Hart00cddda2016-02-16 10:30:37 -0800101
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800102 // Store PIM Interfaces in a map key'd by ConnectPoint
Jonathan Hartfbfe2a82016-03-29 11:36:33 -0700103 private final Map<ConnectPoint, PimInterface> pimInterfaces = Maps.newConcurrentMap();
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800104
Jonathan Hartfbfe2a82016-03-29 11:36:33 -0700105 private final Map<McastRoute, PimInterface> routes = Maps.newConcurrentMap();
Jonathan Hart00cddda2016-02-16 10:30:37 -0800106
Jonathan Hart5af5f142016-01-28 18:45:27 -0800107 private final InternalNetworkConfigListener configListener =
108 new InternalNetworkConfigListener();
109 private final InternalInterfaceListener interfaceListener =
110 new InternalInterfaceListener();
Jonathan Hart00cddda2016-02-16 10:30:37 -0800111 private final InternalMulticastListener multicastListener =
112 new InternalMulticastListener();
Jonathan Hart5af5f142016-01-28 18:45:27 -0800113
114 private final ConfigFactory<ConnectPoint, PimInterfaceConfig> pimConfigFactory
115 = new ConfigFactory<ConnectPoint, PimInterfaceConfig>(
116 SubjectFactories.CONNECT_POINT_SUBJECT_FACTORY, PIM_INTERFACE_CONFIG_CLASS,
117 PIM_INTERFACE_CONFIG_KEY) {
118
119 @Override
120 public PimInterfaceConfig createConfig() {
121 return new PimInterfaceConfig();
122 }
123 };
124
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800125 @Activate
126 public void activate() {
Jonathan Hart5af5f142016-01-28 18:45:27 -0800127 networkConfig.registerConfigFactory(pimConfigFactory);
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800128
Jonathan Hart5af5f142016-01-28 18:45:27 -0800129 // Create PIM Interfaces for each of the existing configured interfaces.
130 Set<ConnectPoint> subjects = networkConfig.getSubjects(
131 ConnectPoint.class, PIM_INTERFACE_CONFIG_CLASS);
132 for (ConnectPoint cp : subjects) {
133 PimInterfaceConfig config = networkConfig.getConfig(cp, PIM_INTERFACE_CONFIG_CLASS);
134 updateInterface(config);
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800135 }
Rusty Eddy4d5a92f2016-01-25 17:12:14 -0800136
Jonathan Hart5af5f142016-01-28 18:45:27 -0800137 networkConfig.addListener(configListener);
138 interfaceService.addListener(interfaceListener);
Jonathan Hart00cddda2016-02-16 10:30:37 -0800139 multicastRouteService.addListener(multicastListener);
140
141 multicastRouteService.getRoutes().forEach(this::addRoute);
Jonathan Hart5af5f142016-01-28 18:45:27 -0800142
Rusty Eddy4d5a92f2016-01-25 17:12:14 -0800143 // Schedule the periodic hello sender.
Jonathan Hart54119bb2016-02-06 18:48:27 -0800144 scheduledExecutorService.scheduleAtFixedRate(
145 SafeRecurringTask.wrap(
Jonathan Hartfbfe2a82016-03-29 11:36:33 -0700146 () -> pimInterfaces.values().forEach(PimInterface::sendHello)),
Jonathan Hart6be70952016-02-12 21:11:26 -0800147 initialHelloDelay, pimHelloPeriod, TimeUnit.MILLISECONDS);
Jonathan Hart54119bb2016-02-06 18:48:27 -0800148
149 // Schedule task to periodically time out expired neighbors
150 scheduledExecutorService.scheduleAtFixedRate(
151 SafeRecurringTask.wrap(
Jonathan Hartfbfe2a82016-03-29 11:36:33 -0700152 () -> pimInterfaces.values().forEach(PimInterface::checkNeighborTimeouts)),
Jonathan Hart54119bb2016-02-06 18:48:27 -0800153 0, timeoutTaskPeriod, TimeUnit.MILLISECONDS);
Jonathan Hart5af5f142016-01-28 18:45:27 -0800154
Jonathan Hart00cddda2016-02-16 10:30:37 -0800155 scheduledExecutorService.scheduleAtFixedRate(
156 SafeRecurringTask.wrap(
Jonathan Hartfbfe2a82016-03-29 11:36:33 -0700157 () -> pimInterfaces.values().forEach(PimInterface::sendJoins)),
Jonathan Hart00cddda2016-02-16 10:30:37 -0800158 0, joinTaskPeriod, TimeUnit.MILLISECONDS);
159
Jonathan Hart5af5f142016-01-28 18:45:27 -0800160 log.info("Started");
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800161 }
162
163 @Deactivate
164 public void deactivate() {
Jonathan Hart5af5f142016-01-28 18:45:27 -0800165 interfaceService.removeListener(interfaceListener);
166 networkConfig.removeListener(configListener);
Jonathan Hart00cddda2016-02-16 10:30:37 -0800167 multicastRouteService.removeListener(multicastListener);
Jonathan Hart5af5f142016-01-28 18:45:27 -0800168 networkConfig.unregisterConfigFactory(pimConfigFactory);
Rusty Eddy4d5a92f2016-01-25 17:12:14 -0800169
170 // Shutdown the periodic hello task.
Jonathan Hart54119bb2016-02-06 18:48:27 -0800171 scheduledExecutorService.shutdown();
Rusty Eddy4d5a92f2016-01-25 17:12:14 -0800172
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800173 log.info("Stopped");
174 }
175
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800176 @Override
Jonathan Hartfbfe2a82016-03-29 11:36:33 -0700177 public PimInterface getPimInterface(ConnectPoint cp) {
178 PimInterface pi = pimInterfaces.get(cp);
Jonathan Hart7f4bc522016-02-20 11:32:43 -0800179 if (pi == null && log.isTraceEnabled()) {
180 log.trace("We have been asked for an Interface we don't have: {}", cp);
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800181 }
182 return pi;
183 }
Jonathan Hart5af5f142016-01-28 18:45:27 -0800184
185 @Override
Jonathan Hartfbfe2a82016-03-29 11:36:33 -0700186 public Set<PimInterface> getPimInterfaces() {
Jonathan Hart5af5f142016-01-28 18:45:27 -0800187 return ImmutableSet.copyOf(pimInterfaces.values());
188 }
189
190 private void updateInterface(PimInterfaceConfig config) {
191 ConnectPoint cp = config.subject();
192
193 if (!config.isEnabled()) {
194 removeInterface(cp);
195 return;
196 }
197
198 String intfName = config.getInterfaceName();
199 Interface intf = interfaceService.getInterfaceByName(cp, intfName);
200
201 if (intf == null) {
202 log.debug("Interface configuration missing: {}", config.getInterfaceName());
203 return;
204 }
205
206
207 log.debug("Updating Interface for " + intf.connectPoint().toString());
208 pimInterfaces.computeIfAbsent(cp, k -> buildPimInterface(config, intf));
209 }
210
211 private void removeInterface(ConnectPoint cp) {
212 pimInterfaces.remove(cp);
213 }
214
Jonathan Hartfbfe2a82016-03-29 11:36:33 -0700215 private PimInterface buildPimInterface(PimInterfaceConfig config, Interface intf) {
216 PimInterface.Builder builder = PimInterface.builder()
Jonathan Hart5af5f142016-01-28 18:45:27 -0800217 .withPacketService(packetService)
218 .withInterface(intf);
219
Sho SHIMIZUef7e2902016-02-12 18:38:29 -0800220 config.getHelloInterval().ifPresent(builder::withHelloInterval);
221 config.getHoldTime().ifPresent(builder::withHoldTime);
222 config.getPriority().ifPresent(builder::withPriority);
223 config.getPropagationDelay().ifPresent(builder::withPropagationDelay);
224 config.getOverrideInterval().ifPresent(builder::withOverrideInterval);
Jonathan Hart5af5f142016-01-28 18:45:27 -0800225
226 return builder.build();
227 }
228
Jonathan Hart00cddda2016-02-16 10:30:37 -0800229 private void addRoute(McastRoute route) {
Jonathan Hartfbfe2a82016-03-29 11:36:33 -0700230 PimInterface pimInterface = getSourceInterface(route);
Jonathan Hart00cddda2016-02-16 10:30:37 -0800231
232 if (pimInterface == null) {
233 return;
234 }
235
Jonathan Hart32be5172016-05-24 21:13:36 -0700236 multicastRouteService.addSource(route, pimInterface.getInterface().connectPoint());
237
Jonathan Hart00cddda2016-02-16 10:30:37 -0800238 routes.put(route, pimInterface);
239 }
240
241 private void removeRoute(McastRoute route) {
Jonathan Hartfbfe2a82016-03-29 11:36:33 -0700242 PimInterface pimInterface = routes.remove(route);
Jonathan Hart00cddda2016-02-16 10:30:37 -0800243
244 if (pimInterface == null) {
245 return;
246 }
247
248 pimInterface.removeRoute(route);
249 }
250
Jonathan Hartfbfe2a82016-03-29 11:36:33 -0700251 private PimInterface getSourceInterface(McastRoute route) {
Jonathan Hart92ca5d32016-04-14 18:05:52 -0700252 Route unicastRoute = unicastRouteService.longestPrefixMatch(route.source());
Jonathan Hart00cddda2016-02-16 10:30:37 -0800253
Jonathan Hart92ca5d32016-04-14 18:05:52 -0700254 if (unicastRoute == null) {
Jonathan Hart00cddda2016-02-16 10:30:37 -0800255 log.warn("No route to source {}", route.source());
256 return null;
257 }
258
Jonathan Hart92ca5d32016-04-14 18:05:52 -0700259 Interface intf = interfaceService.getMatchingInterface(unicastRoute.nextHop());
Jonathan Hart00cddda2016-02-16 10:30:37 -0800260
261 if (intf == null) {
Jonathan Hart92ca5d32016-04-14 18:05:52 -0700262 log.warn("No interface with route to next hop {}", unicastRoute.nextHop());
Jonathan Hart00cddda2016-02-16 10:30:37 -0800263 return null;
264 }
265
Jonathan Hartfbfe2a82016-03-29 11:36:33 -0700266 PimInterface pimInterface = pimInterfaces.get(intf.connectPoint());
Jonathan Hart00cddda2016-02-16 10:30:37 -0800267
268 if (pimInterface == null) {
269 log.warn("PIM is not enabled on interface {}", intf);
270 return null;
271 }
272
Jonathan Hart92ca5d32016-04-14 18:05:52 -0700273 Set<Host> hosts = hostService.getHostsByIp(unicastRoute.nextHop());
Jonathan Hart00cddda2016-02-16 10:30:37 -0800274 Host host = null;
275 for (Host h : hosts) {
276 if (h.vlan().equals(intf.vlan())) {
277 host = h;
278 }
279 }
280 if (host == null) {
Jonathan Hart92ca5d32016-04-14 18:05:52 -0700281 log.warn("Next hop host entry not found: {}", unicastRoute.nextHop());
Jonathan Hart00cddda2016-02-16 10:30:37 -0800282 return null;
283 }
284
Jonathan Hart92ca5d32016-04-14 18:05:52 -0700285 pimInterface.addRoute(route, unicastRoute.nextHop(), host.mac());
Jonathan Hart00cddda2016-02-16 10:30:37 -0800286
287 return pimInterface;
288 }
289
Jonathan Hart5af5f142016-01-28 18:45:27 -0800290 /**
291 * Listener for network config events.
292 */
293 private class InternalNetworkConfigListener implements NetworkConfigListener {
294
295 @Override
296 public void event(NetworkConfigEvent event) {
297 if (event.configClass() != PIM_INTERFACE_CONFIG_CLASS) {
298 return;
299 }
300
301 switch (event.type()) {
302 case CONFIG_REGISTERED:
303 case CONFIG_UNREGISTERED:
304 break;
305 case CONFIG_ADDED:
306 case CONFIG_UPDATED:
307 ConnectPoint cp = (ConnectPoint) event.subject();
308 PimInterfaceConfig config = networkConfig.getConfig(
309 cp, PIM_INTERFACE_CONFIG_CLASS);
310
311 updateInterface(config);
312 break;
313 case CONFIG_REMOVED:
314 removeInterface((ConnectPoint) event.subject());
315 break;
316 default:
317 break;
318 }
319 }
320 }
321
322 /**
323 * Listener for interface events.
324 */
325 private class InternalInterfaceListener implements InterfaceListener {
326
327 @Override
328 public void event(InterfaceEvent event) {
329 switch (event.type()) {
330 case INTERFACE_ADDED:
331 PimInterfaceConfig config = networkConfig.getConfig(
332 event.subject().connectPoint(), PIM_INTERFACE_CONFIG_CLASS);
333
334 if (config != null) {
335 updateInterface(config);
336 }
337 break;
338 case INTERFACE_UPDATED:
339 break;
340 case INTERFACE_REMOVED:
341 removeInterface(event.subject().connectPoint());
342 break;
343 default:
344 break;
345
346 }
347 }
348 }
Jonathan Hart00cddda2016-02-16 10:30:37 -0800349
350 /**
351 * Listener for multicast route events.
352 */
353 private class InternalMulticastListener implements McastListener {
354 @Override
355 public void event(McastEvent event) {
356 switch (event.type()) {
357 case ROUTE_ADDED:
358 addRoute(event.subject().route());
359 break;
360 case ROUTE_REMOVED:
361 removeRoute(event.subject().route());
362 break;
363 case SOURCE_ADDED:
364 case SINK_ADDED:
365 case SINK_REMOVED:
366 default:
367 break;
368 }
369 }
370 }
Rusty Eddy4ae5aa82015-12-15 12:58:27 -0800371}