blob: 2025e0b9b5f51b78df371865450c179f4a353485 [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;
70import org.opencord.sadis.BaseInformationService;
71import org.opencord.sadis.SadisService;
72import org.opencord.sadis.SubscriberAndDeviceInformation;
73import org.osgi.service.component.annotations.Activate;
74import org.osgi.service.component.annotations.Component;
75import org.osgi.service.component.annotations.Deactivate;
76import org.osgi.service.component.annotations.Reference;
77import org.osgi.service.component.annotations.ReferenceCardinality;
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 {
113
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800114 private static final String APP_NAME = "org.opencord.igmpproxy";
115
ke han81a38b92017-03-10 18:41:44 +0800116 private static final Class<IgmpproxyConfig> IGMPPROXY_CONFIG_CLASS =
117 IgmpproxyConfig.class;
118 private static final Class<IgmpproxySsmTranslateConfig> IGMPPROXY_SSM_CONFIG_CLASS =
119 IgmpproxySsmTranslateConfig.class;
120 private static final Class<McastConfig> MCAST_CONFIG_CLASS =
121 McastConfig.class;
Esin Karamaneff10392019-06-27 18:09:13 +0000122
ke han81a38b92017-03-10 18:41:44 +0800123 private static ApplicationId appId;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800124
ke han81a38b92017-03-10 18:41:44 +0800125 private static int unSolicitedTimeout = 3; // unit is 1 sec
126 private static int keepAliveCount = 3;
127 private static int lastQueryInterval = 2; //unit is 1 sec
128 private static int lastQueryCount = 2;
129 private static boolean fastLeave = true;
130 private static boolean withRAUplink = true;
131 private static boolean withRADownlink = false;
132 private static boolean periodicQuery = true;
133 private static short mvlan = 4000;
Esin Karaman586f1d62020-06-04 10:15:34 +0000134 private static short mvlanInner = VlanId.NONE.toShort();
ke han81a38b92017-03-10 18:41:44 +0800135 private static byte igmpCos = 7;
Esin Karaman586f1d62020-06-04 10:15:34 +0000136 private static byte igmpUniCos = 7;
ke han81a38b92017-03-10 18:41:44 +0800137 public static boolean connectPointMode = true;
138 public static ConnectPoint connectPoint = null;
Esin Karamaneff10392019-06-27 18:09:13 +0000139 private static ConnectPoint sourceDeviceAndPort = null;
140 private static boolean enableIgmpProvisioning = false;
Esin Karamanb38700c2019-09-17 13:01:25 +0000141 private static boolean igmpOnPodBasis = false;
Arjun E Kb0018fd2020-04-07 13:26:40 +0000142 private static boolean outgoingIgmpWithV3 = true;
Esin Karamaneff10392019-06-27 18:09:13 +0000143
144 private static final Integer MAX_PRIORITY = 10000;
145 private static final String INSTALLED = "installed";
146 private static final String REMOVED = "removed";
147 private static final String INSTALLATION = "installation";
148 private static final String REMOVAL = "removal";
Esin Karaman00e16b72020-02-21 10:32:39 +0000149 private static final String NNI_PREFIX = "nni";
ke han81a38b92017-03-10 18:41:44 +0800150
ke han29af27b2017-09-08 10:29:12 +0800151 private static boolean pimSSmInterworking = false;
152 private static final String DEFAULT_PIMSSM_HOST = "127.0.0.1";
ke han81a38b92017-03-10 18:41:44 +0800153 private final ScheduledExecutorService scheduledExecutorService =
154 Executors.newScheduledThreadPool(1);
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800155
Carmelo Casconebef302e2019-11-14 19:58:20 -0800156 @Reference(cardinality = ReferenceCardinality.MANDATORY)
ke han81a38b92017-03-10 18:41:44 +0800157 protected CoreService coreService;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800158
Carmelo Casconebef302e2019-11-14 19:58:20 -0800159 @Reference(cardinality = ReferenceCardinality.MANDATORY)
ke han81a38b92017-03-10 18:41:44 +0800160 protected PacketService packetService;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800161
Carmelo Casconebef302e2019-11-14 19:58:20 -0800162 @Reference(cardinality = ReferenceCardinality.MANDATORY)
ke han81a38b92017-03-10 18:41:44 +0800163 protected MastershipService mastershipService;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800164
Carmelo Casconebef302e2019-11-14 19:58:20 -0800165 @Reference(cardinality = ReferenceCardinality.MANDATORY)
ke han81a38b92017-03-10 18:41:44 +0800166 protected FlowRuleService flowRuleService;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800167
Carmelo Casconebef302e2019-11-14 19:58:20 -0800168 @Reference(cardinality = ReferenceCardinality.MANDATORY)
ke han81a38b92017-03-10 18:41:44 +0800169 protected DeviceService deviceService;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800170
Carmelo Casconebef302e2019-11-14 19:58:20 -0800171 @Reference(cardinality = ReferenceCardinality.MANDATORY)
ke han81a38b92017-03-10 18:41:44 +0800172 protected FlowObjectiveService flowObjectiveService;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800173
Carmelo Casconebef302e2019-11-14 19:58:20 -0800174 @Reference(cardinality = ReferenceCardinality.MANDATORY)
ke han81a38b92017-03-10 18:41:44 +0800175 protected NetworkConfigRegistry networkConfig;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800176
Carmelo Casconebef302e2019-11-14 19:58:20 -0800177 @Reference(cardinality = ReferenceCardinality.MANDATORY)
ke han81a38b92017-03-10 18:41:44 +0800178 protected MulticastRouteService multicastService;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800179
180 @Reference(cardinality = ReferenceCardinality.MANDATORY)
181 protected SadisService sadisService;
182
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000183 @Reference(cardinality = ReferenceCardinality.MANDATORY)
184 protected IgmpStatisticsService igmpStatisticsManager;
185
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000186 @Reference(cardinality = ReferenceCardinality.MANDATORY)
187 protected GroupMemberStore groupMemberStore;
188
189 @Reference(cardinality = ReferenceCardinality.MANDATORY)
190 protected StateMachineService stateMachineService;
191
192 @Reference(cardinality = ReferenceCardinality.MANDATORY)
193 protected IgmpLeadershipService igmpLeadershipService;
194
ke han81a38b92017-03-10 18:41:44 +0800195 private IgmpPacketProcessor processor = new IgmpPacketProcessor();
196 private Logger log = LoggerFactory.getLogger(getClass());
197 private ApplicationId coreAppId;
198 private Map<Ip4Address, Ip4Address> ssmTranslateTable = new ConcurrentHashMap<>();
Esin Karamaneff10392019-06-27 18:09:13 +0000199
ke han81a38b92017-03-10 18:41:44 +0800200 private InternalNetworkConfigListener configListener =
201 new InternalNetworkConfigListener();
202 private DeviceListener deviceListener = new InternalDeviceListener();
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800203
ke han81a38b92017-03-10 18:41:44 +0800204 private ConfigFactory<ApplicationId, IgmpproxyConfig> igmpproxyConfigFactory =
205 new ConfigFactory<ApplicationId, IgmpproxyConfig>(
206 SubjectFactories.APP_SUBJECT_FACTORY, IGMPPROXY_CONFIG_CLASS, "igmpproxy") {
207 @Override
208 public IgmpproxyConfig createConfig() {
209 return new IgmpproxyConfig();
210 }
211 };
212 private ConfigFactory<ApplicationId, IgmpproxySsmTranslateConfig> igmpproxySsmConfigFactory =
213 new ConfigFactory<ApplicationId, IgmpproxySsmTranslateConfig>(
214 SubjectFactories.APP_SUBJECT_FACTORY, IGMPPROXY_SSM_CONFIG_CLASS, "ssmTranslate", true) {
215 @Override
216 public IgmpproxySsmTranslateConfig createConfig() {
217 return new IgmpproxySsmTranslateConfig();
218 }
219 };
Esin Karamaneff10392019-06-27 18:09:13 +0000220
ke han81a38b92017-03-10 18:41:44 +0800221 private int maxResp = 10; //unit is 1 sec
222 private int keepAliveInterval = 120; //unit is 1 sec
223
onurka85b2d9c2021-01-19 22:49:19 +0300224 private int numberOfIgmpReportProcessorThreads = 20;
225 ExecutorService[] igmpReportProcessServiceExecutorList;
226
Esin Karamanb38700c2019-09-17 13:01:25 +0000227 private ExecutorService eventExecutor;
228
ke han81a38b92017-03-10 18:41:44 +0800229 public static int getUnsolicitedTimeout() {
230 return unSolicitedTimeout;
231 }
232
Arjun E Kb0018fd2020-04-07 13:26:40 +0000233 public static boolean outgoingIgmpWithV3() {
234 return outgoingIgmpWithV3;
235 }
236
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800237 protected BaseInformationService<SubscriberAndDeviceInformation> subsService;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800238
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000239 private List<Byte> validMembershipModes = Arrays.asList(MODE_IS_INCLUDE, MODE_IS_EXCLUDE, CHANGE_TO_INCLUDE_MODE,
240 CHANGE_TO_EXCLUDE_MODE, ALLOW_NEW_SOURCES, BLOCK_OLD_SOURCES);
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000241
ke han81a38b92017-03-10 18:41:44 +0800242 @Activate
243 protected void activate() {
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800244 appId = coreService.registerApplication(APP_NAME);
ke han81a38b92017-03-10 18:41:44 +0800245 coreAppId = coreService.registerApplication(CoreService.CORE_APP_NAME);
246 packetService.addProcessor(processor, PacketProcessor.director(4));
Esin Karaman4a9075d2020-07-14 14:46:14 +0000247 IgmpSender.init(packetService, igmpStatisticsManager);
ke han81a38b92017-03-10 18:41:44 +0800248
ke han81a38b92017-03-10 18:41:44 +0800249 networkConfig.registerConfigFactory(igmpproxySsmConfigFactory);
250 networkConfig.registerConfigFactory(igmpproxyConfigFactory);
251 networkConfig.addListener(configListener);
252
253 configListener.reconfigureNetwork(networkConfig.getConfig(appId, IGMPPROXY_CONFIG_CLASS));
254 configListener.reconfigureSsmTable(networkConfig.getConfig(appId, IGMPPROXY_SSM_CONFIG_CLASS));
255
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800256 subsService = sadisService.getSubscriberInfoService();
ke han81a38b92017-03-10 18:41:44 +0800257 if (connectPointMode) {
258 provisionConnectPointFlows();
259 } else {
260 provisionUplinkFlows();
261 }
262
263 McastConfig config = networkConfig.getConfig(coreAppId, MCAST_CONFIG_CLASS);
264 if (config != null) {
265 mvlan = config.egressVlan().toShort();
Deepa Vaddireddy88134a42017-07-05 12:16:03 +0530266 IgmpSender.getInstance().setMvlan(mvlan);
Esin Karaman586f1d62020-06-04 10:15:34 +0000267 mvlanInner = config.egressInnerVlan().toShort();
268 IgmpSender.getInstance().setMvlanInner(mvlanInner);
ke han81a38b92017-03-10 18:41:44 +0800269 }
270 deviceService.addListener(deviceListener);
Sonal Kasliwalddc3ff22019-11-18 11:52:49 +0000271 scheduledExecutorService.scheduleAtFixedRate(new IgmpProxyTimerTask(), 1000, 1000, TimeUnit.MILLISECONDS);
onurka85b2d9c2021-01-19 22:49:19 +0300272 eventExecutor = Executors.newSingleThreadScheduledExecutor(groupedThreads("cord/igmpproxy",
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000273 "events-igmp-%d", log));
onurka85b2d9c2021-01-19 22:49:19 +0300274 initializeIgmpReportProcessServiceExecutors();
ke han81a38b92017-03-10 18:41:44 +0800275 log.info("Started");
276 }
277
278 @Deactivate
279 protected void deactivate() {
280 scheduledExecutorService.shutdown();
Esin Karamanb38700c2019-09-17 13:01:25 +0000281 eventExecutor.shutdown();
onurka85b2d9c2021-01-19 22:49:19 +0300282 shutdownIgmpReportProcessServiceExecutors();
ke han81a38b92017-03-10 18:41:44 +0800283
284 // de-register and null our handler
285 networkConfig.removeListener(configListener);
ke han81a38b92017-03-10 18:41:44 +0800286 networkConfig.unregisterConfigFactory(igmpproxyConfigFactory);
287 networkConfig.unregisterConfigFactory(igmpproxySsmConfigFactory);
288 deviceService.removeListener(deviceListener);
289 packetService.removeProcessor(processor);
290 flowRuleService.removeFlowRulesById(appId);
ke han81a38b92017-03-10 18:41:44 +0800291 log.info("Stopped");
292 }
293
onurka85b2d9c2021-01-19 22:49:19 +0300294 private void initializeIgmpReportProcessServiceExecutors() {
295 igmpReportProcessServiceExecutorList = new ExecutorService[numberOfIgmpReportProcessorThreads];
296 for (int i = 0; i < numberOfIgmpReportProcessorThreads; i++) {
297 ThreadFactory igmpReportProcessorThreadFactory =
298 new ThreadFactoryBuilder().setNameFormat("report-processor-igmp-" + i)
299 .setUncaughtExceptionHandler((t, e) ->
300 log.error("Uncaught exception on {}: ", t.getName(), e))
301 .build();
302 ExecutorService igmpReportProcessServiceExecutor = Executors.newSingleThreadExecutor(
303 igmpReportProcessorThreadFactory);
304 igmpReportProcessServiceExecutorList[i] = igmpReportProcessServiceExecutor;
305 }
306 }
307 private void shutdownIgmpReportProcessServiceExecutors() {
308 for (ExecutorService executor : igmpReportProcessServiceExecutorList) {
309 executor.shutdown();
310 }
311 }
312
ke han81a38b92017-03-10 18:41:44 +0800313 protected Ip4Address getDeviceIp(DeviceId ofDeviceId) {
314 try {
315 String[] mgmtAddress = deviceService.getDevice(ofDeviceId)
316 .annotations().value(AnnotationKeys.MANAGEMENT_ADDRESS).split(":");
317 return Ip4Address.valueOf(mgmtAddress[0]);
318 } catch (Exception ex) {
onurka85b2d9c2021-01-19 22:49:19 +0300319 log.info("No valid Ipaddress for {}", ofDeviceId);
ke han81a38b92017-03-10 18:41:44 +0800320 return null;
321 }
322 }
323
324 private void processIgmpQuery(IGMPQuery igmpQuery, ConnectPoint cp, int maxResp) {
325
326 DeviceId deviceId = cp.deviceId();
327 Ip4Address gAddr = igmpQuery.getGaddr().getIp4Address();
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000328 maxResp = calculateMaxResp(maxResp);
329 if (gAddr != null && !gAddr.isZero()) {
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000330 stateMachineService.specialQuery(deviceId, gAddr, maxResp);
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300331 igmpStatisticsManager.increaseStat(IgmpStatisticType.IGMP_GRP_SPECIFIC_MEMBERSHIP_QUERY);
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000332 } else {
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000333 stateMachineService.generalQuery(deviceId, maxResp);
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300334 igmpStatisticsManager.increaseStat(IgmpStatisticType.IGMP_GENERAL_MEMBERSHIP_QUERY);
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000335 }
336 }
ke han81a38b92017-03-10 18:41:44 +0800337
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000338 private void processIgmpConnectPointQuery(IGMPQuery igmpQuery, ConnectPoint cp, int maxResp) {
339
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000340 Ip4Address gAddr = igmpQuery.getGaddr().getIp4Address();
Esin Karaman00e16b72020-02-21 10:32:39 +0000341 final int maxResponseTime = calculateMaxResp(maxResp);
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000342 //The query is received on the ConnectPoint
343 // send query accordingly to the registered OLT devices.
344 if (gAddr != null && !gAddr.isZero()) {
Esin Karaman00e16b72020-02-21 10:32:39 +0000345 deviceService.getAvailableDevices().forEach(device -> {
346 Optional<SubscriberAndDeviceInformation> accessDevice = getSubscriberAndDeviceInformation(device.id());
347 if (accessDevice.isPresent()) {
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000348 stateMachineService.specialQuery(device.id(), gAddr, maxResponseTime);
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300349 igmpStatisticsManager.increaseStat(IgmpStatisticType.IGMP_GRP_AND_SRC_SPESIFIC_MEMBERSHIP_QUERY);
Esin Karaman00e16b72020-02-21 10:32:39 +0000350 }
351 });
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300352 igmpStatisticsManager.increaseStat(IgmpStatisticType.CURRENT_GRP_NUMBER_COUNTER);
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000353 } else {
354 //Don't know which group is targeted by the query
355 //So query all the members(in all the OLTs) and proxy their reports
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000356 stateMachineService.generalQuery(maxResponseTime);
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300357 igmpStatisticsManager.increaseStat(IgmpStatisticType.IGMP_GENERAL_MEMBERSHIP_QUERY);
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000358 }
359 }
360
361
362 private int calculateMaxResp(int maxResp) {
ke han81a38b92017-03-10 18:41:44 +0800363 if (maxResp >= 128) {
364 int mant = maxResp & 0xf;
365 int exp = (maxResp >> 4) & 0x7;
366 maxResp = (mant | 0x10) << (exp + 3);
367 }
368
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000369 return (maxResp + 5) / 10;
ke han81a38b92017-03-10 18:41:44 +0800370 }
371
onurka85b2d9c2021-01-19 22:49:19 +0300372 private void queueIgmpReport(IGMPMembership igmpGroup, VlanId vlan, ConnectPoint cp, byte igmpType) {
373 int packetHashCode = Objects.hash(igmpGroup.getGaddr(), connectPoint);
374 int threadId = Math.abs(packetHashCode % numberOfIgmpReportProcessorThreads);
375 log.debug("IGMP report for ConnectPoint {} and group IP {} shall be processed in thread #{}",
376 cp, igmpGroup.getGaddr(), threadId);
377
378 igmpReportProcessServiceExecutorList[threadId].execute(
379 () -> processIgmpReport(igmpGroup, vlan, cp, igmpType));
380 }
381
ke han81a38b92017-03-10 18:41:44 +0800382 private Ip4Address ssmTranslateRoute(IpAddress group) {
383 return ssmTranslateTable.get(group);
384 }
385
386 private void processIgmpReport(IGMPMembership igmpGroup, VlanId vlan, ConnectPoint cp, byte igmpType) {
387 DeviceId deviceId = cp.deviceId();
388 PortNumber portNumber = cp.port();
389
Andrea Campanella2c70a572020-06-05 13:31:45 +0200390 log.debug("Processing IGMP report on membership {} for vlan {} on port {} with type {}",
onurka85b2d9c2021-01-19 22:49:19 +0300391 igmpGroup, vlan, cp, igmpType);
Andrea Campanella2c70a572020-06-05 13:31:45 +0200392
ke han81a38b92017-03-10 18:41:44 +0800393 Ip4Address groupIp = igmpGroup.getGaddr().getIp4Address();
394 if (!groupIp.isMulticast()) {
onurka85b2d9c2021-01-19 22:49:19 +0300395 log.info("{} is not a valid group address", groupIp);
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300396 igmpStatisticsManager.increaseStat(IgmpStatisticType.FAIL_JOIN_REQ_UNKNOWN_MULTICAST_IP_COUNTER);
ke han81a38b92017-03-10 18:41:44 +0800397 return;
398 }
399 Ip4Address srcIp = getDeviceIp(deviceId);
400
401 byte recordType = igmpGroup.getRecordType();
402 boolean join = false;
403
404 ArrayList<Ip4Address> sourceList = new ArrayList<>();
405
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000406 if (!validMembershipModes.contains(recordType)) {
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300407 igmpStatisticsManager.increaseStat(IgmpStatisticType.REPORTS_RX_WITH_WRONG_MODE_COUNTER);
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000408 }
ke han81a38b92017-03-10 18:41:44 +0800409 if (igmpGroup.getSources().size() > 0) {
410 igmpGroup.getSources().forEach(source -> sourceList.add(source.getIp4Address()));
411 if (recordType == IGMPMembership.CHANGE_TO_EXCLUDE_MODE ||
412 recordType == IGMPMembership.MODE_IS_EXCLUDE ||
413 recordType == IGMPMembership.BLOCK_OLD_SOURCES) {
414 join = false;
415 } else if (recordType == IGMPMembership.CHANGE_TO_INCLUDE_MODE ||
416 recordType == IGMPMembership.MODE_IS_INCLUDE ||
417 recordType == IGMPMembership.ALLOW_NEW_SOURCES) {
418 join = true;
419 }
420 } else {
ke han29af27b2017-09-08 10:29:12 +0800421 IpAddress src = null;
422 if (pimSSmInterworking) {
423 src = ssmTranslateRoute(groupIp);
424 if (src == null) {
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000425 log.info("no ssm translate for group {}", groupIp);
ke han29af27b2017-09-08 10:29:12 +0800426 return;
427 }
428 } else {
429 src = IpAddress.valueOf(DEFAULT_PIMSSM_HOST);
ke han81a38b92017-03-10 18:41:44 +0800430 }
431 sourceList.add(src.getIp4Address());
432 if (recordType == IGMPMembership.CHANGE_TO_EXCLUDE_MODE ||
433 recordType == IGMPMembership.MODE_IS_EXCLUDE ||
434 recordType == IGMPMembership.BLOCK_OLD_SOURCES) {
435 join = true;
436 } else if (recordType == IGMPMembership.CHANGE_TO_INCLUDE_MODE ||
437 recordType == IGMPMembership.MODE_IS_INCLUDE ||
438 recordType == IGMPMembership.ALLOW_NEW_SOURCES) {
439 join = false;
440 }
441 }
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000442 GroupMemberId groupMemberKey = GroupMemberId.of(groupIp, deviceId, portNumber);
onurka85b2d9c2021-01-19 22:49:19 +0300443 log.debug("{} for {}", join ? "Join" : "Leave", groupMemberKey);
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000444 GroupMember groupMember = groupMemberStore.getGroupMember(groupMemberKey);
ke han81a38b92017-03-10 18:41:44 +0800445
446 if (join) {
Andrea Campanella2c70a572020-06-05 13:31:45 +0200447 log.debug("Received join on {} for vlan {}", cp, vlan);
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300448 igmpStatisticsManager.increaseStat(IgmpStatisticType.IGMP_JOIN_REQ);
Andrea Campanella2c70a572020-06-05 13:31:45 +0200449
ke han81a38b92017-03-10 18:41:44 +0800450 if (groupMember == null) {
Esin Karamaneff10392019-06-27 18:09:13 +0000451 Optional<ConnectPoint> sourceConfigured = getSource();
452 if (!sourceConfigured.isPresent()) {
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300453 igmpStatisticsManager.increaseStat(IgmpStatisticType.IGMP_FAIL_JOIN_REQ);
Esin Karamaneff10392019-06-27 18:09:13 +0000454 log.warn("Unable to process IGMP Join from {} since no source " +
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000455 "configuration is found.", deviceId);
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300456 igmpStatisticsManager
457 .increaseStat(IgmpStatisticType.FAIL_JOIN_REQ_INSUFF_PERMISSION_ACCESS_COUNTER);
Esin Karamaneff10392019-06-27 18:09:13 +0000458 return;
459 }
Esin Karaman00e16b72020-02-21 10:32:39 +0000460
461 Optional<PortNumber> deviceUplink = getDeviceUplink(deviceId);
462 if (deviceUplink.isEmpty()) {
463 log.warn("Unable to process IGMP Join since uplink port " +
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000464 "of the device {} is not found.", deviceId);
Esin Karaman00e16b72020-02-21 10:32:39 +0000465 return;
466 }
467
468 if (igmpType == IGMP.TYPE_IGMPV2_MEMBERSHIP_REPORT) {
469 groupMember = new GroupMember(groupIp, vlan, deviceId, portNumber, true);
470 } else {
471 groupMember = new GroupMember(groupIp, vlan, deviceId, portNumber, false);
472 }
473
Esin Karamaneff10392019-06-27 18:09:13 +0000474 HashSet<ConnectPoint> sourceConnectPoints = Sets.newHashSet(sourceConfigured.get());
475
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000476 boolean isJoined = stateMachineService.join(deviceId, groupIp, srcIp, deviceUplink.get());
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000477 if (isJoined) {
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300478 igmpStatisticsManager.increaseStat(IgmpStatisticType.IGMP_SUCCESS_JOIN_RE_JOIN_REQ);
479 igmpStatisticsManager.increaseStat(IgmpStatisticType.IGMP_CHANNEL_JOIN_COUNTER);
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000480 } else {
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300481 igmpStatisticsManager.increaseStat(IgmpStatisticType.IGMP_FAIL_JOIN_REQ);
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000482 }
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000483 groupMemberStore.putGroupMember(groupMember);
484 log.debug("Group member created with id: {}", groupMember.getGroupMemberId());
ke han81a38b92017-03-10 18:41:44 +0800485 groupMember.updateList(recordType, sourceList);
Esin Karamaneff10392019-06-27 18:09:13 +0000486 groupMember.getSourceList().forEach(source -> {
487 McastRoute route = new McastRoute(source, groupIp, McastRoute.Type.IGMP);
488 //add route
489 multicastService.add(route);
490 //add source to the route
491 multicastService.addSources(route, Sets.newHashSet(sourceConnectPoints));
492 //add sink to the route
493 multicastService.addSinks(route, Sets.newHashSet(cp));
494 });
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300495 igmpStatisticsManager.increaseStat(IgmpStatisticType.UNCONFIGURED_GROUP_COUNTER);
Esin Karamaneff10392019-06-27 18:09:13 +0000496
ke han81a38b92017-03-10 18:41:44 +0800497 }
498 groupMember.resetAllTimers();
499 groupMember.updateList(recordType, sourceList);
500 groupMember.setLeave(false);
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000501 //put updated member to the store
502 groupMemberStore.putGroupMember(groupMember);
ke han81a38b92017-03-10 18:41:44 +0800503 } else {
Andrea Campanella2c70a572020-06-05 13:31:45 +0200504 log.debug("Received leave on {} for vlan {}", cp, vlan);
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300505 igmpStatisticsManager.increaseStat(IgmpStatisticType.IGMP_LEAVE_REQ);
ke han81a38b92017-03-10 18:41:44 +0800506 if (groupMember == null) {
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000507 log.info("receive leave but no instance, group {} device: {} port:{}",
508 groupIp, deviceId, portNumber);
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300509 igmpStatisticsManager.increaseStat(IgmpStatisticType.UNCONFIGURED_GROUP_COUNTER);
ke han81a38b92017-03-10 18:41:44 +0800510 return;
511 } else {
512 groupMember.setLeave(true);
513 if (fastLeave) {
514 leaveAction(groupMember);
515 } else {
516 sendQuery(groupMember);
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000517 //put modified group member object to the store
518 groupMemberStore.updateGroupMember(groupMember);
ke han81a38b92017-03-10 18:41:44 +0800519 }
520 }
521 }
522 }
523
524 private void leaveAction(GroupMember groupMember) {
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300525 igmpStatisticsManager.increaseStat(IgmpStatisticType.IGMP_DISCONNECT);
ke han81a38b92017-03-10 18:41:44 +0800526 ConnectPoint cp = new ConnectPoint(groupMember.getDeviceId(), groupMember.getPortNumber());
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000527 stateMachineService.leave(groupMember.getDeviceId(), groupMember.getGroupIp());
Esin Karamaneff10392019-06-27 18:09:13 +0000528 groupMember.getSourceList().forEach(source -> multicastService.removeSinks(
ke han81a38b92017-03-10 18:41:44 +0800529 new McastRoute(source, groupMember.getGroupIp(),
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000530 McastRoute.Type.IGMP), Sets.newHashSet(cp)));
531 groupMemberStore.removeGroupMember(groupMember.getGroupMemberId());
ke han81a38b92017-03-10 18:41:44 +0800532 }
533
534 private void sendQuery(GroupMember groupMember) {
535 Ethernet ethpkt;
536 Ip4Address srcIp = getDeviceIp(groupMember.getDeviceId());
537 if (groupMember.getv2()) {
Esin Karaman586f1d62020-06-04 10:15:34 +0000538 ethpkt = IgmpSender.getInstance().buildIgmpV2Query(groupMember.getGroupIp(),
539 srcIp, groupMember.getvlan().toShort());
ke han81a38b92017-03-10 18:41:44 +0800540 } else {
Esin Karaman586f1d62020-06-04 10:15:34 +0000541 ethpkt = IgmpSender.getInstance().buildIgmpV3Query(groupMember.getGroupIp(),
542 srcIp, groupMember.getvlan().toShort());
ke han81a38b92017-03-10 18:41:44 +0800543 }
Esin Karaman586f1d62020-06-04 10:15:34 +0000544 log.debug("Sending IGMP query to {}/{} for group {}: {}",
545 groupMember.getDeviceId(), groupMember.getPortNumber(), groupMember.getGroupIp(), ethpkt);
ke han81a38b92017-03-10 18:41:44 +0800546 IgmpSender.getInstance().sendIgmpPacket(ethpkt, groupMember.getDeviceId(), groupMember.getPortNumber());
547 }
548
Esin Karamaneff10392019-06-27 18:09:13 +0000549 /**
550 * @return connect point of the source if configured; and empty Optional otherwise.
551 */
552 public static Optional<ConnectPoint> getSource() {
553 return sourceDeviceAndPort == null ? Optional.empty() :
554 Optional.of(sourceDeviceAndPort);
ke han81a38b92017-03-10 18:41:44 +0800555 }
556
557 /**
558 * Packet processor responsible for forwarding packets along their paths.
559 */
560 private class IgmpPacketProcessor implements PacketProcessor {
onurka85b2d9c2021-01-19 22:49:19 +0300561
ke han81a38b92017-03-10 18:41:44 +0800562 @Override
563 public void process(PacketContext context) {
Esin Karamanb38700c2019-09-17 13:01:25 +0000564 eventExecutor.execute(() -> {
565 try {
566 InboundPacket pkt = context.inPacket();
onurka85b2d9c2021-01-19 22:49:19 +0300567 log.debug("IgmpPacketProcessor shall process InboundPacket: {}", pkt);
Esin Karamanb38700c2019-09-17 13:01:25 +0000568 Ethernet ethPkt = pkt.parsed();
569 if (ethPkt == null) {
570 return;
571 }
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300572 igmpStatisticsManager.increaseStat(IgmpStatisticType.TOTAL_MSG_RECEIVED);
ke han81a38b92017-03-10 18:41:44 +0800573
Esin Karamanb38700c2019-09-17 13:01:25 +0000574 if (ethPkt.getEtherType() != Ethernet.TYPE_IPV4) {
575 return;
576 }
ke han81a38b92017-03-10 18:41:44 +0800577
Esin Karamanb38700c2019-09-17 13:01:25 +0000578 IPv4 ipv4Pkt = (IPv4) ethPkt.getPayload();
ke han81a38b92017-03-10 18:41:44 +0800579
Esin Karamanb38700c2019-09-17 13:01:25 +0000580 if (ipv4Pkt.getProtocol() != IPv4.PROTOCOL_IGMP) {
581 return;
582 }
ke han81a38b92017-03-10 18:41:44 +0800583
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300584 igmpStatisticsManager.increaseStat(IgmpStatisticType.IGMP_VALID_CHECKSUM_COUNTER);
Esin Karamanb38700c2019-09-17 13:01:25 +0000585 short vlan = ethPkt.getVlanID();
586 DeviceId deviceId = pkt.receivedFrom().deviceId();
ke han81a38b92017-03-10 18:41:44 +0800587
Esin Karaman00e16b72020-02-21 10:32:39 +0000588 if (!isConnectPoint(deviceId, pkt.receivedFrom().port()) &&
589 !getSubscriberAndDeviceInformation(deviceId).isPresent()) {
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000590 log.error("Device not registered in netcfg : {}", deviceId);
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300591 igmpStatisticsManager
592 .increaseStat(IgmpStatisticType.FAIL_JOIN_REQ_INSUFF_PERMISSION_ACCESS_COUNTER);
Esin Karamanb38700c2019-09-17 13:01:25 +0000593 return;
594 }
ke han81a38b92017-03-10 18:41:44 +0800595
Esin Karamanb38700c2019-09-17 13:01:25 +0000596 IGMP igmp = (IGMP) ipv4Pkt.getPayload();
Esin Karamance5ce512020-02-25 15:58:14 +0000597
598 Optional<PortNumber> deviceUpLinkOpt = getDeviceUplink(deviceId);
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000599 PortNumber upLinkPort = deviceUpLinkOpt.isPresent() ? deviceUpLinkOpt.get() : null;
Esin Karamanb38700c2019-09-17 13:01:25 +0000600 switch (igmp.getIgmpType()) {
601 case IGMP.TYPE_IGMPV3_MEMBERSHIP_QUERY:
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300602 igmpStatisticsManager.increaseStat(IgmpStatisticType.IGMP_V3_MEMBERSHIP_QUERY);
Esin Karamanb38700c2019-09-17 13:01:25 +0000603 //Discard Query from OLT’s non-uplink port’s
Esin Karamance5ce512020-02-25 15:58:14 +0000604 if (!pkt.receivedFrom().port().equals(upLinkPort)) {
Esin Karamanb38700c2019-09-17 13:01:25 +0000605 if (isConnectPoint(deviceId, pkt.receivedFrom().port())) {
606 log.info("IGMP Picked up query from connectPoint");
607 //OK to process packet
608 processIgmpConnectPointQuery((IGMPQuery) igmp.getGroups().get(0),
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000609 pkt.receivedFrom(),
610 0xff & igmp.getMaxRespField());
Esin Karamanb38700c2019-09-17 13:01:25 +0000611 break;
612 } else {
613 //Not OK to process packet
Esin Karamance5ce512020-02-25 15:58:14 +0000614 log.warn("IGMP Picked up query from non-uplink port {}", upLinkPort);
Esin Karamanb38700c2019-09-17 13:01:25 +0000615 return;
616 }
617 }
ke han81a38b92017-03-10 18:41:44 +0800618
Esin Karamanb38700c2019-09-17 13:01:25 +0000619 processIgmpQuery((IGMPQuery) igmp.getGroups().get(0), pkt.receivedFrom(),
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000620 0xff & igmp.getMaxRespField());
Esin Karamanb38700c2019-09-17 13:01:25 +0000621 break;
622 case IGMP.TYPE_IGMPV1_MEMBERSHIP_REPORT:
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300623 igmpStatisticsManager.increaseStat(IgmpStatisticType.IGMP_V1_MEMBERSHIP_REPORT);
Esin Karamanb38700c2019-09-17 13:01:25 +0000624 log.debug("IGMP version 1 message types are not currently supported.");
625 break;
626 case IGMP.TYPE_IGMPV3_MEMBERSHIP_REPORT:
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300627 igmpStatisticsManager.increaseStat(IgmpStatisticType.IGMP_V3_MEMBERSHIP_REPORT);
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000628 processIgmpMessage(pkt, igmp, upLinkPort, vlan);
629 break;
Esin Karamanb38700c2019-09-17 13:01:25 +0000630 case IGMP.TYPE_IGMPV2_MEMBERSHIP_REPORT:
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300631 igmpStatisticsManager.increaseStat(IgmpStatisticType.IGMP_V2_MEMBERSHIP_REPORT);
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000632 processIgmpMessage(pkt, igmp, upLinkPort, vlan);
633 break;
Esin Karamanb38700c2019-09-17 13:01:25 +0000634 case IGMP.TYPE_IGMPV2_LEAVE_GROUP:
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300635 igmpStatisticsManager.increaseStat(IgmpStatisticType.IGMP_V2_LEAVE_GROUP);
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000636 processIgmpMessage(pkt, igmp, upLinkPort, vlan);
Esin Karamanb38700c2019-09-17 13:01:25 +0000637 break;
ke han81a38b92017-03-10 18:41:44 +0800638
Esin Karamanb38700c2019-09-17 13:01:25 +0000639 default:
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000640 log.warn("Unknown IGMP message type: {}", igmp.getIgmpType());
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300641 igmpStatisticsManager.increaseStat(IgmpStatisticType.INVALID_IGMP_MSG_RECEIVED);
642 igmpStatisticsManager.increaseStat(IgmpStatisticType.UNKNOWN_IGMP_TYPE_PACKETS_RX_COUNTER);
Esin Karamanb38700c2019-09-17 13:01:25 +0000643 break;
644 }
645
646 } catch (Exception ex) {
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000647 log.error("igmp process error : ", ex);
ke han81a38b92017-03-10 18:41:44 +0800648 }
Esin Karamanb38700c2019-09-17 13:01:25 +0000649 });
ke han81a38b92017-03-10 18:41:44 +0800650 }
651 }
652
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000653 private void processIgmpMessage(InboundPacket pkt, IGMP igmp, PortNumber upLinkPort, short vlan) {
654 //Discard join/leave from OLT’s uplink port’s
655 if (pkt.receivedFrom().port().equals(upLinkPort) ||
656 isConnectPoint(pkt.receivedFrom().deviceId(), pkt.receivedFrom().port())) {
657 log.info("IGMP Picked up join/leave from uplink/connectPoint port");
658 return;
659 }
660
661 Iterator<IGMPGroup> itr = igmp.getGroups().iterator();
662 while (itr.hasNext()) {
663 IGMPGroup group = itr.next();
onurka85b2d9c2021-01-19 22:49:19 +0300664 log.debug("IGMPGroup {}", group.getGaddr());
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000665 if (group instanceof IGMPMembership) {
onurka85b2d9c2021-01-19 22:49:19 +0300666 queueIgmpReport((IGMPMembership) group, VlanId.vlanId(vlan),
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000667 pkt.receivedFrom(), igmp.getIgmpType());
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000668 } else {
669 IGMPMembership mgroup = new IGMPMembership(group.getGaddr().getIp4Address());
670 mgroup.setRecordType(igmp.getIgmpType() == IGMP.TYPE_IGMPV2_MEMBERSHIP_REPORT ?
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000671 IGMPMembership.MODE_IS_EXCLUDE :
672 IGMPMembership.MODE_IS_INCLUDE);
onurka85b2d9c2021-01-19 22:49:19 +0300673 queueIgmpReport(mgroup, VlanId.vlanId(vlan),
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000674 pkt.receivedFrom(), igmp.getIgmpType());
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000675 }
676 }
677
678 }
679
ke han81a38b92017-03-10 18:41:44 +0800680 private class IgmpProxyTimerTask extends TimerTask {
681 public void run() {
682 try {
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000683 stateMachineService.timeOut1s();
ke han81a38b92017-03-10 18:41:44 +0800684 queryMembers();
685 } catch (Exception ex) {
686 log.warn("Igmp timer task error : {}", ex.getMessage());
687 }
688 }
689
690 private void queryMembers() {
691 GroupMember groupMember;
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000692 Set<GroupMemberId> keySet = groupMemberStore.getAllGroupMemberIds();
693 for (GroupMemberId key : keySet) {
694 groupMember = groupMemberStore.getGroupMember(key);
695 if (groupMember == null) {
696 continue;
697 }
ke han81a38b92017-03-10 18:41:44 +0800698 DeviceId did = groupMember.getDeviceId();
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000699 if (igmpLeadershipService.isLocalLeader(did)) {
ke han81a38b92017-03-10 18:41:44 +0800700 if (groupMember.isLeave()) {
701 lastQuery(groupMember);
702 } else if (periodicQuery) {
703 periodicQuery(groupMember);
704 }
705 }
706 }
707 }
708
709 private void lastQuery(GroupMember groupMember) {
710 if (groupMember.getLastQueryInterval() < lastQueryInterval) {
711 groupMember.lastQueryInterval(true); // count times
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000712 //put modified group member object to the store
713 groupMemberStore.updateGroupMember(groupMember);
ke han81a38b92017-03-10 18:41:44 +0800714 } else if (groupMember.getLastQueryCount() < lastQueryCount - 1) {
715 sendQuery(groupMember);
716 groupMember.lastQueryInterval(false); // reset count number
717 groupMember.lastQueryCount(true); //count times
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000718 //put modified group member object to the store
719 groupMemberStore.updateGroupMember(groupMember);
ke han81a38b92017-03-10 18:41:44 +0800720 } else if (groupMember.getLastQueryCount() == lastQueryCount - 1) {
721 leaveAction(groupMember);
722 }
723 }
724
725 private void periodicQuery(GroupMember groupMember) {
726 if (groupMember.getKeepAliveQueryInterval() < keepAliveInterval) {
727 groupMember.keepAliveInterval(true);
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000728 //put modified group member object to the store
729 groupMemberStore.updateGroupMember(groupMember);
ke han81a38b92017-03-10 18:41:44 +0800730 } else if (groupMember.getKeepAliveQueryCount() < keepAliveCount) {
731 sendQuery(groupMember);
732 groupMember.keepAliveInterval(false);
733 groupMember.keepAliveQueryCount(true);
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000734 //put modified group member object to the store
735 groupMemberStore.updateGroupMember(groupMember);
ke han81a38b92017-03-10 18:41:44 +0800736 } else if (groupMember.getKeepAliveQueryCount() == keepAliveCount) {
737 leaveAction(groupMember);
738 }
739 }
740
741 }
742
Esin Karaman00e16b72020-02-21 10:32:39 +0000743 public Optional<PortNumber> getDeviceUplink(DeviceId devId) {
744 Device device = deviceService.getDevice(devId);
745 if (device == null || device.serialNumber() == null) {
746 return Optional.empty();
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000747 }
Esin Karaman00e16b72020-02-21 10:32:39 +0000748 Optional<SubscriberAndDeviceInformation> olt = getSubscriberAndDeviceInformation(device.serialNumber());
749 if (olt.isEmpty()) {
750 return Optional.empty();
751 }
752 PortNumber portNumber = PortNumber.portNumber(olt.get().uplinkPort());
753 return validateUpLinkPort(device.id(), portNumber) ?
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000754 Optional.of(portNumber) : Optional.empty();
Esin Karaman00e16b72020-02-21 10:32:39 +0000755 }
756
757 /**
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000758 * @param deviceId device id
Esin Karaman00e16b72020-02-21 10:32:39 +0000759 * @param portNumber port number
760 * @return true if the port name starts with NNI_PREFIX; false otherwise.
761 */
762 public boolean validateUpLinkPort(DeviceId deviceId, PortNumber portNumber) {
763 Port port = deviceService.getPort(deviceId, portNumber);
764 if (port == null) {
765 //port is not discovered by ONOS; so cannot validate it.
766 return false;
767 }
Esin Karamance5ce512020-02-25 15:58:14 +0000768 boolean isValid = port.annotations().value(AnnotationKeys.PORT_NAME) != null &&
Esin Karaman00e16b72020-02-21 10:32:39 +0000769 port.annotations().value(AnnotationKeys.PORT_NAME).startsWith(NNI_PREFIX);
Esin Karamance5ce512020-02-25 15:58:14 +0000770 if (!isValid) {
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000771 log.warn("Port cannot be validated; it is not configured as an NNI port. Device/port: {}/{}",
772 deviceId, portNumber);
Esin Karamance5ce512020-02-25 15:58:14 +0000773 }
774 return isValid;
ke han81a38b92017-03-10 18:41:44 +0800775 }
776
Esin Karamanb38700c2019-09-17 13:01:25 +0000777 public static boolean isIgmpOnPodBasis() {
778 return igmpOnPodBasis;
779 }
780
ke han81a38b92017-03-10 18:41:44 +0800781 private void processFilterObjective(DeviceId devId, PortNumber port, boolean remove) {
Esin Karamaneff10392019-06-27 18:09:13 +0000782 if (!enableIgmpProvisioning) {
783 log.debug("IGMP trap rules won't be installed since enableIgmpProvisioning flag is not set");
784 return;
785 }
ke han81a38b92017-03-10 18:41:44 +0800786 //TODO migrate to packet requests when packet service uses filtering objectives
787 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
788
789 builder = remove ? builder.deny() : builder.permit();
790
791 FilteringObjective igmp = builder
792 .withKey(Criteria.matchInPort(port))
793 .addCondition(Criteria.matchEthType(EthType.EtherType.IPV4.ethType()))
794 .addCondition(Criteria.matchIPProtocol(IPv4.PROTOCOL_IGMP))
795 .withMeta(DefaultTrafficTreatment.builder().setOutput(PortNumber.CONTROLLER).build())
796 .fromApp(appId)
Esin Karamaneff10392019-06-27 18:09:13 +0000797 .withPriority(MAX_PRIORITY)
ke han81a38b92017-03-10 18:41:44 +0800798 .add(new ObjectiveContext() {
799 @Override
800 public void onSuccess(Objective objective) {
Esin Karamaneff10392019-06-27 18:09:13 +0000801 log.info("Igmp filter for {} on {} {}.",
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000802 devId, port, (remove) ? REMOVED : INSTALLED);
ke han81a38b92017-03-10 18:41:44 +0800803 }
804
805 @Override
806 public void onError(Objective objective, ObjectiveError error) {
Esin Karamaneff10392019-06-27 18:09:13 +0000807 log.info("Igmp filter {} for device {} on port {} failed because of {}",
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000808 (remove) ? INSTALLATION : REMOVAL, devId, port,
809 error);
ke han81a38b92017-03-10 18:41:44 +0800810 }
811 });
812
813 flowObjectiveService.filter(devId, igmp);
Esin Karamaneff10392019-06-27 18:09:13 +0000814
ke han81a38b92017-03-10 18:41:44 +0800815 }
816
817 private boolean isConnectPoint(DeviceId device, PortNumber port) {
Deepa Vaddireddy01f33b42017-07-02 13:26:53 +0530818 if (connectPoint != null) {
819 return (connectPointMode && connectPoint.deviceId().equals(device)
820 && connectPoint.port().equals(port));
821 } else {
Andrea Campanella2c70a572020-06-05 13:31:45 +0200822 log.debug("connectPoint not configured for device {}", device);
Deepa Vaddireddy01f33b42017-07-02 13:26:53 +0530823 return false;
824 }
ke han81a38b92017-03-10 18:41:44 +0800825 }
Deepa Vaddireddy01f33b42017-07-02 13:26:53 +0530826
ke han81a38b92017-03-10 18:41:44 +0800827 private boolean isUplink(DeviceId device, PortNumber port) {
Esin Karamance5ce512020-02-25 15:58:14 +0000828 if (connectPointMode) {
829 return false;
830 }
831 Optional<PortNumber> upLinkPort = getDeviceUplink(device);
832 return upLinkPort.isPresent() && upLinkPort.get().equals(port);
ke han81a38b92017-03-10 18:41:44 +0800833 }
834
Esin Karaman00e16b72020-02-21 10:32:39 +0000835 /**
836 * Fetches device information associated with the device serial number from SADIS.
837 *
838 * @param serialNumber serial number of a device
839 * @return device information; an empty Optional otherwise.
840 */
841 private Optional<SubscriberAndDeviceInformation> getSubscriberAndDeviceInformation(String serialNumber) {
842 long start = System.currentTimeMillis();
843 try {
844 return Optional.ofNullable(sadisService.getSubscriberInfoService().get(serialNumber));
845 } finally {
846 if (log.isDebugEnabled()) {
847 // SADIS can call remote systems to fetch device data and this calls can take a long time.
848 // This measurement is just for monitoring these kinds of situations.
849 log.debug("Device fetched from SADIS. Elapsed {} msec", System.currentTimeMillis() - start);
850 }
851
852 }
853 }
854
855 /**
856 * Fetches device information associated with the device serial number from SADIS.
857 *
858 * @param deviceId device id
859 * @return device information; an empty Optional otherwise.
860 */
861 private Optional<SubscriberAndDeviceInformation> getSubscriberAndDeviceInformation(DeviceId deviceId) {
862 Device device = deviceService.getDevice(deviceId);
863 if (device == null || device.serialNumber() == null) {
864 return Optional.empty();
865 }
866 return getSubscriberAndDeviceInformation(device.serialNumber());
867 }
868
ke han81a38b92017-03-10 18:41:44 +0800869 private class InternalDeviceListener implements DeviceListener {
870 @Override
871 public void event(DeviceEvent event) {
Esin Karaman09b41e52020-06-01 10:52:55 +0000872 eventExecutor.execute(() -> {
873 DeviceId devId = event.subject().id();
874 Port p = event.port();
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +0000875
Esin Karaman09b41e52020-06-01 10:52:55 +0000876 if (!igmpLeadershipService.isLocalLeader(devId)) {
877 return;
878 }
879 if (getSubscriberAndDeviceInformation(devId).isEmpty() &&
880 !(p != null && isConnectPoint(devId, p.number()))) {
881 return;
882 }
883 PortNumber port;
ke han81a38b92017-03-10 18:41:44 +0800884
Esin Karaman09b41e52020-06-01 10:52:55 +0000885 switch (event.type()) {
886
887 case DEVICE_ADDED:
888 case DEVICE_UPDATED:
889 case DEVICE_REMOVED:
890 case DEVICE_SUSPENDED:
891 case DEVICE_AVAILABILITY_CHANGED:
892 case PORT_STATS_UPDATED:
893 break;
894 case PORT_ADDED:
895 port = p.number();
896 if (getSubscriberAndDeviceInformation(devId).isPresent() &&
897 !isUplink(devId, port) && !isConnectPoint(devId, port)) {
ke han81a38b92017-03-10 18:41:44 +0800898 processFilterObjective(devId, port, false);
Esin Karaman09b41e52020-06-01 10:52:55 +0000899 } else if (isUplink(devId, port)) {
900 provisionUplinkFlows();
901 } else if (isConnectPoint(devId, port)) {
ke han81a38b92017-03-10 18:41:44 +0800902 provisionConnectPointFlows();
ke han81a38b92017-03-10 18:41:44 +0800903 }
Esin Karaman09b41e52020-06-01 10:52:55 +0000904 onSourceStateChanged(devId, port, true);
905 break;
906 case PORT_UPDATED:
907 port = p.number();
908 if (getSubscriberAndDeviceInformation(devId).isPresent() &&
909 !isUplink(devId, port) && !isConnectPoint(devId, port)) {
910 processFilterObjective(devId, port, !event.port().isEnabled());
911 } else if (isUplink(devId, port)) {
912 if (event.port().isEnabled()) {
913 provisionUplinkFlows(devId);
914 } else {
915 processFilterObjective(devId, port, true);
916 }
917 } else if (isConnectPoint(devId, port)) {
918 if (event.port().isEnabled()) {
919 provisionConnectPointFlows();
920 } else {
921 unprovisionConnectPointFlows();
922 }
923 }
924 onSourceStateChanged(devId, port, event.port().isEnabled());
925 break;
926 case PORT_REMOVED:
927 port = p.number();
928 processFilterObjective(devId, port, true);
929 onSourceStateChanged(devId, port, false);
930 break;
931 default:
932 log.info("Unknown device event {}", event.type());
933 break;
934 }
935 });
ke han81a38b92017-03-10 18:41:44 +0800936 }
937
938 @Override
939 public boolean isRelevant(DeviceEvent event) {
940 return true;
941 }
942 }
943
Esin Karaman09b41e52020-06-01 10:52:55 +0000944 private Set<McastRoute> multicastRoutesOfIgmpProxy() {
945 Set<McastRoute> routes = new HashSet<>(); //using set to eliminate multiple entities
946 groupMemberStore.getAllGroupMemberIds().forEach(groupMemberId -> {
947 GroupMember groupMember = groupMemberStore.getGroupMember(groupMemberId);
948 if (groupMember != null) {
949 groupMember.getSourceList().forEach(source -> {
950 //regenerate the routes created by this application
951 routes.add(new McastRoute(source, groupMemberId.getGroupIp(), McastRoute.Type.IGMP));
952 });
953 }
954 });
955 return routes;
956 }
957
958 private void onSourceStateChanged(DeviceId deviceId, PortNumber portNumber, boolean enabled) {
959 if (!(getSource().isPresent() &&
960 getSource().get().deviceId().equals(deviceId) &&
961 getSource().get().port().equals(portNumber))) {
962 //connect point is not configured as the source
963 log.debug("{}/{} is not the source cp. Stopped processing it further", deviceId, portNumber);
964 return;
965 }
966 log.info("source device:port is {}. DeviceId={}, portNumber={}",
967 (enabled ? "enabled. Restoring the source" :
968 "disabled. Deleting it from multicast routes"), deviceId, portNumber);
969
970 Set<McastRoute> routes = multicastRoutesOfIgmpProxy();
971 routes.forEach(route -> {
972 if (enabled) {
973 //add source to the route
974 multicastService.addSources(route, Sets.newHashSet(new ConnectPoint(deviceId, portNumber)));
975 } else {
976 //remove the source from the route
977 multicastService.removeSources(route, Sets.newHashSet(new ConnectPoint(deviceId, portNumber)));
978 }
979 });
980 }
981
ke han81a38b92017-03-10 18:41:44 +0800982 private class InternalNetworkConfigListener implements NetworkConfigListener {
983
984 private void reconfigureNetwork(IgmpproxyConfig cfg) {
Esin Karamaneff10392019-06-27 18:09:13 +0000985 IgmpproxyConfig newCfg = cfg == null ? new IgmpproxyConfig() : cfg;
ke han81a38b92017-03-10 18:41:44 +0800986
987 unSolicitedTimeout = newCfg.unsolicitedTimeOut();
988 maxResp = newCfg.maxResp();
989 keepAliveInterval = newCfg.keepAliveInterval();
990 keepAliveCount = newCfg.keepAliveCount();
991 lastQueryInterval = newCfg.lastQueryInterval();
992 lastQueryCount = newCfg.lastQueryCount();
993 withRAUplink = newCfg.withRAUplink();
994 withRADownlink = newCfg.withRADownlink();
995 igmpCos = newCfg.igmpCos();
Esin Karaman586f1d62020-06-04 10:15:34 +0000996 igmpUniCos = newCfg.igmpUniCos(); // defines priority bit of IGMP query message sent to UNI ports
ke han81a38b92017-03-10 18:41:44 +0800997 periodicQuery = newCfg.periodicQuery();
998 fastLeave = newCfg.fastLeave();
ke han29af27b2017-09-08 10:29:12 +0800999 pimSSmInterworking = newCfg.pimSsmInterworking();
Esin Karamaneff10392019-06-27 18:09:13 +00001000 enableIgmpProvisioning = newCfg.enableIgmpProvisioning();
Esin Karamanb38700c2019-09-17 13:01:25 +00001001 igmpOnPodBasis = newCfg.igmpOnPodBasis();
onurka85b2d9c2021-01-19 22:49:19 +03001002 if (numberOfIgmpReportProcessorThreads != newCfg.numberOfIgmpReportProcessorThreads()) {
1003 numberOfIgmpReportProcessorThreads = newCfg.numberOfIgmpReportProcessorThreads();
1004 shutdownIgmpReportProcessServiceExecutors();
1005 initializeIgmpReportProcessServiceExecutors();
1006 }
Arjun E Kb0018fd2020-04-07 13:26:40 +00001007 if (newCfg.outgoingIgmpWithV3() != null &&
1008 outgoingIgmpWithV3 != newCfg.outgoingIgmpWithV3()) {
1009 outgoingIgmpWithV3 = newCfg.outgoingIgmpWithV3();
1010 }
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +00001011
1012 if (connectPointMode != newCfg.connectPointMode() ||
1013 connectPoint != newCfg.connectPoint()) {
ke han81a38b92017-03-10 18:41:44 +08001014 connectPointMode = newCfg.connectPointMode();
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +00001015 connectPoint = newCfg.connectPoint();
ke han81a38b92017-03-10 18:41:44 +08001016 if (connectPointMode) {
1017 unprovisionUplinkFlows();
1018 provisionConnectPointFlows();
1019 } else {
1020 unprovisionConnectPointFlows();
1021 provisionUplinkFlows();
1022 }
1023 }
1024 if (connectPoint != null) {
Esin Karamaneff10392019-06-27 18:09:13 +00001025 log.info("connect point : {}", connectPoint);
ke han81a38b92017-03-10 18:41:44 +08001026 }
Esin Karamaneff10392019-06-27 18:09:13 +00001027 log.info("mode: {}", connectPointMode);
1028
1029 getSourceConnectPoint(newCfg);
ke han81a38b92017-03-10 18:41:44 +08001030
1031 IgmpSender.getInstance().setIgmpCos(igmpCos);
Esin Karaman586f1d62020-06-04 10:15:34 +00001032 IgmpSender.getInstance().setIgmpUniCos(igmpUniCos);
ke han81a38b92017-03-10 18:41:44 +08001033 IgmpSender.getInstance().setMaxResp(maxResp);
1034 IgmpSender.getInstance().setMvlan(mvlan);
Esin Karaman586f1d62020-06-04 10:15:34 +00001035 IgmpSender.getInstance().setMvlanInner(mvlanInner);
ke han81a38b92017-03-10 18:41:44 +08001036 IgmpSender.getInstance().setWithRADownlink(withRADownlink);
1037 IgmpSender.getInstance().setWithRAUplink(withRAUplink);
Esin Karamaneff10392019-06-27 18:09:13 +00001038 }
ke han81a38b92017-03-10 18:41:44 +08001039
Esin Karamaneff10392019-06-27 18:09:13 +00001040 void getSourceConnectPoint(IgmpproxyConfig cfg) {
Esin Karaman09b41e52020-06-01 10:52:55 +00001041 ConnectPoint oldSourceDevPort = sourceDeviceAndPort;
Esin Karamaneff10392019-06-27 18:09:13 +00001042 sourceDeviceAndPort = cfg.getSourceDeviceAndPort();
1043 if (sourceDeviceAndPort != null) {
1044 log.debug("source parameter configured to {}", sourceDeviceAndPort);
1045 }
Esin Karaman09b41e52020-06-01 10:52:55 +00001046 if (oldSourceDevPort != null && !oldSourceDevPort.equals(sourceDeviceAndPort)) {
1047 //source config has changed, remove the old source from multicast routes
1048 onSourceStateChanged(oldSourceDevPort.deviceId(), oldSourceDevPort.port(), false);
1049 }
1050 if (sourceDeviceAndPort != null && !sourceDeviceAndPort.equals(oldSourceDevPort)) {
1051 //add new source to the existing routes
1052 onSourceStateChanged(sourceDeviceAndPort.deviceId(), sourceDeviceAndPort.port(), true);
1053 }
ke han81a38b92017-03-10 18:41:44 +08001054 }
1055
1056 public void reconfigureSsmTable(IgmpproxySsmTranslateConfig cfg) {
1057 if (cfg == null) {
1058 return;
1059 }
1060 Collection<McastRoute> translations = cfg.getSsmTranslations();
1061 for (McastRoute route : translations) {
Esin Karamaneff10392019-06-27 18:09:13 +00001062 ssmTranslateTable.put(route.group().getIp4Address(), route.source().get().getIp4Address());
ke han81a38b92017-03-10 18:41:44 +08001063 }
1064 }
1065
1066 @Override
1067 public void event(NetworkConfigEvent event) {
1068 switch (event.type()) {
1069 case CONFIG_ADDED:
1070 case CONFIG_UPDATED:
Matteo Scandolo7482bbe2020-02-12 14:37:09 -08001071 // NOTE how to know if something has changed in sadis?
ke han81a38b92017-03-10 18:41:44 +08001072
1073 if (event.configClass().equals(IGMPPROXY_CONFIG_CLASS)) {
1074 IgmpproxyConfig config = networkConfig.getConfig(appId, IGMPPROXY_CONFIG_CLASS);
1075 if (config != null) {
Esin Karamaneff10392019-06-27 18:09:13 +00001076 log.info("igmpproxy config received. {}", config);
ke han81a38b92017-03-10 18:41:44 +08001077 reconfigureNetwork(config);
1078 }
1079 }
1080
1081 if (event.configClass().equals(IGMPPROXY_SSM_CONFIG_CLASS)) {
1082 IgmpproxySsmTranslateConfig config = networkConfig.getConfig(appId, IGMPPROXY_SSM_CONFIG_CLASS);
1083 if (config != null) {
1084 reconfigureSsmTable(config);
1085 }
1086 }
1087
1088 if (event.configClass().equals(MCAST_CONFIG_CLASS)) {
1089 McastConfig config = networkConfig.getConfig(coreAppId, MCAST_CONFIG_CLASS);
Esin Karaman586f1d62020-06-04 10:15:34 +00001090 boolean vlanConfigChanged = config != null && mvlan != config.egressVlan().toShort();
1091 boolean innerVlanConfigChanged = config != null &&
1092 mvlanInner != config.egressInnerVlan().toShort();
1093
1094 if (vlanConfigChanged || innerVlanConfigChanged) {
1095 log.info("igmpproxy vlan config received. {}", config);
1096 //at least one of the vlan configs has changed. Call leave before setting new values
Esin Karaman4a9075d2020-07-14 14:46:14 +00001097 groupMemberStore.getAllGroupMembers().forEach(m -> {
1098 if (igmpLeadershipService.isLocalLeader(m.getDeviceId())) {
1099 leaveAction(m);
1100 }
1101 });
Esin Karaman586f1d62020-06-04 10:15:34 +00001102 if (vlanConfigChanged) {
1103 mvlan = config.egressVlan().toShort();
1104 IgmpSender.getInstance().setMvlan(mvlan);
1105 }
1106 if (innerVlanConfigChanged) {
1107 mvlanInner = config.egressInnerVlan().toShort();
1108 IgmpSender.getInstance().setMvlanInner(mvlanInner);
1109 }
ke han81a38b92017-03-10 18:41:44 +08001110 }
1111 }
1112
1113 log.info("Reconfigured");
1114 break;
1115 case CONFIG_REGISTERED:
1116 case CONFIG_UNREGISTERED:
1117 break;
1118 case CONFIG_REMOVED:
Matteo Scandolo7482bbe2020-02-12 14:37:09 -08001119 // NOTE how to know if something has changed in sadis?
ke han81a38b92017-03-10 18:41:44 +08001120 default:
1121 break;
1122 }
1123 }
1124 }
1125
ke han81a38b92017-03-10 18:41:44 +08001126 private void provisionUplinkFlows(DeviceId deviceId) {
1127 if (connectPointMode) {
1128 return;
1129 }
1130
Esin Karaman00e16b72020-02-21 10:32:39 +00001131 Optional<PortNumber> upLink = getDeviceUplink(deviceId);
1132 if (upLink.isPresent()) {
1133 processFilterObjective(deviceId, upLink.get(), false);
1134 }
ke han81a38b92017-03-10 18:41:44 +08001135 }
1136
1137 private void provisionUplinkFlows() {
1138 if (connectPointMode) {
1139 return;
1140 }
Esin Karaman00e16b72020-02-21 10:32:39 +00001141 deviceService.getAvailableDevices().forEach(device -> {
1142 Optional<SubscriberAndDeviceInformation> accessDevice = getSubscriberAndDeviceInformation(device.id());
1143 if (accessDevice.isPresent()) {
1144 provisionUplinkFlows(device.id());
1145 }
1146 });
ke han81a38b92017-03-10 18:41:44 +08001147 }
Esin Karaman00e16b72020-02-21 10:32:39 +00001148
ke han81a38b92017-03-10 18:41:44 +08001149 private void unprovisionUplinkFlows() {
Esin Karaman00e16b72020-02-21 10:32:39 +00001150 deviceService.getAvailableDevices().forEach(device -> {
1151 Optional<SubscriberAndDeviceInformation> accessDevices = getSubscriberAndDeviceInformation(device.id());
1152 if (accessDevices.isPresent()) {
1153 Optional<PortNumber> upLink = getDeviceUplink(device.id());
1154 if (upLink.isPresent()) {
1155 processFilterObjective(device.id(), upLink.get(), true);
1156 }
1157 }
1158 });
ke han81a38b92017-03-10 18:41:44 +08001159 }
1160
1161 private void provisionConnectPointFlows() {
1162 if ((!connectPointMode) || connectPoint == null) {
1163 return;
1164 }
1165
1166 processFilterObjective(connectPoint.deviceId(), connectPoint.port(), false);
1167 }
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +00001168
ke han81a38b92017-03-10 18:41:44 +08001169 private void unprovisionConnectPointFlows() {
1170 if (connectPoint == null) {
1171 return;
1172 }
1173 processFilterObjective(connectPoint.deviceId(), connectPoint.port(), true);
1174 }
Shubham Sharma47f2caf2020-02-18 12:13:40 +00001175
onurka85b2d9c2021-01-19 22:49:19 +03001176}