blob: 133ff185682d13d997e31c229b1fbcd3d3e6e475 [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;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -080019import org.onosproject.net.Device;
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +000020import org.opencord.igmpproxy.IgmpLeadershipService;
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +030021import org.opencord.igmpproxy.IgmpStatisticType;
developere400c582020-03-24 19:42:08 +010022import org.opencord.igmpproxy.IgmpStatisticsService;
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +000023import org.opencord.igmpproxy.impl.store.groupmember.GroupMember;
24import org.opencord.igmpproxy.GroupMemberId;
25import org.opencord.igmpproxy.impl.store.groupmember.GroupMemberStore;
26import org.opencord.igmpproxy.statemachine.StateMachineService;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -080027import org.opencord.sadis.BaseInformationService;
28import org.opencord.sadis.SadisService;
29import org.opencord.sadis.SubscriberAndDeviceInformation;
Carmelo Casconebef302e2019-11-14 19:58:20 -080030import org.osgi.service.component.annotations.Activate;
31import org.osgi.service.component.annotations.Component;
32import org.osgi.service.component.annotations.Deactivate;
33import org.osgi.service.component.annotations.Reference;
34import org.osgi.service.component.annotations.ReferenceCardinality;
ke han81a38b92017-03-10 18:41:44 +080035import org.onlab.packet.EthType;
36import org.onlab.packet.Ethernet;
37import org.onlab.packet.IGMP;
38import org.onlab.packet.IGMPGroup;
39import org.onlab.packet.IGMPMembership;
40import org.onlab.packet.IGMPQuery;
41import org.onlab.packet.IPv4;
42import org.onlab.packet.Ip4Address;
43import org.onlab.packet.IpAddress;
44import org.onlab.packet.VlanId;
45import org.onosproject.core.ApplicationId;
46import org.onosproject.core.CoreService;
ke han81a38b92017-03-10 18:41:44 +080047import org.onosproject.mastership.MastershipService;
48import org.onosproject.net.AnnotationKeys;
49import org.onosproject.net.ConnectPoint;
50import org.onosproject.net.DeviceId;
51import org.onosproject.net.Port;
52import org.onosproject.net.PortNumber;
53import org.onosproject.net.config.ConfigFactory;
54import org.onosproject.net.config.NetworkConfigEvent;
55import org.onosproject.net.config.NetworkConfigListener;
56import org.onosproject.net.config.NetworkConfigRegistry;
Jonathan Hart488e1142018-05-02 17:30:05 -070057import org.onosproject.net.config.basics.McastConfig;
ke han81a38b92017-03-10 18:41:44 +080058import org.onosproject.net.config.basics.SubjectFactories;
59import org.onosproject.net.device.DeviceEvent;
60import org.onosproject.net.device.DeviceListener;
61import org.onosproject.net.device.DeviceService;
62import org.onosproject.net.flow.DefaultTrafficTreatment;
63import org.onosproject.net.flow.FlowRuleService;
64import org.onosproject.net.flow.criteria.Criteria;
65import org.onosproject.net.flowobjective.DefaultFilteringObjective;
66import org.onosproject.net.flowobjective.FilteringObjective;
67import org.onosproject.net.flowobjective.FlowObjectiveService;
68import org.onosproject.net.flowobjective.Objective;
69import org.onosproject.net.flowobjective.ObjectiveContext;
70import org.onosproject.net.flowobjective.ObjectiveError;
Esin Karamaneff10392019-06-27 18:09:13 +000071import org.onosproject.mcast.api.McastRoute;
72import org.onosproject.mcast.api.MulticastRouteService;
ke han81a38b92017-03-10 18:41:44 +080073import org.onosproject.net.packet.InboundPacket;
74import org.onosproject.net.packet.PacketContext;
75import org.onosproject.net.packet.PacketProcessor;
76import org.onosproject.net.packet.PacketService;
ke han81a38b92017-03-10 18:41:44 +080077import org.slf4j.Logger;
78import org.slf4j.LoggerFactory;
79
80import java.util.ArrayList;
Sonal Kasliwalf11c0672020-03-18 11:11:50 +000081import java.util.Arrays;
82import java.util.List;
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;
ke han81a38b92017-03-10 18:41:44 +080086import java.util.Map;
Esin Karamaneff10392019-06-27 18:09:13 +000087import java.util.Optional;
ke han81a38b92017-03-10 18:41:44 +080088import java.util.Set;
89import java.util.TimerTask;
90import java.util.concurrent.ConcurrentHashMap;
Esin Karamanb38700c2019-09-17 13:01:25 +000091import java.util.concurrent.ExecutorService;
ke han81a38b92017-03-10 18:41:44 +080092import java.util.concurrent.Executors;
93import java.util.concurrent.ScheduledExecutorService;
94import java.util.concurrent.TimeUnit;
95
Sonal Kasliwalf11c0672020-03-18 11:11:50 +000096import static org.onlab.packet.IGMPMembership.MODE_IS_INCLUDE;
97import static org.onlab.packet.IGMPMembership.MODE_IS_EXCLUDE;
98import static org.onlab.packet.IGMPMembership.CHANGE_TO_INCLUDE_MODE;
99import static org.onlab.packet.IGMPMembership.CHANGE_TO_EXCLUDE_MODE;
100import static org.onlab.packet.IGMPMembership.ALLOW_NEW_SOURCES;
101import static org.onlab.packet.IGMPMembership.BLOCK_OLD_SOURCES;
102
Esin Karamanb38700c2019-09-17 13:01:25 +0000103import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
104import static org.onlab.util.Tools.groupedThreads;
105
ke han81a38b92017-03-10 18:41:44 +0800106/**
107 * Igmp process application, use proxy mode, support first join/ last leave , fast leave
108 * period query and keep alive, packet out igmp message to uplink port features.
109 */
110@Component(immediate = true)
111public class IgmpManager {
112
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800113 private static final String APP_NAME = "org.opencord.igmpproxy";
114
ke han81a38b92017-03-10 18:41:44 +0800115 private static final Class<IgmpproxyConfig> IGMPPROXY_CONFIG_CLASS =
116 IgmpproxyConfig.class;
117 private static final Class<IgmpproxySsmTranslateConfig> IGMPPROXY_SSM_CONFIG_CLASS =
118 IgmpproxySsmTranslateConfig.class;
119 private static final Class<McastConfig> MCAST_CONFIG_CLASS =
120 McastConfig.class;
Esin Karamaneff10392019-06-27 18:09:13 +0000121
ke han81a38b92017-03-10 18:41:44 +0800122 private static ApplicationId appId;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800123
ke han81a38b92017-03-10 18:41:44 +0800124 private static int unSolicitedTimeout = 3; // unit is 1 sec
125 private static int keepAliveCount = 3;
126 private static int lastQueryInterval = 2; //unit is 1 sec
127 private static int lastQueryCount = 2;
128 private static boolean fastLeave = true;
129 private static boolean withRAUplink = true;
130 private static boolean withRADownlink = false;
131 private static boolean periodicQuery = true;
132 private static short mvlan = 4000;
Esin Karaman586f1d62020-06-04 10:15:34 +0000133 private static short mvlanInner = VlanId.NONE.toShort();
ke han81a38b92017-03-10 18:41:44 +0800134 private static byte igmpCos = 7;
Esin Karaman586f1d62020-06-04 10:15:34 +0000135 private static byte igmpUniCos = 7;
ke han81a38b92017-03-10 18:41:44 +0800136 public static boolean connectPointMode = true;
137 public static ConnectPoint connectPoint = null;
Esin Karamaneff10392019-06-27 18:09:13 +0000138 private static ConnectPoint sourceDeviceAndPort = null;
139 private static boolean enableIgmpProvisioning = false;
Esin Karamanb38700c2019-09-17 13:01:25 +0000140 private static boolean igmpOnPodBasis = false;
Arjun E Kb0018fd2020-04-07 13:26:40 +0000141 private static boolean outgoingIgmpWithV3 = true;
Esin Karamaneff10392019-06-27 18:09:13 +0000142
143 private static final Integer MAX_PRIORITY = 10000;
144 private static final String INSTALLED = "installed";
145 private static final String REMOVED = "removed";
146 private static final String INSTALLATION = "installation";
147 private static final String REMOVAL = "removal";
Esin Karaman00e16b72020-02-21 10:32:39 +0000148 private static final String NNI_PREFIX = "nni";
ke han81a38b92017-03-10 18:41:44 +0800149
ke han29af27b2017-09-08 10:29:12 +0800150 private static boolean pimSSmInterworking = false;
151 private static final String DEFAULT_PIMSSM_HOST = "127.0.0.1";
ke han81a38b92017-03-10 18:41:44 +0800152 private final ScheduledExecutorService scheduledExecutorService =
153 Executors.newScheduledThreadPool(1);
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800154
Carmelo Casconebef302e2019-11-14 19:58:20 -0800155 @Reference(cardinality = ReferenceCardinality.MANDATORY)
ke han81a38b92017-03-10 18:41:44 +0800156 protected CoreService coreService;
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 PacketService packetService;
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 MastershipService mastershipService;
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 FlowRuleService flowRuleService;
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 DeviceService deviceService;
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 FlowObjectiveService flowObjectiveService;
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 NetworkConfigRegistry networkConfig;
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 MulticastRouteService multicastService;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800178
179 @Reference(cardinality = ReferenceCardinality.MANDATORY)
180 protected SadisService sadisService;
181
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000182 @Reference(cardinality = ReferenceCardinality.MANDATORY)
183 protected IgmpStatisticsService igmpStatisticsManager;
184
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000185 @Reference(cardinality = ReferenceCardinality.MANDATORY)
186 protected GroupMemberStore groupMemberStore;
187
188 @Reference(cardinality = ReferenceCardinality.MANDATORY)
189 protected StateMachineService stateMachineService;
190
191 @Reference(cardinality = ReferenceCardinality.MANDATORY)
192 protected IgmpLeadershipService igmpLeadershipService;
193
ke han81a38b92017-03-10 18:41:44 +0800194 private IgmpPacketProcessor processor = new IgmpPacketProcessor();
195 private Logger log = LoggerFactory.getLogger(getClass());
196 private ApplicationId coreAppId;
197 private Map<Ip4Address, Ip4Address> ssmTranslateTable = new ConcurrentHashMap<>();
Esin Karamaneff10392019-06-27 18:09:13 +0000198
ke han81a38b92017-03-10 18:41:44 +0800199 private InternalNetworkConfigListener configListener =
200 new InternalNetworkConfigListener();
201 private DeviceListener deviceListener = new InternalDeviceListener();
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800202
ke han81a38b92017-03-10 18:41:44 +0800203 private ConfigFactory<ApplicationId, IgmpproxyConfig> igmpproxyConfigFactory =
204 new ConfigFactory<ApplicationId, IgmpproxyConfig>(
205 SubjectFactories.APP_SUBJECT_FACTORY, IGMPPROXY_CONFIG_CLASS, "igmpproxy") {
206 @Override
207 public IgmpproxyConfig createConfig() {
208 return new IgmpproxyConfig();
209 }
210 };
211 private ConfigFactory<ApplicationId, IgmpproxySsmTranslateConfig> igmpproxySsmConfigFactory =
212 new ConfigFactory<ApplicationId, IgmpproxySsmTranslateConfig>(
213 SubjectFactories.APP_SUBJECT_FACTORY, IGMPPROXY_SSM_CONFIG_CLASS, "ssmTranslate", true) {
214 @Override
215 public IgmpproxySsmTranslateConfig createConfig() {
216 return new IgmpproxySsmTranslateConfig();
217 }
218 };
Esin Karamaneff10392019-06-27 18:09:13 +0000219
ke han81a38b92017-03-10 18:41:44 +0800220 private int maxResp = 10; //unit is 1 sec
221 private int keepAliveInterval = 120; //unit is 1 sec
222
Esin Karamanb38700c2019-09-17 13:01:25 +0000223 private ExecutorService eventExecutor;
224
ke han81a38b92017-03-10 18:41:44 +0800225 public static int getUnsolicitedTimeout() {
226 return unSolicitedTimeout;
227 }
228
Arjun E Kb0018fd2020-04-07 13:26:40 +0000229 public static boolean outgoingIgmpWithV3() {
230 return outgoingIgmpWithV3;
231 }
232
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800233 protected BaseInformationService<SubscriberAndDeviceInformation> subsService;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800234
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000235 private List<Byte> validMembershipModes = Arrays.asList(MODE_IS_INCLUDE, MODE_IS_EXCLUDE, CHANGE_TO_INCLUDE_MODE,
236 CHANGE_TO_EXCLUDE_MODE, ALLOW_NEW_SOURCES, BLOCK_OLD_SOURCES);
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000237
ke han81a38b92017-03-10 18:41:44 +0800238 @Activate
239 protected void activate() {
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800240 appId = coreService.registerApplication(APP_NAME);
ke han81a38b92017-03-10 18:41:44 +0800241 coreAppId = coreService.registerApplication(CoreService.CORE_APP_NAME);
242 packetService.addProcessor(processor, PacketProcessor.director(4));
Esin Karaman592bf322020-07-14 14:46:14 +0000243 IgmpSender.init(packetService, igmpStatisticsManager);
ke han81a38b92017-03-10 18:41:44 +0800244
ke han81a38b92017-03-10 18:41:44 +0800245 networkConfig.registerConfigFactory(igmpproxySsmConfigFactory);
246 networkConfig.registerConfigFactory(igmpproxyConfigFactory);
247 networkConfig.addListener(configListener);
248
249 configListener.reconfigureNetwork(networkConfig.getConfig(appId, IGMPPROXY_CONFIG_CLASS));
250 configListener.reconfigureSsmTable(networkConfig.getConfig(appId, IGMPPROXY_SSM_CONFIG_CLASS));
251
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800252 subsService = sadisService.getSubscriberInfoService();
ke han81a38b92017-03-10 18:41:44 +0800253 if (connectPointMode) {
254 provisionConnectPointFlows();
255 } else {
256 provisionUplinkFlows();
257 }
258
259 McastConfig config = networkConfig.getConfig(coreAppId, MCAST_CONFIG_CLASS);
260 if (config != null) {
261 mvlan = config.egressVlan().toShort();
Deepa Vaddireddy88134a42017-07-05 12:16:03 +0530262 IgmpSender.getInstance().setMvlan(mvlan);
Esin Karaman586f1d62020-06-04 10:15:34 +0000263 mvlanInner = config.egressInnerVlan().toShort();
264 IgmpSender.getInstance().setMvlanInner(mvlanInner);
ke han81a38b92017-03-10 18:41:44 +0800265 }
266 deviceService.addListener(deviceListener);
Sonal Kasliwalddc3ff22019-11-18 11:52:49 +0000267 scheduledExecutorService.scheduleAtFixedRate(new IgmpProxyTimerTask(), 1000, 1000, TimeUnit.MILLISECONDS);
Esin Karamanb38700c2019-09-17 13:01:25 +0000268 eventExecutor = newSingleThreadScheduledExecutor(groupedThreads("cord/igmpproxy",
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000269 "events-igmp-%d", log));
ke han81a38b92017-03-10 18:41:44 +0800270 log.info("Started");
271 }
272
273 @Deactivate
274 protected void deactivate() {
275 scheduledExecutorService.shutdown();
Esin Karamanb38700c2019-09-17 13:01:25 +0000276 eventExecutor.shutdown();
ke han81a38b92017-03-10 18:41:44 +0800277
278 // de-register and null our handler
279 networkConfig.removeListener(configListener);
ke han81a38b92017-03-10 18:41:44 +0800280 networkConfig.unregisterConfigFactory(igmpproxyConfigFactory);
281 networkConfig.unregisterConfigFactory(igmpproxySsmConfigFactory);
282 deviceService.removeListener(deviceListener);
283 packetService.removeProcessor(processor);
284 flowRuleService.removeFlowRulesById(appId);
ke han81a38b92017-03-10 18:41:44 +0800285 log.info("Stopped");
286 }
287
288 protected Ip4Address getDeviceIp(DeviceId ofDeviceId) {
289 try {
290 String[] mgmtAddress = deviceService.getDevice(ofDeviceId)
291 .annotations().value(AnnotationKeys.MANAGEMENT_ADDRESS).split(":");
292 return Ip4Address.valueOf(mgmtAddress[0]);
293 } catch (Exception ex) {
294 log.info("No valid Ipaddress for " + ofDeviceId.toString());
295 return null;
296 }
297 }
298
299 private void processIgmpQuery(IGMPQuery igmpQuery, ConnectPoint cp, int maxResp) {
300
301 DeviceId deviceId = cp.deviceId();
302 Ip4Address gAddr = igmpQuery.getGaddr().getIp4Address();
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000303 maxResp = calculateMaxResp(maxResp);
304 if (gAddr != null && !gAddr.isZero()) {
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000305 stateMachineService.specialQuery(deviceId, gAddr, maxResp);
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300306 igmpStatisticsManager.increaseStat(IgmpStatisticType.IGMP_GRP_SPECIFIC_MEMBERSHIP_QUERY);
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000307 } else {
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000308 stateMachineService.generalQuery(deviceId, maxResp);
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300309 igmpStatisticsManager.increaseStat(IgmpStatisticType.IGMP_GENERAL_MEMBERSHIP_QUERY);
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000310 }
311 }
ke han81a38b92017-03-10 18:41:44 +0800312
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000313 private void processIgmpConnectPointQuery(IGMPQuery igmpQuery, ConnectPoint cp, int maxResp) {
314
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000315 Ip4Address gAddr = igmpQuery.getGaddr().getIp4Address();
Esin Karaman00e16b72020-02-21 10:32:39 +0000316 final int maxResponseTime = calculateMaxResp(maxResp);
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000317 //The query is received on the ConnectPoint
318 // send query accordingly to the registered OLT devices.
319 if (gAddr != null && !gAddr.isZero()) {
Esin Karaman00e16b72020-02-21 10:32:39 +0000320 deviceService.getAvailableDevices().forEach(device -> {
321 Optional<SubscriberAndDeviceInformation> accessDevice = getSubscriberAndDeviceInformation(device.id());
322 if (accessDevice.isPresent()) {
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000323 stateMachineService.specialQuery(device.id(), gAddr, maxResponseTime);
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300324 igmpStatisticsManager.increaseStat(IgmpStatisticType.IGMP_GRP_AND_SRC_SPESIFIC_MEMBERSHIP_QUERY);
Esin Karaman00e16b72020-02-21 10:32:39 +0000325 }
326 });
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300327 igmpStatisticsManager.increaseStat(IgmpStatisticType.CURRENT_GRP_NUMBER_COUNTER);
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000328 } else {
329 //Don't know which group is targeted by the query
330 //So query all the members(in all the OLTs) and proxy their reports
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000331 stateMachineService.generalQuery(maxResponseTime);
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300332 igmpStatisticsManager.increaseStat(IgmpStatisticType.IGMP_GENERAL_MEMBERSHIP_QUERY);
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000333 }
334 }
335
336
337 private int calculateMaxResp(int maxResp) {
ke han81a38b92017-03-10 18:41:44 +0800338 if (maxResp >= 128) {
339 int mant = maxResp & 0xf;
340 int exp = (maxResp >> 4) & 0x7;
341 maxResp = (mant | 0x10) << (exp + 3);
342 }
343
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000344 return (maxResp + 5) / 10;
ke han81a38b92017-03-10 18:41:44 +0800345 }
346
347 private Ip4Address ssmTranslateRoute(IpAddress group) {
348 return ssmTranslateTable.get(group);
349 }
350
351 private void processIgmpReport(IGMPMembership igmpGroup, VlanId vlan, ConnectPoint cp, byte igmpType) {
352 DeviceId deviceId = cp.deviceId();
353 PortNumber portNumber = cp.port();
354
Andrea Campanella2c70a572020-06-05 13:31:45 +0200355 log.debug("Processing IGMP report on membership {} for vlan {} on port {} with type {}",
356 igmpGroup, vlan, cp, igmpType);
357
ke han81a38b92017-03-10 18:41:44 +0800358 Ip4Address groupIp = igmpGroup.getGaddr().getIp4Address();
359 if (!groupIp.isMulticast()) {
360 log.info(groupIp.toString() + " is not a valid group address");
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300361 igmpStatisticsManager.increaseStat(IgmpStatisticType.FAIL_JOIN_REQ_UNKNOWN_MULTICAST_IP_COUNTER);
ke han81a38b92017-03-10 18:41:44 +0800362 return;
363 }
364 Ip4Address srcIp = getDeviceIp(deviceId);
365
366 byte recordType = igmpGroup.getRecordType();
367 boolean join = false;
368
369 ArrayList<Ip4Address> sourceList = new ArrayList<>();
370
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000371 if (!validMembershipModes.contains(recordType)) {
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300372 igmpStatisticsManager.increaseStat(IgmpStatisticType.REPORTS_RX_WITH_WRONG_MODE_COUNTER);
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000373 }
ke han81a38b92017-03-10 18:41:44 +0800374 if (igmpGroup.getSources().size() > 0) {
375 igmpGroup.getSources().forEach(source -> sourceList.add(source.getIp4Address()));
376 if (recordType == IGMPMembership.CHANGE_TO_EXCLUDE_MODE ||
377 recordType == IGMPMembership.MODE_IS_EXCLUDE ||
378 recordType == IGMPMembership.BLOCK_OLD_SOURCES) {
379 join = false;
380 } else if (recordType == IGMPMembership.CHANGE_TO_INCLUDE_MODE ||
381 recordType == IGMPMembership.MODE_IS_INCLUDE ||
382 recordType == IGMPMembership.ALLOW_NEW_SOURCES) {
383 join = true;
384 }
385 } else {
ke han29af27b2017-09-08 10:29:12 +0800386 IpAddress src = null;
387 if (pimSSmInterworking) {
388 src = ssmTranslateRoute(groupIp);
389 if (src == null) {
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000390 log.info("no ssm translate for group {}", groupIp);
ke han29af27b2017-09-08 10:29:12 +0800391 return;
392 }
393 } else {
394 src = IpAddress.valueOf(DEFAULT_PIMSSM_HOST);
ke han81a38b92017-03-10 18:41:44 +0800395 }
396 sourceList.add(src.getIp4Address());
397 if (recordType == IGMPMembership.CHANGE_TO_EXCLUDE_MODE ||
398 recordType == IGMPMembership.MODE_IS_EXCLUDE ||
399 recordType == IGMPMembership.BLOCK_OLD_SOURCES) {
400 join = true;
401 } else if (recordType == IGMPMembership.CHANGE_TO_INCLUDE_MODE ||
402 recordType == IGMPMembership.MODE_IS_INCLUDE ||
403 recordType == IGMPMembership.ALLOW_NEW_SOURCES) {
404 join = false;
405 }
406 }
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000407 GroupMemberId groupMemberKey = GroupMemberId.of(groupIp, deviceId, portNumber);
408 GroupMember groupMember = groupMemberStore.getGroupMember(groupMemberKey);
ke han81a38b92017-03-10 18:41:44 +0800409
410 if (join) {
Andrea Campanella2c70a572020-06-05 13:31:45 +0200411 log.debug("Received join on {} for vlan {}", cp, vlan);
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300412 igmpStatisticsManager.increaseStat(IgmpStatisticType.IGMP_JOIN_REQ);
Andrea Campanella2c70a572020-06-05 13:31:45 +0200413
ke han81a38b92017-03-10 18:41:44 +0800414 if (groupMember == null) {
Esin Karamaneff10392019-06-27 18:09:13 +0000415 Optional<ConnectPoint> sourceConfigured = getSource();
416 if (!sourceConfigured.isPresent()) {
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300417 igmpStatisticsManager.increaseStat(IgmpStatisticType.IGMP_FAIL_JOIN_REQ);
Esin Karamaneff10392019-06-27 18:09:13 +0000418 log.warn("Unable to process IGMP Join from {} since no source " +
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000419 "configuration is found.", deviceId);
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300420 igmpStatisticsManager
421 .increaseStat(IgmpStatisticType.FAIL_JOIN_REQ_INSUFF_PERMISSION_ACCESS_COUNTER);
Esin Karamaneff10392019-06-27 18:09:13 +0000422 return;
423 }
Esin Karaman00e16b72020-02-21 10:32:39 +0000424
425 Optional<PortNumber> deviceUplink = getDeviceUplink(deviceId);
426 if (deviceUplink.isEmpty()) {
427 log.warn("Unable to process IGMP Join since uplink port " +
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000428 "of the device {} is not found.", deviceId);
Esin Karaman00e16b72020-02-21 10:32:39 +0000429 return;
430 }
431
432 if (igmpType == IGMP.TYPE_IGMPV2_MEMBERSHIP_REPORT) {
433 groupMember = new GroupMember(groupIp, vlan, deviceId, portNumber, true);
434 } else {
435 groupMember = new GroupMember(groupIp, vlan, deviceId, portNumber, false);
436 }
437
Esin Karamaneff10392019-06-27 18:09:13 +0000438 HashSet<ConnectPoint> sourceConnectPoints = Sets.newHashSet(sourceConfigured.get());
439
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000440 boolean isJoined = stateMachineService.join(deviceId, groupIp, srcIp, deviceUplink.get());
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000441 if (isJoined) {
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300442 igmpStatisticsManager.increaseStat(IgmpStatisticType.IGMP_SUCCESS_JOIN_RE_JOIN_REQ);
443 igmpStatisticsManager.increaseStat(IgmpStatisticType.IGMP_CHANNEL_JOIN_COUNTER);
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000444 } else {
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300445 igmpStatisticsManager.increaseStat(IgmpStatisticType.IGMP_FAIL_JOIN_REQ);
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000446 }
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000447 groupMemberStore.putGroupMember(groupMember);
448 log.debug("Group member created with id: {}", groupMember.getGroupMemberId());
ke han81a38b92017-03-10 18:41:44 +0800449 groupMember.updateList(recordType, sourceList);
Esin Karamaneff10392019-06-27 18:09:13 +0000450 groupMember.getSourceList().forEach(source -> {
451 McastRoute route = new McastRoute(source, groupIp, McastRoute.Type.IGMP);
452 //add route
453 multicastService.add(route);
454 //add source to the route
455 multicastService.addSources(route, Sets.newHashSet(sourceConnectPoints));
456 //add sink to the route
457 multicastService.addSinks(route, Sets.newHashSet(cp));
458 });
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300459 igmpStatisticsManager.increaseStat(IgmpStatisticType.UNCONFIGURED_GROUP_COUNTER);
Esin Karamaneff10392019-06-27 18:09:13 +0000460
ke han81a38b92017-03-10 18:41:44 +0800461 }
462 groupMember.resetAllTimers();
463 groupMember.updateList(recordType, sourceList);
464 groupMember.setLeave(false);
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000465 //put updated member to the store
466 groupMemberStore.putGroupMember(groupMember);
ke han81a38b92017-03-10 18:41:44 +0800467 } else {
Andrea Campanella2c70a572020-06-05 13:31:45 +0200468 log.debug("Received leave on {} for vlan {}", cp, vlan);
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300469 igmpStatisticsManager.increaseStat(IgmpStatisticType.IGMP_LEAVE_REQ);
ke han81a38b92017-03-10 18:41:44 +0800470 if (groupMember == null) {
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000471 log.info("receive leave but no instance, group {} device: {} port:{}",
472 groupIp, deviceId, portNumber);
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300473 igmpStatisticsManager.increaseStat(IgmpStatisticType.UNCONFIGURED_GROUP_COUNTER);
ke han81a38b92017-03-10 18:41:44 +0800474 return;
475 } else {
476 groupMember.setLeave(true);
477 if (fastLeave) {
478 leaveAction(groupMember);
479 } else {
480 sendQuery(groupMember);
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000481 //put modified group member object to the store
482 groupMemberStore.updateGroupMember(groupMember);
ke han81a38b92017-03-10 18:41:44 +0800483 }
484 }
485 }
486 }
487
488 private void leaveAction(GroupMember groupMember) {
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300489 igmpStatisticsManager.increaseStat(IgmpStatisticType.IGMP_DISCONNECT);
ke han81a38b92017-03-10 18:41:44 +0800490 ConnectPoint cp = new ConnectPoint(groupMember.getDeviceId(), groupMember.getPortNumber());
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000491 stateMachineService.leave(groupMember.getDeviceId(), groupMember.getGroupIp());
Esin Karamaneff10392019-06-27 18:09:13 +0000492 groupMember.getSourceList().forEach(source -> multicastService.removeSinks(
ke han81a38b92017-03-10 18:41:44 +0800493 new McastRoute(source, groupMember.getGroupIp(),
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000494 McastRoute.Type.IGMP), Sets.newHashSet(cp)));
495 groupMemberStore.removeGroupMember(groupMember.getGroupMemberId());
ke han81a38b92017-03-10 18:41:44 +0800496 }
497
498 private void sendQuery(GroupMember groupMember) {
499 Ethernet ethpkt;
500 Ip4Address srcIp = getDeviceIp(groupMember.getDeviceId());
501 if (groupMember.getv2()) {
Esin Karaman586f1d62020-06-04 10:15:34 +0000502 ethpkt = IgmpSender.getInstance().buildIgmpV2Query(groupMember.getGroupIp(),
503 srcIp, groupMember.getvlan().toShort());
ke han81a38b92017-03-10 18:41:44 +0800504 } else {
Esin Karaman586f1d62020-06-04 10:15:34 +0000505 ethpkt = IgmpSender.getInstance().buildIgmpV3Query(groupMember.getGroupIp(),
506 srcIp, groupMember.getvlan().toShort());
ke han81a38b92017-03-10 18:41:44 +0800507 }
Esin Karaman586f1d62020-06-04 10:15:34 +0000508 log.debug("Sending IGMP query to {}/{} for group {}: {}",
509 groupMember.getDeviceId(), groupMember.getPortNumber(), groupMember.getGroupIp(), ethpkt);
ke han81a38b92017-03-10 18:41:44 +0800510 IgmpSender.getInstance().sendIgmpPacket(ethpkt, groupMember.getDeviceId(), groupMember.getPortNumber());
511 }
512
Esin Karamaneff10392019-06-27 18:09:13 +0000513 /**
514 * @return connect point of the source if configured; and empty Optional otherwise.
515 */
516 public static Optional<ConnectPoint> getSource() {
517 return sourceDeviceAndPort == null ? Optional.empty() :
518 Optional.of(sourceDeviceAndPort);
ke han81a38b92017-03-10 18:41:44 +0800519 }
520
521 /**
522 * Packet processor responsible for forwarding packets along their paths.
523 */
524 private class IgmpPacketProcessor implements PacketProcessor {
525 @Override
526 public void process(PacketContext context) {
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000527
Esin Karamanb38700c2019-09-17 13:01:25 +0000528 eventExecutor.execute(() -> {
529 try {
530 InboundPacket pkt = context.inPacket();
531 Ethernet ethPkt = pkt.parsed();
532 if (ethPkt == null) {
533 return;
534 }
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300535 igmpStatisticsManager.increaseStat(IgmpStatisticType.TOTAL_MSG_RECEIVED);
ke han81a38b92017-03-10 18:41:44 +0800536
Esin Karamanb38700c2019-09-17 13:01:25 +0000537 if (ethPkt.getEtherType() != Ethernet.TYPE_IPV4) {
538 return;
539 }
ke han81a38b92017-03-10 18:41:44 +0800540
Esin Karamanb38700c2019-09-17 13:01:25 +0000541 IPv4 ipv4Pkt = (IPv4) ethPkt.getPayload();
ke han81a38b92017-03-10 18:41:44 +0800542
Esin Karamanb38700c2019-09-17 13:01:25 +0000543 if (ipv4Pkt.getProtocol() != IPv4.PROTOCOL_IGMP) {
544 return;
545 }
ke han81a38b92017-03-10 18:41:44 +0800546
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300547 igmpStatisticsManager.increaseStat(IgmpStatisticType.IGMP_VALID_CHECKSUM_COUNTER);
Esin Karamanb38700c2019-09-17 13:01:25 +0000548 short vlan = ethPkt.getVlanID();
549 DeviceId deviceId = pkt.receivedFrom().deviceId();
ke han81a38b92017-03-10 18:41:44 +0800550
Esin Karaman00e16b72020-02-21 10:32:39 +0000551 if (!isConnectPoint(deviceId, pkt.receivedFrom().port()) &&
552 !getSubscriberAndDeviceInformation(deviceId).isPresent()) {
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000553 log.error("Device not registered in netcfg : {}", deviceId);
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300554 igmpStatisticsManager
555 .increaseStat(IgmpStatisticType.FAIL_JOIN_REQ_INSUFF_PERMISSION_ACCESS_COUNTER);
Esin Karamanb38700c2019-09-17 13:01:25 +0000556 return;
557 }
ke han81a38b92017-03-10 18:41:44 +0800558
Esin Karamanb38700c2019-09-17 13:01:25 +0000559 IGMP igmp = (IGMP) ipv4Pkt.getPayload();
Esin Karamance5ce512020-02-25 15:58:14 +0000560
561 Optional<PortNumber> deviceUpLinkOpt = getDeviceUplink(deviceId);
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000562 PortNumber upLinkPort = deviceUpLinkOpt.isPresent() ? deviceUpLinkOpt.get() : null;
Esin Karamanb38700c2019-09-17 13:01:25 +0000563 switch (igmp.getIgmpType()) {
564 case IGMP.TYPE_IGMPV3_MEMBERSHIP_QUERY:
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300565 igmpStatisticsManager.increaseStat(IgmpStatisticType.IGMP_V3_MEMBERSHIP_QUERY);
Esin Karamanb38700c2019-09-17 13:01:25 +0000566 //Discard Query from OLT’s non-uplink port’s
Esin Karamance5ce512020-02-25 15:58:14 +0000567 if (!pkt.receivedFrom().port().equals(upLinkPort)) {
Esin Karamanb38700c2019-09-17 13:01:25 +0000568 if (isConnectPoint(deviceId, pkt.receivedFrom().port())) {
569 log.info("IGMP Picked up query from connectPoint");
570 //OK to process packet
571 processIgmpConnectPointQuery((IGMPQuery) igmp.getGroups().get(0),
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000572 pkt.receivedFrom(),
573 0xff & igmp.getMaxRespField());
Esin Karamanb38700c2019-09-17 13:01:25 +0000574 break;
575 } else {
576 //Not OK to process packet
Esin Karamance5ce512020-02-25 15:58:14 +0000577 log.warn("IGMP Picked up query from non-uplink port {}", upLinkPort);
Esin Karamanb38700c2019-09-17 13:01:25 +0000578 return;
579 }
580 }
ke han81a38b92017-03-10 18:41:44 +0800581
Esin Karamanb38700c2019-09-17 13:01:25 +0000582 processIgmpQuery((IGMPQuery) igmp.getGroups().get(0), pkt.receivedFrom(),
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000583 0xff & igmp.getMaxRespField());
Esin Karamanb38700c2019-09-17 13:01:25 +0000584 break;
585 case IGMP.TYPE_IGMPV1_MEMBERSHIP_REPORT:
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300586 igmpStatisticsManager.increaseStat(IgmpStatisticType.IGMP_V1_MEMBERSHIP_REPORT);
Esin Karamanb38700c2019-09-17 13:01:25 +0000587 log.debug("IGMP version 1 message types are not currently supported.");
588 break;
589 case IGMP.TYPE_IGMPV3_MEMBERSHIP_REPORT:
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300590 igmpStatisticsManager.increaseStat(IgmpStatisticType.IGMP_V3_MEMBERSHIP_REPORT);
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000591 processIgmpMessage(pkt, igmp, upLinkPort, vlan);
592 break;
Esin Karamanb38700c2019-09-17 13:01:25 +0000593 case IGMP.TYPE_IGMPV2_MEMBERSHIP_REPORT:
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300594 igmpStatisticsManager.increaseStat(IgmpStatisticType.IGMP_V2_MEMBERSHIP_REPORT);
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000595 processIgmpMessage(pkt, igmp, upLinkPort, vlan);
596 break;
Esin Karamanb38700c2019-09-17 13:01:25 +0000597 case IGMP.TYPE_IGMPV2_LEAVE_GROUP:
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300598 igmpStatisticsManager.increaseStat(IgmpStatisticType.IGMP_V2_LEAVE_GROUP);
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000599 processIgmpMessage(pkt, igmp, upLinkPort, vlan);
Esin Karamanb38700c2019-09-17 13:01:25 +0000600 break;
ke han81a38b92017-03-10 18:41:44 +0800601
Esin Karamanb38700c2019-09-17 13:01:25 +0000602 default:
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000603 log.warn("Unknown IGMP message type: {}", igmp.getIgmpType());
Ilayda Ozdemir0872abd2020-06-03 20:20:20 +0300604 igmpStatisticsManager.increaseStat(IgmpStatisticType.INVALID_IGMP_MSG_RECEIVED);
605 igmpStatisticsManager.increaseStat(IgmpStatisticType.UNKNOWN_IGMP_TYPE_PACKETS_RX_COUNTER);
Esin Karamanb38700c2019-09-17 13:01:25 +0000606 break;
607 }
608
609 } catch (Exception ex) {
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000610 log.error("igmp process error : ", ex);
ke han81a38b92017-03-10 18:41:44 +0800611 }
Esin Karamanb38700c2019-09-17 13:01:25 +0000612 });
ke han81a38b92017-03-10 18:41:44 +0800613 }
614 }
615
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000616 private void processIgmpMessage(InboundPacket pkt, IGMP igmp, PortNumber upLinkPort, short vlan) {
617 //Discard join/leave from OLT’s uplink port’s
618 if (pkt.receivedFrom().port().equals(upLinkPort) ||
619 isConnectPoint(pkt.receivedFrom().deviceId(), pkt.receivedFrom().port())) {
620 log.info("IGMP Picked up join/leave from uplink/connectPoint port");
621 return;
622 }
623
624 Iterator<IGMPGroup> itr = igmp.getGroups().iterator();
625 while (itr.hasNext()) {
626 IGMPGroup group = itr.next();
627 if (group instanceof IGMPMembership) {
628 processIgmpReport((IGMPMembership) group, VlanId.vlanId(vlan),
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000629 pkt.receivedFrom(), igmp.getIgmpType());
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000630 } else {
631 IGMPMembership mgroup = new IGMPMembership(group.getGaddr().getIp4Address());
632 mgroup.setRecordType(igmp.getIgmpType() == IGMP.TYPE_IGMPV2_MEMBERSHIP_REPORT ?
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000633 IGMPMembership.MODE_IS_EXCLUDE :
634 IGMPMembership.MODE_IS_INCLUDE);
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000635 processIgmpReport(mgroup, VlanId.vlanId(vlan),
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000636 pkt.receivedFrom(), igmp.getIgmpType());
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000637 }
638 }
639
640 }
641
ke han81a38b92017-03-10 18:41:44 +0800642 private class IgmpProxyTimerTask extends TimerTask {
643 public void run() {
644 try {
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000645 stateMachineService.timeOut1s();
ke han81a38b92017-03-10 18:41:44 +0800646 queryMembers();
647 } catch (Exception ex) {
648 log.warn("Igmp timer task error : {}", ex.getMessage());
649 }
650 }
651
652 private void queryMembers() {
653 GroupMember groupMember;
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000654 Set<GroupMemberId> keySet = groupMemberStore.getAllGroupMemberIds();
655 for (GroupMemberId key : keySet) {
656 groupMember = groupMemberStore.getGroupMember(key);
657 if (groupMember == null) {
658 continue;
659 }
ke han81a38b92017-03-10 18:41:44 +0800660 DeviceId did = groupMember.getDeviceId();
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000661 if (igmpLeadershipService.isLocalLeader(did)) {
ke han81a38b92017-03-10 18:41:44 +0800662 if (groupMember.isLeave()) {
663 lastQuery(groupMember);
664 } else if (periodicQuery) {
665 periodicQuery(groupMember);
666 }
667 }
668 }
669 }
670
671 private void lastQuery(GroupMember groupMember) {
672 if (groupMember.getLastQueryInterval() < lastQueryInterval) {
673 groupMember.lastQueryInterval(true); // count times
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000674 //put modified group member object to the store
675 groupMemberStore.updateGroupMember(groupMember);
ke han81a38b92017-03-10 18:41:44 +0800676 } else if (groupMember.getLastQueryCount() < lastQueryCount - 1) {
677 sendQuery(groupMember);
678 groupMember.lastQueryInterval(false); // reset count number
679 groupMember.lastQueryCount(true); //count times
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000680 //put modified group member object to the store
681 groupMemberStore.updateGroupMember(groupMember);
ke han81a38b92017-03-10 18:41:44 +0800682 } else if (groupMember.getLastQueryCount() == lastQueryCount - 1) {
683 leaveAction(groupMember);
684 }
685 }
686
687 private void periodicQuery(GroupMember groupMember) {
688 if (groupMember.getKeepAliveQueryInterval() < keepAliveInterval) {
689 groupMember.keepAliveInterval(true);
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000690 //put modified group member object to the store
691 groupMemberStore.updateGroupMember(groupMember);
ke han81a38b92017-03-10 18:41:44 +0800692 } else if (groupMember.getKeepAliveQueryCount() < keepAliveCount) {
693 sendQuery(groupMember);
694 groupMember.keepAliveInterval(false);
695 groupMember.keepAliveQueryCount(true);
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000696 //put modified group member object to the store
697 groupMemberStore.updateGroupMember(groupMember);
ke han81a38b92017-03-10 18:41:44 +0800698 } else if (groupMember.getKeepAliveQueryCount() == keepAliveCount) {
699 leaveAction(groupMember);
700 }
701 }
702
703 }
704
Esin Karaman00e16b72020-02-21 10:32:39 +0000705 public Optional<PortNumber> getDeviceUplink(DeviceId devId) {
706 Device device = deviceService.getDevice(devId);
707 if (device == null || device.serialNumber() == null) {
708 return Optional.empty();
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000709 }
Esin Karaman00e16b72020-02-21 10:32:39 +0000710 Optional<SubscriberAndDeviceInformation> olt = getSubscriberAndDeviceInformation(device.serialNumber());
711 if (olt.isEmpty()) {
712 return Optional.empty();
713 }
714 PortNumber portNumber = PortNumber.portNumber(olt.get().uplinkPort());
715 return validateUpLinkPort(device.id(), portNumber) ?
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000716 Optional.of(portNumber) : Optional.empty();
Esin Karaman00e16b72020-02-21 10:32:39 +0000717 }
718
719 /**
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000720 * @param deviceId device id
Esin Karaman00e16b72020-02-21 10:32:39 +0000721 * @param portNumber port number
722 * @return true if the port name starts with NNI_PREFIX; false otherwise.
723 */
724 public boolean validateUpLinkPort(DeviceId deviceId, PortNumber portNumber) {
725 Port port = deviceService.getPort(deviceId, portNumber);
726 if (port == null) {
727 //port is not discovered by ONOS; so cannot validate it.
728 return false;
729 }
Esin Karamance5ce512020-02-25 15:58:14 +0000730 boolean isValid = port.annotations().value(AnnotationKeys.PORT_NAME) != null &&
Esin Karaman00e16b72020-02-21 10:32:39 +0000731 port.annotations().value(AnnotationKeys.PORT_NAME).startsWith(NNI_PREFIX);
Esin Karamance5ce512020-02-25 15:58:14 +0000732 if (!isValid) {
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000733 log.warn("Port cannot be validated; it is not configured as an NNI port. Device/port: {}/{}",
734 deviceId, portNumber);
Esin Karamance5ce512020-02-25 15:58:14 +0000735 }
736 return isValid;
ke han81a38b92017-03-10 18:41:44 +0800737 }
738
Esin Karamanb38700c2019-09-17 13:01:25 +0000739 public static boolean isIgmpOnPodBasis() {
740 return igmpOnPodBasis;
741 }
742
ke han81a38b92017-03-10 18:41:44 +0800743 private void processFilterObjective(DeviceId devId, PortNumber port, boolean remove) {
Esin Karamaneff10392019-06-27 18:09:13 +0000744 if (!enableIgmpProvisioning) {
745 log.debug("IGMP trap rules won't be installed since enableIgmpProvisioning flag is not set");
746 return;
747 }
ke han81a38b92017-03-10 18:41:44 +0800748 //TODO migrate to packet requests when packet service uses filtering objectives
749 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
750
751 builder = remove ? builder.deny() : builder.permit();
752
753 FilteringObjective igmp = builder
754 .withKey(Criteria.matchInPort(port))
755 .addCondition(Criteria.matchEthType(EthType.EtherType.IPV4.ethType()))
756 .addCondition(Criteria.matchIPProtocol(IPv4.PROTOCOL_IGMP))
757 .withMeta(DefaultTrafficTreatment.builder().setOutput(PortNumber.CONTROLLER).build())
758 .fromApp(appId)
Esin Karamaneff10392019-06-27 18:09:13 +0000759 .withPriority(MAX_PRIORITY)
ke han81a38b92017-03-10 18:41:44 +0800760 .add(new ObjectiveContext() {
761 @Override
762 public void onSuccess(Objective objective) {
Esin Karamaneff10392019-06-27 18:09:13 +0000763 log.info("Igmp filter for {} on {} {}.",
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000764 devId, port, (remove) ? REMOVED : INSTALLED);
ke han81a38b92017-03-10 18:41:44 +0800765 }
766
767 @Override
768 public void onError(Objective objective, ObjectiveError error) {
Esin Karamaneff10392019-06-27 18:09:13 +0000769 log.info("Igmp filter {} for device {} on port {} failed because of {}",
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000770 (remove) ? INSTALLATION : REMOVAL, devId, port,
771 error);
ke han81a38b92017-03-10 18:41:44 +0800772 }
773 });
774
775 flowObjectiveService.filter(devId, igmp);
Esin Karamaneff10392019-06-27 18:09:13 +0000776
ke han81a38b92017-03-10 18:41:44 +0800777 }
778
779 private boolean isConnectPoint(DeviceId device, PortNumber port) {
Deepa Vaddireddy01f33b42017-07-02 13:26:53 +0530780 if (connectPoint != null) {
781 return (connectPointMode && connectPoint.deviceId().equals(device)
782 && connectPoint.port().equals(port));
783 } else {
Andrea Campanella2c70a572020-06-05 13:31:45 +0200784 log.debug("connectPoint not configured for device {}", device);
Deepa Vaddireddy01f33b42017-07-02 13:26:53 +0530785 return false;
786 }
ke han81a38b92017-03-10 18:41:44 +0800787 }
Deepa Vaddireddy01f33b42017-07-02 13:26:53 +0530788
ke han81a38b92017-03-10 18:41:44 +0800789 private boolean isUplink(DeviceId device, PortNumber port) {
Esin Karamance5ce512020-02-25 15:58:14 +0000790 if (connectPointMode) {
791 return false;
792 }
793 Optional<PortNumber> upLinkPort = getDeviceUplink(device);
794 return upLinkPort.isPresent() && upLinkPort.get().equals(port);
ke han81a38b92017-03-10 18:41:44 +0800795 }
796
Esin Karaman00e16b72020-02-21 10:32:39 +0000797 /**
798 * Fetches device information associated with the device serial number from SADIS.
799 *
800 * @param serialNumber serial number of a device
801 * @return device information; an empty Optional otherwise.
802 */
803 private Optional<SubscriberAndDeviceInformation> getSubscriberAndDeviceInformation(String serialNumber) {
804 long start = System.currentTimeMillis();
805 try {
806 return Optional.ofNullable(sadisService.getSubscriberInfoService().get(serialNumber));
807 } finally {
808 if (log.isDebugEnabled()) {
809 // SADIS can call remote systems to fetch device data and this calls can take a long time.
810 // This measurement is just for monitoring these kinds of situations.
811 log.debug("Device fetched from SADIS. Elapsed {} msec", System.currentTimeMillis() - start);
812 }
813
814 }
815 }
816
817 /**
818 * Fetches device information associated with the device serial number from SADIS.
819 *
820 * @param deviceId device id
821 * @return device information; an empty Optional otherwise.
822 */
823 private Optional<SubscriberAndDeviceInformation> getSubscriberAndDeviceInformation(DeviceId deviceId) {
824 Device device = deviceService.getDevice(deviceId);
825 if (device == null || device.serialNumber() == null) {
826 return Optional.empty();
827 }
828 return getSubscriberAndDeviceInformation(device.serialNumber());
829 }
830
ke han81a38b92017-03-10 18:41:44 +0800831 private class InternalDeviceListener implements DeviceListener {
832 @Override
833 public void event(DeviceEvent event) {
Esin Karaman09b41e52020-06-01 10:52:55 +0000834 eventExecutor.execute(() -> {
835 DeviceId devId = event.subject().id();
836 Port p = event.port();
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +0000837
Esin Karaman09b41e52020-06-01 10:52:55 +0000838 if (!igmpLeadershipService.isLocalLeader(devId)) {
839 return;
840 }
841 if (getSubscriberAndDeviceInformation(devId).isEmpty() &&
842 !(p != null && isConnectPoint(devId, p.number()))) {
843 return;
844 }
845 PortNumber port;
ke han81a38b92017-03-10 18:41:44 +0800846
Esin Karaman09b41e52020-06-01 10:52:55 +0000847 switch (event.type()) {
848
849 case DEVICE_ADDED:
850 case DEVICE_UPDATED:
851 case DEVICE_REMOVED:
852 case DEVICE_SUSPENDED:
853 case DEVICE_AVAILABILITY_CHANGED:
854 case PORT_STATS_UPDATED:
855 break;
856 case PORT_ADDED:
857 port = p.number();
858 if (getSubscriberAndDeviceInformation(devId).isPresent() &&
859 !isUplink(devId, port) && !isConnectPoint(devId, port)) {
ke han81a38b92017-03-10 18:41:44 +0800860 processFilterObjective(devId, port, false);
Esin Karaman09b41e52020-06-01 10:52:55 +0000861 } else if (isUplink(devId, port)) {
862 provisionUplinkFlows();
863 } else if (isConnectPoint(devId, port)) {
ke han81a38b92017-03-10 18:41:44 +0800864 provisionConnectPointFlows();
ke han81a38b92017-03-10 18:41:44 +0800865 }
Esin Karaman09b41e52020-06-01 10:52:55 +0000866 onSourceStateChanged(devId, port, true);
867 break;
868 case PORT_UPDATED:
869 port = p.number();
870 if (getSubscriberAndDeviceInformation(devId).isPresent() &&
871 !isUplink(devId, port) && !isConnectPoint(devId, port)) {
872 processFilterObjective(devId, port, !event.port().isEnabled());
873 } else if (isUplink(devId, port)) {
874 if (event.port().isEnabled()) {
875 provisionUplinkFlows(devId);
876 } else {
877 processFilterObjective(devId, port, true);
878 }
879 } else if (isConnectPoint(devId, port)) {
880 if (event.port().isEnabled()) {
881 provisionConnectPointFlows();
882 } else {
883 unprovisionConnectPointFlows();
884 }
885 }
886 onSourceStateChanged(devId, port, event.port().isEnabled());
887 break;
888 case PORT_REMOVED:
889 port = p.number();
890 processFilterObjective(devId, port, true);
891 onSourceStateChanged(devId, port, false);
892 break;
893 default:
894 log.info("Unknown device event {}", event.type());
895 break;
896 }
897 });
ke han81a38b92017-03-10 18:41:44 +0800898 }
899
900 @Override
901 public boolean isRelevant(DeviceEvent event) {
902 return true;
903 }
904 }
905
Esin Karaman09b41e52020-06-01 10:52:55 +0000906 private Set<McastRoute> multicastRoutesOfIgmpProxy() {
907 Set<McastRoute> routes = new HashSet<>(); //using set to eliminate multiple entities
908 groupMemberStore.getAllGroupMemberIds().forEach(groupMemberId -> {
909 GroupMember groupMember = groupMemberStore.getGroupMember(groupMemberId);
910 if (groupMember != null) {
911 groupMember.getSourceList().forEach(source -> {
912 //regenerate the routes created by this application
913 routes.add(new McastRoute(source, groupMemberId.getGroupIp(), McastRoute.Type.IGMP));
914 });
915 }
916 });
917 return routes;
918 }
919
920 private void onSourceStateChanged(DeviceId deviceId, PortNumber portNumber, boolean enabled) {
921 if (!(getSource().isPresent() &&
922 getSource().get().deviceId().equals(deviceId) &&
923 getSource().get().port().equals(portNumber))) {
924 //connect point is not configured as the source
925 log.debug("{}/{} is not the source cp. Stopped processing it further", deviceId, portNumber);
926 return;
927 }
928 log.info("source device:port is {}. DeviceId={}, portNumber={}",
929 (enabled ? "enabled. Restoring the source" :
930 "disabled. Deleting it from multicast routes"), deviceId, portNumber);
931
932 Set<McastRoute> routes = multicastRoutesOfIgmpProxy();
933 routes.forEach(route -> {
934 if (enabled) {
935 //add source to the route
936 multicastService.addSources(route, Sets.newHashSet(new ConnectPoint(deviceId, portNumber)));
937 } else {
938 //remove the source from the route
939 multicastService.removeSources(route, Sets.newHashSet(new ConnectPoint(deviceId, portNumber)));
940 }
941 });
942 }
943
ke han81a38b92017-03-10 18:41:44 +0800944 private class InternalNetworkConfigListener implements NetworkConfigListener {
945
946 private void reconfigureNetwork(IgmpproxyConfig cfg) {
Esin Karamaneff10392019-06-27 18:09:13 +0000947 IgmpproxyConfig newCfg = cfg == null ? new IgmpproxyConfig() : cfg;
ke han81a38b92017-03-10 18:41:44 +0800948
949 unSolicitedTimeout = newCfg.unsolicitedTimeOut();
950 maxResp = newCfg.maxResp();
951 keepAliveInterval = newCfg.keepAliveInterval();
952 keepAliveCount = newCfg.keepAliveCount();
953 lastQueryInterval = newCfg.lastQueryInterval();
954 lastQueryCount = newCfg.lastQueryCount();
955 withRAUplink = newCfg.withRAUplink();
956 withRADownlink = newCfg.withRADownlink();
957 igmpCos = newCfg.igmpCos();
Esin Karaman586f1d62020-06-04 10:15:34 +0000958 igmpUniCos = newCfg.igmpUniCos(); // defines priority bit of IGMP query message sent to UNI ports
ke han81a38b92017-03-10 18:41:44 +0800959 periodicQuery = newCfg.periodicQuery();
960 fastLeave = newCfg.fastLeave();
ke han29af27b2017-09-08 10:29:12 +0800961 pimSSmInterworking = newCfg.pimSsmInterworking();
Esin Karamaneff10392019-06-27 18:09:13 +0000962 enableIgmpProvisioning = newCfg.enableIgmpProvisioning();
Esin Karamanb38700c2019-09-17 13:01:25 +0000963 igmpOnPodBasis = newCfg.igmpOnPodBasis();
Arjun E Kb0018fd2020-04-07 13:26:40 +0000964 if (newCfg.outgoingIgmpWithV3() != null &&
965 outgoingIgmpWithV3 != newCfg.outgoingIgmpWithV3()) {
966 outgoingIgmpWithV3 = newCfg.outgoingIgmpWithV3();
967 }
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +0000968
969 if (connectPointMode != newCfg.connectPointMode() ||
970 connectPoint != newCfg.connectPoint()) {
ke han81a38b92017-03-10 18:41:44 +0800971 connectPointMode = newCfg.connectPointMode();
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +0000972 connectPoint = newCfg.connectPoint();
ke han81a38b92017-03-10 18:41:44 +0800973 if (connectPointMode) {
974 unprovisionUplinkFlows();
975 provisionConnectPointFlows();
976 } else {
977 unprovisionConnectPointFlows();
978 provisionUplinkFlows();
979 }
980 }
981 if (connectPoint != null) {
Esin Karamaneff10392019-06-27 18:09:13 +0000982 log.info("connect point : {}", connectPoint);
ke han81a38b92017-03-10 18:41:44 +0800983 }
Esin Karamaneff10392019-06-27 18:09:13 +0000984 log.info("mode: {}", connectPointMode);
985
986 getSourceConnectPoint(newCfg);
ke han81a38b92017-03-10 18:41:44 +0800987
988 IgmpSender.getInstance().setIgmpCos(igmpCos);
Esin Karaman586f1d62020-06-04 10:15:34 +0000989 IgmpSender.getInstance().setIgmpUniCos(igmpUniCos);
ke han81a38b92017-03-10 18:41:44 +0800990 IgmpSender.getInstance().setMaxResp(maxResp);
991 IgmpSender.getInstance().setMvlan(mvlan);
Esin Karaman586f1d62020-06-04 10:15:34 +0000992 IgmpSender.getInstance().setMvlanInner(mvlanInner);
ke han81a38b92017-03-10 18:41:44 +0800993 IgmpSender.getInstance().setWithRADownlink(withRADownlink);
994 IgmpSender.getInstance().setWithRAUplink(withRAUplink);
Esin Karamaneff10392019-06-27 18:09:13 +0000995 }
ke han81a38b92017-03-10 18:41:44 +0800996
Esin Karamaneff10392019-06-27 18:09:13 +0000997 void getSourceConnectPoint(IgmpproxyConfig cfg) {
Esin Karaman09b41e52020-06-01 10:52:55 +0000998 ConnectPoint oldSourceDevPort = sourceDeviceAndPort;
Esin Karamaneff10392019-06-27 18:09:13 +0000999 sourceDeviceAndPort = cfg.getSourceDeviceAndPort();
1000 if (sourceDeviceAndPort != null) {
1001 log.debug("source parameter configured to {}", sourceDeviceAndPort);
1002 }
Esin Karaman09b41e52020-06-01 10:52:55 +00001003 if (oldSourceDevPort != null && !oldSourceDevPort.equals(sourceDeviceAndPort)) {
1004 //source config has changed, remove the old source from multicast routes
1005 onSourceStateChanged(oldSourceDevPort.deviceId(), oldSourceDevPort.port(), false);
1006 }
1007 if (sourceDeviceAndPort != null && !sourceDeviceAndPort.equals(oldSourceDevPort)) {
1008 //add new source to the existing routes
1009 onSourceStateChanged(sourceDeviceAndPort.deviceId(), sourceDeviceAndPort.port(), true);
1010 }
ke han81a38b92017-03-10 18:41:44 +08001011 }
1012
1013 public void reconfigureSsmTable(IgmpproxySsmTranslateConfig cfg) {
1014 if (cfg == null) {
1015 return;
1016 }
1017 Collection<McastRoute> translations = cfg.getSsmTranslations();
1018 for (McastRoute route : translations) {
Esin Karamaneff10392019-06-27 18:09:13 +00001019 ssmTranslateTable.put(route.group().getIp4Address(), route.source().get().getIp4Address());
ke han81a38b92017-03-10 18:41:44 +08001020 }
1021 }
1022
1023 @Override
1024 public void event(NetworkConfigEvent event) {
1025 switch (event.type()) {
1026 case CONFIG_ADDED:
1027 case CONFIG_UPDATED:
Matteo Scandolo7482bbe2020-02-12 14:37:09 -08001028 // NOTE how to know if something has changed in sadis?
ke han81a38b92017-03-10 18:41:44 +08001029
1030 if (event.configClass().equals(IGMPPROXY_CONFIG_CLASS)) {
1031 IgmpproxyConfig config = networkConfig.getConfig(appId, IGMPPROXY_CONFIG_CLASS);
1032 if (config != null) {
Esin Karamaneff10392019-06-27 18:09:13 +00001033 log.info("igmpproxy config received. {}", config);
ke han81a38b92017-03-10 18:41:44 +08001034 reconfigureNetwork(config);
1035 }
1036 }
1037
1038 if (event.configClass().equals(IGMPPROXY_SSM_CONFIG_CLASS)) {
1039 IgmpproxySsmTranslateConfig config = networkConfig.getConfig(appId, IGMPPROXY_SSM_CONFIG_CLASS);
1040 if (config != null) {
1041 reconfigureSsmTable(config);
1042 }
1043 }
1044
1045 if (event.configClass().equals(MCAST_CONFIG_CLASS)) {
1046 McastConfig config = networkConfig.getConfig(coreAppId, MCAST_CONFIG_CLASS);
Esin Karaman586f1d62020-06-04 10:15:34 +00001047 boolean vlanConfigChanged = config != null && mvlan != config.egressVlan().toShort();
1048 boolean innerVlanConfigChanged = config != null &&
1049 mvlanInner != config.egressInnerVlan().toShort();
1050
1051 if (vlanConfigChanged || innerVlanConfigChanged) {
1052 log.info("igmpproxy vlan config received. {}", config);
1053 //at least one of the vlan configs has changed. Call leave before setting new values
Esin Karaman592bf322020-07-14 14:46:14 +00001054 groupMemberStore.getAllGroupMembers().forEach(m -> {
1055 if (igmpLeadershipService.isLocalLeader(m.getDeviceId())) {
1056 leaveAction(m);
1057 }
1058 });
Esin Karaman586f1d62020-06-04 10:15:34 +00001059 if (vlanConfigChanged) {
1060 mvlan = config.egressVlan().toShort();
1061 IgmpSender.getInstance().setMvlan(mvlan);
1062 }
1063 if (innerVlanConfigChanged) {
1064 mvlanInner = config.egressInnerVlan().toShort();
1065 IgmpSender.getInstance().setMvlanInner(mvlanInner);
1066 }
ke han81a38b92017-03-10 18:41:44 +08001067 }
1068 }
1069
1070 log.info("Reconfigured");
1071 break;
1072 case CONFIG_REGISTERED:
1073 case CONFIG_UNREGISTERED:
1074 break;
1075 case CONFIG_REMOVED:
Matteo Scandolo7482bbe2020-02-12 14:37:09 -08001076 // NOTE how to know if something has changed in sadis?
ke han81a38b92017-03-10 18:41:44 +08001077 default:
1078 break;
1079 }
1080 }
1081 }
1082
ke han81a38b92017-03-10 18:41:44 +08001083 private void provisionUplinkFlows(DeviceId deviceId) {
1084 if (connectPointMode) {
1085 return;
1086 }
1087
Esin Karaman00e16b72020-02-21 10:32:39 +00001088 Optional<PortNumber> upLink = getDeviceUplink(deviceId);
1089 if (upLink.isPresent()) {
1090 processFilterObjective(deviceId, upLink.get(), false);
1091 }
ke han81a38b92017-03-10 18:41:44 +08001092 }
1093
1094 private void provisionUplinkFlows() {
1095 if (connectPointMode) {
1096 return;
1097 }
Esin Karaman00e16b72020-02-21 10:32:39 +00001098 deviceService.getAvailableDevices().forEach(device -> {
1099 Optional<SubscriberAndDeviceInformation> accessDevice = getSubscriberAndDeviceInformation(device.id());
1100 if (accessDevice.isPresent()) {
1101 provisionUplinkFlows(device.id());
1102 }
1103 });
ke han81a38b92017-03-10 18:41:44 +08001104 }
Esin Karaman00e16b72020-02-21 10:32:39 +00001105
ke han81a38b92017-03-10 18:41:44 +08001106 private void unprovisionUplinkFlows() {
Esin Karaman00e16b72020-02-21 10:32:39 +00001107 deviceService.getAvailableDevices().forEach(device -> {
1108 Optional<SubscriberAndDeviceInformation> accessDevices = getSubscriberAndDeviceInformation(device.id());
1109 if (accessDevices.isPresent()) {
1110 Optional<PortNumber> upLink = getDeviceUplink(device.id());
1111 if (upLink.isPresent()) {
1112 processFilterObjective(device.id(), upLink.get(), true);
1113 }
1114 }
1115 });
ke han81a38b92017-03-10 18:41:44 +08001116 }
1117
1118 private void provisionConnectPointFlows() {
1119 if ((!connectPointMode) || connectPoint == null) {
1120 return;
1121 }
1122
1123 processFilterObjective(connectPoint.deviceId(), connectPoint.port(), false);
1124 }
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +00001125
ke han81a38b92017-03-10 18:41:44 +08001126 private void unprovisionConnectPointFlows() {
1127 if (connectPoint == null) {
1128 return;
1129 }
1130 processFilterObjective(connectPoint.deviceId(), connectPoint.port(), true);
1131 }
Shubham Sharma47f2caf2020-02-18 12:13:40 +00001132
ke han81a38b92017-03-10 18:41:44 +08001133}