blob: fbe96c6d77ed4c0a513516d9438db75308feac1c [file] [log] [blame]
David K. Bainbridged77028f2017-08-01 12:47:55 -07001/*
Brian O'Connor4d084702017-08-03 22:45:58 -07002 * Copyright 2017-present Open Networking Foundation
David K. Bainbridged77028f2017-08-01 12:47:55 -07003 *
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 */
developere400c582020-03-24 19:42:08 +010016package org.opencord.igmpproxy.impl;
ke han81a38b92017-03-10 18:41:44 +080017
Esin Karamaneff10392019-06-27 18:09:13 +000018import com.google.common.collect.Sets;
onurka85b2d9c2021-01-19 22:49:19 +030019import com.google.common.util.concurrent.ThreadFactoryBuilder;
ke han81a38b92017-03-10 18:41:44 +080020import org.onlab.packet.EthType;
21import org.onlab.packet.Ethernet;
22import org.onlab.packet.IGMP;
23import org.onlab.packet.IGMPGroup;
24import org.onlab.packet.IGMPMembership;
25import org.onlab.packet.IGMPQuery;
26import org.onlab.packet.IPv4;
27import org.onlab.packet.Ip4Address;
28import org.onlab.packet.IpAddress;
29import org.onlab.packet.VlanId;
30import org.onosproject.core.ApplicationId;
31import org.onosproject.core.CoreService;
ke han81a38b92017-03-10 18:41:44 +080032import org.onosproject.mastership.MastershipService;
onurka85b2d9c2021-01-19 22:49:19 +030033import org.onosproject.mcast.api.McastRoute;
34import org.onosproject.mcast.api.MulticastRouteService;
ke han81a38b92017-03-10 18:41:44 +080035import org.onosproject.net.AnnotationKeys;
36import org.onosproject.net.ConnectPoint;
onurka85b2d9c2021-01-19 22:49:19 +030037import org.onosproject.net.Device;
ke han81a38b92017-03-10 18:41:44 +080038import org.onosproject.net.DeviceId;
39import org.onosproject.net.Port;
40import org.onosproject.net.PortNumber;
41import org.onosproject.net.config.ConfigFactory;
42import org.onosproject.net.config.NetworkConfigEvent;
43import org.onosproject.net.config.NetworkConfigListener;
44import org.onosproject.net.config.NetworkConfigRegistry;
Jonathan Hart488e1142018-05-02 17:30:05 -070045import org.onosproject.net.config.basics.McastConfig;
ke han81a38b92017-03-10 18:41:44 +080046import org.onosproject.net.config.basics.SubjectFactories;
47import org.onosproject.net.device.DeviceEvent;
48import org.onosproject.net.device.DeviceListener;
49import org.onosproject.net.device.DeviceService;
50import org.onosproject.net.flow.DefaultTrafficTreatment;
51import org.onosproject.net.flow.FlowRuleService;
52import org.onosproject.net.flow.criteria.Criteria;
53import org.onosproject.net.flowobjective.DefaultFilteringObjective;
54import org.onosproject.net.flowobjective.FilteringObjective;
55import org.onosproject.net.flowobjective.FlowObjectiveService;
56import org.onosproject.net.flowobjective.Objective;
57import org.onosproject.net.flowobjective.ObjectiveContext;
58import org.onosproject.net.flowobjective.ObjectiveError;
ke han81a38b92017-03-10 18:41:44 +080059import org.onosproject.net.packet.InboundPacket;
60import org.onosproject.net.packet.PacketContext;
61import org.onosproject.net.packet.PacketProcessor;
62import org.onosproject.net.packet.PacketService;
onurka85b2d9c2021-01-19 22:49:19 +030063import org.opencord.igmpproxy.GroupMemberId;
64import org.opencord.igmpproxy.IgmpLeadershipService;
65import org.opencord.igmpproxy.IgmpStatisticType;
66import org.opencord.igmpproxy.IgmpStatisticsService;
67import org.opencord.igmpproxy.impl.store.groupmember.GroupMember;
68import org.opencord.igmpproxy.impl.store.groupmember.GroupMemberStore;
69import org.opencord.igmpproxy.statemachine.StateMachineService;
onurka85b2d9c2021-01-19 22:49:19 +030070import org.opencord.sadis.SadisService;
71import org.opencord.sadis.SubscriberAndDeviceInformation;
72import org.osgi.service.component.annotations.Activate;
73import org.osgi.service.component.annotations.Component;
74import org.osgi.service.component.annotations.Deactivate;
75import org.osgi.service.component.annotations.Reference;
76import org.osgi.service.component.annotations.ReferenceCardinality;
Ilayda Ozdemire3505102021-02-23 12:56:15 +000077import org.osgi.service.component.annotations.ReferencePolicy;
ke han81a38b92017-03-10 18:41:44 +080078import org.slf4j.Logger;
79import org.slf4j.LoggerFactory;
80
81import java.util.ArrayList;
Sonal Kasliwalf11c0672020-03-18 11:11:50 +000082import java.util.Arrays;
ke han81a38b92017-03-10 18:41:44 +080083import java.util.Collection;
Esin Karamaneff10392019-06-27 18:09:13 +000084import java.util.HashSet;
ke han81a38b92017-03-10 18:41:44 +080085import java.util.Iterator;
onurka85b2d9c2021-01-19 22:49:19 +030086import java.util.List;
ke han81a38b92017-03-10 18:41:44 +080087import java.util.Map;
onurka85b2d9c2021-01-19 22:49:19 +030088import java.util.Objects;
Esin Karamaneff10392019-06-27 18:09:13 +000089import java.util.Optional;
ke han81a38b92017-03-10 18:41:44 +080090import java.util.Set;
91import java.util.TimerTask;
92import java.util.concurrent.ConcurrentHashMap;
Esin Karamanb38700c2019-09-17 13:01:25 +000093import java.util.concurrent.ExecutorService;
ke han81a38b92017-03-10 18:41:44 +080094import java.util.concurrent.Executors;
95import java.util.concurrent.ScheduledExecutorService;
onurka85b2d9c2021-01-19 22:49:19 +030096import java.util.concurrent.ThreadFactory;
ke han81a38b92017-03-10 18:41:44 +080097import java.util.concurrent.TimeUnit;
98
Sonal Kasliwalf11c0672020-03-18 11:11:50 +000099import static org.onlab.packet.IGMPMembership.ALLOW_NEW_SOURCES;
100import static org.onlab.packet.IGMPMembership.BLOCK_OLD_SOURCES;
onurka85b2d9c2021-01-19 22:49:19 +0300101import static org.onlab.packet.IGMPMembership.CHANGE_TO_EXCLUDE_MODE;
102import static org.onlab.packet.IGMPMembership.CHANGE_TO_INCLUDE_MODE;
103import static org.onlab.packet.IGMPMembership.MODE_IS_EXCLUDE;
104import static org.onlab.packet.IGMPMembership.MODE_IS_INCLUDE;
Esin Karamanb38700c2019-09-17 13:01:25 +0000105import static org.onlab.util.Tools.groupedThreads;
106
ke han81a38b92017-03-10 18:41:44 +0800107/**
108 * Igmp process application, use proxy mode, support first join/ last leave , fast leave
109 * period query and keep alive, packet out igmp message to uplink port features.
110 */
111@Component(immediate = true)
112public class IgmpManager {
Ilayda Ozdemire3505102021-02-23 12:56:15 +0000113 private static final String MCAST_NOT_RUNNING = "Multicast is not running.";
114 private static final String SADIS_NOT_RUNNING = "Sadis is not running.";
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800115 private static final String APP_NAME = "org.opencord.igmpproxy";
116
ke han81a38b92017-03-10 18:41:44 +0800117 private static final Class<IgmpproxyConfig> IGMPPROXY_CONFIG_CLASS =
118 IgmpproxyConfig.class;
119 private static final Class<IgmpproxySsmTranslateConfig> IGMPPROXY_SSM_CONFIG_CLASS =
120 IgmpproxySsmTranslateConfig.class;
121 private static final Class<McastConfig> MCAST_CONFIG_CLASS =
122 McastConfig.class;
Esin Karamaneff10392019-06-27 18:09:13 +0000123
ke han81a38b92017-03-10 18:41:44 +0800124 private static ApplicationId appId;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800125
ke han81a38b92017-03-10 18:41:44 +0800126 private static int unSolicitedTimeout = 3; // unit is 1 sec
127 private static int keepAliveCount = 3;
128 private static int lastQueryInterval = 2; //unit is 1 sec
129 private static int lastQueryCount = 2;
130 private static boolean fastLeave = true;
131 private static boolean withRAUplink = true;
132 private static boolean withRADownlink = false;
133 private static boolean periodicQuery = true;
134 private static short mvlan = 4000;
Esin Karaman586f1d62020-06-04 10:15:34 +0000135 private static short mvlanInner = VlanId.NONE.toShort();
ke han81a38b92017-03-10 18:41:44 +0800136 private static byte igmpCos = 7;
Esin Karaman586f1d62020-06-04 10:15:34 +0000137 private static byte igmpUniCos = 7;
ke han81a38b92017-03-10 18:41:44 +0800138 public static boolean connectPointMode = true;
139 public static ConnectPoint connectPoint = null;
Esin Karamaneff10392019-06-27 18:09:13 +0000140 private static ConnectPoint sourceDeviceAndPort = null;
141 private static boolean enableIgmpProvisioning = false;
Esin Karamanb38700c2019-09-17 13:01:25 +0000142 private static boolean igmpOnPodBasis = false;
Arjun E Kb0018fd2020-04-07 13:26:40 +0000143 private static boolean outgoingIgmpWithV3 = true;
Esin Karamaneff10392019-06-27 18:09:13 +0000144
145 private static final Integer MAX_PRIORITY = 10000;
146 private static final String INSTALLED = "installed";
147 private static final String REMOVED = "removed";
148 private static final String INSTALLATION = "installation";
149 private static final String REMOVAL = "removal";
Esin Karaman00e16b72020-02-21 10:32:39 +0000150 private static final String NNI_PREFIX = "nni";
ke han81a38b92017-03-10 18:41:44 +0800151
ke han29af27b2017-09-08 10:29:12 +0800152 private static boolean pimSSmInterworking = false;
153 private static final String DEFAULT_PIMSSM_HOST = "127.0.0.1";
ke han81a38b92017-03-10 18:41:44 +0800154 private final ScheduledExecutorService scheduledExecutorService =
155 Executors.newScheduledThreadPool(1);
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800156
Carmelo Casconebef302e2019-11-14 19:58:20 -0800157 @Reference(cardinality = ReferenceCardinality.MANDATORY)
ke han81a38b92017-03-10 18:41:44 +0800158 protected CoreService coreService;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800159
Carmelo Casconebef302e2019-11-14 19:58:20 -0800160 @Reference(cardinality = ReferenceCardinality.MANDATORY)
ke han81a38b92017-03-10 18:41:44 +0800161 protected PacketService packetService;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800162
Carmelo Casconebef302e2019-11-14 19:58:20 -0800163 @Reference(cardinality = ReferenceCardinality.MANDATORY)
ke han81a38b92017-03-10 18:41:44 +0800164 protected MastershipService mastershipService;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800165
Carmelo Casconebef302e2019-11-14 19:58:20 -0800166 @Reference(cardinality = ReferenceCardinality.MANDATORY)
ke han81a38b92017-03-10 18:41:44 +0800167 protected FlowRuleService flowRuleService;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800168
Carmelo Casconebef302e2019-11-14 19:58:20 -0800169 @Reference(cardinality = ReferenceCardinality.MANDATORY)
ke han81a38b92017-03-10 18:41:44 +0800170 protected DeviceService deviceService;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800171
Carmelo Casconebef302e2019-11-14 19:58:20 -0800172 @Reference(cardinality = ReferenceCardinality.MANDATORY)
ke han81a38b92017-03-10 18:41:44 +0800173 protected FlowObjectiveService flowObjectiveService;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800174
Carmelo Casconebef302e2019-11-14 19:58:20 -0800175 @Reference(cardinality = ReferenceCardinality.MANDATORY)
ke han81a38b92017-03-10 18:41:44 +0800176 protected NetworkConfigRegistry networkConfig;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800177
Ilayda Ozdemire3505102021-02-23 12:56:15 +0000178 @Reference(cardinality = ReferenceCardinality.OPTIONAL,
179 bind = "bindMcastRouteService",
180 unbind = "unbindMcastRouteService",
181 policy = ReferencePolicy.DYNAMIC)
182 protected volatile MulticastRouteService multicastService;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800183
Ilayda Ozdemire3505102021-02-23 12:56:15 +0000184 @Reference(cardinality = ReferenceCardinality.OPTIONAL,
185 bind = "bindSadisService",
186 unbind = "unbindSadisService",
187 policy = ReferencePolicy.DYNAMIC)
188 protected volatile SadisService sadisService;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800189
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000190 @Reference(cardinality = ReferenceCardinality.MANDATORY)
191 protected IgmpStatisticsService igmpStatisticsManager;
192
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000193 @Reference(cardinality = ReferenceCardinality.MANDATORY)
194 protected GroupMemberStore groupMemberStore;
195
196 @Reference(cardinality = ReferenceCardinality.MANDATORY)
197 protected StateMachineService stateMachineService;
198
199 @Reference(cardinality = ReferenceCardinality.MANDATORY)
200 protected IgmpLeadershipService igmpLeadershipService;
201
ke han81a38b92017-03-10 18:41:44 +0800202 private IgmpPacketProcessor processor = new IgmpPacketProcessor();
203 private Logger log = LoggerFactory.getLogger(getClass());
204 private ApplicationId coreAppId;
205 private Map<Ip4Address, Ip4Address> ssmTranslateTable = new ConcurrentHashMap<>();
Esin Karamaneff10392019-06-27 18:09:13 +0000206
ke han81a38b92017-03-10 18:41:44 +0800207 private InternalNetworkConfigListener configListener =
208 new InternalNetworkConfigListener();
209 private DeviceListener deviceListener = new InternalDeviceListener();
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800210
ke han81a38b92017-03-10 18:41:44 +0800211 private ConfigFactory<ApplicationId, IgmpproxyConfig> igmpproxyConfigFactory =
212 new ConfigFactory<ApplicationId, IgmpproxyConfig>(
213 SubjectFactories.APP_SUBJECT_FACTORY, IGMPPROXY_CONFIG_CLASS, "igmpproxy") {
214 @Override
215 public IgmpproxyConfig createConfig() {
216 return new IgmpproxyConfig();
217 }
218 };
219 private ConfigFactory<ApplicationId, IgmpproxySsmTranslateConfig> igmpproxySsmConfigFactory =
220 new ConfigFactory<ApplicationId, IgmpproxySsmTranslateConfig>(
221 SubjectFactories.APP_SUBJECT_FACTORY, IGMPPROXY_SSM_CONFIG_CLASS, "ssmTranslate", true) {
222 @Override
223 public IgmpproxySsmTranslateConfig createConfig() {
224 return new IgmpproxySsmTranslateConfig();
225 }
226 };
Esin Karamaneff10392019-06-27 18:09:13 +0000227
ke han81a38b92017-03-10 18:41:44 +0800228 private int maxResp = 10; //unit is 1 sec
229 private int keepAliveInterval = 120; //unit is 1 sec
230
onurka85b2d9c2021-01-19 22:49:19 +0300231 private int numberOfIgmpReportProcessorThreads = 20;
232 ExecutorService[] igmpReportProcessServiceExecutorList;
233
Esin Karamanb38700c2019-09-17 13:01:25 +0000234 private ExecutorService eventExecutor;
235
ke han81a38b92017-03-10 18:41:44 +0800236 public static int getUnsolicitedTimeout() {
237 return unSolicitedTimeout;
238 }
239
Arjun E Kb0018fd2020-04-07 13:26:40 +0000240 public static boolean outgoingIgmpWithV3() {
241 return outgoingIgmpWithV3;
242 }
243
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000244 private List<Byte> validMembershipModes = Arrays.asList(MODE_IS_INCLUDE, MODE_IS_EXCLUDE, CHANGE_TO_INCLUDE_MODE,
245 CHANGE_TO_EXCLUDE_MODE, ALLOW_NEW_SOURCES, BLOCK_OLD_SOURCES);
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000246
ke han81a38b92017-03-10 18:41:44 +0800247 @Activate
248 protected void activate() {
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800249 appId = coreService.registerApplication(APP_NAME);
ke han81a38b92017-03-10 18:41:44 +0800250 coreAppId = coreService.registerApplication(CoreService.CORE_APP_NAME);
251 packetService.addProcessor(processor, PacketProcessor.director(4));
Esin Karaman4a9075d2020-07-14 14:46:14 +0000252 IgmpSender.init(packetService, igmpStatisticsManager);
ke han81a38b92017-03-10 18:41:44 +0800253
ke han81a38b92017-03-10 18:41:44 +0800254 networkConfig.registerConfigFactory(igmpproxySsmConfigFactory);
255 networkConfig.registerConfigFactory(igmpproxyConfigFactory);
256 networkConfig.addListener(configListener);
257
258 configListener.reconfigureNetwork(networkConfig.getConfig(appId, IGMPPROXY_CONFIG_CLASS));
259 configListener.reconfigureSsmTable(networkConfig.getConfig(appId, IGMPPROXY_SSM_CONFIG_CLASS));
260
ke han81a38b92017-03-10 18:41:44 +0800261 if (connectPointMode) {
262 provisionConnectPointFlows();
263 } else {
264 provisionUplinkFlows();
265 }
266
267 McastConfig config = networkConfig.getConfig(coreAppId, MCAST_CONFIG_CLASS);
268 if (config != null) {
269 mvlan = config.egressVlan().toShort();
Deepa Vaddireddy88134a42017-07-05 12:16:03 +0530270 IgmpSender.getInstance().setMvlan(mvlan);
Esin Karaman586f1d62020-06-04 10:15:34 +0000271 mvlanInner = config.egressInnerVlan().toShort();
272 IgmpSender.getInstance().setMvlanInner(mvlanInner);
ke han81a38b92017-03-10 18:41:44 +0800273 }
274 deviceService.addListener(deviceListener);
Sonal Kasliwalddc3ff22019-11-18 11:52:49 +0000275 scheduledExecutorService.scheduleAtFixedRate(new IgmpProxyTimerTask(), 1000, 1000, TimeUnit.MILLISECONDS);
onurka85b2d9c2021-01-19 22:49:19 +0300276 eventExecutor = Executors.newSingleThreadScheduledExecutor(groupedThreads("cord/igmpproxy",
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000277 "events-igmp-%d", log));
onurka85b2d9c2021-01-19 22:49:19 +0300278 initializeIgmpReportProcessServiceExecutors();
ke han81a38b92017-03-10 18:41:44 +0800279 log.info("Started");
280 }
281
282 @Deactivate
283 protected void deactivate() {
284 scheduledExecutorService.shutdown();
Esin Karamanb38700c2019-09-17 13:01:25 +0000285 eventExecutor.shutdown();
onurka85b2d9c2021-01-19 22:49:19 +0300286 shutdownIgmpReportProcessServiceExecutors();
ke han81a38b92017-03-10 18:41:44 +0800287
288 // de-register and null our handler
289 networkConfig.removeListener(configListener);
ke han81a38b92017-03-10 18:41:44 +0800290 networkConfig.unregisterConfigFactory(igmpproxyConfigFactory);
291 networkConfig.unregisterConfigFactory(igmpproxySsmConfigFactory);
292 deviceService.removeListener(deviceListener);
293 packetService.removeProcessor(processor);
ke han81a38b92017-03-10 18:41:44 +0800294 log.info("Stopped");
295 }
296
onurka85b2d9c2021-01-19 22:49:19 +0300297 private void initializeIgmpReportProcessServiceExecutors() {
298 igmpReportProcessServiceExecutorList = new ExecutorService[numberOfIgmpReportProcessorThreads];
299 for (int i = 0; i < numberOfIgmpReportProcessorThreads; i++) {
300 ThreadFactory igmpReportProcessorThreadFactory =
301 new ThreadFactoryBuilder().setNameFormat("report-processor-igmp-" + i)
302 .setUncaughtExceptionHandler((t, e) ->
303 log.error("Uncaught exception on {}: ", t.getName(), e))
304 .build();
305 ExecutorService igmpReportProcessServiceExecutor = Executors.newSingleThreadExecutor(
306 igmpReportProcessorThreadFactory);
307 igmpReportProcessServiceExecutorList[i] = igmpReportProcessServiceExecutor;
308 }
309 }
Ilayda Ozdemire3505102021-02-23 12:56:15 +0000310
onurka85b2d9c2021-01-19 22:49:19 +0300311 private void shutdownIgmpReportProcessServiceExecutors() {
312 for (ExecutorService executor : igmpReportProcessServiceExecutorList) {
313 executor.shutdown();
314 }
315 }
316
Ilayda Ozdemire3505102021-02-23 12:56:15 +0000317 protected void bindSadisService(SadisService service) {
318 sadisService = service;
319 log.info("Sadis-service binds to onos.");
320 }
321
322 protected void unbindSadisService(SadisService service) {
323 sadisService = null;
324 log.info("Sadis-service unbinds from onos.");
325 }
326
327 protected void bindMcastRouteService(MulticastRouteService service) {
328 multicastService = service;
329 log.info("Multicast route service binds to onos.");
330 }
331
332 protected void unbindMcastRouteService(MulticastRouteService service) {
333 multicastService = null;
334 log.info("Multicast route service unbinds from onos.");
335 }
336
ke han81a38b92017-03-10 18:41:44 +0800337 protected Ip4Address getDeviceIp(DeviceId ofDeviceId) {
338 try {
339 String[] mgmtAddress = deviceService.getDevice(ofDeviceId)
340 .annotations().value(AnnotationKeys.MANAGEMENT_ADDRESS).split(":");
341 return Ip4Address.valueOf(mgmtAddress[0]);
342 } catch (Exception ex) {
onurka85b2d9c2021-01-19 22:49:19 +0300343 log.info("No valid Ipaddress for {}", ofDeviceId);
ke han81a38b92017-03-10 18:41:44 +0800344 return null;
345 }
346 }
347
348 private void processIgmpQuery(IGMPQuery igmpQuery, ConnectPoint cp, int maxResp) {
349
350 DeviceId deviceId = cp.deviceId();
351 Ip4Address gAddr = igmpQuery.getGaddr().getIp4Address();
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000352 maxResp = calculateMaxResp(maxResp);
353 if (gAddr != null && !gAddr.isZero()) {
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000354 stateMachineService.specialQuery(deviceId, gAddr, maxResp);
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300355 igmpStatisticsManager.increaseStat(IgmpStatisticType.IGMP_GRP_SPECIFIC_MEMBERSHIP_QUERY);
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000356 } else {
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000357 stateMachineService.generalQuery(deviceId, maxResp);
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300358 igmpStatisticsManager.increaseStat(IgmpStatisticType.IGMP_GENERAL_MEMBERSHIP_QUERY);
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000359 }
360 }
ke han81a38b92017-03-10 18:41:44 +0800361
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000362 private void processIgmpConnectPointQuery(IGMPQuery igmpQuery, ConnectPoint cp, int maxResp) {
363
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000364 Ip4Address gAddr = igmpQuery.getGaddr().getIp4Address();
Esin Karaman00e16b72020-02-21 10:32:39 +0000365 final int maxResponseTime = calculateMaxResp(maxResp);
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000366 //The query is received on the ConnectPoint
367 // send query accordingly to the registered OLT devices.
368 if (gAddr != null && !gAddr.isZero()) {
Esin Karaman00e16b72020-02-21 10:32:39 +0000369 deviceService.getAvailableDevices().forEach(device -> {
370 Optional<SubscriberAndDeviceInformation> accessDevice = getSubscriberAndDeviceInformation(device.id());
371 if (accessDevice.isPresent()) {
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000372 stateMachineService.specialQuery(device.id(), gAddr, maxResponseTime);
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300373 igmpStatisticsManager.increaseStat(IgmpStatisticType.IGMP_GRP_AND_SRC_SPESIFIC_MEMBERSHIP_QUERY);
Esin Karaman00e16b72020-02-21 10:32:39 +0000374 }
375 });
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300376 igmpStatisticsManager.increaseStat(IgmpStatisticType.CURRENT_GRP_NUMBER_COUNTER);
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000377 } else {
378 //Don't know which group is targeted by the query
379 //So query all the members(in all the OLTs) and proxy their reports
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000380 stateMachineService.generalQuery(maxResponseTime);
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300381 igmpStatisticsManager.increaseStat(IgmpStatisticType.IGMP_GENERAL_MEMBERSHIP_QUERY);
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000382 }
383 }
384
385
386 private int calculateMaxResp(int maxResp) {
ke han81a38b92017-03-10 18:41:44 +0800387 if (maxResp >= 128) {
388 int mant = maxResp & 0xf;
389 int exp = (maxResp >> 4) & 0x7;
390 maxResp = (mant | 0x10) << (exp + 3);
391 }
392
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000393 return (maxResp + 5) / 10;
ke han81a38b92017-03-10 18:41:44 +0800394 }
395
onurka85b2d9c2021-01-19 22:49:19 +0300396 private void queueIgmpReport(IGMPMembership igmpGroup, VlanId vlan, ConnectPoint cp, byte igmpType) {
397 int packetHashCode = Objects.hash(igmpGroup.getGaddr(), connectPoint);
398 int threadId = Math.abs(packetHashCode % numberOfIgmpReportProcessorThreads);
399 log.debug("IGMP report for ConnectPoint {} and group IP {} shall be processed in thread #{}",
400 cp, igmpGroup.getGaddr(), threadId);
401
402 igmpReportProcessServiceExecutorList[threadId].execute(
403 () -> processIgmpReport(igmpGroup, vlan, cp, igmpType));
404 }
405
ke han81a38b92017-03-10 18:41:44 +0800406 private Ip4Address ssmTranslateRoute(IpAddress group) {
407 return ssmTranslateTable.get(group);
408 }
409
410 private void processIgmpReport(IGMPMembership igmpGroup, VlanId vlan, ConnectPoint cp, byte igmpType) {
Ilayda Ozdemire3505102021-02-23 12:56:15 +0000411 if (multicastService == null) {
412 log.warn(MCAST_NOT_RUNNING);
413 return;
414 }
415
ke han81a38b92017-03-10 18:41:44 +0800416 DeviceId deviceId = cp.deviceId();
417 PortNumber portNumber = cp.port();
418
Andrea Campanella2c70a572020-06-05 13:31:45 +0200419 log.debug("Processing IGMP report on membership {} for vlan {} on port {} with type {}",
onurka85b2d9c2021-01-19 22:49:19 +0300420 igmpGroup, vlan, cp, igmpType);
Andrea Campanella2c70a572020-06-05 13:31:45 +0200421
ke han81a38b92017-03-10 18:41:44 +0800422 Ip4Address groupIp = igmpGroup.getGaddr().getIp4Address();
423 if (!groupIp.isMulticast()) {
onurka85b2d9c2021-01-19 22:49:19 +0300424 log.info("{} is not a valid group address", groupIp);
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300425 igmpStatisticsManager.increaseStat(IgmpStatisticType.FAIL_JOIN_REQ_UNKNOWN_MULTICAST_IP_COUNTER);
ke han81a38b92017-03-10 18:41:44 +0800426 return;
427 }
428 Ip4Address srcIp = getDeviceIp(deviceId);
429
430 byte recordType = igmpGroup.getRecordType();
431 boolean join = false;
432
433 ArrayList<Ip4Address> sourceList = new ArrayList<>();
434
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000435 if (!validMembershipModes.contains(recordType)) {
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300436 igmpStatisticsManager.increaseStat(IgmpStatisticType.REPORTS_RX_WITH_WRONG_MODE_COUNTER);
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000437 }
ke han81a38b92017-03-10 18:41:44 +0800438 if (igmpGroup.getSources().size() > 0) {
439 igmpGroup.getSources().forEach(source -> sourceList.add(source.getIp4Address()));
440 if (recordType == IGMPMembership.CHANGE_TO_EXCLUDE_MODE ||
441 recordType == IGMPMembership.MODE_IS_EXCLUDE ||
442 recordType == IGMPMembership.BLOCK_OLD_SOURCES) {
443 join = false;
444 } else if (recordType == IGMPMembership.CHANGE_TO_INCLUDE_MODE ||
445 recordType == IGMPMembership.MODE_IS_INCLUDE ||
446 recordType == IGMPMembership.ALLOW_NEW_SOURCES) {
447 join = true;
448 }
449 } else {
ke han29af27b2017-09-08 10:29:12 +0800450 IpAddress src = null;
451 if (pimSSmInterworking) {
452 src = ssmTranslateRoute(groupIp);
453 if (src == null) {
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000454 log.info("no ssm translate for group {}", groupIp);
ke han29af27b2017-09-08 10:29:12 +0800455 return;
456 }
457 } else {
458 src = IpAddress.valueOf(DEFAULT_PIMSSM_HOST);
ke han81a38b92017-03-10 18:41:44 +0800459 }
460 sourceList.add(src.getIp4Address());
461 if (recordType == IGMPMembership.CHANGE_TO_EXCLUDE_MODE ||
462 recordType == IGMPMembership.MODE_IS_EXCLUDE ||
463 recordType == IGMPMembership.BLOCK_OLD_SOURCES) {
464 join = true;
465 } else if (recordType == IGMPMembership.CHANGE_TO_INCLUDE_MODE ||
466 recordType == IGMPMembership.MODE_IS_INCLUDE ||
467 recordType == IGMPMembership.ALLOW_NEW_SOURCES) {
468 join = false;
469 }
470 }
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000471 GroupMemberId groupMemberKey = GroupMemberId.of(groupIp, deviceId, portNumber);
onurka85b2d9c2021-01-19 22:49:19 +0300472 log.debug("{} for {}", join ? "Join" : "Leave", groupMemberKey);
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000473 GroupMember groupMember = groupMemberStore.getGroupMember(groupMemberKey);
ke han81a38b92017-03-10 18:41:44 +0800474
475 if (join) {
Andrea Campanella2c70a572020-06-05 13:31:45 +0200476 log.debug("Received join on {} for vlan {}", cp, vlan);
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300477 igmpStatisticsManager.increaseStat(IgmpStatisticType.IGMP_JOIN_REQ);
Andrea Campanella2c70a572020-06-05 13:31:45 +0200478
ke han81a38b92017-03-10 18:41:44 +0800479 if (groupMember == null) {
Esin Karamaneff10392019-06-27 18:09:13 +0000480 Optional<ConnectPoint> sourceConfigured = getSource();
481 if (!sourceConfigured.isPresent()) {
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300482 igmpStatisticsManager.increaseStat(IgmpStatisticType.IGMP_FAIL_JOIN_REQ);
Esin Karamaneff10392019-06-27 18:09:13 +0000483 log.warn("Unable to process IGMP Join from {} since no source " +
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000484 "configuration is found.", deviceId);
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300485 igmpStatisticsManager
486 .increaseStat(IgmpStatisticType.FAIL_JOIN_REQ_INSUFF_PERMISSION_ACCESS_COUNTER);
Esin Karamaneff10392019-06-27 18:09:13 +0000487 return;
488 }
Esin Karaman00e16b72020-02-21 10:32:39 +0000489
490 Optional<PortNumber> deviceUplink = getDeviceUplink(deviceId);
491 if (deviceUplink.isEmpty()) {
492 log.warn("Unable to process IGMP Join since uplink port " +
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000493 "of the device {} is not found.", deviceId);
Esin Karaman00e16b72020-02-21 10:32:39 +0000494 return;
495 }
496
497 if (igmpType == IGMP.TYPE_IGMPV2_MEMBERSHIP_REPORT) {
498 groupMember = new GroupMember(groupIp, vlan, deviceId, portNumber, true);
499 } else {
500 groupMember = new GroupMember(groupIp, vlan, deviceId, portNumber, false);
501 }
502
Esin Karamaneff10392019-06-27 18:09:13 +0000503 HashSet<ConnectPoint> sourceConnectPoints = Sets.newHashSet(sourceConfigured.get());
504
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000505 boolean isJoined = stateMachineService.join(deviceId, groupIp, srcIp, deviceUplink.get());
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000506 if (isJoined) {
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300507 igmpStatisticsManager.increaseStat(IgmpStatisticType.IGMP_SUCCESS_JOIN_RE_JOIN_REQ);
508 igmpStatisticsManager.increaseStat(IgmpStatisticType.IGMP_CHANNEL_JOIN_COUNTER);
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000509 } else {
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300510 igmpStatisticsManager.increaseStat(IgmpStatisticType.IGMP_FAIL_JOIN_REQ);
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000511 }
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000512 groupMemberStore.putGroupMember(groupMember);
513 log.debug("Group member created with id: {}", groupMember.getGroupMemberId());
ke han81a38b92017-03-10 18:41:44 +0800514 groupMember.updateList(recordType, sourceList);
Esin Karamaneff10392019-06-27 18:09:13 +0000515 groupMember.getSourceList().forEach(source -> {
516 McastRoute route = new McastRoute(source, groupIp, McastRoute.Type.IGMP);
517 //add route
518 multicastService.add(route);
519 //add source to the route
520 multicastService.addSources(route, Sets.newHashSet(sourceConnectPoints));
521 //add sink to the route
522 multicastService.addSinks(route, Sets.newHashSet(cp));
523 });
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300524 igmpStatisticsManager.increaseStat(IgmpStatisticType.UNCONFIGURED_GROUP_COUNTER);
Esin Karamaneff10392019-06-27 18:09:13 +0000525
ke han81a38b92017-03-10 18:41:44 +0800526 }
527 groupMember.resetAllTimers();
528 groupMember.updateList(recordType, sourceList);
529 groupMember.setLeave(false);
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000530 //put updated member to the store
531 groupMemberStore.putGroupMember(groupMember);
ke han81a38b92017-03-10 18:41:44 +0800532 } else {
Andrea Campanella2c70a572020-06-05 13:31:45 +0200533 log.debug("Received leave on {} for vlan {}", cp, vlan);
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300534 igmpStatisticsManager.increaseStat(IgmpStatisticType.IGMP_LEAVE_REQ);
ke han81a38b92017-03-10 18:41:44 +0800535 if (groupMember == null) {
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000536 log.info("receive leave but no instance, group {} device: {} port:{}",
537 groupIp, deviceId, portNumber);
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300538 igmpStatisticsManager.increaseStat(IgmpStatisticType.UNCONFIGURED_GROUP_COUNTER);
ke han81a38b92017-03-10 18:41:44 +0800539 return;
540 } else {
541 groupMember.setLeave(true);
542 if (fastLeave) {
543 leaveAction(groupMember);
544 } else {
545 sendQuery(groupMember);
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000546 //put modified group member object to the store
547 groupMemberStore.updateGroupMember(groupMember);
ke han81a38b92017-03-10 18:41:44 +0800548 }
549 }
550 }
551 }
552
553 private void leaveAction(GroupMember groupMember) {
Ilayda Ozdemire3505102021-02-23 12:56:15 +0000554 if (multicastService == null) {
555 log.warn(MCAST_NOT_RUNNING);
556 return;
557 }
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300558 igmpStatisticsManager.increaseStat(IgmpStatisticType.IGMP_DISCONNECT);
ke han81a38b92017-03-10 18:41:44 +0800559 ConnectPoint cp = new ConnectPoint(groupMember.getDeviceId(), groupMember.getPortNumber());
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000560 stateMachineService.leave(groupMember.getDeviceId(), groupMember.getGroupIp());
Esin Karamaneff10392019-06-27 18:09:13 +0000561 groupMember.getSourceList().forEach(source -> multicastService.removeSinks(
ke han81a38b92017-03-10 18:41:44 +0800562 new McastRoute(source, groupMember.getGroupIp(),
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000563 McastRoute.Type.IGMP), Sets.newHashSet(cp)));
564 groupMemberStore.removeGroupMember(groupMember.getGroupMemberId());
ke han81a38b92017-03-10 18:41:44 +0800565 }
566
567 private void sendQuery(GroupMember groupMember) {
568 Ethernet ethpkt;
569 Ip4Address srcIp = getDeviceIp(groupMember.getDeviceId());
570 if (groupMember.getv2()) {
Esin Karaman586f1d62020-06-04 10:15:34 +0000571 ethpkt = IgmpSender.getInstance().buildIgmpV2Query(groupMember.getGroupIp(),
572 srcIp, groupMember.getvlan().toShort());
ke han81a38b92017-03-10 18:41:44 +0800573 } else {
Esin Karaman586f1d62020-06-04 10:15:34 +0000574 ethpkt = IgmpSender.getInstance().buildIgmpV3Query(groupMember.getGroupIp(),
575 srcIp, groupMember.getvlan().toShort());
ke han81a38b92017-03-10 18:41:44 +0800576 }
Esin Karaman586f1d62020-06-04 10:15:34 +0000577 log.debug("Sending IGMP query to {}/{} for group {}: {}",
578 groupMember.getDeviceId(), groupMember.getPortNumber(), groupMember.getGroupIp(), ethpkt);
ke han81a38b92017-03-10 18:41:44 +0800579 IgmpSender.getInstance().sendIgmpPacket(ethpkt, groupMember.getDeviceId(), groupMember.getPortNumber());
580 }
581
Esin Karamaneff10392019-06-27 18:09:13 +0000582 /**
583 * @return connect point of the source if configured; and empty Optional otherwise.
584 */
585 public static Optional<ConnectPoint> getSource() {
586 return sourceDeviceAndPort == null ? Optional.empty() :
587 Optional.of(sourceDeviceAndPort);
ke han81a38b92017-03-10 18:41:44 +0800588 }
589
590 /**
591 * Packet processor responsible for forwarding packets along their paths.
592 */
593 private class IgmpPacketProcessor implements PacketProcessor {
onurka85b2d9c2021-01-19 22:49:19 +0300594
ke han81a38b92017-03-10 18:41:44 +0800595 @Override
596 public void process(PacketContext context) {
Esin Karamanb38700c2019-09-17 13:01:25 +0000597 eventExecutor.execute(() -> {
598 try {
599 InboundPacket pkt = context.inPacket();
onurka85b2d9c2021-01-19 22:49:19 +0300600 log.debug("IgmpPacketProcessor shall process InboundPacket: {}", pkt);
Esin Karamanb38700c2019-09-17 13:01:25 +0000601 Ethernet ethPkt = pkt.parsed();
602 if (ethPkt == null) {
603 return;
604 }
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300605 igmpStatisticsManager.increaseStat(IgmpStatisticType.TOTAL_MSG_RECEIVED);
ke han81a38b92017-03-10 18:41:44 +0800606
Esin Karamanb38700c2019-09-17 13:01:25 +0000607 if (ethPkt.getEtherType() != Ethernet.TYPE_IPV4) {
608 return;
609 }
ke han81a38b92017-03-10 18:41:44 +0800610
Esin Karamanb38700c2019-09-17 13:01:25 +0000611 IPv4 ipv4Pkt = (IPv4) ethPkt.getPayload();
ke han81a38b92017-03-10 18:41:44 +0800612
Esin Karamanb38700c2019-09-17 13:01:25 +0000613 if (ipv4Pkt.getProtocol() != IPv4.PROTOCOL_IGMP) {
614 return;
615 }
ke han81a38b92017-03-10 18:41:44 +0800616
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300617 igmpStatisticsManager.increaseStat(IgmpStatisticType.IGMP_VALID_CHECKSUM_COUNTER);
Esin Karamanb38700c2019-09-17 13:01:25 +0000618 short vlan = ethPkt.getVlanID();
619 DeviceId deviceId = pkt.receivedFrom().deviceId();
ke han81a38b92017-03-10 18:41:44 +0800620
Esin Karaman00e16b72020-02-21 10:32:39 +0000621 if (!isConnectPoint(deviceId, pkt.receivedFrom().port()) &&
622 !getSubscriberAndDeviceInformation(deviceId).isPresent()) {
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000623 log.error("Device not registered in netcfg : {}", deviceId);
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300624 igmpStatisticsManager
625 .increaseStat(IgmpStatisticType.FAIL_JOIN_REQ_INSUFF_PERMISSION_ACCESS_COUNTER);
Esin Karamanb38700c2019-09-17 13:01:25 +0000626 return;
627 }
ke han81a38b92017-03-10 18:41:44 +0800628
Esin Karamanb38700c2019-09-17 13:01:25 +0000629 IGMP igmp = (IGMP) ipv4Pkt.getPayload();
Esin Karamance5ce512020-02-25 15:58:14 +0000630
631 Optional<PortNumber> deviceUpLinkOpt = getDeviceUplink(deviceId);
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000632 PortNumber upLinkPort = deviceUpLinkOpt.isPresent() ? deviceUpLinkOpt.get() : null;
Esin Karamanb38700c2019-09-17 13:01:25 +0000633 switch (igmp.getIgmpType()) {
634 case IGMP.TYPE_IGMPV3_MEMBERSHIP_QUERY:
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300635 igmpStatisticsManager.increaseStat(IgmpStatisticType.IGMP_V3_MEMBERSHIP_QUERY);
Esin Karamanb38700c2019-09-17 13:01:25 +0000636 //Discard Query from OLT’s non-uplink port’s
Esin Karamance5ce512020-02-25 15:58:14 +0000637 if (!pkt.receivedFrom().port().equals(upLinkPort)) {
Esin Karamanb38700c2019-09-17 13:01:25 +0000638 if (isConnectPoint(deviceId, pkt.receivedFrom().port())) {
639 log.info("IGMP Picked up query from connectPoint");
640 //OK to process packet
641 processIgmpConnectPointQuery((IGMPQuery) igmp.getGroups().get(0),
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000642 pkt.receivedFrom(),
643 0xff & igmp.getMaxRespField());
Esin Karamanb38700c2019-09-17 13:01:25 +0000644 break;
645 } else {
646 //Not OK to process packet
Esin Karamance5ce512020-02-25 15:58:14 +0000647 log.warn("IGMP Picked up query from non-uplink port {}", upLinkPort);
Esin Karamanb38700c2019-09-17 13:01:25 +0000648 return;
649 }
650 }
ke han81a38b92017-03-10 18:41:44 +0800651
Esin Karamanb38700c2019-09-17 13:01:25 +0000652 processIgmpQuery((IGMPQuery) igmp.getGroups().get(0), pkt.receivedFrom(),
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000653 0xff & igmp.getMaxRespField());
Esin Karamanb38700c2019-09-17 13:01:25 +0000654 break;
655 case IGMP.TYPE_IGMPV1_MEMBERSHIP_REPORT:
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300656 igmpStatisticsManager.increaseStat(IgmpStatisticType.IGMP_V1_MEMBERSHIP_REPORT);
Esin Karamanb38700c2019-09-17 13:01:25 +0000657 log.debug("IGMP version 1 message types are not currently supported.");
658 break;
659 case IGMP.TYPE_IGMPV3_MEMBERSHIP_REPORT:
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300660 igmpStatisticsManager.increaseStat(IgmpStatisticType.IGMP_V3_MEMBERSHIP_REPORT);
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000661 processIgmpMessage(pkt, igmp, upLinkPort, vlan);
662 break;
Esin Karamanb38700c2019-09-17 13:01:25 +0000663 case IGMP.TYPE_IGMPV2_MEMBERSHIP_REPORT:
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300664 igmpStatisticsManager.increaseStat(IgmpStatisticType.IGMP_V2_MEMBERSHIP_REPORT);
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000665 processIgmpMessage(pkt, igmp, upLinkPort, vlan);
666 break;
Esin Karamanb38700c2019-09-17 13:01:25 +0000667 case IGMP.TYPE_IGMPV2_LEAVE_GROUP:
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300668 igmpStatisticsManager.increaseStat(IgmpStatisticType.IGMP_V2_LEAVE_GROUP);
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000669 processIgmpMessage(pkt, igmp, upLinkPort, vlan);
Esin Karamanb38700c2019-09-17 13:01:25 +0000670 break;
ke han81a38b92017-03-10 18:41:44 +0800671
Esin Karamanb38700c2019-09-17 13:01:25 +0000672 default:
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000673 log.warn("Unknown IGMP message type: {}", igmp.getIgmpType());
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300674 igmpStatisticsManager.increaseStat(IgmpStatisticType.INVALID_IGMP_MSG_RECEIVED);
675 igmpStatisticsManager.increaseStat(IgmpStatisticType.UNKNOWN_IGMP_TYPE_PACKETS_RX_COUNTER);
Esin Karamanb38700c2019-09-17 13:01:25 +0000676 break;
677 }
678
679 } catch (Exception ex) {
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000680 log.error("igmp process error : ", ex);
ke han81a38b92017-03-10 18:41:44 +0800681 }
Esin Karamanb38700c2019-09-17 13:01:25 +0000682 });
ke han81a38b92017-03-10 18:41:44 +0800683 }
684 }
685
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000686 private void processIgmpMessage(InboundPacket pkt, IGMP igmp, PortNumber upLinkPort, short vlan) {
687 //Discard join/leave from OLT’s uplink port’s
688 if (pkt.receivedFrom().port().equals(upLinkPort) ||
689 isConnectPoint(pkt.receivedFrom().deviceId(), pkt.receivedFrom().port())) {
690 log.info("IGMP Picked up join/leave from uplink/connectPoint port");
691 return;
692 }
693
694 Iterator<IGMPGroup> itr = igmp.getGroups().iterator();
695 while (itr.hasNext()) {
696 IGMPGroup group = itr.next();
onurka85b2d9c2021-01-19 22:49:19 +0300697 log.debug("IGMPGroup {}", group.getGaddr());
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000698 if (group instanceof IGMPMembership) {
onurka85b2d9c2021-01-19 22:49:19 +0300699 queueIgmpReport((IGMPMembership) group, VlanId.vlanId(vlan),
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000700 pkt.receivedFrom(), igmp.getIgmpType());
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000701 } else {
702 IGMPMembership mgroup = new IGMPMembership(group.getGaddr().getIp4Address());
703 mgroup.setRecordType(igmp.getIgmpType() == IGMP.TYPE_IGMPV2_MEMBERSHIP_REPORT ?
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000704 IGMPMembership.MODE_IS_EXCLUDE :
705 IGMPMembership.MODE_IS_INCLUDE);
onurka85b2d9c2021-01-19 22:49:19 +0300706 queueIgmpReport(mgroup, VlanId.vlanId(vlan),
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000707 pkt.receivedFrom(), igmp.getIgmpType());
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000708 }
709 }
710
711 }
712
ke han81a38b92017-03-10 18:41:44 +0800713 private class IgmpProxyTimerTask extends TimerTask {
714 public void run() {
715 try {
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000716 stateMachineService.timeOut1s();
ke han81a38b92017-03-10 18:41:44 +0800717 queryMembers();
718 } catch (Exception ex) {
719 log.warn("Igmp timer task error : {}", ex.getMessage());
720 }
721 }
722
723 private void queryMembers() {
724 GroupMember groupMember;
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000725 Set<GroupMemberId> keySet = groupMemberStore.getAllGroupMemberIds();
726 for (GroupMemberId key : keySet) {
727 groupMember = groupMemberStore.getGroupMember(key);
728 if (groupMember == null) {
729 continue;
730 }
ke han81a38b92017-03-10 18:41:44 +0800731 DeviceId did = groupMember.getDeviceId();
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000732 if (igmpLeadershipService.isLocalLeader(did)) {
ke han81a38b92017-03-10 18:41:44 +0800733 if (groupMember.isLeave()) {
734 lastQuery(groupMember);
735 } else if (periodicQuery) {
736 periodicQuery(groupMember);
737 }
738 }
739 }
740 }
741
742 private void lastQuery(GroupMember groupMember) {
743 if (groupMember.getLastQueryInterval() < lastQueryInterval) {
744 groupMember.lastQueryInterval(true); // count times
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000745 //put modified group member object to the store
746 groupMemberStore.updateGroupMember(groupMember);
ke han81a38b92017-03-10 18:41:44 +0800747 } else if (groupMember.getLastQueryCount() < lastQueryCount - 1) {
748 sendQuery(groupMember);
749 groupMember.lastQueryInterval(false); // reset count number
750 groupMember.lastQueryCount(true); //count times
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000751 //put modified group member object to the store
752 groupMemberStore.updateGroupMember(groupMember);
ke han81a38b92017-03-10 18:41:44 +0800753 } else if (groupMember.getLastQueryCount() == lastQueryCount - 1) {
754 leaveAction(groupMember);
755 }
756 }
757
758 private void periodicQuery(GroupMember groupMember) {
759 if (groupMember.getKeepAliveQueryInterval() < keepAliveInterval) {
760 groupMember.keepAliveInterval(true);
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000761 //put modified group member object to the store
762 groupMemberStore.updateGroupMember(groupMember);
ke han81a38b92017-03-10 18:41:44 +0800763 } else if (groupMember.getKeepAliveQueryCount() < keepAliveCount) {
764 sendQuery(groupMember);
765 groupMember.keepAliveInterval(false);
766 groupMember.keepAliveQueryCount(true);
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000767 //put modified group member object to the store
768 groupMemberStore.updateGroupMember(groupMember);
ke han81a38b92017-03-10 18:41:44 +0800769 } else if (groupMember.getKeepAliveQueryCount() == keepAliveCount) {
770 leaveAction(groupMember);
771 }
772 }
773
774 }
775
Esin Karaman00e16b72020-02-21 10:32:39 +0000776 public Optional<PortNumber> getDeviceUplink(DeviceId devId) {
777 Device device = deviceService.getDevice(devId);
778 if (device == null || device.serialNumber() == null) {
779 return Optional.empty();
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000780 }
Esin Karaman00e16b72020-02-21 10:32:39 +0000781 Optional<SubscriberAndDeviceInformation> olt = getSubscriberAndDeviceInformation(device.serialNumber());
782 if (olt.isEmpty()) {
783 return Optional.empty();
784 }
785 PortNumber portNumber = PortNumber.portNumber(olt.get().uplinkPort());
786 return validateUpLinkPort(device.id(), portNumber) ?
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000787 Optional.of(portNumber) : Optional.empty();
Esin Karaman00e16b72020-02-21 10:32:39 +0000788 }
789
790 /**
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000791 * @param deviceId device id
Esin Karaman00e16b72020-02-21 10:32:39 +0000792 * @param portNumber port number
793 * @return true if the port name starts with NNI_PREFIX; false otherwise.
794 */
795 public boolean validateUpLinkPort(DeviceId deviceId, PortNumber portNumber) {
796 Port port = deviceService.getPort(deviceId, portNumber);
797 if (port == null) {
798 //port is not discovered by ONOS; so cannot validate it.
799 return false;
800 }
Esin Karamance5ce512020-02-25 15:58:14 +0000801 boolean isValid = port.annotations().value(AnnotationKeys.PORT_NAME) != null &&
Esin Karaman00e16b72020-02-21 10:32:39 +0000802 port.annotations().value(AnnotationKeys.PORT_NAME).startsWith(NNI_PREFIX);
Esin Karamance5ce512020-02-25 15:58:14 +0000803 if (!isValid) {
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000804 log.warn("Port cannot be validated; it is not configured as an NNI port. Device/port: {}/{}",
805 deviceId, portNumber);
Esin Karamance5ce512020-02-25 15:58:14 +0000806 }
807 return isValid;
ke han81a38b92017-03-10 18:41:44 +0800808 }
809
Esin Karamanb38700c2019-09-17 13:01:25 +0000810 public static boolean isIgmpOnPodBasis() {
811 return igmpOnPodBasis;
812 }
813
ke han81a38b92017-03-10 18:41:44 +0800814 private void processFilterObjective(DeviceId devId, PortNumber port, boolean remove) {
Esin Karamaneff10392019-06-27 18:09:13 +0000815 if (!enableIgmpProvisioning) {
816 log.debug("IGMP trap rules won't be installed since enableIgmpProvisioning flag is not set");
817 return;
818 }
ke han81a38b92017-03-10 18:41:44 +0800819 //TODO migrate to packet requests when packet service uses filtering objectives
820 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
821
822 builder = remove ? builder.deny() : builder.permit();
823
824 FilteringObjective igmp = builder
825 .withKey(Criteria.matchInPort(port))
826 .addCondition(Criteria.matchEthType(EthType.EtherType.IPV4.ethType()))
827 .addCondition(Criteria.matchIPProtocol(IPv4.PROTOCOL_IGMP))
828 .withMeta(DefaultTrafficTreatment.builder().setOutput(PortNumber.CONTROLLER).build())
829 .fromApp(appId)
Esin Karamaneff10392019-06-27 18:09:13 +0000830 .withPriority(MAX_PRIORITY)
ke han81a38b92017-03-10 18:41:44 +0800831 .add(new ObjectiveContext() {
832 @Override
833 public void onSuccess(Objective objective) {
Esin Karamaneff10392019-06-27 18:09:13 +0000834 log.info("Igmp filter for {} on {} {}.",
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000835 devId, port, (remove) ? REMOVED : INSTALLED);
ke han81a38b92017-03-10 18:41:44 +0800836 }
837
838 @Override
839 public void onError(Objective objective, ObjectiveError error) {
Esin Karamaneff10392019-06-27 18:09:13 +0000840 log.info("Igmp filter {} for device {} on port {} failed because of {}",
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000841 (remove) ? INSTALLATION : REMOVAL, devId, port,
842 error);
ke han81a38b92017-03-10 18:41:44 +0800843 }
844 });
845
846 flowObjectiveService.filter(devId, igmp);
Esin Karamaneff10392019-06-27 18:09:13 +0000847
ke han81a38b92017-03-10 18:41:44 +0800848 }
849
850 private boolean isConnectPoint(DeviceId device, PortNumber port) {
Deepa Vaddireddy01f33b42017-07-02 13:26:53 +0530851 if (connectPoint != null) {
852 return (connectPointMode && connectPoint.deviceId().equals(device)
853 && connectPoint.port().equals(port));
854 } else {
Andrea Campanella2c70a572020-06-05 13:31:45 +0200855 log.debug("connectPoint not configured for device {}", device);
Deepa Vaddireddy01f33b42017-07-02 13:26:53 +0530856 return false;
857 }
ke han81a38b92017-03-10 18:41:44 +0800858 }
Deepa Vaddireddy01f33b42017-07-02 13:26:53 +0530859
ke han81a38b92017-03-10 18:41:44 +0800860 private boolean isUplink(DeviceId device, PortNumber port) {
Esin Karamance5ce512020-02-25 15:58:14 +0000861 if (connectPointMode) {
862 return false;
863 }
864 Optional<PortNumber> upLinkPort = getDeviceUplink(device);
865 return upLinkPort.isPresent() && upLinkPort.get().equals(port);
ke han81a38b92017-03-10 18:41:44 +0800866 }
867
Esin Karaman00e16b72020-02-21 10:32:39 +0000868 /**
869 * Fetches device information associated with the device serial number from SADIS.
870 *
871 * @param serialNumber serial number of a device
872 * @return device information; an empty Optional otherwise.
873 */
874 private Optional<SubscriberAndDeviceInformation> getSubscriberAndDeviceInformation(String serialNumber) {
875 long start = System.currentTimeMillis();
876 try {
Ilayda Ozdemire3505102021-02-23 12:56:15 +0000877 if (sadisService == null) {
878 log.warn(SADIS_NOT_RUNNING);
879 return Optional.empty();
880 }
Esin Karaman00e16b72020-02-21 10:32:39 +0000881 return Optional.ofNullable(sadisService.getSubscriberInfoService().get(serialNumber));
882 } finally {
883 if (log.isDebugEnabled()) {
884 // SADIS can call remote systems to fetch device data and this calls can take a long time.
885 // This measurement is just for monitoring these kinds of situations.
886 log.debug("Device fetched from SADIS. Elapsed {} msec", System.currentTimeMillis() - start);
887 }
Esin Karaman00e16b72020-02-21 10:32:39 +0000888 }
889 }
890
891 /**
892 * Fetches device information associated with the device serial number from SADIS.
893 *
894 * @param deviceId device id
895 * @return device information; an empty Optional otherwise.
896 */
897 private Optional<SubscriberAndDeviceInformation> getSubscriberAndDeviceInformation(DeviceId deviceId) {
898 Device device = deviceService.getDevice(deviceId);
899 if (device == null || device.serialNumber() == null) {
900 return Optional.empty();
901 }
902 return getSubscriberAndDeviceInformation(device.serialNumber());
903 }
904
ke han81a38b92017-03-10 18:41:44 +0800905 private class InternalDeviceListener implements DeviceListener {
906 @Override
907 public void event(DeviceEvent event) {
Esin Karaman09b41e52020-06-01 10:52:55 +0000908 eventExecutor.execute(() -> {
909 DeviceId devId = event.subject().id();
910 Port p = event.port();
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +0000911
Esin Karaman09b41e52020-06-01 10:52:55 +0000912 if (!igmpLeadershipService.isLocalLeader(devId)) {
913 return;
914 }
915 if (getSubscriberAndDeviceInformation(devId).isEmpty() &&
916 !(p != null && isConnectPoint(devId, p.number()))) {
917 return;
918 }
919 PortNumber port;
ke han81a38b92017-03-10 18:41:44 +0800920
Esin Karaman09b41e52020-06-01 10:52:55 +0000921 switch (event.type()) {
922
923 case DEVICE_ADDED:
924 case DEVICE_UPDATED:
925 case DEVICE_REMOVED:
926 case DEVICE_SUSPENDED:
927 case DEVICE_AVAILABILITY_CHANGED:
928 case PORT_STATS_UPDATED:
929 break;
930 case PORT_ADDED:
931 port = p.number();
932 if (getSubscriberAndDeviceInformation(devId).isPresent() &&
933 !isUplink(devId, port) && !isConnectPoint(devId, port)) {
ke han81a38b92017-03-10 18:41:44 +0800934 processFilterObjective(devId, port, false);
Esin Karaman09b41e52020-06-01 10:52:55 +0000935 } else if (isUplink(devId, port)) {
936 provisionUplinkFlows();
937 } else if (isConnectPoint(devId, port)) {
ke han81a38b92017-03-10 18:41:44 +0800938 provisionConnectPointFlows();
ke han81a38b92017-03-10 18:41:44 +0800939 }
Esin Karaman09b41e52020-06-01 10:52:55 +0000940 onSourceStateChanged(devId, port, true);
941 break;
942 case PORT_UPDATED:
943 port = p.number();
944 if (getSubscriberAndDeviceInformation(devId).isPresent() &&
945 !isUplink(devId, port) && !isConnectPoint(devId, port)) {
946 processFilterObjective(devId, port, !event.port().isEnabled());
947 } else if (isUplink(devId, port)) {
948 if (event.port().isEnabled()) {
949 provisionUplinkFlows(devId);
950 } else {
951 processFilterObjective(devId, port, true);
952 }
953 } else if (isConnectPoint(devId, port)) {
954 if (event.port().isEnabled()) {
955 provisionConnectPointFlows();
956 } else {
957 unprovisionConnectPointFlows();
958 }
959 }
960 onSourceStateChanged(devId, port, event.port().isEnabled());
961 break;
962 case PORT_REMOVED:
963 port = p.number();
964 processFilterObjective(devId, port, true);
965 onSourceStateChanged(devId, port, false);
966 break;
967 default:
968 log.info("Unknown device event {}", event.type());
969 break;
970 }
971 });
ke han81a38b92017-03-10 18:41:44 +0800972 }
973
974 @Override
975 public boolean isRelevant(DeviceEvent event) {
976 return true;
977 }
978 }
979
Esin Karaman09b41e52020-06-01 10:52:55 +0000980 private Set<McastRoute> multicastRoutesOfIgmpProxy() {
981 Set<McastRoute> routes = new HashSet<>(); //using set to eliminate multiple entities
982 groupMemberStore.getAllGroupMemberIds().forEach(groupMemberId -> {
983 GroupMember groupMember = groupMemberStore.getGroupMember(groupMemberId);
984 if (groupMember != null) {
985 groupMember.getSourceList().forEach(source -> {
986 //regenerate the routes created by this application
987 routes.add(new McastRoute(source, groupMemberId.getGroupIp(), McastRoute.Type.IGMP));
988 });
989 }
990 });
991 return routes;
992 }
993
994 private void onSourceStateChanged(DeviceId deviceId, PortNumber portNumber, boolean enabled) {
Ilayda Ozdemire3505102021-02-23 12:56:15 +0000995 if (multicastService == null) {
996 log.warn(MCAST_NOT_RUNNING);
997 return;
998 }
Esin Karaman09b41e52020-06-01 10:52:55 +0000999 if (!(getSource().isPresent() &&
1000 getSource().get().deviceId().equals(deviceId) &&
1001 getSource().get().port().equals(portNumber))) {
1002 //connect point is not configured as the source
1003 log.debug("{}/{} is not the source cp. Stopped processing it further", deviceId, portNumber);
1004 return;
1005 }
1006 log.info("source device:port is {}. DeviceId={}, portNumber={}",
1007 (enabled ? "enabled. Restoring the source" :
1008 "disabled. Deleting it from multicast routes"), deviceId, portNumber);
1009
1010 Set<McastRoute> routes = multicastRoutesOfIgmpProxy();
1011 routes.forEach(route -> {
1012 if (enabled) {
1013 //add source to the route
1014 multicastService.addSources(route, Sets.newHashSet(new ConnectPoint(deviceId, portNumber)));
1015 } else {
1016 //remove the source from the route
1017 multicastService.removeSources(route, Sets.newHashSet(new ConnectPoint(deviceId, portNumber)));
1018 }
1019 });
1020 }
1021
ke han81a38b92017-03-10 18:41:44 +08001022 private class InternalNetworkConfigListener implements NetworkConfigListener {
1023
1024 private void reconfigureNetwork(IgmpproxyConfig cfg) {
Esin Karamaneff10392019-06-27 18:09:13 +00001025 IgmpproxyConfig newCfg = cfg == null ? new IgmpproxyConfig() : cfg;
ke han81a38b92017-03-10 18:41:44 +08001026
1027 unSolicitedTimeout = newCfg.unsolicitedTimeOut();
1028 maxResp = newCfg.maxResp();
1029 keepAliveInterval = newCfg.keepAliveInterval();
1030 keepAliveCount = newCfg.keepAliveCount();
1031 lastQueryInterval = newCfg.lastQueryInterval();
1032 lastQueryCount = newCfg.lastQueryCount();
1033 withRAUplink = newCfg.withRAUplink();
1034 withRADownlink = newCfg.withRADownlink();
1035 igmpCos = newCfg.igmpCos();
Esin Karaman586f1d62020-06-04 10:15:34 +00001036 igmpUniCos = newCfg.igmpUniCos(); // defines priority bit of IGMP query message sent to UNI ports
ke han81a38b92017-03-10 18:41:44 +08001037 periodicQuery = newCfg.periodicQuery();
1038 fastLeave = newCfg.fastLeave();
ke han29af27b2017-09-08 10:29:12 +08001039 pimSSmInterworking = newCfg.pimSsmInterworking();
Esin Karamaneff10392019-06-27 18:09:13 +00001040 enableIgmpProvisioning = newCfg.enableIgmpProvisioning();
Esin Karamanb38700c2019-09-17 13:01:25 +00001041 igmpOnPodBasis = newCfg.igmpOnPodBasis();
onurka85b2d9c2021-01-19 22:49:19 +03001042 if (numberOfIgmpReportProcessorThreads != newCfg.numberOfIgmpReportProcessorThreads()) {
1043 numberOfIgmpReportProcessorThreads = newCfg.numberOfIgmpReportProcessorThreads();
1044 shutdownIgmpReportProcessServiceExecutors();
1045 initializeIgmpReportProcessServiceExecutors();
1046 }
Arjun E Kb0018fd2020-04-07 13:26:40 +00001047 if (newCfg.outgoingIgmpWithV3() != null &&
1048 outgoingIgmpWithV3 != newCfg.outgoingIgmpWithV3()) {
1049 outgoingIgmpWithV3 = newCfg.outgoingIgmpWithV3();
1050 }
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +00001051
1052 if (connectPointMode != newCfg.connectPointMode() ||
1053 connectPoint != newCfg.connectPoint()) {
ke han81a38b92017-03-10 18:41:44 +08001054 connectPointMode = newCfg.connectPointMode();
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +00001055 connectPoint = newCfg.connectPoint();
ke han81a38b92017-03-10 18:41:44 +08001056 if (connectPointMode) {
1057 unprovisionUplinkFlows();
1058 provisionConnectPointFlows();
1059 } else {
1060 unprovisionConnectPointFlows();
1061 provisionUplinkFlows();
1062 }
1063 }
1064 if (connectPoint != null) {
Esin Karamaneff10392019-06-27 18:09:13 +00001065 log.info("connect point : {}", connectPoint);
ke han81a38b92017-03-10 18:41:44 +08001066 }
Esin Karamaneff10392019-06-27 18:09:13 +00001067 log.info("mode: {}", connectPointMode);
1068
1069 getSourceConnectPoint(newCfg);
ke han81a38b92017-03-10 18:41:44 +08001070
1071 IgmpSender.getInstance().setIgmpCos(igmpCos);
Esin Karaman586f1d62020-06-04 10:15:34 +00001072 IgmpSender.getInstance().setIgmpUniCos(igmpUniCos);
ke han81a38b92017-03-10 18:41:44 +08001073 IgmpSender.getInstance().setMaxResp(maxResp);
1074 IgmpSender.getInstance().setMvlan(mvlan);
Esin Karaman586f1d62020-06-04 10:15:34 +00001075 IgmpSender.getInstance().setMvlanInner(mvlanInner);
ke han81a38b92017-03-10 18:41:44 +08001076 IgmpSender.getInstance().setWithRADownlink(withRADownlink);
1077 IgmpSender.getInstance().setWithRAUplink(withRAUplink);
Esin Karamaneff10392019-06-27 18:09:13 +00001078 }
ke han81a38b92017-03-10 18:41:44 +08001079
Esin Karamaneff10392019-06-27 18:09:13 +00001080 void getSourceConnectPoint(IgmpproxyConfig cfg) {
Esin Karaman09b41e52020-06-01 10:52:55 +00001081 ConnectPoint oldSourceDevPort = sourceDeviceAndPort;
Esin Karamaneff10392019-06-27 18:09:13 +00001082 sourceDeviceAndPort = cfg.getSourceDeviceAndPort();
1083 if (sourceDeviceAndPort != null) {
1084 log.debug("source parameter configured to {}", sourceDeviceAndPort);
1085 }
Esin Karaman09b41e52020-06-01 10:52:55 +00001086 if (oldSourceDevPort != null && !oldSourceDevPort.equals(sourceDeviceAndPort)) {
1087 //source config has changed, remove the old source from multicast routes
1088 onSourceStateChanged(oldSourceDevPort.deviceId(), oldSourceDevPort.port(), false);
1089 }
1090 if (sourceDeviceAndPort != null && !sourceDeviceAndPort.equals(oldSourceDevPort)) {
1091 //add new source to the existing routes
1092 onSourceStateChanged(sourceDeviceAndPort.deviceId(), sourceDeviceAndPort.port(), true);
1093 }
ke han81a38b92017-03-10 18:41:44 +08001094 }
1095
1096 public void reconfigureSsmTable(IgmpproxySsmTranslateConfig cfg) {
1097 if (cfg == null) {
1098 return;
1099 }
1100 Collection<McastRoute> translations = cfg.getSsmTranslations();
1101 for (McastRoute route : translations) {
Esin Karamaneff10392019-06-27 18:09:13 +00001102 ssmTranslateTable.put(route.group().getIp4Address(), route.source().get().getIp4Address());
ke han81a38b92017-03-10 18:41:44 +08001103 }
1104 }
1105
1106 @Override
1107 public void event(NetworkConfigEvent event) {
1108 switch (event.type()) {
1109 case CONFIG_ADDED:
1110 case CONFIG_UPDATED:
Matteo Scandolo7482bbe2020-02-12 14:37:09 -08001111 // NOTE how to know if something has changed in sadis?
ke han81a38b92017-03-10 18:41:44 +08001112
1113 if (event.configClass().equals(IGMPPROXY_CONFIG_CLASS)) {
1114 IgmpproxyConfig config = networkConfig.getConfig(appId, IGMPPROXY_CONFIG_CLASS);
1115 if (config != null) {
Esin Karamaneff10392019-06-27 18:09:13 +00001116 log.info("igmpproxy config received. {}", config);
ke han81a38b92017-03-10 18:41:44 +08001117 reconfigureNetwork(config);
1118 }
1119 }
1120
1121 if (event.configClass().equals(IGMPPROXY_SSM_CONFIG_CLASS)) {
1122 IgmpproxySsmTranslateConfig config = networkConfig.getConfig(appId, IGMPPROXY_SSM_CONFIG_CLASS);
1123 if (config != null) {
1124 reconfigureSsmTable(config);
1125 }
1126 }
1127
1128 if (event.configClass().equals(MCAST_CONFIG_CLASS)) {
1129 McastConfig config = networkConfig.getConfig(coreAppId, MCAST_CONFIG_CLASS);
Esin Karaman586f1d62020-06-04 10:15:34 +00001130 boolean vlanConfigChanged = config != null && mvlan != config.egressVlan().toShort();
1131 boolean innerVlanConfigChanged = config != null &&
1132 mvlanInner != config.egressInnerVlan().toShort();
1133
1134 if (vlanConfigChanged || innerVlanConfigChanged) {
1135 log.info("igmpproxy vlan config received. {}", config);
1136 //at least one of the vlan configs has changed. Call leave before setting new values
Esin Karaman4a9075d2020-07-14 14:46:14 +00001137 groupMemberStore.getAllGroupMembers().forEach(m -> {
1138 if (igmpLeadershipService.isLocalLeader(m.getDeviceId())) {
1139 leaveAction(m);
1140 }
1141 });
Esin Karaman586f1d62020-06-04 10:15:34 +00001142 if (vlanConfigChanged) {
1143 mvlan = config.egressVlan().toShort();
1144 IgmpSender.getInstance().setMvlan(mvlan);
1145 }
1146 if (innerVlanConfigChanged) {
1147 mvlanInner = config.egressInnerVlan().toShort();
1148 IgmpSender.getInstance().setMvlanInner(mvlanInner);
1149 }
ke han81a38b92017-03-10 18:41:44 +08001150 }
1151 }
1152
1153 log.info("Reconfigured");
1154 break;
1155 case CONFIG_REGISTERED:
1156 case CONFIG_UNREGISTERED:
1157 break;
1158 case CONFIG_REMOVED:
Matteo Scandolo7482bbe2020-02-12 14:37:09 -08001159 // NOTE how to know if something has changed in sadis?
ke han81a38b92017-03-10 18:41:44 +08001160 default:
1161 break;
1162 }
1163 }
1164 }
1165
ke han81a38b92017-03-10 18:41:44 +08001166 private void provisionUplinkFlows(DeviceId deviceId) {
1167 if (connectPointMode) {
1168 return;
1169 }
1170
Esin Karaman00e16b72020-02-21 10:32:39 +00001171 Optional<PortNumber> upLink = getDeviceUplink(deviceId);
1172 if (upLink.isPresent()) {
1173 processFilterObjective(deviceId, upLink.get(), false);
1174 }
ke han81a38b92017-03-10 18:41:44 +08001175 }
1176
1177 private void provisionUplinkFlows() {
1178 if (connectPointMode) {
1179 return;
1180 }
Esin Karaman00e16b72020-02-21 10:32:39 +00001181 deviceService.getAvailableDevices().forEach(device -> {
1182 Optional<SubscriberAndDeviceInformation> accessDevice = getSubscriberAndDeviceInformation(device.id());
1183 if (accessDevice.isPresent()) {
1184 provisionUplinkFlows(device.id());
1185 }
1186 });
ke han81a38b92017-03-10 18:41:44 +08001187 }
Esin Karaman00e16b72020-02-21 10:32:39 +00001188
ke han81a38b92017-03-10 18:41:44 +08001189 private void unprovisionUplinkFlows() {
Esin Karaman00e16b72020-02-21 10:32:39 +00001190 deviceService.getAvailableDevices().forEach(device -> {
1191 Optional<SubscriberAndDeviceInformation> accessDevices = getSubscriberAndDeviceInformation(device.id());
1192 if (accessDevices.isPresent()) {
1193 Optional<PortNumber> upLink = getDeviceUplink(device.id());
1194 if (upLink.isPresent()) {
1195 processFilterObjective(device.id(), upLink.get(), true);
1196 }
1197 }
1198 });
ke han81a38b92017-03-10 18:41:44 +08001199 }
1200
1201 private void provisionConnectPointFlows() {
1202 if ((!connectPointMode) || connectPoint == null) {
1203 return;
1204 }
1205
1206 processFilterObjective(connectPoint.deviceId(), connectPoint.port(), false);
1207 }
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +00001208
ke han81a38b92017-03-10 18:41:44 +08001209 private void unprovisionConnectPointFlows() {
1210 if (connectPoint == null) {
1211 return;
1212 }
1213 processFilterObjective(connectPoint.deviceId(), connectPoint.port(), true);
1214 }
Shubham Sharma47f2caf2020-02-18 12:13:40 +00001215
onurka85b2d9c2021-01-19 22:49:19 +03001216}