blob: bb134d58206a88878a554a8a6ef193976ab4b187 [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 */
ke han81a38b92017-03-10 18:41:44 +080016package org.opencord.igmpproxy;
17
18import com.google.common.collect.Maps;
Esin Karamaneff10392019-06-27 18:09:13 +000019import com.google.common.collect.Sets;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -080020import org.onosproject.net.Device;
21import org.opencord.sadis.BaseInformationService;
22import org.opencord.sadis.SadisService;
23import org.opencord.sadis.SubscriberAndDeviceInformation;
Carmelo Casconebef302e2019-11-14 19:58:20 -080024import org.osgi.service.component.annotations.Activate;
25import org.osgi.service.component.annotations.Component;
26import org.osgi.service.component.annotations.Deactivate;
27import org.osgi.service.component.annotations.Reference;
28import org.osgi.service.component.annotations.ReferenceCardinality;
ke han81a38b92017-03-10 18:41:44 +080029import org.onlab.packet.EthType;
30import org.onlab.packet.Ethernet;
31import org.onlab.packet.IGMP;
32import org.onlab.packet.IGMPGroup;
33import org.onlab.packet.IGMPMembership;
34import org.onlab.packet.IGMPQuery;
35import org.onlab.packet.IPv4;
36import org.onlab.packet.Ip4Address;
37import org.onlab.packet.IpAddress;
38import org.onlab.packet.VlanId;
39import org.onosproject.core.ApplicationId;
40import org.onosproject.core.CoreService;
ke han81a38b92017-03-10 18:41:44 +080041import org.onosproject.mastership.MastershipService;
42import org.onosproject.net.AnnotationKeys;
43import org.onosproject.net.ConnectPoint;
44import org.onosproject.net.DeviceId;
45import org.onosproject.net.Port;
46import org.onosproject.net.PortNumber;
47import org.onosproject.net.config.ConfigFactory;
48import org.onosproject.net.config.NetworkConfigEvent;
49import org.onosproject.net.config.NetworkConfigListener;
50import org.onosproject.net.config.NetworkConfigRegistry;
Jonathan Hart488e1142018-05-02 17:30:05 -070051import org.onosproject.net.config.basics.McastConfig;
ke han81a38b92017-03-10 18:41:44 +080052import org.onosproject.net.config.basics.SubjectFactories;
53import org.onosproject.net.device.DeviceEvent;
54import org.onosproject.net.device.DeviceListener;
55import org.onosproject.net.device.DeviceService;
56import org.onosproject.net.flow.DefaultTrafficTreatment;
57import org.onosproject.net.flow.FlowRuleService;
58import org.onosproject.net.flow.criteria.Criteria;
59import org.onosproject.net.flowobjective.DefaultFilteringObjective;
60import org.onosproject.net.flowobjective.FilteringObjective;
61import org.onosproject.net.flowobjective.FlowObjectiveService;
62import org.onosproject.net.flowobjective.Objective;
63import org.onosproject.net.flowobjective.ObjectiveContext;
64import org.onosproject.net.flowobjective.ObjectiveError;
Esin Karamaneff10392019-06-27 18:09:13 +000065import org.onosproject.mcast.api.McastRoute;
66import org.onosproject.mcast.api.MulticastRouteService;
ke han81a38b92017-03-10 18:41:44 +080067import org.onosproject.net.packet.InboundPacket;
68import org.onosproject.net.packet.PacketContext;
69import org.onosproject.net.packet.PacketProcessor;
70import org.onosproject.net.packet.PacketService;
ke han81a38b92017-03-10 18:41:44 +080071import org.slf4j.Logger;
72import org.slf4j.LoggerFactory;
73
74import java.util.ArrayList;
75import java.util.Collection;
Esin Karamaneff10392019-06-27 18:09:13 +000076import java.util.HashSet;
ke han81a38b92017-03-10 18:41:44 +080077import java.util.Iterator;
ke han81a38b92017-03-10 18:41:44 +080078import java.util.Map;
Esin Karamaneff10392019-06-27 18:09:13 +000079import java.util.Optional;
ke han81a38b92017-03-10 18:41:44 +080080import java.util.Set;
81import java.util.TimerTask;
82import java.util.concurrent.ConcurrentHashMap;
Esin Karamanb38700c2019-09-17 13:01:25 +000083import java.util.concurrent.ExecutorService;
ke han81a38b92017-03-10 18:41:44 +080084import java.util.concurrent.Executors;
85import java.util.concurrent.ScheduledExecutorService;
86import java.util.concurrent.TimeUnit;
87
Esin Karamanb38700c2019-09-17 13:01:25 +000088import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
89import static org.onlab.util.Tools.groupedThreads;
90
ke han81a38b92017-03-10 18:41:44 +080091/**
92 * Igmp process application, use proxy mode, support first join/ last leave , fast leave
93 * period query and keep alive, packet out igmp message to uplink port features.
94 */
95@Component(immediate = true)
96public class IgmpManager {
97
Matteo Scandolo7482bbe2020-02-12 14:37:09 -080098 private static final String APP_NAME = "org.opencord.igmpproxy";
99
ke han81a38b92017-03-10 18:41:44 +0800100 private static final Class<IgmpproxyConfig> IGMPPROXY_CONFIG_CLASS =
101 IgmpproxyConfig.class;
102 private static final Class<IgmpproxySsmTranslateConfig> IGMPPROXY_SSM_CONFIG_CLASS =
103 IgmpproxySsmTranslateConfig.class;
104 private static final Class<McastConfig> MCAST_CONFIG_CLASS =
105 McastConfig.class;
Esin Karamaneff10392019-06-27 18:09:13 +0000106
ke han81a38b92017-03-10 18:41:44 +0800107 public static Map<String, GroupMember> groupMemberMap = Maps.newConcurrentMap();
108 private static ApplicationId appId;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800109
ke han81a38b92017-03-10 18:41:44 +0800110 private static int unSolicitedTimeout = 3; // unit is 1 sec
111 private static int keepAliveCount = 3;
112 private static int lastQueryInterval = 2; //unit is 1 sec
113 private static int lastQueryCount = 2;
114 private static boolean fastLeave = true;
115 private static boolean withRAUplink = true;
116 private static boolean withRADownlink = false;
117 private static boolean periodicQuery = true;
118 private static short mvlan = 4000;
119 private static byte igmpCos = 7;
120 public static boolean connectPointMode = true;
121 public static ConnectPoint connectPoint = null;
Esin Karamaneff10392019-06-27 18:09:13 +0000122 private static ConnectPoint sourceDeviceAndPort = null;
123 private static boolean enableIgmpProvisioning = false;
Esin Karamanb38700c2019-09-17 13:01:25 +0000124 private static boolean igmpOnPodBasis = false;
Esin Karamaneff10392019-06-27 18:09:13 +0000125
126 private static final Integer MAX_PRIORITY = 10000;
127 private static final String INSTALLED = "installed";
128 private static final String REMOVED = "removed";
129 private static final String INSTALLATION = "installation";
130 private static final String REMOVAL = "removal";
Esin Karaman00e16b72020-02-21 10:32:39 +0000131 private static final String NNI_PREFIX = "nni";
ke han81a38b92017-03-10 18:41:44 +0800132
ke han29af27b2017-09-08 10:29:12 +0800133 private static boolean pimSSmInterworking = false;
134 private static final String DEFAULT_PIMSSM_HOST = "127.0.0.1";
ke han81a38b92017-03-10 18:41:44 +0800135 private final ScheduledExecutorService scheduledExecutorService =
136 Executors.newScheduledThreadPool(1);
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800137
Carmelo Casconebef302e2019-11-14 19:58:20 -0800138 @Reference(cardinality = ReferenceCardinality.MANDATORY)
ke han81a38b92017-03-10 18:41:44 +0800139 protected CoreService coreService;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800140
Carmelo Casconebef302e2019-11-14 19:58:20 -0800141 @Reference(cardinality = ReferenceCardinality.MANDATORY)
ke han81a38b92017-03-10 18:41:44 +0800142 protected PacketService packetService;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800143
Carmelo Casconebef302e2019-11-14 19:58:20 -0800144 @Reference(cardinality = ReferenceCardinality.MANDATORY)
ke han81a38b92017-03-10 18:41:44 +0800145 protected MastershipService mastershipService;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800146
Carmelo Casconebef302e2019-11-14 19:58:20 -0800147 @Reference(cardinality = ReferenceCardinality.MANDATORY)
ke han81a38b92017-03-10 18:41:44 +0800148 protected FlowRuleService flowRuleService;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800149
Carmelo Casconebef302e2019-11-14 19:58:20 -0800150 @Reference(cardinality = ReferenceCardinality.MANDATORY)
ke han81a38b92017-03-10 18:41:44 +0800151 protected DeviceService deviceService;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800152
Carmelo Casconebef302e2019-11-14 19:58:20 -0800153 @Reference(cardinality = ReferenceCardinality.MANDATORY)
ke han81a38b92017-03-10 18:41:44 +0800154 protected FlowObjectiveService flowObjectiveService;
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 NetworkConfigRegistry networkConfig;
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 MulticastRouteService multicastService;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800161
162 @Reference(cardinality = ReferenceCardinality.MANDATORY)
163 protected SadisService sadisService;
164
ke han81a38b92017-03-10 18:41:44 +0800165 private IgmpPacketProcessor processor = new IgmpPacketProcessor();
166 private Logger log = LoggerFactory.getLogger(getClass());
167 private ApplicationId coreAppId;
168 private Map<Ip4Address, Ip4Address> ssmTranslateTable = new ConcurrentHashMap<>();
Esin Karamaneff10392019-06-27 18:09:13 +0000169
ke han81a38b92017-03-10 18:41:44 +0800170 private InternalNetworkConfigListener configListener =
171 new InternalNetworkConfigListener();
172 private DeviceListener deviceListener = new InternalDeviceListener();
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800173
ke han81a38b92017-03-10 18:41:44 +0800174 private ConfigFactory<ApplicationId, IgmpproxyConfig> igmpproxyConfigFactory =
175 new ConfigFactory<ApplicationId, IgmpproxyConfig>(
176 SubjectFactories.APP_SUBJECT_FACTORY, IGMPPROXY_CONFIG_CLASS, "igmpproxy") {
177 @Override
178 public IgmpproxyConfig createConfig() {
179 return new IgmpproxyConfig();
180 }
181 };
182 private ConfigFactory<ApplicationId, IgmpproxySsmTranslateConfig> igmpproxySsmConfigFactory =
183 new ConfigFactory<ApplicationId, IgmpproxySsmTranslateConfig>(
184 SubjectFactories.APP_SUBJECT_FACTORY, IGMPPROXY_SSM_CONFIG_CLASS, "ssmTranslate", true) {
185 @Override
186 public IgmpproxySsmTranslateConfig createConfig() {
187 return new IgmpproxySsmTranslateConfig();
188 }
189 };
Esin Karamaneff10392019-06-27 18:09:13 +0000190
ke han81a38b92017-03-10 18:41:44 +0800191 private int maxResp = 10; //unit is 1 sec
192 private int keepAliveInterval = 120; //unit is 1 sec
193
Esin Karamanb38700c2019-09-17 13:01:25 +0000194 private ExecutorService eventExecutor;
195
ke han81a38b92017-03-10 18:41:44 +0800196 public static int getUnsolicitedTimeout() {
197 return unSolicitedTimeout;
198 }
199
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800200 protected BaseInformationService<SubscriberAndDeviceInformation> subsService;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800201
ke han81a38b92017-03-10 18:41:44 +0800202 @Activate
203 protected void activate() {
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800204 appId = coreService.registerApplication(APP_NAME);
ke han81a38b92017-03-10 18:41:44 +0800205 coreAppId = coreService.registerApplication(CoreService.CORE_APP_NAME);
206 packetService.addProcessor(processor, PacketProcessor.director(4));
207 IgmpSender.init(packetService, mastershipService);
208
ke han81a38b92017-03-10 18:41:44 +0800209 networkConfig.registerConfigFactory(igmpproxySsmConfigFactory);
210 networkConfig.registerConfigFactory(igmpproxyConfigFactory);
211 networkConfig.addListener(configListener);
212
213 configListener.reconfigureNetwork(networkConfig.getConfig(appId, IGMPPROXY_CONFIG_CLASS));
214 configListener.reconfigureSsmTable(networkConfig.getConfig(appId, IGMPPROXY_SSM_CONFIG_CLASS));
215
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800216 subsService = sadisService.getSubscriberInfoService();
ke han81a38b92017-03-10 18:41:44 +0800217
ke han81a38b92017-03-10 18:41:44 +0800218 if (connectPointMode) {
219 provisionConnectPointFlows();
220 } else {
221 provisionUplinkFlows();
222 }
223
224 McastConfig config = networkConfig.getConfig(coreAppId, MCAST_CONFIG_CLASS);
225 if (config != null) {
226 mvlan = config.egressVlan().toShort();
Deepa Vaddireddy88134a42017-07-05 12:16:03 +0530227 IgmpSender.getInstance().setMvlan(mvlan);
ke han81a38b92017-03-10 18:41:44 +0800228 }
229 deviceService.addListener(deviceListener);
Sonal Kasliwalddc3ff22019-11-18 11:52:49 +0000230 scheduledExecutorService.scheduleAtFixedRate(new IgmpProxyTimerTask(), 1000, 1000, TimeUnit.MILLISECONDS);
Esin Karamanb38700c2019-09-17 13:01:25 +0000231 eventExecutor = newSingleThreadScheduledExecutor(groupedThreads("cord/igmpproxy",
232 "events-igmp-%d", log));
ke han81a38b92017-03-10 18:41:44 +0800233
234 log.info("Started");
235 }
236
237 @Deactivate
238 protected void deactivate() {
239 scheduledExecutorService.shutdown();
Esin Karamanb38700c2019-09-17 13:01:25 +0000240 eventExecutor.shutdown();
ke han81a38b92017-03-10 18:41:44 +0800241
242 // de-register and null our handler
243 networkConfig.removeListener(configListener);
ke han81a38b92017-03-10 18:41:44 +0800244 networkConfig.unregisterConfigFactory(igmpproxyConfigFactory);
245 networkConfig.unregisterConfigFactory(igmpproxySsmConfigFactory);
246 deviceService.removeListener(deviceListener);
247 packetService.removeProcessor(processor);
248 flowRuleService.removeFlowRulesById(appId);
249
250 log.info("Stopped");
251 }
252
253 protected Ip4Address getDeviceIp(DeviceId ofDeviceId) {
254 try {
255 String[] mgmtAddress = deviceService.getDevice(ofDeviceId)
256 .annotations().value(AnnotationKeys.MANAGEMENT_ADDRESS).split(":");
257 return Ip4Address.valueOf(mgmtAddress[0]);
258 } catch (Exception ex) {
259 log.info("No valid Ipaddress for " + ofDeviceId.toString());
260 return null;
261 }
262 }
263
264 private void processIgmpQuery(IGMPQuery igmpQuery, ConnectPoint cp, int maxResp) {
265
266 DeviceId deviceId = cp.deviceId();
267 Ip4Address gAddr = igmpQuery.getGaddr().getIp4Address();
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000268 maxResp = calculateMaxResp(maxResp);
269 if (gAddr != null && !gAddr.isZero()) {
270 StateMachine.specialQuery(deviceId, gAddr, maxResp);
271 } else {
272 StateMachine.generalQuery(deviceId, maxResp);
273 }
274 }
ke han81a38b92017-03-10 18:41:44 +0800275
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000276 private void processIgmpConnectPointQuery(IGMPQuery igmpQuery, ConnectPoint cp, int maxResp) {
277
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000278 Ip4Address gAddr = igmpQuery.getGaddr().getIp4Address();
Esin Karaman00e16b72020-02-21 10:32:39 +0000279 final int maxResponseTime = calculateMaxResp(maxResp);
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000280 //The query is received on the ConnectPoint
281 // send query accordingly to the registered OLT devices.
282 if (gAddr != null && !gAddr.isZero()) {
Esin Karaman00e16b72020-02-21 10:32:39 +0000283 deviceService.getAvailableDevices().forEach(device -> {
284 Optional<SubscriberAndDeviceInformation> accessDevice = getSubscriberAndDeviceInformation(device.id());
285 if (accessDevice.isPresent()) {
286 StateMachine.specialQuery(device.id(), gAddr, maxResponseTime);
287 }
288 });
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000289 } else {
290 //Don't know which group is targeted by the query
291 //So query all the members(in all the OLTs) and proxy their reports
Esin Karaman00e16b72020-02-21 10:32:39 +0000292 StateMachine.generalQuery(maxResponseTime);
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000293 }
294 }
295
296
297 private int calculateMaxResp(int maxResp) {
ke han81a38b92017-03-10 18:41:44 +0800298 if (maxResp >= 128) {
299 int mant = maxResp & 0xf;
300 int exp = (maxResp >> 4) & 0x7;
301 maxResp = (mant | 0x10) << (exp + 3);
302 }
303
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000304 return (maxResp + 5) / 10;
ke han81a38b92017-03-10 18:41:44 +0800305 }
306
307 private Ip4Address ssmTranslateRoute(IpAddress group) {
308 return ssmTranslateTable.get(group);
309 }
310
311 private void processIgmpReport(IGMPMembership igmpGroup, VlanId vlan, ConnectPoint cp, byte igmpType) {
312 DeviceId deviceId = cp.deviceId();
313 PortNumber portNumber = cp.port();
314
315 Ip4Address groupIp = igmpGroup.getGaddr().getIp4Address();
316 if (!groupIp.isMulticast()) {
317 log.info(groupIp.toString() + " is not a valid group address");
318 return;
319 }
320 Ip4Address srcIp = getDeviceIp(deviceId);
321
322 byte recordType = igmpGroup.getRecordType();
323 boolean join = false;
324
325 ArrayList<Ip4Address> sourceList = new ArrayList<>();
326
327 if (igmpGroup.getSources().size() > 0) {
328 igmpGroup.getSources().forEach(source -> sourceList.add(source.getIp4Address()));
329 if (recordType == IGMPMembership.CHANGE_TO_EXCLUDE_MODE ||
330 recordType == IGMPMembership.MODE_IS_EXCLUDE ||
331 recordType == IGMPMembership.BLOCK_OLD_SOURCES) {
332 join = false;
333 } else if (recordType == IGMPMembership.CHANGE_TO_INCLUDE_MODE ||
334 recordType == IGMPMembership.MODE_IS_INCLUDE ||
335 recordType == IGMPMembership.ALLOW_NEW_SOURCES) {
336 join = true;
337 }
338 } else {
ke han29af27b2017-09-08 10:29:12 +0800339 IpAddress src = null;
340 if (pimSSmInterworking) {
341 src = ssmTranslateRoute(groupIp);
342 if (src == null) {
343 log.info("no ssm translate for group " + groupIp.toString());
344 return;
345 }
346 } else {
347 src = IpAddress.valueOf(DEFAULT_PIMSSM_HOST);
ke han81a38b92017-03-10 18:41:44 +0800348 }
349 sourceList.add(src.getIp4Address());
350 if (recordType == IGMPMembership.CHANGE_TO_EXCLUDE_MODE ||
351 recordType == IGMPMembership.MODE_IS_EXCLUDE ||
352 recordType == IGMPMembership.BLOCK_OLD_SOURCES) {
353 join = true;
354 } else if (recordType == IGMPMembership.CHANGE_TO_INCLUDE_MODE ||
355 recordType == IGMPMembership.MODE_IS_INCLUDE ||
356 recordType == IGMPMembership.ALLOW_NEW_SOURCES) {
357 join = false;
358 }
359 }
360 String groupMemberKey = GroupMember.getkey(groupIp, deviceId, portNumber);
361 GroupMember groupMember = groupMemberMap.get(groupMemberKey);
362
363 if (join) {
364 if (groupMember == null) {
Esin Karamaneff10392019-06-27 18:09:13 +0000365 Optional<ConnectPoint> sourceConfigured = getSource();
366 if (!sourceConfigured.isPresent()) {
367 log.warn("Unable to process IGMP Join from {} since no source " +
368 "configuration is found.", deviceId);
369 return;
370 }
Esin Karaman00e16b72020-02-21 10:32:39 +0000371
372 Optional<PortNumber> deviceUplink = getDeviceUplink(deviceId);
373 if (deviceUplink.isEmpty()) {
374 log.warn("Unable to process IGMP Join since uplink port " +
375 "of the device {} is not found.", deviceId);
376 return;
377 }
378
379 if (igmpType == IGMP.TYPE_IGMPV2_MEMBERSHIP_REPORT) {
380 groupMember = new GroupMember(groupIp, vlan, deviceId, portNumber, true);
381 } else {
382 groupMember = new GroupMember(groupIp, vlan, deviceId, portNumber, false);
383 }
384
Esin Karamaneff10392019-06-27 18:09:13 +0000385 HashSet<ConnectPoint> sourceConnectPoints = Sets.newHashSet(sourceConfigured.get());
386
Esin Karaman00e16b72020-02-21 10:32:39 +0000387 StateMachine.join(deviceId, groupIp, srcIp, deviceUplink.get());
ke han81a38b92017-03-10 18:41:44 +0800388 groupMemberMap.put(groupMemberKey, groupMember);
389 groupMember.updateList(recordType, sourceList);
Esin Karamaneff10392019-06-27 18:09:13 +0000390 groupMember.getSourceList().forEach(source -> {
391 McastRoute route = new McastRoute(source, groupIp, McastRoute.Type.IGMP);
392 //add route
393 multicastService.add(route);
394 //add source to the route
395 multicastService.addSources(route, Sets.newHashSet(sourceConnectPoints));
396 //add sink to the route
397 multicastService.addSinks(route, Sets.newHashSet(cp));
398 });
399
ke han81a38b92017-03-10 18:41:44 +0800400 }
401 groupMember.resetAllTimers();
402 groupMember.updateList(recordType, sourceList);
403 groupMember.setLeave(false);
404 } else {
405 if (groupMember == null) {
406 log.info("receive leave but no instance, group " + groupIp.toString() +
407 " device:" + deviceId.toString() + " port:" + portNumber.toString());
408 return;
409 } else {
410 groupMember.setLeave(true);
411 if (fastLeave) {
412 leaveAction(groupMember);
413 } else {
414 sendQuery(groupMember);
415 }
416 }
417 }
418 }
419
420 private void leaveAction(GroupMember groupMember) {
421 ConnectPoint cp = new ConnectPoint(groupMember.getDeviceId(), groupMember.getPortNumber());
422 StateMachine.leave(groupMember.getDeviceId(), groupMember.getGroupIp());
Esin Karamaneff10392019-06-27 18:09:13 +0000423 groupMember.getSourceList().forEach(source -> multicastService.removeSinks(
ke han81a38b92017-03-10 18:41:44 +0800424 new McastRoute(source, groupMember.getGroupIp(),
Esin Karamaneff10392019-06-27 18:09:13 +0000425 McastRoute.Type.IGMP), Sets.newHashSet(cp)));
ke han81a38b92017-03-10 18:41:44 +0800426 groupMemberMap.remove(groupMember.getId());
427 }
428
429 private void sendQuery(GroupMember groupMember) {
430 Ethernet ethpkt;
431 Ip4Address srcIp = getDeviceIp(groupMember.getDeviceId());
432 if (groupMember.getv2()) {
433 ethpkt = IgmpSender.getInstance().buildIgmpV2Query(groupMember.getGroupIp(), srcIp);
434 } else {
435 ethpkt = IgmpSender.getInstance().buildIgmpV3Query(groupMember.getGroupIp(), srcIp);
436 }
437 IgmpSender.getInstance().sendIgmpPacket(ethpkt, groupMember.getDeviceId(), groupMember.getPortNumber());
438 }
439
Esin Karamaneff10392019-06-27 18:09:13 +0000440 /**
441 * @return connect point of the source if configured; and empty Optional otherwise.
442 */
443 public static Optional<ConnectPoint> getSource() {
444 return sourceDeviceAndPort == null ? Optional.empty() :
445 Optional.of(sourceDeviceAndPort);
ke han81a38b92017-03-10 18:41:44 +0800446 }
447
448 /**
449 * Packet processor responsible for forwarding packets along their paths.
450 */
451 private class IgmpPacketProcessor implements PacketProcessor {
452 @Override
453 public void process(PacketContext context) {
Esin Karamanb38700c2019-09-17 13:01:25 +0000454 eventExecutor.execute(() -> {
455 try {
456 InboundPacket pkt = context.inPacket();
457 Ethernet ethPkt = pkt.parsed();
458 if (ethPkt == null) {
459 return;
460 }
ke han81a38b92017-03-10 18:41:44 +0800461
Esin Karamanb38700c2019-09-17 13:01:25 +0000462 if (ethPkt.getEtherType() != Ethernet.TYPE_IPV4) {
463 return;
464 }
ke han81a38b92017-03-10 18:41:44 +0800465
Esin Karamanb38700c2019-09-17 13:01:25 +0000466 IPv4 ipv4Pkt = (IPv4) ethPkt.getPayload();
ke han81a38b92017-03-10 18:41:44 +0800467
Esin Karamanb38700c2019-09-17 13:01:25 +0000468 if (ipv4Pkt.getProtocol() != IPv4.PROTOCOL_IGMP) {
469 return;
470 }
ke han81a38b92017-03-10 18:41:44 +0800471
Esin Karamanb38700c2019-09-17 13:01:25 +0000472 short vlan = ethPkt.getVlanID();
473 DeviceId deviceId = pkt.receivedFrom().deviceId();
ke han81a38b92017-03-10 18:41:44 +0800474
Esin Karaman00e16b72020-02-21 10:32:39 +0000475 if (!isConnectPoint(deviceId, pkt.receivedFrom().port()) &&
476 !getSubscriberAndDeviceInformation(deviceId).isPresent()) {
Esin Karamanb38700c2019-09-17 13:01:25 +0000477 log.error("Device not registered in netcfg :" + deviceId.toString());
478 return;
479 }
ke han81a38b92017-03-10 18:41:44 +0800480
Esin Karamanb38700c2019-09-17 13:01:25 +0000481 IGMP igmp = (IGMP) ipv4Pkt.getPayload();
Esin Karamance5ce512020-02-25 15:58:14 +0000482
483 Optional<PortNumber> deviceUpLinkOpt = getDeviceUplink(deviceId);
484 PortNumber upLinkPort = deviceUpLinkOpt.isPresent() ? deviceUpLinkOpt.get() : null;
Esin Karamanb38700c2019-09-17 13:01:25 +0000485 switch (igmp.getIgmpType()) {
486 case IGMP.TYPE_IGMPV3_MEMBERSHIP_QUERY:
487 //Discard Query from OLT’s non-uplink port’s
Esin Karamance5ce512020-02-25 15:58:14 +0000488 if (!pkt.receivedFrom().port().equals(upLinkPort)) {
Esin Karamanb38700c2019-09-17 13:01:25 +0000489 if (isConnectPoint(deviceId, pkt.receivedFrom().port())) {
490 log.info("IGMP Picked up query from connectPoint");
491 //OK to process packet
492 processIgmpConnectPointQuery((IGMPQuery) igmp.getGroups().get(0),
493 pkt.receivedFrom(),
494 0xff & igmp.getMaxRespField());
495 break;
496 } else {
497 //Not OK to process packet
Esin Karamance5ce512020-02-25 15:58:14 +0000498 log.warn("IGMP Picked up query from non-uplink port {}", upLinkPort);
Esin Karamanb38700c2019-09-17 13:01:25 +0000499 return;
500 }
501 }
ke han81a38b92017-03-10 18:41:44 +0800502
Esin Karamanb38700c2019-09-17 13:01:25 +0000503 processIgmpQuery((IGMPQuery) igmp.getGroups().get(0), pkt.receivedFrom(),
504 0xff & igmp.getMaxRespField());
505 break;
506 case IGMP.TYPE_IGMPV1_MEMBERSHIP_REPORT:
507 log.debug("IGMP version 1 message types are not currently supported.");
508 break;
509 case IGMP.TYPE_IGMPV3_MEMBERSHIP_REPORT:
510 case IGMP.TYPE_IGMPV2_MEMBERSHIP_REPORT:
511 case IGMP.TYPE_IGMPV2_LEAVE_GROUP:
512 //Discard join/leave from OLT’s uplink port’s
Esin Karamance5ce512020-02-25 15:58:14 +0000513 if (pkt.receivedFrom().port().equals(upLinkPort) ||
Esin Karamanb38700c2019-09-17 13:01:25 +0000514 isConnectPoint(deviceId, pkt.receivedFrom().port())) {
515 log.info("IGMP Picked up join/leave from uplink/connectPoint port");
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000516 return;
517 }
ke han81a38b92017-03-10 18:41:44 +0800518
Esin Karamanb38700c2019-09-17 13:01:25 +0000519 Iterator<IGMPGroup> itr = igmp.getGroups().iterator();
520 while (itr.hasNext()) {
521 IGMPGroup group = itr.next();
522 if (group instanceof IGMPMembership) {
523 processIgmpReport((IGMPMembership) group, VlanId.vlanId(vlan),
524 pkt.receivedFrom(), igmp.getIgmpType());
525 } else if (group instanceof IGMPQuery) {
526 IGMPMembership mgroup;
527 mgroup = new IGMPMembership(group.getGaddr().getIp4Address());
528 mgroup.setRecordType(igmp.getIgmpType() == IGMP.TYPE_IGMPV2_MEMBERSHIP_REPORT ?
529 IGMPMembership.MODE_IS_EXCLUDE :
530 IGMPMembership.MODE_IS_INCLUDE);
531 processIgmpReport(mgroup, VlanId.vlanId(vlan),
532 pkt.receivedFrom(), igmp.getIgmpType());
533 }
ke han81a38b92017-03-10 18:41:44 +0800534 }
Esin Karamanb38700c2019-09-17 13:01:25 +0000535 break;
ke han81a38b92017-03-10 18:41:44 +0800536
Esin Karamanb38700c2019-09-17 13:01:25 +0000537 default:
538 log.info("wrong IGMP v3 type:" + igmp.getIgmpType());
539 break;
540 }
541
542 } catch (Exception ex) {
543 log.error("igmp process error : {} ", ex);
544 ex.printStackTrace();
ke han81a38b92017-03-10 18:41:44 +0800545 }
Esin Karamanb38700c2019-09-17 13:01:25 +0000546 });
ke han81a38b92017-03-10 18:41:44 +0800547 }
548 }
549
550 private class IgmpProxyTimerTask extends TimerTask {
551 public void run() {
552 try {
553 IgmpTimer.timeOut1s();
554 queryMembers();
555 } catch (Exception ex) {
556 log.warn("Igmp timer task error : {}", ex.getMessage());
557 }
558 }
559
560 private void queryMembers() {
561 GroupMember groupMember;
562 Set groupMemberSet = groupMemberMap.entrySet();
563 Iterator itr = groupMemberSet.iterator();
564 while (itr.hasNext()) {
565 Map.Entry entry = (Map.Entry) itr.next();
566 groupMember = (GroupMember) entry.getValue();
567 DeviceId did = groupMember.getDeviceId();
568 if (mastershipService.isLocalMaster(did)) {
569 if (groupMember.isLeave()) {
570 lastQuery(groupMember);
571 } else if (periodicQuery) {
572 periodicQuery(groupMember);
573 }
574 }
575 }
576 }
577
578 private void lastQuery(GroupMember groupMember) {
579 if (groupMember.getLastQueryInterval() < lastQueryInterval) {
580 groupMember.lastQueryInterval(true); // count times
581 } else if (groupMember.getLastQueryCount() < lastQueryCount - 1) {
582 sendQuery(groupMember);
583 groupMember.lastQueryInterval(false); // reset count number
584 groupMember.lastQueryCount(true); //count times
585 } else if (groupMember.getLastQueryCount() == lastQueryCount - 1) {
586 leaveAction(groupMember);
587 }
588 }
589
590 private void periodicQuery(GroupMember groupMember) {
591 if (groupMember.getKeepAliveQueryInterval() < keepAliveInterval) {
592 groupMember.keepAliveInterval(true);
593 } else if (groupMember.getKeepAliveQueryCount() < keepAliveCount) {
594 sendQuery(groupMember);
595 groupMember.keepAliveInterval(false);
596 groupMember.keepAliveQueryCount(true);
597 } else if (groupMember.getKeepAliveQueryCount() == keepAliveCount) {
598 leaveAction(groupMember);
599 }
600 }
601
602 }
603
Esin Karaman00e16b72020-02-21 10:32:39 +0000604 public Optional<PortNumber> getDeviceUplink(DeviceId devId) {
605 Device device = deviceService.getDevice(devId);
606 if (device == null || device.serialNumber() == null) {
607 return Optional.empty();
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000608 }
Esin Karaman00e16b72020-02-21 10:32:39 +0000609 Optional<SubscriberAndDeviceInformation> olt = getSubscriberAndDeviceInformation(device.serialNumber());
610 if (olt.isEmpty()) {
611 return Optional.empty();
612 }
613 PortNumber portNumber = PortNumber.portNumber(olt.get().uplinkPort());
614 return validateUpLinkPort(device.id(), portNumber) ?
615 Optional.of(portNumber) : Optional.empty();
616 }
617
618 /**
619 *
620 * @param deviceId device id
621 * @param portNumber port number
622 * @return true if the port name starts with NNI_PREFIX; false otherwise.
623 */
624 public boolean validateUpLinkPort(DeviceId deviceId, PortNumber portNumber) {
625 Port port = deviceService.getPort(deviceId, portNumber);
626 if (port == null) {
627 //port is not discovered by ONOS; so cannot validate it.
628 return false;
629 }
Esin Karamance5ce512020-02-25 15:58:14 +0000630 boolean isValid = port.annotations().value(AnnotationKeys.PORT_NAME) != null &&
Esin Karaman00e16b72020-02-21 10:32:39 +0000631 port.annotations().value(AnnotationKeys.PORT_NAME).startsWith(NNI_PREFIX);
Esin Karamance5ce512020-02-25 15:58:14 +0000632 if (!isValid) {
633 log.warn("Port cannot be validated; it is not configured as an NNI port." +
634 "Device/port: {}/{}", deviceId, portNumber);
635 }
636 return isValid;
ke han81a38b92017-03-10 18:41:44 +0800637 }
638
Esin Karamanb38700c2019-09-17 13:01:25 +0000639 public static boolean isIgmpOnPodBasis() {
640 return igmpOnPodBasis;
641 }
642
ke han81a38b92017-03-10 18:41:44 +0800643 private void processFilterObjective(DeviceId devId, PortNumber port, boolean remove) {
Esin Karamaneff10392019-06-27 18:09:13 +0000644 if (!enableIgmpProvisioning) {
645 log.debug("IGMP trap rules won't be installed since enableIgmpProvisioning flag is not set");
646 return;
647 }
ke han81a38b92017-03-10 18:41:44 +0800648 //TODO migrate to packet requests when packet service uses filtering objectives
649 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
650
651 builder = remove ? builder.deny() : builder.permit();
652
653 FilteringObjective igmp = builder
654 .withKey(Criteria.matchInPort(port))
655 .addCondition(Criteria.matchEthType(EthType.EtherType.IPV4.ethType()))
656 .addCondition(Criteria.matchIPProtocol(IPv4.PROTOCOL_IGMP))
657 .withMeta(DefaultTrafficTreatment.builder().setOutput(PortNumber.CONTROLLER).build())
658 .fromApp(appId)
Esin Karamaneff10392019-06-27 18:09:13 +0000659 .withPriority(MAX_PRIORITY)
ke han81a38b92017-03-10 18:41:44 +0800660 .add(new ObjectiveContext() {
661 @Override
662 public void onSuccess(Objective objective) {
Esin Karamaneff10392019-06-27 18:09:13 +0000663 log.info("Igmp filter for {} on {} {}.",
664 devId, port, (remove) ? REMOVED : INSTALLED);
ke han81a38b92017-03-10 18:41:44 +0800665 }
666
667 @Override
668 public void onError(Objective objective, ObjectiveError error) {
Esin Karamaneff10392019-06-27 18:09:13 +0000669 log.info("Igmp filter {} for device {} on port {} failed because of {}",
670 (remove) ? INSTALLATION : REMOVAL, devId, port,
671 error);
ke han81a38b92017-03-10 18:41:44 +0800672 }
673 });
674
675 flowObjectiveService.filter(devId, igmp);
Esin Karamaneff10392019-06-27 18:09:13 +0000676
ke han81a38b92017-03-10 18:41:44 +0800677 }
678
679 private boolean isConnectPoint(DeviceId device, PortNumber port) {
Deepa Vaddireddy01f33b42017-07-02 13:26:53 +0530680 if (connectPoint != null) {
681 return (connectPointMode && connectPoint.deviceId().equals(device)
682 && connectPoint.port().equals(port));
683 } else {
684 log.info("connectPoint not configured for device {}", device);
685 return false;
686 }
ke han81a38b92017-03-10 18:41:44 +0800687 }
Deepa Vaddireddy01f33b42017-07-02 13:26:53 +0530688
ke han81a38b92017-03-10 18:41:44 +0800689 private boolean isUplink(DeviceId device, PortNumber port) {
Esin Karamance5ce512020-02-25 15:58:14 +0000690 if (connectPointMode) {
691 return false;
692 }
693 Optional<PortNumber> upLinkPort = getDeviceUplink(device);
694 return upLinkPort.isPresent() && upLinkPort.get().equals(port);
ke han81a38b92017-03-10 18:41:44 +0800695 }
696
Esin Karaman00e16b72020-02-21 10:32:39 +0000697 /**
698 * Fetches device information associated with the device serial number from SADIS.
699 *
700 * @param serialNumber serial number of a device
701 * @return device information; an empty Optional otherwise.
702 */
703 private Optional<SubscriberAndDeviceInformation> getSubscriberAndDeviceInformation(String serialNumber) {
704 long start = System.currentTimeMillis();
705 try {
706 return Optional.ofNullable(sadisService.getSubscriberInfoService().get(serialNumber));
707 } finally {
708 if (log.isDebugEnabled()) {
709 // SADIS can call remote systems to fetch device data and this calls can take a long time.
710 // This measurement is just for monitoring these kinds of situations.
711 log.debug("Device fetched from SADIS. Elapsed {} msec", System.currentTimeMillis() - start);
712 }
713
714 }
715 }
716
717 /**
718 * Fetches device information associated with the device serial number from SADIS.
719 *
720 * @param deviceId device id
721 * @return device information; an empty Optional otherwise.
722 */
723 private Optional<SubscriberAndDeviceInformation> getSubscriberAndDeviceInformation(DeviceId deviceId) {
724 Device device = deviceService.getDevice(deviceId);
725 if (device == null || device.serialNumber() == null) {
726 return Optional.empty();
727 }
728 return getSubscriberAndDeviceInformation(device.serialNumber());
729 }
730
ke han81a38b92017-03-10 18:41:44 +0800731 private class InternalDeviceListener implements DeviceListener {
732 @Override
733 public void event(DeviceEvent event) {
734 DeviceId devId = event.subject().id();
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +0000735 Port p = event.port();
Esin Karaman00e16b72020-02-21 10:32:39 +0000736 if (getSubscriberAndDeviceInformation(devId).isEmpty() &&
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +0000737 !(p != null && isConnectPoint(devId, p.number()))) {
ke han81a38b92017-03-10 18:41:44 +0800738 return;
739 }
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +0000740 PortNumber port;
741
ke han81a38b92017-03-10 18:41:44 +0800742 switch (event.type()) {
743
744 case DEVICE_ADDED:
745 case DEVICE_UPDATED:
746 case DEVICE_REMOVED:
747 case DEVICE_SUSPENDED:
748 case DEVICE_AVAILABILITY_CHANGED:
749 case PORT_STATS_UPDATED:
750 break;
751 case PORT_ADDED:
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +0000752 port = p.number();
Esin Karaman00e16b72020-02-21 10:32:39 +0000753 if (getSubscriberAndDeviceInformation(devId).isPresent() &&
754 !isUplink(devId, port) && !isConnectPoint(devId, port)) {
ke han81a38b92017-03-10 18:41:44 +0800755 processFilterObjective(devId, port, false);
756 } else if (isUplink(devId, port)) {
757 provisionUplinkFlows();
758 } else if (isConnectPoint(devId, port)) {
759 provisionConnectPointFlows();
760 }
761 break;
762 case PORT_UPDATED:
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +0000763 port = p.number();
Esin Karaman00e16b72020-02-21 10:32:39 +0000764 if (getSubscriberAndDeviceInformation(devId).isPresent() &&
765 !isUplink(devId, port) && !isConnectPoint(devId, port)) {
ke han81a38b92017-03-10 18:41:44 +0800766 if (event.port().isEnabled()) {
767 processFilterObjective(devId, port, false);
768 } else {
769 processFilterObjective(devId, port, true);
770 }
771 } else if (isUplink(devId, port)) {
772 if (event.port().isEnabled()) {
773 provisionUplinkFlows(devId);
774 } else {
775 processFilterObjective(devId, port, true);
776 }
777 } else if (isConnectPoint(devId, port)) {
778 if (event.port().isEnabled()) {
779 provisionConnectPointFlows();
780 } else {
781 unprovisionConnectPointFlows();
782 }
783 }
784 break;
785 case PORT_REMOVED:
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +0000786 port = p.number();
ke han81a38b92017-03-10 18:41:44 +0800787 processFilterObjective(devId, port, true);
788 break;
789 default:
790 log.info("Unknown device event {}", event.type());
791 break;
792 }
793 }
794
795 @Override
796 public boolean isRelevant(DeviceEvent event) {
797 return true;
798 }
799 }
800
801 private class InternalNetworkConfigListener implements NetworkConfigListener {
802
803 private void reconfigureNetwork(IgmpproxyConfig cfg) {
Esin Karamaneff10392019-06-27 18:09:13 +0000804 IgmpproxyConfig newCfg = cfg == null ? new IgmpproxyConfig() : cfg;
ke han81a38b92017-03-10 18:41:44 +0800805
806 unSolicitedTimeout = newCfg.unsolicitedTimeOut();
807 maxResp = newCfg.maxResp();
808 keepAliveInterval = newCfg.keepAliveInterval();
809 keepAliveCount = newCfg.keepAliveCount();
810 lastQueryInterval = newCfg.lastQueryInterval();
811 lastQueryCount = newCfg.lastQueryCount();
812 withRAUplink = newCfg.withRAUplink();
813 withRADownlink = newCfg.withRADownlink();
814 igmpCos = newCfg.igmpCos();
815 periodicQuery = newCfg.periodicQuery();
816 fastLeave = newCfg.fastLeave();
ke han29af27b2017-09-08 10:29:12 +0800817 pimSSmInterworking = newCfg.pimSsmInterworking();
Esin Karamaneff10392019-06-27 18:09:13 +0000818 enableIgmpProvisioning = newCfg.enableIgmpProvisioning();
Esin Karamanb38700c2019-09-17 13:01:25 +0000819 igmpOnPodBasis = newCfg.igmpOnPodBasis();
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +0000820
821 if (connectPointMode != newCfg.connectPointMode() ||
822 connectPoint != newCfg.connectPoint()) {
ke han81a38b92017-03-10 18:41:44 +0800823 connectPointMode = newCfg.connectPointMode();
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +0000824 connectPoint = newCfg.connectPoint();
ke han81a38b92017-03-10 18:41:44 +0800825 if (connectPointMode) {
826 unprovisionUplinkFlows();
827 provisionConnectPointFlows();
828 } else {
829 unprovisionConnectPointFlows();
830 provisionUplinkFlows();
831 }
832 }
833 if (connectPoint != null) {
Esin Karamaneff10392019-06-27 18:09:13 +0000834 log.info("connect point : {}", connectPoint);
ke han81a38b92017-03-10 18:41:44 +0800835 }
Esin Karamaneff10392019-06-27 18:09:13 +0000836 log.info("mode: {}", connectPointMode);
837
838 getSourceConnectPoint(newCfg);
ke han81a38b92017-03-10 18:41:44 +0800839
840 IgmpSender.getInstance().setIgmpCos(igmpCos);
841 IgmpSender.getInstance().setMaxResp(maxResp);
842 IgmpSender.getInstance().setMvlan(mvlan);
843 IgmpSender.getInstance().setWithRADownlink(withRADownlink);
844 IgmpSender.getInstance().setWithRAUplink(withRAUplink);
Esin Karamaneff10392019-06-27 18:09:13 +0000845 }
ke han81a38b92017-03-10 18:41:44 +0800846
Esin Karamaneff10392019-06-27 18:09:13 +0000847 void getSourceConnectPoint(IgmpproxyConfig cfg) {
848 sourceDeviceAndPort = cfg.getSourceDeviceAndPort();
849 if (sourceDeviceAndPort != null) {
850 log.debug("source parameter configured to {}", sourceDeviceAndPort);
851 }
ke han81a38b92017-03-10 18:41:44 +0800852 }
853
854 public void reconfigureSsmTable(IgmpproxySsmTranslateConfig cfg) {
855 if (cfg == null) {
856 return;
857 }
858 Collection<McastRoute> translations = cfg.getSsmTranslations();
859 for (McastRoute route : translations) {
Esin Karamaneff10392019-06-27 18:09:13 +0000860 ssmTranslateTable.put(route.group().getIp4Address(), route.source().get().getIp4Address());
ke han81a38b92017-03-10 18:41:44 +0800861 }
862 }
863
864 @Override
865 public void event(NetworkConfigEvent event) {
866 switch (event.type()) {
867 case CONFIG_ADDED:
868 case CONFIG_UPDATED:
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800869 // NOTE how to know if something has changed in sadis?
ke han81a38b92017-03-10 18:41:44 +0800870
871 if (event.configClass().equals(IGMPPROXY_CONFIG_CLASS)) {
872 IgmpproxyConfig config = networkConfig.getConfig(appId, IGMPPROXY_CONFIG_CLASS);
873 if (config != null) {
Esin Karamaneff10392019-06-27 18:09:13 +0000874 log.info("igmpproxy config received. {}", config);
ke han81a38b92017-03-10 18:41:44 +0800875 reconfigureNetwork(config);
876 }
877 }
878
879 if (event.configClass().equals(IGMPPROXY_SSM_CONFIG_CLASS)) {
880 IgmpproxySsmTranslateConfig config = networkConfig.getConfig(appId, IGMPPROXY_SSM_CONFIG_CLASS);
881 if (config != null) {
882 reconfigureSsmTable(config);
883 }
884 }
885
886 if (event.configClass().equals(MCAST_CONFIG_CLASS)) {
887 McastConfig config = networkConfig.getConfig(coreAppId, MCAST_CONFIG_CLASS);
888 if (config != null && mvlan != config.egressVlan().toShort()) {
889 mvlan = config.egressVlan().toShort();
Deepa Vaddireddy88134a42017-07-05 12:16:03 +0530890 IgmpSender.getInstance().setMvlan(mvlan);
ke han81a38b92017-03-10 18:41:44 +0800891 groupMemberMap.values().forEach(m -> leaveAction(m));
892 }
893 }
894
895 log.info("Reconfigured");
896 break;
897 case CONFIG_REGISTERED:
898 case CONFIG_UNREGISTERED:
899 break;
900 case CONFIG_REMOVED:
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800901 // NOTE how to know if something has changed in sadis?
ke han81a38b92017-03-10 18:41:44 +0800902 default:
903 break;
904 }
905 }
906 }
907
ke han81a38b92017-03-10 18:41:44 +0800908 private void provisionUplinkFlows(DeviceId deviceId) {
909 if (connectPointMode) {
910 return;
911 }
912
Esin Karaman00e16b72020-02-21 10:32:39 +0000913 Optional<PortNumber> upLink = getDeviceUplink(deviceId);
914 if (upLink.isPresent()) {
915 processFilterObjective(deviceId, upLink.get(), false);
916 }
ke han81a38b92017-03-10 18:41:44 +0800917 }
918
919 private void provisionUplinkFlows() {
920 if (connectPointMode) {
921 return;
922 }
Esin Karaman00e16b72020-02-21 10:32:39 +0000923 deviceService.getAvailableDevices().forEach(device -> {
924 Optional<SubscriberAndDeviceInformation> accessDevice = getSubscriberAndDeviceInformation(device.id());
925 if (accessDevice.isPresent()) {
926 provisionUplinkFlows(device.id());
927 }
928 });
ke han81a38b92017-03-10 18:41:44 +0800929 }
Esin Karaman00e16b72020-02-21 10:32:39 +0000930
ke han81a38b92017-03-10 18:41:44 +0800931 private void unprovisionUplinkFlows() {
Esin Karaman00e16b72020-02-21 10:32:39 +0000932 deviceService.getAvailableDevices().forEach(device -> {
933 Optional<SubscriberAndDeviceInformation> accessDevices = getSubscriberAndDeviceInformation(device.id());
934 if (accessDevices.isPresent()) {
935 Optional<PortNumber> upLink = getDeviceUplink(device.id());
936 if (upLink.isPresent()) {
937 processFilterObjective(device.id(), upLink.get(), true);
938 }
939 }
940 });
ke han81a38b92017-03-10 18:41:44 +0800941 }
942
943 private void provisionConnectPointFlows() {
944 if ((!connectPointMode) || connectPoint == null) {
945 return;
946 }
947
948 processFilterObjective(connectPoint.deviceId(), connectPoint.port(), false);
949 }
950 private void unprovisionConnectPointFlows() {
951 if (connectPoint == null) {
952 return;
953 }
954 processFilterObjective(connectPoint.deviceId(), connectPoint.port(), true);
955 }
956}