blob: 5a05394b0743c0d4e5a74846b30f109bb3cd39a7 [file] [log] [blame]
David K. Bainbridged77028f2017-08-01 12:47:55 -07001/*
Joey Armstrong4509c442023-01-03 14:05:28 -05002 * Copyright 2017-2023 Open Networking Foundation (ONF) and the ONF Contributors
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;
Andrea Campanellae5beaf32021-03-12 10:59:42 +0100139 //This is the uplink connect point
ke han81a38b92017-03-10 18:41:44 +0800140 public static ConnectPoint connectPoint = null;
Esin Karamaneff10392019-06-27 18:09:13 +0000141 private static ConnectPoint sourceDeviceAndPort = null;
142 private static boolean enableIgmpProvisioning = false;
Esin Karamanb38700c2019-09-17 13:01:25 +0000143 private static boolean igmpOnPodBasis = false;
Arjun E Kb0018fd2020-04-07 13:26:40 +0000144 private static boolean outgoingIgmpWithV3 = true;
Esin Karamaneff10392019-06-27 18:09:13 +0000145
146 private static final Integer MAX_PRIORITY = 10000;
147 private static final String INSTALLED = "installed";
148 private static final String REMOVED = "removed";
149 private static final String INSTALLATION = "installation";
150 private static final String REMOVAL = "removal";
Esin Karaman00e16b72020-02-21 10:32:39 +0000151 private static final String NNI_PREFIX = "nni";
ke han81a38b92017-03-10 18:41:44 +0800152
ke han29af27b2017-09-08 10:29:12 +0800153 private static boolean pimSSmInterworking = false;
154 private static final String DEFAULT_PIMSSM_HOST = "127.0.0.1";
ke han81a38b92017-03-10 18:41:44 +0800155 private final ScheduledExecutorService scheduledExecutorService =
156 Executors.newScheduledThreadPool(1);
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800157
Carmelo Casconebef302e2019-11-14 19:58:20 -0800158 @Reference(cardinality = ReferenceCardinality.MANDATORY)
ke han81a38b92017-03-10 18:41:44 +0800159 protected CoreService coreService;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800160
Carmelo Casconebef302e2019-11-14 19:58:20 -0800161 @Reference(cardinality = ReferenceCardinality.MANDATORY)
ke han81a38b92017-03-10 18:41:44 +0800162 protected PacketService packetService;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800163
Carmelo Casconebef302e2019-11-14 19:58:20 -0800164 @Reference(cardinality = ReferenceCardinality.MANDATORY)
ke han81a38b92017-03-10 18:41:44 +0800165 protected MastershipService mastershipService;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800166
Carmelo Casconebef302e2019-11-14 19:58:20 -0800167 @Reference(cardinality = ReferenceCardinality.MANDATORY)
ke han81a38b92017-03-10 18:41:44 +0800168 protected FlowRuleService flowRuleService;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800169
Carmelo Casconebef302e2019-11-14 19:58:20 -0800170 @Reference(cardinality = ReferenceCardinality.MANDATORY)
ke han81a38b92017-03-10 18:41:44 +0800171 protected DeviceService deviceService;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800172
Carmelo Casconebef302e2019-11-14 19:58:20 -0800173 @Reference(cardinality = ReferenceCardinality.MANDATORY)
ke han81a38b92017-03-10 18:41:44 +0800174 protected FlowObjectiveService flowObjectiveService;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800175
Carmelo Casconebef302e2019-11-14 19:58:20 -0800176 @Reference(cardinality = ReferenceCardinality.MANDATORY)
ke han81a38b92017-03-10 18:41:44 +0800177 protected NetworkConfigRegistry networkConfig;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800178
Ilayda Ozdemire3505102021-02-23 12:56:15 +0000179 @Reference(cardinality = ReferenceCardinality.OPTIONAL,
180 bind = "bindMcastRouteService",
181 unbind = "unbindMcastRouteService",
182 policy = ReferencePolicy.DYNAMIC)
183 protected volatile MulticastRouteService multicastService;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800184
Ilayda Ozdemire3505102021-02-23 12:56:15 +0000185 @Reference(cardinality = ReferenceCardinality.OPTIONAL,
186 bind = "bindSadisService",
187 unbind = "unbindSadisService",
188 policy = ReferencePolicy.DYNAMIC)
189 protected volatile SadisService sadisService;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800190
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000191 @Reference(cardinality = ReferenceCardinality.MANDATORY)
192 protected IgmpStatisticsService igmpStatisticsManager;
193
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000194 @Reference(cardinality = ReferenceCardinality.MANDATORY)
195 protected GroupMemberStore groupMemberStore;
196
197 @Reference(cardinality = ReferenceCardinality.MANDATORY)
198 protected StateMachineService stateMachineService;
199
200 @Reference(cardinality = ReferenceCardinality.MANDATORY)
201 protected IgmpLeadershipService igmpLeadershipService;
202
ke han81a38b92017-03-10 18:41:44 +0800203 private IgmpPacketProcessor processor = new IgmpPacketProcessor();
204 private Logger log = LoggerFactory.getLogger(getClass());
205 private ApplicationId coreAppId;
206 private Map<Ip4Address, Ip4Address> ssmTranslateTable = new ConcurrentHashMap<>();
Esin Karamaneff10392019-06-27 18:09:13 +0000207
ke han81a38b92017-03-10 18:41:44 +0800208 private InternalNetworkConfigListener configListener =
209 new InternalNetworkConfigListener();
210 private DeviceListener deviceListener = new InternalDeviceListener();
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800211
ke han81a38b92017-03-10 18:41:44 +0800212 private ConfigFactory<ApplicationId, IgmpproxyConfig> igmpproxyConfigFactory =
213 new ConfigFactory<ApplicationId, IgmpproxyConfig>(
214 SubjectFactories.APP_SUBJECT_FACTORY, IGMPPROXY_CONFIG_CLASS, "igmpproxy") {
215 @Override
216 public IgmpproxyConfig createConfig() {
217 return new IgmpproxyConfig();
218 }
219 };
220 private ConfigFactory<ApplicationId, IgmpproxySsmTranslateConfig> igmpproxySsmConfigFactory =
221 new ConfigFactory<ApplicationId, IgmpproxySsmTranslateConfig>(
222 SubjectFactories.APP_SUBJECT_FACTORY, IGMPPROXY_SSM_CONFIG_CLASS, "ssmTranslate", true) {
223 @Override
224 public IgmpproxySsmTranslateConfig createConfig() {
225 return new IgmpproxySsmTranslateConfig();
226 }
227 };
Esin Karamaneff10392019-06-27 18:09:13 +0000228
ke han81a38b92017-03-10 18:41:44 +0800229 private int maxResp = 10; //unit is 1 sec
230 private int keepAliveInterval = 120; //unit is 1 sec
231
onurka85b2d9c2021-01-19 22:49:19 +0300232 private int numberOfIgmpReportProcessorThreads = 20;
233 ExecutorService[] igmpReportProcessServiceExecutorList;
234
Esin Karamanb38700c2019-09-17 13:01:25 +0000235 private ExecutorService eventExecutor;
236
ke han81a38b92017-03-10 18:41:44 +0800237 public static int getUnsolicitedTimeout() {
238 return unSolicitedTimeout;
239 }
240
Arjun E Kb0018fd2020-04-07 13:26:40 +0000241 public static boolean outgoingIgmpWithV3() {
242 return outgoingIgmpWithV3;
243 }
244
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000245 private List<Byte> validMembershipModes = Arrays.asList(MODE_IS_INCLUDE, MODE_IS_EXCLUDE, CHANGE_TO_INCLUDE_MODE,
246 CHANGE_TO_EXCLUDE_MODE, ALLOW_NEW_SOURCES, BLOCK_OLD_SOURCES);
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000247
ke han81a38b92017-03-10 18:41:44 +0800248 @Activate
249 protected void activate() {
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800250 appId = coreService.registerApplication(APP_NAME);
ke han81a38b92017-03-10 18:41:44 +0800251 coreAppId = coreService.registerApplication(CoreService.CORE_APP_NAME);
252 packetService.addProcessor(processor, PacketProcessor.director(4));
Esin Karaman4a9075d2020-07-14 14:46:14 +0000253 IgmpSender.init(packetService, igmpStatisticsManager);
ke han81a38b92017-03-10 18:41:44 +0800254
ke han81a38b92017-03-10 18:41:44 +0800255 networkConfig.registerConfigFactory(igmpproxySsmConfigFactory);
256 networkConfig.registerConfigFactory(igmpproxyConfigFactory);
257 networkConfig.addListener(configListener);
258
259 configListener.reconfigureNetwork(networkConfig.getConfig(appId, IGMPPROXY_CONFIG_CLASS));
260 configListener.reconfigureSsmTable(networkConfig.getConfig(appId, IGMPPROXY_SSM_CONFIG_CLASS));
261
ke han81a38b92017-03-10 18:41:44 +0800262 if (connectPointMode) {
263 provisionConnectPointFlows();
264 } else {
265 provisionUplinkFlows();
266 }
267
268 McastConfig config = networkConfig.getConfig(coreAppId, MCAST_CONFIG_CLASS);
269 if (config != null) {
270 mvlan = config.egressVlan().toShort();
Deepa Vaddireddy88134a42017-07-05 12:16:03 +0530271 IgmpSender.getInstance().setMvlan(mvlan);
Esin Karaman586f1d62020-06-04 10:15:34 +0000272 mvlanInner = config.egressInnerVlan().toShort();
273 IgmpSender.getInstance().setMvlanInner(mvlanInner);
ke han81a38b92017-03-10 18:41:44 +0800274 }
275 deviceService.addListener(deviceListener);
Sonal Kasliwalddc3ff22019-11-18 11:52:49 +0000276 scheduledExecutorService.scheduleAtFixedRate(new IgmpProxyTimerTask(), 1000, 1000, TimeUnit.MILLISECONDS);
onurka85b2d9c2021-01-19 22:49:19 +0300277 eventExecutor = Executors.newSingleThreadScheduledExecutor(groupedThreads("cord/igmpproxy",
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000278 "events-igmp-%d", log));
onurka85b2d9c2021-01-19 22:49:19 +0300279 initializeIgmpReportProcessServiceExecutors();
ke han81a38b92017-03-10 18:41:44 +0800280 log.info("Started");
281 }
282
283 @Deactivate
284 protected void deactivate() {
285 scheduledExecutorService.shutdown();
Esin Karamanb38700c2019-09-17 13:01:25 +0000286 eventExecutor.shutdown();
onurka85b2d9c2021-01-19 22:49:19 +0300287 shutdownIgmpReportProcessServiceExecutors();
ke han81a38b92017-03-10 18:41:44 +0800288
289 // de-register and null our handler
290 networkConfig.removeListener(configListener);
ke han81a38b92017-03-10 18:41:44 +0800291 networkConfig.unregisterConfigFactory(igmpproxyConfigFactory);
292 networkConfig.unregisterConfigFactory(igmpproxySsmConfigFactory);
293 deviceService.removeListener(deviceListener);
294 packetService.removeProcessor(processor);
ke han81a38b92017-03-10 18:41:44 +0800295 log.info("Stopped");
296 }
297
onurka85b2d9c2021-01-19 22:49:19 +0300298 private void initializeIgmpReportProcessServiceExecutors() {
299 igmpReportProcessServiceExecutorList = new ExecutorService[numberOfIgmpReportProcessorThreads];
300 for (int i = 0; i < numberOfIgmpReportProcessorThreads; i++) {
301 ThreadFactory igmpReportProcessorThreadFactory =
302 new ThreadFactoryBuilder().setNameFormat("report-processor-igmp-" + i)
303 .setUncaughtExceptionHandler((t, e) ->
304 log.error("Uncaught exception on {}: ", t.getName(), e))
305 .build();
306 ExecutorService igmpReportProcessServiceExecutor = Executors.newSingleThreadExecutor(
307 igmpReportProcessorThreadFactory);
308 igmpReportProcessServiceExecutorList[i] = igmpReportProcessServiceExecutor;
309 }
310 }
Ilayda Ozdemire3505102021-02-23 12:56:15 +0000311
onurka85b2d9c2021-01-19 22:49:19 +0300312 private void shutdownIgmpReportProcessServiceExecutors() {
313 for (ExecutorService executor : igmpReportProcessServiceExecutorList) {
314 executor.shutdown();
315 }
316 }
317
Ilayda Ozdemire3505102021-02-23 12:56:15 +0000318 protected void bindSadisService(SadisService service) {
319 sadisService = service;
320 log.info("Sadis-service binds to onos.");
321 }
322
323 protected void unbindSadisService(SadisService service) {
324 sadisService = null;
325 log.info("Sadis-service unbinds from onos.");
326 }
327
328 protected void bindMcastRouteService(MulticastRouteService service) {
329 multicastService = service;
330 log.info("Multicast route service binds to onos.");
331 }
332
333 protected void unbindMcastRouteService(MulticastRouteService service) {
334 multicastService = null;
335 log.info("Multicast route service unbinds from onos.");
336 }
337
ke han81a38b92017-03-10 18:41:44 +0800338 protected Ip4Address getDeviceIp(DeviceId ofDeviceId) {
339 try {
340 String[] mgmtAddress = deviceService.getDevice(ofDeviceId)
341 .annotations().value(AnnotationKeys.MANAGEMENT_ADDRESS).split(":");
342 return Ip4Address.valueOf(mgmtAddress[0]);
343 } catch (Exception ex) {
onurka85b2d9c2021-01-19 22:49:19 +0300344 log.info("No valid Ipaddress for {}", ofDeviceId);
ke han81a38b92017-03-10 18:41:44 +0800345 return null;
346 }
347 }
348
349 private void processIgmpQuery(IGMPQuery igmpQuery, ConnectPoint cp, int maxResp) {
350
351 DeviceId deviceId = cp.deviceId();
352 Ip4Address gAddr = igmpQuery.getGaddr().getIp4Address();
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000353 maxResp = calculateMaxResp(maxResp);
354 if (gAddr != null && !gAddr.isZero()) {
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000355 stateMachineService.specialQuery(deviceId, gAddr, maxResp);
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300356 igmpStatisticsManager.increaseStat(IgmpStatisticType.IGMP_GRP_SPECIFIC_MEMBERSHIP_QUERY);
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000357 } else {
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000358 stateMachineService.generalQuery(deviceId, maxResp);
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300359 igmpStatisticsManager.increaseStat(IgmpStatisticType.IGMP_GENERAL_MEMBERSHIP_QUERY);
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000360 }
361 }
ke han81a38b92017-03-10 18:41:44 +0800362
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000363 private void processIgmpConnectPointQuery(IGMPQuery igmpQuery, ConnectPoint cp, int maxResp) {
364
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000365 Ip4Address gAddr = igmpQuery.getGaddr().getIp4Address();
Esin Karaman00e16b72020-02-21 10:32:39 +0000366 final int maxResponseTime = calculateMaxResp(maxResp);
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000367 //The query is received on the ConnectPoint
368 // send query accordingly to the registered OLT devices.
369 if (gAddr != null && !gAddr.isZero()) {
Esin Karaman00e16b72020-02-21 10:32:39 +0000370 deviceService.getAvailableDevices().forEach(device -> {
371 Optional<SubscriberAndDeviceInformation> accessDevice = getSubscriberAndDeviceInformation(device.id());
372 if (accessDevice.isPresent()) {
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000373 stateMachineService.specialQuery(device.id(), gAddr, maxResponseTime);
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300374 igmpStatisticsManager.increaseStat(IgmpStatisticType.IGMP_GRP_AND_SRC_SPESIFIC_MEMBERSHIP_QUERY);
Esin Karaman00e16b72020-02-21 10:32:39 +0000375 }
376 });
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300377 igmpStatisticsManager.increaseStat(IgmpStatisticType.CURRENT_GRP_NUMBER_COUNTER);
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000378 } else {
379 //Don't know which group is targeted by the query
380 //So query all the members(in all the OLTs) and proxy their reports
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000381 stateMachineService.generalQuery(maxResponseTime);
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300382 igmpStatisticsManager.increaseStat(IgmpStatisticType.IGMP_GENERAL_MEMBERSHIP_QUERY);
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000383 }
384 }
385
386
387 private int calculateMaxResp(int maxResp) {
ke han81a38b92017-03-10 18:41:44 +0800388 if (maxResp >= 128) {
389 int mant = maxResp & 0xf;
390 int exp = (maxResp >> 4) & 0x7;
391 maxResp = (mant | 0x10) << (exp + 3);
392 }
393
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000394 return (maxResp + 5) / 10;
ke han81a38b92017-03-10 18:41:44 +0800395 }
396
onurka85b2d9c2021-01-19 22:49:19 +0300397 private void queueIgmpReport(IGMPMembership igmpGroup, VlanId vlan, ConnectPoint cp, byte igmpType) {
Andrea Campanellae5beaf32021-03-12 10:59:42 +0100398 int packetHashCode = Objects.hash(igmpGroup.getGaddr(), cp);
onurka85b2d9c2021-01-19 22:49:19 +0300399 int threadId = Math.abs(packetHashCode % numberOfIgmpReportProcessorThreads);
400 log.debug("IGMP report for ConnectPoint {} and group IP {} shall be processed in thread #{}",
401 cp, igmpGroup.getGaddr(), threadId);
402
403 igmpReportProcessServiceExecutorList[threadId].execute(
404 () -> processIgmpReport(igmpGroup, vlan, cp, igmpType));
405 }
406
ke han81a38b92017-03-10 18:41:44 +0800407 private Ip4Address ssmTranslateRoute(IpAddress group) {
408 return ssmTranslateTable.get(group);
409 }
410
411 private void processIgmpReport(IGMPMembership igmpGroup, VlanId vlan, ConnectPoint cp, byte igmpType) {
Ilayda Ozdemire3505102021-02-23 12:56:15 +0000412 if (multicastService == null) {
413 log.warn(MCAST_NOT_RUNNING);
414 return;
415 }
416
ke han81a38b92017-03-10 18:41:44 +0800417 DeviceId deviceId = cp.deviceId();
418 PortNumber portNumber = cp.port();
419
Andrea Campanella2c70a572020-06-05 13:31:45 +0200420 log.debug("Processing IGMP report on membership {} for vlan {} on port {} with type {}",
onurka85b2d9c2021-01-19 22:49:19 +0300421 igmpGroup, vlan, cp, igmpType);
Andrea Campanella2c70a572020-06-05 13:31:45 +0200422
ke han81a38b92017-03-10 18:41:44 +0800423 Ip4Address groupIp = igmpGroup.getGaddr().getIp4Address();
424 if (!groupIp.isMulticast()) {
onurka85b2d9c2021-01-19 22:49:19 +0300425 log.info("{} is not a valid group address", groupIp);
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300426 igmpStatisticsManager.increaseStat(IgmpStatisticType.FAIL_JOIN_REQ_UNKNOWN_MULTICAST_IP_COUNTER);
ke han81a38b92017-03-10 18:41:44 +0800427 return;
428 }
429 Ip4Address srcIp = getDeviceIp(deviceId);
430
431 byte recordType = igmpGroup.getRecordType();
432 boolean join = false;
433
434 ArrayList<Ip4Address> sourceList = new ArrayList<>();
435
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000436 if (!validMembershipModes.contains(recordType)) {
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300437 igmpStatisticsManager.increaseStat(IgmpStatisticType.REPORTS_RX_WITH_WRONG_MODE_COUNTER);
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000438 }
ke han81a38b92017-03-10 18:41:44 +0800439 if (igmpGroup.getSources().size() > 0) {
440 igmpGroup.getSources().forEach(source -> sourceList.add(source.getIp4Address()));
441 if (recordType == IGMPMembership.CHANGE_TO_EXCLUDE_MODE ||
442 recordType == IGMPMembership.MODE_IS_EXCLUDE ||
443 recordType == IGMPMembership.BLOCK_OLD_SOURCES) {
444 join = false;
445 } else if (recordType == IGMPMembership.CHANGE_TO_INCLUDE_MODE ||
446 recordType == IGMPMembership.MODE_IS_INCLUDE ||
447 recordType == IGMPMembership.ALLOW_NEW_SOURCES) {
448 join = true;
449 }
450 } else {
ke han29af27b2017-09-08 10:29:12 +0800451 IpAddress src = null;
452 if (pimSSmInterworking) {
453 src = ssmTranslateRoute(groupIp);
454 if (src == null) {
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000455 log.info("no ssm translate for group {}", groupIp);
ke han29af27b2017-09-08 10:29:12 +0800456 return;
457 }
458 } else {
459 src = IpAddress.valueOf(DEFAULT_PIMSSM_HOST);
ke han81a38b92017-03-10 18:41:44 +0800460 }
461 sourceList.add(src.getIp4Address());
462 if (recordType == IGMPMembership.CHANGE_TO_EXCLUDE_MODE ||
463 recordType == IGMPMembership.MODE_IS_EXCLUDE ||
464 recordType == IGMPMembership.BLOCK_OLD_SOURCES) {
465 join = true;
466 } else if (recordType == IGMPMembership.CHANGE_TO_INCLUDE_MODE ||
467 recordType == IGMPMembership.MODE_IS_INCLUDE ||
468 recordType == IGMPMembership.ALLOW_NEW_SOURCES) {
469 join = false;
470 }
471 }
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000472 GroupMemberId groupMemberKey = GroupMemberId.of(groupIp, deviceId, portNumber);
onurka85b2d9c2021-01-19 22:49:19 +0300473 log.debug("{} for {}", join ? "Join" : "Leave", groupMemberKey);
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000474 GroupMember groupMember = groupMemberStore.getGroupMember(groupMemberKey);
ke han81a38b92017-03-10 18:41:44 +0800475
476 if (join) {
Andrea Campanella2c70a572020-06-05 13:31:45 +0200477 log.debug("Received join on {} for vlan {}", cp, vlan);
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300478 igmpStatisticsManager.increaseStat(IgmpStatisticType.IGMP_JOIN_REQ);
Andrea Campanella2c70a572020-06-05 13:31:45 +0200479
ke han81a38b92017-03-10 18:41:44 +0800480 if (groupMember == null) {
Esin Karamaneff10392019-06-27 18:09:13 +0000481 Optional<ConnectPoint> sourceConfigured = getSource();
482 if (!sourceConfigured.isPresent()) {
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300483 igmpStatisticsManager.increaseStat(IgmpStatisticType.IGMP_FAIL_JOIN_REQ);
Esin Karamaneff10392019-06-27 18:09:13 +0000484 log.warn("Unable to process IGMP Join from {} since no source " +
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000485 "configuration is found.", deviceId);
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300486 igmpStatisticsManager
487 .increaseStat(IgmpStatisticType.FAIL_JOIN_REQ_INSUFF_PERMISSION_ACCESS_COUNTER);
Esin Karamaneff10392019-06-27 18:09:13 +0000488 return;
489 }
Esin Karaman00e16b72020-02-21 10:32:39 +0000490
491 Optional<PortNumber> deviceUplink = getDeviceUplink(deviceId);
492 if (deviceUplink.isEmpty()) {
493 log.warn("Unable to process IGMP Join since uplink port " +
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000494 "of the device {} is not found.", deviceId);
Esin Karaman00e16b72020-02-21 10:32:39 +0000495 return;
496 }
497
498 if (igmpType == IGMP.TYPE_IGMPV2_MEMBERSHIP_REPORT) {
499 groupMember = new GroupMember(groupIp, vlan, deviceId, portNumber, true);
500 } else {
501 groupMember = new GroupMember(groupIp, vlan, deviceId, portNumber, false);
502 }
503
Esin Karamaneff10392019-06-27 18:09:13 +0000504 HashSet<ConnectPoint> sourceConnectPoints = Sets.newHashSet(sourceConfigured.get());
505
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000506 boolean isJoined = stateMachineService.join(deviceId, groupIp, srcIp, deviceUplink.get());
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000507 if (isJoined) {
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300508 igmpStatisticsManager.increaseStat(IgmpStatisticType.IGMP_SUCCESS_JOIN_RE_JOIN_REQ);
509 igmpStatisticsManager.increaseStat(IgmpStatisticType.IGMP_CHANNEL_JOIN_COUNTER);
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000510 } else {
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300511 igmpStatisticsManager.increaseStat(IgmpStatisticType.IGMP_FAIL_JOIN_REQ);
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000512 }
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000513 groupMemberStore.putGroupMember(groupMember);
514 log.debug("Group member created with id: {}", groupMember.getGroupMemberId());
ke han81a38b92017-03-10 18:41:44 +0800515 groupMember.updateList(recordType, sourceList);
Esin Karamaneff10392019-06-27 18:09:13 +0000516 groupMember.getSourceList().forEach(source -> {
517 McastRoute route = new McastRoute(source, groupIp, McastRoute.Type.IGMP);
518 //add route
519 multicastService.add(route);
520 //add source to the route
521 multicastService.addSources(route, Sets.newHashSet(sourceConnectPoints));
522 //add sink to the route
523 multicastService.addSinks(route, Sets.newHashSet(cp));
524 });
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300525 igmpStatisticsManager.increaseStat(IgmpStatisticType.UNCONFIGURED_GROUP_COUNTER);
Esin Karamaneff10392019-06-27 18:09:13 +0000526
ke han81a38b92017-03-10 18:41:44 +0800527 }
528 groupMember.resetAllTimers();
529 groupMember.updateList(recordType, sourceList);
530 groupMember.setLeave(false);
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000531 //put updated member to the store
532 groupMemberStore.putGroupMember(groupMember);
ke han81a38b92017-03-10 18:41:44 +0800533 } else {
Andrea Campanella2c70a572020-06-05 13:31:45 +0200534 log.debug("Received leave on {} for vlan {}", cp, vlan);
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300535 igmpStatisticsManager.increaseStat(IgmpStatisticType.IGMP_LEAVE_REQ);
ke han81a38b92017-03-10 18:41:44 +0800536 if (groupMember == null) {
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000537 log.info("receive leave but no instance, group {} device: {} port:{}",
538 groupIp, deviceId, portNumber);
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300539 igmpStatisticsManager.increaseStat(IgmpStatisticType.UNCONFIGURED_GROUP_COUNTER);
ke han81a38b92017-03-10 18:41:44 +0800540 return;
541 } else {
542 groupMember.setLeave(true);
543 if (fastLeave) {
544 leaveAction(groupMember);
545 } else {
546 sendQuery(groupMember);
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000547 //put modified group member object to the store
548 groupMemberStore.updateGroupMember(groupMember);
ke han81a38b92017-03-10 18:41:44 +0800549 }
550 }
551 }
552 }
553
554 private void leaveAction(GroupMember groupMember) {
Ilayda Ozdemire3505102021-02-23 12:56:15 +0000555 if (multicastService == null) {
556 log.warn(MCAST_NOT_RUNNING);
557 return;
558 }
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300559 igmpStatisticsManager.increaseStat(IgmpStatisticType.IGMP_DISCONNECT);
ke han81a38b92017-03-10 18:41:44 +0800560 ConnectPoint cp = new ConnectPoint(groupMember.getDeviceId(), groupMember.getPortNumber());
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000561 stateMachineService.leave(groupMember.getDeviceId(), groupMember.getGroupIp());
Esin Karamaneff10392019-06-27 18:09:13 +0000562 groupMember.getSourceList().forEach(source -> multicastService.removeSinks(
ke han81a38b92017-03-10 18:41:44 +0800563 new McastRoute(source, groupMember.getGroupIp(),
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000564 McastRoute.Type.IGMP), Sets.newHashSet(cp)));
565 groupMemberStore.removeGroupMember(groupMember.getGroupMemberId());
ke han81a38b92017-03-10 18:41:44 +0800566 }
567
568 private void sendQuery(GroupMember groupMember) {
569 Ethernet ethpkt;
570 Ip4Address srcIp = getDeviceIp(groupMember.getDeviceId());
571 if (groupMember.getv2()) {
Esin Karaman586f1d62020-06-04 10:15:34 +0000572 ethpkt = IgmpSender.getInstance().buildIgmpV2Query(groupMember.getGroupIp(),
573 srcIp, groupMember.getvlan().toShort());
ke han81a38b92017-03-10 18:41:44 +0800574 } else {
Esin Karaman586f1d62020-06-04 10:15:34 +0000575 ethpkt = IgmpSender.getInstance().buildIgmpV3Query(groupMember.getGroupIp(),
576 srcIp, groupMember.getvlan().toShort());
ke han81a38b92017-03-10 18:41:44 +0800577 }
Esin Karaman586f1d62020-06-04 10:15:34 +0000578 log.debug("Sending IGMP query to {}/{} for group {}: {}",
579 groupMember.getDeviceId(), groupMember.getPortNumber(), groupMember.getGroupIp(), ethpkt);
ke han81a38b92017-03-10 18:41:44 +0800580 IgmpSender.getInstance().sendIgmpPacket(ethpkt, groupMember.getDeviceId(), groupMember.getPortNumber());
581 }
582
Esin Karamaneff10392019-06-27 18:09:13 +0000583 /**
584 * @return connect point of the source if configured; and empty Optional otherwise.
585 */
586 public static Optional<ConnectPoint> getSource() {
587 return sourceDeviceAndPort == null ? Optional.empty() :
588 Optional.of(sourceDeviceAndPort);
ke han81a38b92017-03-10 18:41:44 +0800589 }
590
591 /**
592 * Packet processor responsible for forwarding packets along their paths.
593 */
594 private class IgmpPacketProcessor implements PacketProcessor {
onurka85b2d9c2021-01-19 22:49:19 +0300595
ke han81a38b92017-03-10 18:41:44 +0800596 @Override
597 public void process(PacketContext context) {
Esin Karamanb38700c2019-09-17 13:01:25 +0000598 eventExecutor.execute(() -> {
599 try {
600 InboundPacket pkt = context.inPacket();
onurka85b2d9c2021-01-19 22:49:19 +0300601 log.debug("IgmpPacketProcessor shall process InboundPacket: {}", pkt);
Esin Karamanb38700c2019-09-17 13:01:25 +0000602 Ethernet ethPkt = pkt.parsed();
603 if (ethPkt == null) {
604 return;
605 }
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300606 igmpStatisticsManager.increaseStat(IgmpStatisticType.TOTAL_MSG_RECEIVED);
ke han81a38b92017-03-10 18:41:44 +0800607
Esin Karamanb38700c2019-09-17 13:01:25 +0000608 if (ethPkt.getEtherType() != Ethernet.TYPE_IPV4) {
609 return;
610 }
ke han81a38b92017-03-10 18:41:44 +0800611
Esin Karamanb38700c2019-09-17 13:01:25 +0000612 IPv4 ipv4Pkt = (IPv4) ethPkt.getPayload();
ke han81a38b92017-03-10 18:41:44 +0800613
Esin Karamanb38700c2019-09-17 13:01:25 +0000614 if (ipv4Pkt.getProtocol() != IPv4.PROTOCOL_IGMP) {
615 return;
616 }
ke han81a38b92017-03-10 18:41:44 +0800617
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300618 igmpStatisticsManager.increaseStat(IgmpStatisticType.IGMP_VALID_CHECKSUM_COUNTER);
Esin Karamanb38700c2019-09-17 13:01:25 +0000619 short vlan = ethPkt.getVlanID();
620 DeviceId deviceId = pkt.receivedFrom().deviceId();
ke han81a38b92017-03-10 18:41:44 +0800621
Esin Karaman00e16b72020-02-21 10:32:39 +0000622 if (!isConnectPoint(deviceId, pkt.receivedFrom().port()) &&
623 !getSubscriberAndDeviceInformation(deviceId).isPresent()) {
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000624 log.error("Device not registered in netcfg : {}", deviceId);
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300625 igmpStatisticsManager
626 .increaseStat(IgmpStatisticType.FAIL_JOIN_REQ_INSUFF_PERMISSION_ACCESS_COUNTER);
Esin Karamanb38700c2019-09-17 13:01:25 +0000627 return;
628 }
ke han81a38b92017-03-10 18:41:44 +0800629
Esin Karamanb38700c2019-09-17 13:01:25 +0000630 IGMP igmp = (IGMP) ipv4Pkt.getPayload();
Esin Karamance5ce512020-02-25 15:58:14 +0000631
632 Optional<PortNumber> deviceUpLinkOpt = getDeviceUplink(deviceId);
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000633 PortNumber upLinkPort = deviceUpLinkOpt.isPresent() ? deviceUpLinkOpt.get() : null;
Esin Karamanb38700c2019-09-17 13:01:25 +0000634 switch (igmp.getIgmpType()) {
635 case IGMP.TYPE_IGMPV3_MEMBERSHIP_QUERY:
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300636 igmpStatisticsManager.increaseStat(IgmpStatisticType.IGMP_V3_MEMBERSHIP_QUERY);
Esin Karamanb38700c2019-09-17 13:01:25 +0000637 //Discard Query from OLT’s non-uplink port’s
Esin Karamance5ce512020-02-25 15:58:14 +0000638 if (!pkt.receivedFrom().port().equals(upLinkPort)) {
Esin Karamanb38700c2019-09-17 13:01:25 +0000639 if (isConnectPoint(deviceId, pkt.receivedFrom().port())) {
640 log.info("IGMP Picked up query from connectPoint");
641 //OK to process packet
642 processIgmpConnectPointQuery((IGMPQuery) igmp.getGroups().get(0),
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000643 pkt.receivedFrom(),
644 0xff & igmp.getMaxRespField());
Esin Karamanb38700c2019-09-17 13:01:25 +0000645 break;
646 } else {
647 //Not OK to process packet
Esin Karamance5ce512020-02-25 15:58:14 +0000648 log.warn("IGMP Picked up query from non-uplink port {}", upLinkPort);
Esin Karamanb38700c2019-09-17 13:01:25 +0000649 return;
650 }
651 }
ke han81a38b92017-03-10 18:41:44 +0800652
Esin Karamanb38700c2019-09-17 13:01:25 +0000653 processIgmpQuery((IGMPQuery) igmp.getGroups().get(0), pkt.receivedFrom(),
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000654 0xff & igmp.getMaxRespField());
Esin Karamanb38700c2019-09-17 13:01:25 +0000655 break;
656 case IGMP.TYPE_IGMPV1_MEMBERSHIP_REPORT:
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300657 igmpStatisticsManager.increaseStat(IgmpStatisticType.IGMP_V1_MEMBERSHIP_REPORT);
Esin Karamanb38700c2019-09-17 13:01:25 +0000658 log.debug("IGMP version 1 message types are not currently supported.");
659 break;
660 case IGMP.TYPE_IGMPV3_MEMBERSHIP_REPORT:
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300661 igmpStatisticsManager.increaseStat(IgmpStatisticType.IGMP_V3_MEMBERSHIP_REPORT);
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000662 processIgmpMessage(pkt, igmp, upLinkPort, vlan);
663 break;
Esin Karamanb38700c2019-09-17 13:01:25 +0000664 case IGMP.TYPE_IGMPV2_MEMBERSHIP_REPORT:
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300665 igmpStatisticsManager.increaseStat(IgmpStatisticType.IGMP_V2_MEMBERSHIP_REPORT);
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000666 processIgmpMessage(pkt, igmp, upLinkPort, vlan);
667 break;
Esin Karamanb38700c2019-09-17 13:01:25 +0000668 case IGMP.TYPE_IGMPV2_LEAVE_GROUP:
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300669 igmpStatisticsManager.increaseStat(IgmpStatisticType.IGMP_V2_LEAVE_GROUP);
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000670 processIgmpMessage(pkt, igmp, upLinkPort, vlan);
Esin Karamanb38700c2019-09-17 13:01:25 +0000671 break;
ke han81a38b92017-03-10 18:41:44 +0800672
Esin Karamanb38700c2019-09-17 13:01:25 +0000673 default:
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000674 log.warn("Unknown IGMP message type: {}", igmp.getIgmpType());
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300675 igmpStatisticsManager.increaseStat(IgmpStatisticType.INVALID_IGMP_MSG_RECEIVED);
676 igmpStatisticsManager.increaseStat(IgmpStatisticType.UNKNOWN_IGMP_TYPE_PACKETS_RX_COUNTER);
Esin Karamanb38700c2019-09-17 13:01:25 +0000677 break;
678 }
679
680 } catch (Exception ex) {
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000681 log.error("igmp process error : ", ex);
ke han81a38b92017-03-10 18:41:44 +0800682 }
Esin Karamanb38700c2019-09-17 13:01:25 +0000683 });
ke han81a38b92017-03-10 18:41:44 +0800684 }
685 }
686
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000687 private void processIgmpMessage(InboundPacket pkt, IGMP igmp, PortNumber upLinkPort, short vlan) {
688 //Discard join/leave from OLT’s uplink port’s
689 if (pkt.receivedFrom().port().equals(upLinkPort) ||
690 isConnectPoint(pkt.receivedFrom().deviceId(), pkt.receivedFrom().port())) {
691 log.info("IGMP Picked up join/leave from uplink/connectPoint port");
692 return;
693 }
694
695 Iterator<IGMPGroup> itr = igmp.getGroups().iterator();
696 while (itr.hasNext()) {
697 IGMPGroup group = itr.next();
onurka85b2d9c2021-01-19 22:49:19 +0300698 log.debug("IGMPGroup {}", group.getGaddr());
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000699 if (group instanceof IGMPMembership) {
onurka85b2d9c2021-01-19 22:49:19 +0300700 queueIgmpReport((IGMPMembership) group, VlanId.vlanId(vlan),
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000701 pkt.receivedFrom(), igmp.getIgmpType());
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000702 } else {
703 IGMPMembership mgroup = new IGMPMembership(group.getGaddr().getIp4Address());
704 mgroup.setRecordType(igmp.getIgmpType() == IGMP.TYPE_IGMPV2_MEMBERSHIP_REPORT ?
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000705 IGMPMembership.MODE_IS_EXCLUDE :
706 IGMPMembership.MODE_IS_INCLUDE);
onurka85b2d9c2021-01-19 22:49:19 +0300707 queueIgmpReport(mgroup, VlanId.vlanId(vlan),
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000708 pkt.receivedFrom(), igmp.getIgmpType());
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000709 }
710 }
711
712 }
713
ke han81a38b92017-03-10 18:41:44 +0800714 private class IgmpProxyTimerTask extends TimerTask {
715 public void run() {
716 try {
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000717 stateMachineService.timeOut1s();
ke han81a38b92017-03-10 18:41:44 +0800718 queryMembers();
719 } catch (Exception ex) {
720 log.warn("Igmp timer task error : {}", ex.getMessage());
721 }
722 }
723
724 private void queryMembers() {
725 GroupMember groupMember;
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000726 Set<GroupMemberId> keySet = groupMemberStore.getAllGroupMemberIds();
727 for (GroupMemberId key : keySet) {
728 groupMember = groupMemberStore.getGroupMember(key);
729 if (groupMember == null) {
730 continue;
731 }
ke han81a38b92017-03-10 18:41:44 +0800732 DeviceId did = groupMember.getDeviceId();
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000733 if (igmpLeadershipService.isLocalLeader(did)) {
ke han81a38b92017-03-10 18:41:44 +0800734 if (groupMember.isLeave()) {
735 lastQuery(groupMember);
736 } else if (periodicQuery) {
737 periodicQuery(groupMember);
738 }
739 }
740 }
741 }
742
743 private void lastQuery(GroupMember groupMember) {
744 if (groupMember.getLastQueryInterval() < lastQueryInterval) {
745 groupMember.lastQueryInterval(true); // count times
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000746 //put modified group member object to the store
747 groupMemberStore.updateGroupMember(groupMember);
ke han81a38b92017-03-10 18:41:44 +0800748 } else if (groupMember.getLastQueryCount() < lastQueryCount - 1) {
749 sendQuery(groupMember);
750 groupMember.lastQueryInterval(false); // reset count number
751 groupMember.lastQueryCount(true); //count times
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000752 //put modified group member object to the store
753 groupMemberStore.updateGroupMember(groupMember);
ke han81a38b92017-03-10 18:41:44 +0800754 } else if (groupMember.getLastQueryCount() == lastQueryCount - 1) {
755 leaveAction(groupMember);
756 }
757 }
758
759 private void periodicQuery(GroupMember groupMember) {
760 if (groupMember.getKeepAliveQueryInterval() < keepAliveInterval) {
761 groupMember.keepAliveInterval(true);
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000762 //put modified group member object to the store
763 groupMemberStore.updateGroupMember(groupMember);
ke han81a38b92017-03-10 18:41:44 +0800764 } else if (groupMember.getKeepAliveQueryCount() < keepAliveCount) {
765 sendQuery(groupMember);
766 groupMember.keepAliveInterval(false);
767 groupMember.keepAliveQueryCount(true);
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000768 //put modified group member object to the store
769 groupMemberStore.updateGroupMember(groupMember);
ke han81a38b92017-03-10 18:41:44 +0800770 } else if (groupMember.getKeepAliveQueryCount() == keepAliveCount) {
771 leaveAction(groupMember);
772 }
773 }
774
775 }
776
Esin Karaman00e16b72020-02-21 10:32:39 +0000777 public Optional<PortNumber> getDeviceUplink(DeviceId devId) {
778 Device device = deviceService.getDevice(devId);
779 if (device == null || device.serialNumber() == null) {
780 return Optional.empty();
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000781 }
Esin Karaman00e16b72020-02-21 10:32:39 +0000782 Optional<SubscriberAndDeviceInformation> olt = getSubscriberAndDeviceInformation(device.serialNumber());
783 if (olt.isEmpty()) {
784 return Optional.empty();
785 }
786 PortNumber portNumber = PortNumber.portNumber(olt.get().uplinkPort());
787 return validateUpLinkPort(device.id(), portNumber) ?
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000788 Optional.of(portNumber) : Optional.empty();
Esin Karaman00e16b72020-02-21 10:32:39 +0000789 }
790
791 /**
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000792 * @param deviceId device id
Esin Karaman00e16b72020-02-21 10:32:39 +0000793 * @param portNumber port number
794 * @return true if the port name starts with NNI_PREFIX; false otherwise.
795 */
796 public boolean validateUpLinkPort(DeviceId deviceId, PortNumber portNumber) {
797 Port port = deviceService.getPort(deviceId, portNumber);
798 if (port == null) {
799 //port is not discovered by ONOS; so cannot validate it.
800 return false;
801 }
Esin Karamance5ce512020-02-25 15:58:14 +0000802 boolean isValid = port.annotations().value(AnnotationKeys.PORT_NAME) != null &&
Esin Karaman00e16b72020-02-21 10:32:39 +0000803 port.annotations().value(AnnotationKeys.PORT_NAME).startsWith(NNI_PREFIX);
Esin Karamance5ce512020-02-25 15:58:14 +0000804 if (!isValid) {
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000805 log.warn("Port cannot be validated; it is not configured as an NNI port. Device/port: {}/{}",
806 deviceId, portNumber);
Esin Karamance5ce512020-02-25 15:58:14 +0000807 }
808 return isValid;
ke han81a38b92017-03-10 18:41:44 +0800809 }
810
Esin Karamanb38700c2019-09-17 13:01:25 +0000811 public static boolean isIgmpOnPodBasis() {
812 return igmpOnPodBasis;
813 }
814
ke han81a38b92017-03-10 18:41:44 +0800815 private void processFilterObjective(DeviceId devId, PortNumber port, boolean remove) {
Esin Karamaneff10392019-06-27 18:09:13 +0000816 if (!enableIgmpProvisioning) {
817 log.debug("IGMP trap rules won't be installed since enableIgmpProvisioning flag is not set");
818 return;
819 }
ke han81a38b92017-03-10 18:41:44 +0800820 //TODO migrate to packet requests when packet service uses filtering objectives
821 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
822
823 builder = remove ? builder.deny() : builder.permit();
824
825 FilteringObjective igmp = builder
826 .withKey(Criteria.matchInPort(port))
827 .addCondition(Criteria.matchEthType(EthType.EtherType.IPV4.ethType()))
828 .addCondition(Criteria.matchIPProtocol(IPv4.PROTOCOL_IGMP))
829 .withMeta(DefaultTrafficTreatment.builder().setOutput(PortNumber.CONTROLLER).build())
830 .fromApp(appId)
Esin Karamaneff10392019-06-27 18:09:13 +0000831 .withPriority(MAX_PRIORITY)
ke han81a38b92017-03-10 18:41:44 +0800832 .add(new ObjectiveContext() {
833 @Override
834 public void onSuccess(Objective objective) {
Esin Karamaneff10392019-06-27 18:09:13 +0000835 log.info("Igmp filter for {} on {} {}.",
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000836 devId, port, (remove) ? REMOVED : INSTALLED);
ke han81a38b92017-03-10 18:41:44 +0800837 }
838
839 @Override
840 public void onError(Objective objective, ObjectiveError error) {
Esin Karamaneff10392019-06-27 18:09:13 +0000841 log.info("Igmp filter {} for device {} on port {} failed because of {}",
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000842 (remove) ? INSTALLATION : REMOVAL, devId, port,
843 error);
ke han81a38b92017-03-10 18:41:44 +0800844 }
845 });
846
847 flowObjectiveService.filter(devId, igmp);
Esin Karamaneff10392019-06-27 18:09:13 +0000848
ke han81a38b92017-03-10 18:41:44 +0800849 }
850
851 private boolean isConnectPoint(DeviceId device, PortNumber port) {
Deepa Vaddireddy01f33b42017-07-02 13:26:53 +0530852 if (connectPoint != null) {
853 return (connectPointMode && connectPoint.deviceId().equals(device)
854 && connectPoint.port().equals(port));
855 } else {
Andrea Campanella2c70a572020-06-05 13:31:45 +0200856 log.debug("connectPoint not configured for device {}", device);
Deepa Vaddireddy01f33b42017-07-02 13:26:53 +0530857 return false;
858 }
ke han81a38b92017-03-10 18:41:44 +0800859 }
Deepa Vaddireddy01f33b42017-07-02 13:26:53 +0530860
ke han81a38b92017-03-10 18:41:44 +0800861 private boolean isUplink(DeviceId device, PortNumber port) {
Esin Karamance5ce512020-02-25 15:58:14 +0000862 if (connectPointMode) {
863 return false;
864 }
865 Optional<PortNumber> upLinkPort = getDeviceUplink(device);
866 return upLinkPort.isPresent() && upLinkPort.get().equals(port);
ke han81a38b92017-03-10 18:41:44 +0800867 }
868
Esin Karaman00e16b72020-02-21 10:32:39 +0000869 /**
870 * Fetches device information associated with the device serial number from SADIS.
871 *
872 * @param serialNumber serial number of a device
873 * @return device information; an empty Optional otherwise.
874 */
875 private Optional<SubscriberAndDeviceInformation> getSubscriberAndDeviceInformation(String serialNumber) {
876 long start = System.currentTimeMillis();
877 try {
Ilayda Ozdemire3505102021-02-23 12:56:15 +0000878 if (sadisService == null) {
879 log.warn(SADIS_NOT_RUNNING);
880 return Optional.empty();
881 }
Esin Karaman00e16b72020-02-21 10:32:39 +0000882 return Optional.ofNullable(sadisService.getSubscriberInfoService().get(serialNumber));
883 } finally {
884 if (log.isDebugEnabled()) {
885 // SADIS can call remote systems to fetch device data and this calls can take a long time.
886 // This measurement is just for monitoring these kinds of situations.
887 log.debug("Device fetched from SADIS. Elapsed {} msec", System.currentTimeMillis() - start);
888 }
Esin Karaman00e16b72020-02-21 10:32:39 +0000889 }
890 }
891
892 /**
893 * Fetches device information associated with the device serial number from SADIS.
894 *
895 * @param deviceId device id
896 * @return device information; an empty Optional otherwise.
897 */
898 private Optional<SubscriberAndDeviceInformation> getSubscriberAndDeviceInformation(DeviceId deviceId) {
899 Device device = deviceService.getDevice(deviceId);
900 if (device == null || device.serialNumber() == null) {
901 return Optional.empty();
902 }
903 return getSubscriberAndDeviceInformation(device.serialNumber());
904 }
905
ke han81a38b92017-03-10 18:41:44 +0800906 private class InternalDeviceListener implements DeviceListener {
907 @Override
908 public void event(DeviceEvent event) {
Esin Karaman09b41e52020-06-01 10:52:55 +0000909 eventExecutor.execute(() -> {
910 DeviceId devId = event.subject().id();
911 Port p = event.port();
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +0000912
Esin Karaman09b41e52020-06-01 10:52:55 +0000913 if (!igmpLeadershipService.isLocalLeader(devId)) {
914 return;
915 }
916 if (getSubscriberAndDeviceInformation(devId).isEmpty() &&
917 !(p != null && isConnectPoint(devId, p.number()))) {
918 return;
919 }
920 PortNumber port;
ke han81a38b92017-03-10 18:41:44 +0800921
Esin Karaman09b41e52020-06-01 10:52:55 +0000922 switch (event.type()) {
923
924 case DEVICE_ADDED:
925 case DEVICE_UPDATED:
926 case DEVICE_REMOVED:
927 case DEVICE_SUSPENDED:
928 case DEVICE_AVAILABILITY_CHANGED:
929 case PORT_STATS_UPDATED:
930 break;
931 case PORT_ADDED:
932 port = p.number();
933 if (getSubscriberAndDeviceInformation(devId).isPresent() &&
934 !isUplink(devId, port) && !isConnectPoint(devId, port)) {
ke han81a38b92017-03-10 18:41:44 +0800935 processFilterObjective(devId, port, false);
Esin Karaman09b41e52020-06-01 10:52:55 +0000936 } else if (isUplink(devId, port)) {
937 provisionUplinkFlows();
938 } else if (isConnectPoint(devId, port)) {
ke han81a38b92017-03-10 18:41:44 +0800939 provisionConnectPointFlows();
ke han81a38b92017-03-10 18:41:44 +0800940 }
Esin Karaman09b41e52020-06-01 10:52:55 +0000941 onSourceStateChanged(devId, port, true);
942 break;
943 case PORT_UPDATED:
944 port = p.number();
945 if (getSubscriberAndDeviceInformation(devId).isPresent() &&
946 !isUplink(devId, port) && !isConnectPoint(devId, port)) {
947 processFilterObjective(devId, port, !event.port().isEnabled());
948 } else if (isUplink(devId, port)) {
949 if (event.port().isEnabled()) {
950 provisionUplinkFlows(devId);
951 } else {
952 processFilterObjective(devId, port, true);
953 }
954 } else if (isConnectPoint(devId, port)) {
955 if (event.port().isEnabled()) {
956 provisionConnectPointFlows();
957 } else {
958 unprovisionConnectPointFlows();
959 }
960 }
961 onSourceStateChanged(devId, port, event.port().isEnabled());
962 break;
963 case PORT_REMOVED:
964 port = p.number();
965 processFilterObjective(devId, port, true);
966 onSourceStateChanged(devId, port, false);
967 break;
968 default:
969 log.info("Unknown device event {}", event.type());
970 break;
971 }
972 });
ke han81a38b92017-03-10 18:41:44 +0800973 }
974
975 @Override
976 public boolean isRelevant(DeviceEvent event) {
977 return true;
978 }
979 }
980
Esin Karaman09b41e52020-06-01 10:52:55 +0000981 private Set<McastRoute> multicastRoutesOfIgmpProxy() {
982 Set<McastRoute> routes = new HashSet<>(); //using set to eliminate multiple entities
983 groupMemberStore.getAllGroupMemberIds().forEach(groupMemberId -> {
984 GroupMember groupMember = groupMemberStore.getGroupMember(groupMemberId);
985 if (groupMember != null) {
986 groupMember.getSourceList().forEach(source -> {
987 //regenerate the routes created by this application
988 routes.add(new McastRoute(source, groupMemberId.getGroupIp(), McastRoute.Type.IGMP));
989 });
990 }
991 });
992 return routes;
993 }
994
995 private void onSourceStateChanged(DeviceId deviceId, PortNumber portNumber, boolean enabled) {
Ilayda Ozdemire3505102021-02-23 12:56:15 +0000996 if (multicastService == null) {
997 log.warn(MCAST_NOT_RUNNING);
998 return;
999 }
Esin Karaman09b41e52020-06-01 10:52:55 +00001000 if (!(getSource().isPresent() &&
1001 getSource().get().deviceId().equals(deviceId) &&
1002 getSource().get().port().equals(portNumber))) {
1003 //connect point is not configured as the source
1004 log.debug("{}/{} is not the source cp. Stopped processing it further", deviceId, portNumber);
1005 return;
1006 }
1007 log.info("source device:port is {}. DeviceId={}, portNumber={}",
1008 (enabled ? "enabled. Restoring the source" :
1009 "disabled. Deleting it from multicast routes"), deviceId, portNumber);
1010
1011 Set<McastRoute> routes = multicastRoutesOfIgmpProxy();
1012 routes.forEach(route -> {
1013 if (enabled) {
1014 //add source to the route
1015 multicastService.addSources(route, Sets.newHashSet(new ConnectPoint(deviceId, portNumber)));
1016 } else {
1017 //remove the source from the route
1018 multicastService.removeSources(route, Sets.newHashSet(new ConnectPoint(deviceId, portNumber)));
1019 }
1020 });
1021 }
1022
ke han81a38b92017-03-10 18:41:44 +08001023 private class InternalNetworkConfigListener implements NetworkConfigListener {
1024
1025 private void reconfigureNetwork(IgmpproxyConfig cfg) {
Esin Karamaneff10392019-06-27 18:09:13 +00001026 IgmpproxyConfig newCfg = cfg == null ? new IgmpproxyConfig() : cfg;
ke han81a38b92017-03-10 18:41:44 +08001027
1028 unSolicitedTimeout = newCfg.unsolicitedTimeOut();
1029 maxResp = newCfg.maxResp();
1030 keepAliveInterval = newCfg.keepAliveInterval();
1031 keepAliveCount = newCfg.keepAliveCount();
1032 lastQueryInterval = newCfg.lastQueryInterval();
1033 lastQueryCount = newCfg.lastQueryCount();
1034 withRAUplink = newCfg.withRAUplink();
1035 withRADownlink = newCfg.withRADownlink();
1036 igmpCos = newCfg.igmpCos();
Esin Karaman586f1d62020-06-04 10:15:34 +00001037 igmpUniCos = newCfg.igmpUniCos(); // defines priority bit of IGMP query message sent to UNI ports
ke han81a38b92017-03-10 18:41:44 +08001038 periodicQuery = newCfg.periodicQuery();
1039 fastLeave = newCfg.fastLeave();
ke han29af27b2017-09-08 10:29:12 +08001040 pimSSmInterworking = newCfg.pimSsmInterworking();
Esin Karamaneff10392019-06-27 18:09:13 +00001041 enableIgmpProvisioning = newCfg.enableIgmpProvisioning();
Esin Karamanb38700c2019-09-17 13:01:25 +00001042 igmpOnPodBasis = newCfg.igmpOnPodBasis();
onurka85b2d9c2021-01-19 22:49:19 +03001043 if (numberOfIgmpReportProcessorThreads != newCfg.numberOfIgmpReportProcessorThreads()) {
1044 numberOfIgmpReportProcessorThreads = newCfg.numberOfIgmpReportProcessorThreads();
1045 shutdownIgmpReportProcessServiceExecutors();
1046 initializeIgmpReportProcessServiceExecutors();
1047 }
Arjun E Kb0018fd2020-04-07 13:26:40 +00001048 if (newCfg.outgoingIgmpWithV3() != null &&
1049 outgoingIgmpWithV3 != newCfg.outgoingIgmpWithV3()) {
1050 outgoingIgmpWithV3 = newCfg.outgoingIgmpWithV3();
1051 }
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +00001052
1053 if (connectPointMode != newCfg.connectPointMode() ||
1054 connectPoint != newCfg.connectPoint()) {
ke han81a38b92017-03-10 18:41:44 +08001055 connectPointMode = newCfg.connectPointMode();
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +00001056 connectPoint = newCfg.connectPoint();
ke han81a38b92017-03-10 18:41:44 +08001057 if (connectPointMode) {
1058 unprovisionUplinkFlows();
1059 provisionConnectPointFlows();
1060 } else {
1061 unprovisionConnectPointFlows();
1062 provisionUplinkFlows();
1063 }
1064 }
1065 if (connectPoint != null) {
Esin Karamaneff10392019-06-27 18:09:13 +00001066 log.info("connect point : {}", connectPoint);
ke han81a38b92017-03-10 18:41:44 +08001067 }
Esin Karamaneff10392019-06-27 18:09:13 +00001068 log.info("mode: {}", connectPointMode);
1069
1070 getSourceConnectPoint(newCfg);
ke han81a38b92017-03-10 18:41:44 +08001071
1072 IgmpSender.getInstance().setIgmpCos(igmpCos);
Esin Karaman586f1d62020-06-04 10:15:34 +00001073 IgmpSender.getInstance().setIgmpUniCos(igmpUniCos);
ke han81a38b92017-03-10 18:41:44 +08001074 IgmpSender.getInstance().setMaxResp(maxResp);
1075 IgmpSender.getInstance().setMvlan(mvlan);
Esin Karaman586f1d62020-06-04 10:15:34 +00001076 IgmpSender.getInstance().setMvlanInner(mvlanInner);
ke han81a38b92017-03-10 18:41:44 +08001077 IgmpSender.getInstance().setWithRADownlink(withRADownlink);
1078 IgmpSender.getInstance().setWithRAUplink(withRAUplink);
Esin Karamaneff10392019-06-27 18:09:13 +00001079 }
ke han81a38b92017-03-10 18:41:44 +08001080
Esin Karamaneff10392019-06-27 18:09:13 +00001081 void getSourceConnectPoint(IgmpproxyConfig cfg) {
Esin Karaman09b41e52020-06-01 10:52:55 +00001082 ConnectPoint oldSourceDevPort = sourceDeviceAndPort;
Esin Karamaneff10392019-06-27 18:09:13 +00001083 sourceDeviceAndPort = cfg.getSourceDeviceAndPort();
1084 if (sourceDeviceAndPort != null) {
1085 log.debug("source parameter configured to {}", sourceDeviceAndPort);
1086 }
Esin Karaman09b41e52020-06-01 10:52:55 +00001087 if (oldSourceDevPort != null && !oldSourceDevPort.equals(sourceDeviceAndPort)) {
1088 //source config has changed, remove the old source from multicast routes
1089 onSourceStateChanged(oldSourceDevPort.deviceId(), oldSourceDevPort.port(), false);
1090 }
1091 if (sourceDeviceAndPort != null && !sourceDeviceAndPort.equals(oldSourceDevPort)) {
1092 //add new source to the existing routes
1093 onSourceStateChanged(sourceDeviceAndPort.deviceId(), sourceDeviceAndPort.port(), true);
1094 }
ke han81a38b92017-03-10 18:41:44 +08001095 }
1096
1097 public void reconfigureSsmTable(IgmpproxySsmTranslateConfig cfg) {
1098 if (cfg == null) {
1099 return;
1100 }
1101 Collection<McastRoute> translations = cfg.getSsmTranslations();
1102 for (McastRoute route : translations) {
Esin Karamaneff10392019-06-27 18:09:13 +00001103 ssmTranslateTable.put(route.group().getIp4Address(), route.source().get().getIp4Address());
ke han81a38b92017-03-10 18:41:44 +08001104 }
1105 }
1106
1107 @Override
1108 public void event(NetworkConfigEvent event) {
1109 switch (event.type()) {
1110 case CONFIG_ADDED:
1111 case CONFIG_UPDATED:
Matteo Scandolo7482bbe2020-02-12 14:37:09 -08001112 // NOTE how to know if something has changed in sadis?
ke han81a38b92017-03-10 18:41:44 +08001113
1114 if (event.configClass().equals(IGMPPROXY_CONFIG_CLASS)) {
1115 IgmpproxyConfig config = networkConfig.getConfig(appId, IGMPPROXY_CONFIG_CLASS);
1116 if (config != null) {
Esin Karamaneff10392019-06-27 18:09:13 +00001117 log.info("igmpproxy config received. {}", config);
ke han81a38b92017-03-10 18:41:44 +08001118 reconfigureNetwork(config);
1119 }
1120 }
1121
1122 if (event.configClass().equals(IGMPPROXY_SSM_CONFIG_CLASS)) {
1123 IgmpproxySsmTranslateConfig config = networkConfig.getConfig(appId, IGMPPROXY_SSM_CONFIG_CLASS);
1124 if (config != null) {
1125 reconfigureSsmTable(config);
1126 }
1127 }
1128
1129 if (event.configClass().equals(MCAST_CONFIG_CLASS)) {
1130 McastConfig config = networkConfig.getConfig(coreAppId, MCAST_CONFIG_CLASS);
Esin Karaman586f1d62020-06-04 10:15:34 +00001131 boolean vlanConfigChanged = config != null && mvlan != config.egressVlan().toShort();
1132 boolean innerVlanConfigChanged = config != null &&
1133 mvlanInner != config.egressInnerVlan().toShort();
1134
1135 if (vlanConfigChanged || innerVlanConfigChanged) {
1136 log.info("igmpproxy vlan config received. {}", config);
1137 //at least one of the vlan configs has changed. Call leave before setting new values
Esin Karaman4a9075d2020-07-14 14:46:14 +00001138 groupMemberStore.getAllGroupMembers().forEach(m -> {
1139 if (igmpLeadershipService.isLocalLeader(m.getDeviceId())) {
1140 leaveAction(m);
1141 }
1142 });
Esin Karaman586f1d62020-06-04 10:15:34 +00001143 if (vlanConfigChanged) {
1144 mvlan = config.egressVlan().toShort();
1145 IgmpSender.getInstance().setMvlan(mvlan);
1146 }
1147 if (innerVlanConfigChanged) {
1148 mvlanInner = config.egressInnerVlan().toShort();
1149 IgmpSender.getInstance().setMvlanInner(mvlanInner);
1150 }
ke han81a38b92017-03-10 18:41:44 +08001151 }
1152 }
1153
1154 log.info("Reconfigured");
1155 break;
1156 case CONFIG_REGISTERED:
1157 case CONFIG_UNREGISTERED:
1158 break;
1159 case CONFIG_REMOVED:
Matteo Scandolo7482bbe2020-02-12 14:37:09 -08001160 // NOTE how to know if something has changed in sadis?
ke han81a38b92017-03-10 18:41:44 +08001161 default:
1162 break;
1163 }
1164 }
1165 }
1166
ke han81a38b92017-03-10 18:41:44 +08001167 private void provisionUplinkFlows(DeviceId deviceId) {
1168 if (connectPointMode) {
1169 return;
1170 }
1171
Esin Karaman00e16b72020-02-21 10:32:39 +00001172 Optional<PortNumber> upLink = getDeviceUplink(deviceId);
1173 if (upLink.isPresent()) {
1174 processFilterObjective(deviceId, upLink.get(), false);
1175 }
ke han81a38b92017-03-10 18:41:44 +08001176 }
1177
1178 private void provisionUplinkFlows() {
1179 if (connectPointMode) {
1180 return;
1181 }
Esin Karaman00e16b72020-02-21 10:32:39 +00001182 deviceService.getAvailableDevices().forEach(device -> {
1183 Optional<SubscriberAndDeviceInformation> accessDevice = getSubscriberAndDeviceInformation(device.id());
1184 if (accessDevice.isPresent()) {
1185 provisionUplinkFlows(device.id());
1186 }
1187 });
ke han81a38b92017-03-10 18:41:44 +08001188 }
Esin Karaman00e16b72020-02-21 10:32:39 +00001189
ke han81a38b92017-03-10 18:41:44 +08001190 private void unprovisionUplinkFlows() {
Esin Karaman00e16b72020-02-21 10:32:39 +00001191 deviceService.getAvailableDevices().forEach(device -> {
1192 Optional<SubscriberAndDeviceInformation> accessDevices = getSubscriberAndDeviceInformation(device.id());
1193 if (accessDevices.isPresent()) {
1194 Optional<PortNumber> upLink = getDeviceUplink(device.id());
1195 if (upLink.isPresent()) {
1196 processFilterObjective(device.id(), upLink.get(), true);
1197 }
1198 }
1199 });
ke han81a38b92017-03-10 18:41:44 +08001200 }
1201
1202 private void provisionConnectPointFlows() {
1203 if ((!connectPointMode) || connectPoint == null) {
1204 return;
1205 }
1206
1207 processFilterObjective(connectPoint.deviceId(), connectPoint.port(), false);
1208 }
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +00001209
ke han81a38b92017-03-10 18:41:44 +08001210 private void unprovisionConnectPointFlows() {
1211 if (connectPoint == null) {
1212 return;
1213 }
1214 processFilterObjective(connectPoint.deviceId(), connectPoint.port(), true);
1215 }
Shubham Sharma47f2caf2020-02-18 12:13:40 +00001216
Joey Armstrong4509c442023-01-03 14:05:28 -05001217}