blob: b985b1491e3d3d6f184f615ccb7f97e11920d96d [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
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;
developere400c582020-03-24 19:42:08 +010021import org.opencord.igmpproxy.IgmpStatisticsService;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -080022import org.opencord.sadis.BaseInformationService;
23import org.opencord.sadis.SadisService;
24import org.opencord.sadis.SubscriberAndDeviceInformation;
Carmelo Casconebef302e2019-11-14 19:58:20 -080025import org.osgi.service.component.annotations.Activate;
26import org.osgi.service.component.annotations.Component;
27import org.osgi.service.component.annotations.Deactivate;
28import org.osgi.service.component.annotations.Reference;
29import org.osgi.service.component.annotations.ReferenceCardinality;
ke han81a38b92017-03-10 18:41:44 +080030import org.onlab.packet.EthType;
31import org.onlab.packet.Ethernet;
32import org.onlab.packet.IGMP;
33import org.onlab.packet.IGMPGroup;
34import org.onlab.packet.IGMPMembership;
35import org.onlab.packet.IGMPQuery;
36import org.onlab.packet.IPv4;
37import org.onlab.packet.Ip4Address;
38import org.onlab.packet.IpAddress;
39import org.onlab.packet.VlanId;
40import org.onosproject.core.ApplicationId;
41import org.onosproject.core.CoreService;
ke han81a38b92017-03-10 18:41:44 +080042import org.onosproject.mastership.MastershipService;
43import org.onosproject.net.AnnotationKeys;
44import org.onosproject.net.ConnectPoint;
45import org.onosproject.net.DeviceId;
46import org.onosproject.net.Port;
47import org.onosproject.net.PortNumber;
48import org.onosproject.net.config.ConfigFactory;
49import org.onosproject.net.config.NetworkConfigEvent;
50import org.onosproject.net.config.NetworkConfigListener;
51import org.onosproject.net.config.NetworkConfigRegistry;
Jonathan Hart488e1142018-05-02 17:30:05 -070052import org.onosproject.net.config.basics.McastConfig;
ke han81a38b92017-03-10 18:41:44 +080053import org.onosproject.net.config.basics.SubjectFactories;
54import org.onosproject.net.device.DeviceEvent;
55import org.onosproject.net.device.DeviceListener;
56import org.onosproject.net.device.DeviceService;
57import org.onosproject.net.flow.DefaultTrafficTreatment;
58import org.onosproject.net.flow.FlowRuleService;
59import org.onosproject.net.flow.criteria.Criteria;
60import org.onosproject.net.flowobjective.DefaultFilteringObjective;
61import org.onosproject.net.flowobjective.FilteringObjective;
62import org.onosproject.net.flowobjective.FlowObjectiveService;
63import org.onosproject.net.flowobjective.Objective;
64import org.onosproject.net.flowobjective.ObjectiveContext;
65import org.onosproject.net.flowobjective.ObjectiveError;
Esin Karamaneff10392019-06-27 18:09:13 +000066import org.onosproject.mcast.api.McastRoute;
67import org.onosproject.mcast.api.MulticastRouteService;
ke han81a38b92017-03-10 18:41:44 +080068import org.onosproject.net.packet.InboundPacket;
69import org.onosproject.net.packet.PacketContext;
70import org.onosproject.net.packet.PacketProcessor;
71import org.onosproject.net.packet.PacketService;
ke han81a38b92017-03-10 18:41:44 +080072import org.slf4j.Logger;
73import org.slf4j.LoggerFactory;
74
75import java.util.ArrayList;
Sonal Kasliwalf11c0672020-03-18 11:11:50 +000076import java.util.Arrays;
77import java.util.List;
ke han81a38b92017-03-10 18:41:44 +080078import java.util.Collection;
Esin Karamaneff10392019-06-27 18:09:13 +000079import java.util.HashSet;
ke han81a38b92017-03-10 18:41:44 +080080import java.util.Iterator;
ke han81a38b92017-03-10 18:41:44 +080081import java.util.Map;
Esin Karamaneff10392019-06-27 18:09:13 +000082import java.util.Optional;
ke han81a38b92017-03-10 18:41:44 +080083import java.util.Set;
84import java.util.TimerTask;
85import java.util.concurrent.ConcurrentHashMap;
Esin Karamanb38700c2019-09-17 13:01:25 +000086import java.util.concurrent.ExecutorService;
ke han81a38b92017-03-10 18:41:44 +080087import java.util.concurrent.Executors;
88import java.util.concurrent.ScheduledExecutorService;
89import java.util.concurrent.TimeUnit;
90
Sonal Kasliwalf11c0672020-03-18 11:11:50 +000091import static org.onlab.packet.IGMPMembership.MODE_IS_INCLUDE;
92import static org.onlab.packet.IGMPMembership.MODE_IS_EXCLUDE;
93import static org.onlab.packet.IGMPMembership.CHANGE_TO_INCLUDE_MODE;
94import static org.onlab.packet.IGMPMembership.CHANGE_TO_EXCLUDE_MODE;
95import static org.onlab.packet.IGMPMembership.ALLOW_NEW_SOURCES;
96import static org.onlab.packet.IGMPMembership.BLOCK_OLD_SOURCES;
97
Esin Karamanb38700c2019-09-17 13:01:25 +000098import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
99import static org.onlab.util.Tools.groupedThreads;
100
ke han81a38b92017-03-10 18:41:44 +0800101/**
102 * Igmp process application, use proxy mode, support first join/ last leave , fast leave
103 * period query and keep alive, packet out igmp message to uplink port features.
104 */
105@Component(immediate = true)
106public class IgmpManager {
107
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800108 private static final String APP_NAME = "org.opencord.igmpproxy";
109
ke han81a38b92017-03-10 18:41:44 +0800110 private static final Class<IgmpproxyConfig> IGMPPROXY_CONFIG_CLASS =
111 IgmpproxyConfig.class;
112 private static final Class<IgmpproxySsmTranslateConfig> IGMPPROXY_SSM_CONFIG_CLASS =
113 IgmpproxySsmTranslateConfig.class;
114 private static final Class<McastConfig> MCAST_CONFIG_CLASS =
115 McastConfig.class;
Esin Karamaneff10392019-06-27 18:09:13 +0000116
ke han81a38b92017-03-10 18:41:44 +0800117 public static Map<String, GroupMember> groupMemberMap = Maps.newConcurrentMap();
118 private static ApplicationId appId;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800119
ke han81a38b92017-03-10 18:41:44 +0800120 private static int unSolicitedTimeout = 3; // unit is 1 sec
121 private static int keepAliveCount = 3;
122 private static int lastQueryInterval = 2; //unit is 1 sec
123 private static int lastQueryCount = 2;
124 private static boolean fastLeave = true;
125 private static boolean withRAUplink = true;
126 private static boolean withRADownlink = false;
127 private static boolean periodicQuery = true;
128 private static short mvlan = 4000;
129 private static byte igmpCos = 7;
130 public static boolean connectPointMode = true;
131 public static ConnectPoint connectPoint = null;
Esin Karamaneff10392019-06-27 18:09:13 +0000132 private static ConnectPoint sourceDeviceAndPort = null;
133 private static boolean enableIgmpProvisioning = false;
Esin Karamanb38700c2019-09-17 13:01:25 +0000134 private static boolean igmpOnPodBasis = false;
Arjun E Kb0018fd2020-04-07 13:26:40 +0000135 private static boolean outgoingIgmpWithV3 = true;
Esin Karamaneff10392019-06-27 18:09:13 +0000136
137 private static final Integer MAX_PRIORITY = 10000;
138 private static final String INSTALLED = "installed";
139 private static final String REMOVED = "removed";
140 private static final String INSTALLATION = "installation";
141 private static final String REMOVAL = "removal";
Esin Karaman00e16b72020-02-21 10:32:39 +0000142 private static final String NNI_PREFIX = "nni";
ke han81a38b92017-03-10 18:41:44 +0800143
ke han29af27b2017-09-08 10:29:12 +0800144 private static boolean pimSSmInterworking = false;
145 private static final String DEFAULT_PIMSSM_HOST = "127.0.0.1";
ke han81a38b92017-03-10 18:41:44 +0800146 private final ScheduledExecutorService scheduledExecutorService =
147 Executors.newScheduledThreadPool(1);
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800148
Carmelo Casconebef302e2019-11-14 19:58:20 -0800149 @Reference(cardinality = ReferenceCardinality.MANDATORY)
ke han81a38b92017-03-10 18:41:44 +0800150 protected CoreService coreService;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800151
Carmelo Casconebef302e2019-11-14 19:58:20 -0800152 @Reference(cardinality = ReferenceCardinality.MANDATORY)
ke han81a38b92017-03-10 18:41:44 +0800153 protected PacketService packetService;
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 MastershipService mastershipService;
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 FlowRuleService flowRuleService;
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 DeviceService deviceService;
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 FlowObjectiveService flowObjectiveService;
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 NetworkConfigRegistry networkConfig;
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 MulticastRouteService multicastService;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800172
173 @Reference(cardinality = ReferenceCardinality.MANDATORY)
174 protected SadisService sadisService;
175
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000176 @Reference(cardinality = ReferenceCardinality.MANDATORY)
177 protected IgmpStatisticsService igmpStatisticsManager;
178
ke han81a38b92017-03-10 18:41:44 +0800179 private IgmpPacketProcessor processor = new IgmpPacketProcessor();
180 private Logger log = LoggerFactory.getLogger(getClass());
181 private ApplicationId coreAppId;
182 private Map<Ip4Address, Ip4Address> ssmTranslateTable = new ConcurrentHashMap<>();
Esin Karamaneff10392019-06-27 18:09:13 +0000183
ke han81a38b92017-03-10 18:41:44 +0800184 private InternalNetworkConfigListener configListener =
185 new InternalNetworkConfigListener();
186 private DeviceListener deviceListener = new InternalDeviceListener();
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800187
ke han81a38b92017-03-10 18:41:44 +0800188 private ConfigFactory<ApplicationId, IgmpproxyConfig> igmpproxyConfigFactory =
189 new ConfigFactory<ApplicationId, IgmpproxyConfig>(
190 SubjectFactories.APP_SUBJECT_FACTORY, IGMPPROXY_CONFIG_CLASS, "igmpproxy") {
191 @Override
192 public IgmpproxyConfig createConfig() {
193 return new IgmpproxyConfig();
194 }
195 };
196 private ConfigFactory<ApplicationId, IgmpproxySsmTranslateConfig> igmpproxySsmConfigFactory =
197 new ConfigFactory<ApplicationId, IgmpproxySsmTranslateConfig>(
198 SubjectFactories.APP_SUBJECT_FACTORY, IGMPPROXY_SSM_CONFIG_CLASS, "ssmTranslate", true) {
199 @Override
200 public IgmpproxySsmTranslateConfig createConfig() {
201 return new IgmpproxySsmTranslateConfig();
202 }
203 };
Esin Karamaneff10392019-06-27 18:09:13 +0000204
ke han81a38b92017-03-10 18:41:44 +0800205 private int maxResp = 10; //unit is 1 sec
206 private int keepAliveInterval = 120; //unit is 1 sec
207
Esin Karamanb38700c2019-09-17 13:01:25 +0000208 private ExecutorService eventExecutor;
209
ke han81a38b92017-03-10 18:41:44 +0800210 public static int getUnsolicitedTimeout() {
211 return unSolicitedTimeout;
212 }
213
Arjun E Kb0018fd2020-04-07 13:26:40 +0000214 public static boolean outgoingIgmpWithV3() {
215 return outgoingIgmpWithV3;
216 }
217
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800218 protected BaseInformationService<SubscriberAndDeviceInformation> subsService;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800219
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000220 private List<Byte> validMembershipModes = Arrays.asList(MODE_IS_INCLUDE, MODE_IS_EXCLUDE, CHANGE_TO_INCLUDE_MODE,
221 CHANGE_TO_EXCLUDE_MODE, ALLOW_NEW_SOURCES, BLOCK_OLD_SOURCES);
222
ke han81a38b92017-03-10 18:41:44 +0800223 @Activate
224 protected void activate() {
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800225 appId = coreService.registerApplication(APP_NAME);
ke han81a38b92017-03-10 18:41:44 +0800226 coreAppId = coreService.registerApplication(CoreService.CORE_APP_NAME);
227 packetService.addProcessor(processor, PacketProcessor.director(4));
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000228 IgmpSender.init(packetService, mastershipService, igmpStatisticsManager);
ke han81a38b92017-03-10 18:41:44 +0800229
ke han81a38b92017-03-10 18:41:44 +0800230 networkConfig.registerConfigFactory(igmpproxySsmConfigFactory);
231 networkConfig.registerConfigFactory(igmpproxyConfigFactory);
232 networkConfig.addListener(configListener);
233
234 configListener.reconfigureNetwork(networkConfig.getConfig(appId, IGMPPROXY_CONFIG_CLASS));
235 configListener.reconfigureSsmTable(networkConfig.getConfig(appId, IGMPPROXY_SSM_CONFIG_CLASS));
236
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800237 subsService = sadisService.getSubscriberInfoService();
ke han81a38b92017-03-10 18:41:44 +0800238 if (connectPointMode) {
239 provisionConnectPointFlows();
240 } else {
241 provisionUplinkFlows();
242 }
243
244 McastConfig config = networkConfig.getConfig(coreAppId, MCAST_CONFIG_CLASS);
245 if (config != null) {
246 mvlan = config.egressVlan().toShort();
Deepa Vaddireddy88134a42017-07-05 12:16:03 +0530247 IgmpSender.getInstance().setMvlan(mvlan);
ke han81a38b92017-03-10 18:41:44 +0800248 }
249 deviceService.addListener(deviceListener);
Sonal Kasliwalddc3ff22019-11-18 11:52:49 +0000250 scheduledExecutorService.scheduleAtFixedRate(new IgmpProxyTimerTask(), 1000, 1000, TimeUnit.MILLISECONDS);
Esin Karamanb38700c2019-09-17 13:01:25 +0000251 eventExecutor = newSingleThreadScheduledExecutor(groupedThreads("cord/igmpproxy",
252 "events-igmp-%d", log));
ke han81a38b92017-03-10 18:41:44 +0800253 log.info("Started");
254 }
255
256 @Deactivate
257 protected void deactivate() {
258 scheduledExecutorService.shutdown();
Esin Karamanb38700c2019-09-17 13:01:25 +0000259 eventExecutor.shutdown();
ke han81a38b92017-03-10 18:41:44 +0800260
261 // de-register and null our handler
262 networkConfig.removeListener(configListener);
ke han81a38b92017-03-10 18:41:44 +0800263 networkConfig.unregisterConfigFactory(igmpproxyConfigFactory);
264 networkConfig.unregisterConfigFactory(igmpproxySsmConfigFactory);
265 deviceService.removeListener(deviceListener);
266 packetService.removeProcessor(processor);
267 flowRuleService.removeFlowRulesById(appId);
ke han81a38b92017-03-10 18:41:44 +0800268 log.info("Stopped");
269 }
270
271 protected Ip4Address getDeviceIp(DeviceId ofDeviceId) {
272 try {
273 String[] mgmtAddress = deviceService.getDevice(ofDeviceId)
274 .annotations().value(AnnotationKeys.MANAGEMENT_ADDRESS).split(":");
275 return Ip4Address.valueOf(mgmtAddress[0]);
276 } catch (Exception ex) {
277 log.info("No valid Ipaddress for " + ofDeviceId.toString());
278 return null;
279 }
280 }
281
282 private void processIgmpQuery(IGMPQuery igmpQuery, ConnectPoint cp, int maxResp) {
283
284 DeviceId deviceId = cp.deviceId();
285 Ip4Address gAddr = igmpQuery.getGaddr().getIp4Address();
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000286 maxResp = calculateMaxResp(maxResp);
287 if (gAddr != null && !gAddr.isZero()) {
288 StateMachine.specialQuery(deviceId, gAddr, maxResp);
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000289 igmpStatisticsManager.getIgmpStats().increaseIgmpGrpSpecificMembershipQuery();
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000290 } else {
291 StateMachine.generalQuery(deviceId, maxResp);
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000292 igmpStatisticsManager.getIgmpStats().increaseIgmpGeneralMembershipQuery();
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000293 }
294 }
ke han81a38b92017-03-10 18:41:44 +0800295
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000296 private void processIgmpConnectPointQuery(IGMPQuery igmpQuery, ConnectPoint cp, int maxResp) {
297
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000298 Ip4Address gAddr = igmpQuery.getGaddr().getIp4Address();
Esin Karaman00e16b72020-02-21 10:32:39 +0000299 final int maxResponseTime = calculateMaxResp(maxResp);
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000300 //The query is received on the ConnectPoint
301 // send query accordingly to the registered OLT devices.
302 if (gAddr != null && !gAddr.isZero()) {
Esin Karaman00e16b72020-02-21 10:32:39 +0000303 deviceService.getAvailableDevices().forEach(device -> {
304 Optional<SubscriberAndDeviceInformation> accessDevice = getSubscriberAndDeviceInformation(device.id());
305 if (accessDevice.isPresent()) {
306 StateMachine.specialQuery(device.id(), gAddr, maxResponseTime);
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000307 igmpStatisticsManager.getIgmpStats().increaseIgmpGrpAndSrcSpecificMembershipQuery();
Esin Karaman00e16b72020-02-21 10:32:39 +0000308 }
309 });
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000310 igmpStatisticsManager.getIgmpStats().increaseCurrentGrpNumCounter();
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000311 } else {
312 //Don't know which group is targeted by the query
313 //So query all the members(in all the OLTs) and proxy their reports
Esin Karaman00e16b72020-02-21 10:32:39 +0000314 StateMachine.generalQuery(maxResponseTime);
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000315 igmpStatisticsManager.getIgmpStats().increaseIgmpGeneralMembershipQuery();
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000316 }
317 }
318
319
320 private int calculateMaxResp(int maxResp) {
ke han81a38b92017-03-10 18:41:44 +0800321 if (maxResp >= 128) {
322 int mant = maxResp & 0xf;
323 int exp = (maxResp >> 4) & 0x7;
324 maxResp = (mant | 0x10) << (exp + 3);
325 }
326
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000327 return (maxResp + 5) / 10;
ke han81a38b92017-03-10 18:41:44 +0800328 }
329
330 private Ip4Address ssmTranslateRoute(IpAddress group) {
331 return ssmTranslateTable.get(group);
332 }
333
334 private void processIgmpReport(IGMPMembership igmpGroup, VlanId vlan, ConnectPoint cp, byte igmpType) {
335 DeviceId deviceId = cp.deviceId();
336 PortNumber portNumber = cp.port();
337
338 Ip4Address groupIp = igmpGroup.getGaddr().getIp4Address();
339 if (!groupIp.isMulticast()) {
340 log.info(groupIp.toString() + " is not a valid group address");
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000341 igmpStatisticsManager.getIgmpStats().increaseFailJoinReqUnknownMulticastIpCounter();
ke han81a38b92017-03-10 18:41:44 +0800342 return;
343 }
344 Ip4Address srcIp = getDeviceIp(deviceId);
345
346 byte recordType = igmpGroup.getRecordType();
347 boolean join = false;
348
349 ArrayList<Ip4Address> sourceList = new ArrayList<>();
350
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000351 if (!validMembershipModes.contains(recordType)) {
352 igmpStatisticsManager.getIgmpStats().increaseReportsRxWithWrongModeCounter();
353 }
ke han81a38b92017-03-10 18:41:44 +0800354 if (igmpGroup.getSources().size() > 0) {
355 igmpGroup.getSources().forEach(source -> sourceList.add(source.getIp4Address()));
356 if (recordType == IGMPMembership.CHANGE_TO_EXCLUDE_MODE ||
357 recordType == IGMPMembership.MODE_IS_EXCLUDE ||
358 recordType == IGMPMembership.BLOCK_OLD_SOURCES) {
359 join = false;
360 } else if (recordType == IGMPMembership.CHANGE_TO_INCLUDE_MODE ||
361 recordType == IGMPMembership.MODE_IS_INCLUDE ||
362 recordType == IGMPMembership.ALLOW_NEW_SOURCES) {
363 join = true;
364 }
365 } else {
ke han29af27b2017-09-08 10:29:12 +0800366 IpAddress src = null;
367 if (pimSSmInterworking) {
368 src = ssmTranslateRoute(groupIp);
369 if (src == null) {
370 log.info("no ssm translate for group " + groupIp.toString());
371 return;
372 }
373 } else {
374 src = IpAddress.valueOf(DEFAULT_PIMSSM_HOST);
ke han81a38b92017-03-10 18:41:44 +0800375 }
376 sourceList.add(src.getIp4Address());
377 if (recordType == IGMPMembership.CHANGE_TO_EXCLUDE_MODE ||
378 recordType == IGMPMembership.MODE_IS_EXCLUDE ||
379 recordType == IGMPMembership.BLOCK_OLD_SOURCES) {
380 join = true;
381 } else if (recordType == IGMPMembership.CHANGE_TO_INCLUDE_MODE ||
382 recordType == IGMPMembership.MODE_IS_INCLUDE ||
383 recordType == IGMPMembership.ALLOW_NEW_SOURCES) {
384 join = false;
385 }
386 }
387 String groupMemberKey = GroupMember.getkey(groupIp, deviceId, portNumber);
388 GroupMember groupMember = groupMemberMap.get(groupMemberKey);
389
390 if (join) {
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000391 igmpStatisticsManager.getIgmpStats().increaseIgmpJoinReq();
ke han81a38b92017-03-10 18:41:44 +0800392 if (groupMember == null) {
Esin Karamaneff10392019-06-27 18:09:13 +0000393 Optional<ConnectPoint> sourceConfigured = getSource();
394 if (!sourceConfigured.isPresent()) {
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000395 igmpStatisticsManager.getIgmpStats().increaseIgmpFailJoinReq();
Esin Karamaneff10392019-06-27 18:09:13 +0000396 log.warn("Unable to process IGMP Join from {} since no source " +
397 "configuration is found.", deviceId);
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000398 igmpStatisticsManager.getIgmpStats().increaseFailJoinReqInsuffPermissionAccessCounter();
Esin Karamaneff10392019-06-27 18:09:13 +0000399 return;
400 }
Esin Karaman00e16b72020-02-21 10:32:39 +0000401
402 Optional<PortNumber> deviceUplink = getDeviceUplink(deviceId);
403 if (deviceUplink.isEmpty()) {
404 log.warn("Unable to process IGMP Join since uplink port " +
405 "of the device {} is not found.", deviceId);
406 return;
407 }
408
409 if (igmpType == IGMP.TYPE_IGMPV2_MEMBERSHIP_REPORT) {
410 groupMember = new GroupMember(groupIp, vlan, deviceId, portNumber, true);
411 } else {
412 groupMember = new GroupMember(groupIp, vlan, deviceId, portNumber, false);
413 }
414
Esin Karamaneff10392019-06-27 18:09:13 +0000415 HashSet<ConnectPoint> sourceConnectPoints = Sets.newHashSet(sourceConfigured.get());
416
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000417 boolean isJoined = StateMachine.join(deviceId, groupIp, srcIp, deviceUplink.get());
418 if (isJoined) {
419 igmpStatisticsManager.getIgmpStats().increaseIgmpSuccessJoinRejoinReq();
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000420 igmpStatisticsManager.getIgmpStats().increaseIgmpChannelJoinCounter();
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000421 } else {
422 igmpStatisticsManager.getIgmpStats().increaseIgmpFailJoinReq();
423 }
ke han81a38b92017-03-10 18:41:44 +0800424 groupMemberMap.put(groupMemberKey, groupMember);
425 groupMember.updateList(recordType, sourceList);
Esin Karamaneff10392019-06-27 18:09:13 +0000426 groupMember.getSourceList().forEach(source -> {
427 McastRoute route = new McastRoute(source, groupIp, McastRoute.Type.IGMP);
428 //add route
429 multicastService.add(route);
430 //add source to the route
431 multicastService.addSources(route, Sets.newHashSet(sourceConnectPoints));
432 //add sink to the route
433 multicastService.addSinks(route, Sets.newHashSet(cp));
434 });
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000435 igmpStatisticsManager.getIgmpStats().increaseUnconfiguredGroupCounter();
Esin Karamaneff10392019-06-27 18:09:13 +0000436
ke han81a38b92017-03-10 18:41:44 +0800437 }
438 groupMember.resetAllTimers();
439 groupMember.updateList(recordType, sourceList);
440 groupMember.setLeave(false);
441 } else {
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000442 igmpStatisticsManager.getIgmpStats().increaseIgmpLeaveReq();
ke han81a38b92017-03-10 18:41:44 +0800443 if (groupMember == null) {
444 log.info("receive leave but no instance, group " + groupIp.toString() +
445 " device:" + deviceId.toString() + " port:" + portNumber.toString());
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000446 igmpStatisticsManager.getIgmpStats().increaseUnconfiguredGroupCounter();
ke han81a38b92017-03-10 18:41:44 +0800447 return;
448 } else {
449 groupMember.setLeave(true);
450 if (fastLeave) {
451 leaveAction(groupMember);
452 } else {
453 sendQuery(groupMember);
454 }
455 }
456 }
457 }
458
459 private void leaveAction(GroupMember groupMember) {
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000460 igmpStatisticsManager.getIgmpStats().increaseIgmpDisconnect();
ke han81a38b92017-03-10 18:41:44 +0800461 ConnectPoint cp = new ConnectPoint(groupMember.getDeviceId(), groupMember.getPortNumber());
462 StateMachine.leave(groupMember.getDeviceId(), groupMember.getGroupIp());
Esin Karamaneff10392019-06-27 18:09:13 +0000463 groupMember.getSourceList().forEach(source -> multicastService.removeSinks(
ke han81a38b92017-03-10 18:41:44 +0800464 new McastRoute(source, groupMember.getGroupIp(),
Esin Karamaneff10392019-06-27 18:09:13 +0000465 McastRoute.Type.IGMP), Sets.newHashSet(cp)));
ke han81a38b92017-03-10 18:41:44 +0800466 groupMemberMap.remove(groupMember.getId());
467 }
468
469 private void sendQuery(GroupMember groupMember) {
470 Ethernet ethpkt;
471 Ip4Address srcIp = getDeviceIp(groupMember.getDeviceId());
472 if (groupMember.getv2()) {
473 ethpkt = IgmpSender.getInstance().buildIgmpV2Query(groupMember.getGroupIp(), srcIp);
474 } else {
475 ethpkt = IgmpSender.getInstance().buildIgmpV3Query(groupMember.getGroupIp(), srcIp);
476 }
477 IgmpSender.getInstance().sendIgmpPacket(ethpkt, groupMember.getDeviceId(), groupMember.getPortNumber());
478 }
479
Esin Karamaneff10392019-06-27 18:09:13 +0000480 /**
481 * @return connect point of the source if configured; and empty Optional otherwise.
482 */
483 public static Optional<ConnectPoint> getSource() {
484 return sourceDeviceAndPort == null ? Optional.empty() :
485 Optional.of(sourceDeviceAndPort);
ke han81a38b92017-03-10 18:41:44 +0800486 }
487
488 /**
489 * Packet processor responsible for forwarding packets along their paths.
490 */
491 private class IgmpPacketProcessor implements PacketProcessor {
492 @Override
493 public void process(PacketContext context) {
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000494
Esin Karamanb38700c2019-09-17 13:01:25 +0000495 eventExecutor.execute(() -> {
496 try {
497 InboundPacket pkt = context.inPacket();
498 Ethernet ethPkt = pkt.parsed();
499 if (ethPkt == null) {
500 return;
501 }
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000502 igmpStatisticsManager.getIgmpStats().increaseTotalMsgReceived();
ke han81a38b92017-03-10 18:41:44 +0800503
Esin Karamanb38700c2019-09-17 13:01:25 +0000504 if (ethPkt.getEtherType() != Ethernet.TYPE_IPV4) {
505 return;
506 }
ke han81a38b92017-03-10 18:41:44 +0800507
Esin Karamanb38700c2019-09-17 13:01:25 +0000508 IPv4 ipv4Pkt = (IPv4) ethPkt.getPayload();
ke han81a38b92017-03-10 18:41:44 +0800509
Esin Karamanb38700c2019-09-17 13:01:25 +0000510 if (ipv4Pkt.getProtocol() != IPv4.PROTOCOL_IGMP) {
511 return;
512 }
ke han81a38b92017-03-10 18:41:44 +0800513
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000514 igmpStatisticsManager.getIgmpStats().increaseIgmpValidChecksumCounter();
Esin Karamanb38700c2019-09-17 13:01:25 +0000515 short vlan = ethPkt.getVlanID();
516 DeviceId deviceId = pkt.receivedFrom().deviceId();
ke han81a38b92017-03-10 18:41:44 +0800517
Esin Karaman00e16b72020-02-21 10:32:39 +0000518 if (!isConnectPoint(deviceId, pkt.receivedFrom().port()) &&
519 !getSubscriberAndDeviceInformation(deviceId).isPresent()) {
Esin Karamanb38700c2019-09-17 13:01:25 +0000520 log.error("Device not registered in netcfg :" + deviceId.toString());
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000521 igmpStatisticsManager.getIgmpStats().increaseFailJoinReqInsuffPermissionAccessCounter();
Esin Karamanb38700c2019-09-17 13:01:25 +0000522 return;
523 }
ke han81a38b92017-03-10 18:41:44 +0800524
Esin Karamanb38700c2019-09-17 13:01:25 +0000525 IGMP igmp = (IGMP) ipv4Pkt.getPayload();
Esin Karamance5ce512020-02-25 15:58:14 +0000526
527 Optional<PortNumber> deviceUpLinkOpt = getDeviceUplink(deviceId);
528 PortNumber upLinkPort = deviceUpLinkOpt.isPresent() ? deviceUpLinkOpt.get() : null;
Esin Karamanb38700c2019-09-17 13:01:25 +0000529 switch (igmp.getIgmpType()) {
530 case IGMP.TYPE_IGMPV3_MEMBERSHIP_QUERY:
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000531 igmpStatisticsManager.getIgmpStats().increaseIgmpv3MembershipQuery();
Esin Karamanb38700c2019-09-17 13:01:25 +0000532 //Discard Query from OLT’s non-uplink port’s
Esin Karamance5ce512020-02-25 15:58:14 +0000533 if (!pkt.receivedFrom().port().equals(upLinkPort)) {
Esin Karamanb38700c2019-09-17 13:01:25 +0000534 if (isConnectPoint(deviceId, pkt.receivedFrom().port())) {
535 log.info("IGMP Picked up query from connectPoint");
536 //OK to process packet
537 processIgmpConnectPointQuery((IGMPQuery) igmp.getGroups().get(0),
538 pkt.receivedFrom(),
539 0xff & igmp.getMaxRespField());
540 break;
541 } else {
542 //Not OK to process packet
Esin Karamance5ce512020-02-25 15:58:14 +0000543 log.warn("IGMP Picked up query from non-uplink port {}", upLinkPort);
Esin Karamanb38700c2019-09-17 13:01:25 +0000544 return;
545 }
546 }
ke han81a38b92017-03-10 18:41:44 +0800547
Esin Karamanb38700c2019-09-17 13:01:25 +0000548 processIgmpQuery((IGMPQuery) igmp.getGroups().get(0), pkt.receivedFrom(),
549 0xff & igmp.getMaxRespField());
550 break;
551 case IGMP.TYPE_IGMPV1_MEMBERSHIP_REPORT:
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000552 igmpStatisticsManager.getIgmpStats().increaseIgmpv1MembershipReport();
Esin Karamanb38700c2019-09-17 13:01:25 +0000553 log.debug("IGMP version 1 message types are not currently supported.");
554 break;
555 case IGMP.TYPE_IGMPV3_MEMBERSHIP_REPORT:
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000556 igmpStatisticsManager.getIgmpStats().increaseIgmpv3MembershipReport();
557 processIgmpMessage(pkt, igmp, upLinkPort, vlan);
558 break;
Esin Karamanb38700c2019-09-17 13:01:25 +0000559 case IGMP.TYPE_IGMPV2_MEMBERSHIP_REPORT:
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000560 igmpStatisticsManager.getIgmpStats().increaseIgmpv2MembershipReport();
561 processIgmpMessage(pkt, igmp, upLinkPort, vlan);
562 break;
Esin Karamanb38700c2019-09-17 13:01:25 +0000563 case IGMP.TYPE_IGMPV2_LEAVE_GROUP:
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000564 igmpStatisticsManager.getIgmpStats().increaseIgmpv2LeaveGroup();
565 processIgmpMessage(pkt, igmp, upLinkPort, vlan);
Esin Karamanb38700c2019-09-17 13:01:25 +0000566 break;
ke han81a38b92017-03-10 18:41:44 +0800567
Esin Karamanb38700c2019-09-17 13:01:25 +0000568 default:
Arjun E Kb0018fd2020-04-07 13:26:40 +0000569 log.warn("Unknown IGMP message type:" + igmp.getIgmpType());
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000570 igmpStatisticsManager.getIgmpStats().increaseInvalidIgmpMsgReceived();
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000571 igmpStatisticsManager.getIgmpStats().increaseUnknownIgmpTypePacketsRxCounter();
Esin Karamanb38700c2019-09-17 13:01:25 +0000572 break;
573 }
574
575 } catch (Exception ex) {
576 log.error("igmp process error : {} ", ex);
ke han81a38b92017-03-10 18:41:44 +0800577 }
Esin Karamanb38700c2019-09-17 13:01:25 +0000578 });
ke han81a38b92017-03-10 18:41:44 +0800579 }
580 }
581
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000582 private void processIgmpMessage(InboundPacket pkt, IGMP igmp, PortNumber upLinkPort, short vlan) {
583 //Discard join/leave from OLT’s uplink port’s
584 if (pkt.receivedFrom().port().equals(upLinkPort) ||
585 isConnectPoint(pkt.receivedFrom().deviceId(), pkt.receivedFrom().port())) {
586 log.info("IGMP Picked up join/leave from uplink/connectPoint port");
587 return;
588 }
589
590 Iterator<IGMPGroup> itr = igmp.getGroups().iterator();
591 while (itr.hasNext()) {
592 IGMPGroup group = itr.next();
593 if (group instanceof IGMPMembership) {
594 processIgmpReport((IGMPMembership) group, VlanId.vlanId(vlan),
595 pkt.receivedFrom(), igmp.getIgmpType());
596 } else {
597 IGMPMembership mgroup = new IGMPMembership(group.getGaddr().getIp4Address());
598 mgroup.setRecordType(igmp.getIgmpType() == IGMP.TYPE_IGMPV2_MEMBERSHIP_REPORT ?
599 IGMPMembership.MODE_IS_EXCLUDE :
600 IGMPMembership.MODE_IS_INCLUDE);
601 processIgmpReport(mgroup, VlanId.vlanId(vlan),
602 pkt.receivedFrom(), igmp.getIgmpType());
603 }
604 }
605
606 }
607
ke han81a38b92017-03-10 18:41:44 +0800608 private class IgmpProxyTimerTask extends TimerTask {
609 public void run() {
610 try {
611 IgmpTimer.timeOut1s();
612 queryMembers();
613 } catch (Exception ex) {
614 log.warn("Igmp timer task error : {}", ex.getMessage());
615 }
616 }
617
618 private void queryMembers() {
619 GroupMember groupMember;
620 Set groupMemberSet = groupMemberMap.entrySet();
621 Iterator itr = groupMemberSet.iterator();
622 while (itr.hasNext()) {
623 Map.Entry entry = (Map.Entry) itr.next();
624 groupMember = (GroupMember) entry.getValue();
625 DeviceId did = groupMember.getDeviceId();
626 if (mastershipService.isLocalMaster(did)) {
627 if (groupMember.isLeave()) {
628 lastQuery(groupMember);
629 } else if (periodicQuery) {
630 periodicQuery(groupMember);
631 }
632 }
633 }
634 }
635
636 private void lastQuery(GroupMember groupMember) {
637 if (groupMember.getLastQueryInterval() < lastQueryInterval) {
638 groupMember.lastQueryInterval(true); // count times
639 } else if (groupMember.getLastQueryCount() < lastQueryCount - 1) {
640 sendQuery(groupMember);
641 groupMember.lastQueryInterval(false); // reset count number
642 groupMember.lastQueryCount(true); //count times
643 } else if (groupMember.getLastQueryCount() == lastQueryCount - 1) {
644 leaveAction(groupMember);
645 }
646 }
647
648 private void periodicQuery(GroupMember groupMember) {
649 if (groupMember.getKeepAliveQueryInterval() < keepAliveInterval) {
650 groupMember.keepAliveInterval(true);
651 } else if (groupMember.getKeepAliveQueryCount() < keepAliveCount) {
652 sendQuery(groupMember);
653 groupMember.keepAliveInterval(false);
654 groupMember.keepAliveQueryCount(true);
655 } else if (groupMember.getKeepAliveQueryCount() == keepAliveCount) {
656 leaveAction(groupMember);
657 }
658 }
659
660 }
661
Esin Karaman00e16b72020-02-21 10:32:39 +0000662 public Optional<PortNumber> getDeviceUplink(DeviceId devId) {
663 Device device = deviceService.getDevice(devId);
664 if (device == null || device.serialNumber() == null) {
665 return Optional.empty();
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000666 }
Esin Karaman00e16b72020-02-21 10:32:39 +0000667 Optional<SubscriberAndDeviceInformation> olt = getSubscriberAndDeviceInformation(device.serialNumber());
668 if (olt.isEmpty()) {
669 return Optional.empty();
670 }
671 PortNumber portNumber = PortNumber.portNumber(olt.get().uplinkPort());
672 return validateUpLinkPort(device.id(), portNumber) ?
673 Optional.of(portNumber) : Optional.empty();
674 }
675
676 /**
677 *
678 * @param deviceId device id
679 * @param portNumber port number
680 * @return true if the port name starts with NNI_PREFIX; false otherwise.
681 */
682 public boolean validateUpLinkPort(DeviceId deviceId, PortNumber portNumber) {
683 Port port = deviceService.getPort(deviceId, portNumber);
684 if (port == null) {
685 //port is not discovered by ONOS; so cannot validate it.
686 return false;
687 }
Esin Karamance5ce512020-02-25 15:58:14 +0000688 boolean isValid = port.annotations().value(AnnotationKeys.PORT_NAME) != null &&
Esin Karaman00e16b72020-02-21 10:32:39 +0000689 port.annotations().value(AnnotationKeys.PORT_NAME).startsWith(NNI_PREFIX);
Esin Karamance5ce512020-02-25 15:58:14 +0000690 if (!isValid) {
691 log.warn("Port cannot be validated; it is not configured as an NNI port." +
692 "Device/port: {}/{}", deviceId, portNumber);
693 }
694 return isValid;
ke han81a38b92017-03-10 18:41:44 +0800695 }
696
Esin Karamanb38700c2019-09-17 13:01:25 +0000697 public static boolean isIgmpOnPodBasis() {
698 return igmpOnPodBasis;
699 }
700
ke han81a38b92017-03-10 18:41:44 +0800701 private void processFilterObjective(DeviceId devId, PortNumber port, boolean remove) {
Esin Karamaneff10392019-06-27 18:09:13 +0000702 if (!enableIgmpProvisioning) {
703 log.debug("IGMP trap rules won't be installed since enableIgmpProvisioning flag is not set");
704 return;
705 }
ke han81a38b92017-03-10 18:41:44 +0800706 //TODO migrate to packet requests when packet service uses filtering objectives
707 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
708
709 builder = remove ? builder.deny() : builder.permit();
710
711 FilteringObjective igmp = builder
712 .withKey(Criteria.matchInPort(port))
713 .addCondition(Criteria.matchEthType(EthType.EtherType.IPV4.ethType()))
714 .addCondition(Criteria.matchIPProtocol(IPv4.PROTOCOL_IGMP))
715 .withMeta(DefaultTrafficTreatment.builder().setOutput(PortNumber.CONTROLLER).build())
716 .fromApp(appId)
Esin Karamaneff10392019-06-27 18:09:13 +0000717 .withPriority(MAX_PRIORITY)
ke han81a38b92017-03-10 18:41:44 +0800718 .add(new ObjectiveContext() {
719 @Override
720 public void onSuccess(Objective objective) {
Esin Karamaneff10392019-06-27 18:09:13 +0000721 log.info("Igmp filter for {} on {} {}.",
722 devId, port, (remove) ? REMOVED : INSTALLED);
ke han81a38b92017-03-10 18:41:44 +0800723 }
724
725 @Override
726 public void onError(Objective objective, ObjectiveError error) {
Esin Karamaneff10392019-06-27 18:09:13 +0000727 log.info("Igmp filter {} for device {} on port {} failed because of {}",
728 (remove) ? INSTALLATION : REMOVAL, devId, port,
729 error);
ke han81a38b92017-03-10 18:41:44 +0800730 }
731 });
732
733 flowObjectiveService.filter(devId, igmp);
Esin Karamaneff10392019-06-27 18:09:13 +0000734
ke han81a38b92017-03-10 18:41:44 +0800735 }
736
737 private boolean isConnectPoint(DeviceId device, PortNumber port) {
Deepa Vaddireddy01f33b42017-07-02 13:26:53 +0530738 if (connectPoint != null) {
739 return (connectPointMode && connectPoint.deviceId().equals(device)
740 && connectPoint.port().equals(port));
741 } else {
742 log.info("connectPoint not configured for device {}", device);
743 return false;
744 }
ke han81a38b92017-03-10 18:41:44 +0800745 }
Deepa Vaddireddy01f33b42017-07-02 13:26:53 +0530746
ke han81a38b92017-03-10 18:41:44 +0800747 private boolean isUplink(DeviceId device, PortNumber port) {
Esin Karamance5ce512020-02-25 15:58:14 +0000748 if (connectPointMode) {
749 return false;
750 }
751 Optional<PortNumber> upLinkPort = getDeviceUplink(device);
752 return upLinkPort.isPresent() && upLinkPort.get().equals(port);
ke han81a38b92017-03-10 18:41:44 +0800753 }
754
Esin Karaman00e16b72020-02-21 10:32:39 +0000755 /**
756 * Fetches device information associated with the device serial number from SADIS.
757 *
758 * @param serialNumber serial number of a device
759 * @return device information; an empty Optional otherwise.
760 */
761 private Optional<SubscriberAndDeviceInformation> getSubscriberAndDeviceInformation(String serialNumber) {
762 long start = System.currentTimeMillis();
763 try {
764 return Optional.ofNullable(sadisService.getSubscriberInfoService().get(serialNumber));
765 } finally {
766 if (log.isDebugEnabled()) {
767 // SADIS can call remote systems to fetch device data and this calls can take a long time.
768 // This measurement is just for monitoring these kinds of situations.
769 log.debug("Device fetched from SADIS. Elapsed {} msec", System.currentTimeMillis() - start);
770 }
771
772 }
773 }
774
775 /**
776 * Fetches device information associated with the device serial number from SADIS.
777 *
778 * @param deviceId device id
779 * @return device information; an empty Optional otherwise.
780 */
781 private Optional<SubscriberAndDeviceInformation> getSubscriberAndDeviceInformation(DeviceId deviceId) {
782 Device device = deviceService.getDevice(deviceId);
783 if (device == null || device.serialNumber() == null) {
784 return Optional.empty();
785 }
786 return getSubscriberAndDeviceInformation(device.serialNumber());
787 }
788
ke han81a38b92017-03-10 18:41:44 +0800789 private class InternalDeviceListener implements DeviceListener {
790 @Override
791 public void event(DeviceEvent event) {
792 DeviceId devId = event.subject().id();
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +0000793 Port p = event.port();
Esin Karaman00e16b72020-02-21 10:32:39 +0000794 if (getSubscriberAndDeviceInformation(devId).isEmpty() &&
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +0000795 !(p != null && isConnectPoint(devId, p.number()))) {
ke han81a38b92017-03-10 18:41:44 +0800796 return;
797 }
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +0000798 PortNumber port;
799
ke han81a38b92017-03-10 18:41:44 +0800800 switch (event.type()) {
801
802 case DEVICE_ADDED:
803 case DEVICE_UPDATED:
804 case DEVICE_REMOVED:
805 case DEVICE_SUSPENDED:
806 case DEVICE_AVAILABILITY_CHANGED:
807 case PORT_STATS_UPDATED:
808 break;
809 case PORT_ADDED:
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +0000810 port = p.number();
Esin Karaman00e16b72020-02-21 10:32:39 +0000811 if (getSubscriberAndDeviceInformation(devId).isPresent() &&
812 !isUplink(devId, port) && !isConnectPoint(devId, port)) {
ke han81a38b92017-03-10 18:41:44 +0800813 processFilterObjective(devId, port, false);
814 } else if (isUplink(devId, port)) {
815 provisionUplinkFlows();
816 } else if (isConnectPoint(devId, port)) {
817 provisionConnectPointFlows();
818 }
819 break;
820 case PORT_UPDATED:
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +0000821 port = p.number();
Esin Karaman00e16b72020-02-21 10:32:39 +0000822 if (getSubscriberAndDeviceInformation(devId).isPresent() &&
823 !isUplink(devId, port) && !isConnectPoint(devId, port)) {
ke han81a38b92017-03-10 18:41:44 +0800824 if (event.port().isEnabled()) {
825 processFilterObjective(devId, port, false);
826 } else {
827 processFilterObjective(devId, port, true);
828 }
829 } else if (isUplink(devId, port)) {
830 if (event.port().isEnabled()) {
831 provisionUplinkFlows(devId);
832 } else {
833 processFilterObjective(devId, port, true);
834 }
835 } else if (isConnectPoint(devId, port)) {
836 if (event.port().isEnabled()) {
837 provisionConnectPointFlows();
838 } else {
839 unprovisionConnectPointFlows();
840 }
841 }
842 break;
843 case PORT_REMOVED:
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +0000844 port = p.number();
ke han81a38b92017-03-10 18:41:44 +0800845 processFilterObjective(devId, port, true);
846 break;
847 default:
848 log.info("Unknown device event {}", event.type());
849 break;
850 }
851 }
852
853 @Override
854 public boolean isRelevant(DeviceEvent event) {
855 return true;
856 }
857 }
858
859 private class InternalNetworkConfigListener implements NetworkConfigListener {
860
861 private void reconfigureNetwork(IgmpproxyConfig cfg) {
Esin Karamaneff10392019-06-27 18:09:13 +0000862 IgmpproxyConfig newCfg = cfg == null ? new IgmpproxyConfig() : cfg;
ke han81a38b92017-03-10 18:41:44 +0800863
864 unSolicitedTimeout = newCfg.unsolicitedTimeOut();
865 maxResp = newCfg.maxResp();
866 keepAliveInterval = newCfg.keepAliveInterval();
867 keepAliveCount = newCfg.keepAliveCount();
868 lastQueryInterval = newCfg.lastQueryInterval();
869 lastQueryCount = newCfg.lastQueryCount();
870 withRAUplink = newCfg.withRAUplink();
871 withRADownlink = newCfg.withRADownlink();
872 igmpCos = newCfg.igmpCos();
873 periodicQuery = newCfg.periodicQuery();
874 fastLeave = newCfg.fastLeave();
ke han29af27b2017-09-08 10:29:12 +0800875 pimSSmInterworking = newCfg.pimSsmInterworking();
Esin Karamaneff10392019-06-27 18:09:13 +0000876 enableIgmpProvisioning = newCfg.enableIgmpProvisioning();
Esin Karamanb38700c2019-09-17 13:01:25 +0000877 igmpOnPodBasis = newCfg.igmpOnPodBasis();
Arjun E Kb0018fd2020-04-07 13:26:40 +0000878 if (newCfg.outgoingIgmpWithV3() != null &&
879 outgoingIgmpWithV3 != newCfg.outgoingIgmpWithV3()) {
880 outgoingIgmpWithV3 = newCfg.outgoingIgmpWithV3();
881 }
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +0000882
883 if (connectPointMode != newCfg.connectPointMode() ||
884 connectPoint != newCfg.connectPoint()) {
ke han81a38b92017-03-10 18:41:44 +0800885 connectPointMode = newCfg.connectPointMode();
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +0000886 connectPoint = newCfg.connectPoint();
ke han81a38b92017-03-10 18:41:44 +0800887 if (connectPointMode) {
888 unprovisionUplinkFlows();
889 provisionConnectPointFlows();
890 } else {
891 unprovisionConnectPointFlows();
892 provisionUplinkFlows();
893 }
894 }
895 if (connectPoint != null) {
Esin Karamaneff10392019-06-27 18:09:13 +0000896 log.info("connect point : {}", connectPoint);
ke han81a38b92017-03-10 18:41:44 +0800897 }
Esin Karamaneff10392019-06-27 18:09:13 +0000898 log.info("mode: {}", connectPointMode);
899
900 getSourceConnectPoint(newCfg);
ke han81a38b92017-03-10 18:41:44 +0800901
902 IgmpSender.getInstance().setIgmpCos(igmpCos);
903 IgmpSender.getInstance().setMaxResp(maxResp);
904 IgmpSender.getInstance().setMvlan(mvlan);
905 IgmpSender.getInstance().setWithRADownlink(withRADownlink);
906 IgmpSender.getInstance().setWithRAUplink(withRAUplink);
Esin Karamaneff10392019-06-27 18:09:13 +0000907 }
ke han81a38b92017-03-10 18:41:44 +0800908
Esin Karamaneff10392019-06-27 18:09:13 +0000909 void getSourceConnectPoint(IgmpproxyConfig cfg) {
910 sourceDeviceAndPort = cfg.getSourceDeviceAndPort();
911 if (sourceDeviceAndPort != null) {
912 log.debug("source parameter configured to {}", sourceDeviceAndPort);
913 }
ke han81a38b92017-03-10 18:41:44 +0800914 }
915
916 public void reconfigureSsmTable(IgmpproxySsmTranslateConfig cfg) {
917 if (cfg == null) {
918 return;
919 }
920 Collection<McastRoute> translations = cfg.getSsmTranslations();
921 for (McastRoute route : translations) {
Esin Karamaneff10392019-06-27 18:09:13 +0000922 ssmTranslateTable.put(route.group().getIp4Address(), route.source().get().getIp4Address());
ke han81a38b92017-03-10 18:41:44 +0800923 }
924 }
925
926 @Override
927 public void event(NetworkConfigEvent event) {
928 switch (event.type()) {
929 case CONFIG_ADDED:
930 case CONFIG_UPDATED:
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800931 // NOTE how to know if something has changed in sadis?
ke han81a38b92017-03-10 18:41:44 +0800932
933 if (event.configClass().equals(IGMPPROXY_CONFIG_CLASS)) {
934 IgmpproxyConfig config = networkConfig.getConfig(appId, IGMPPROXY_CONFIG_CLASS);
935 if (config != null) {
Esin Karamaneff10392019-06-27 18:09:13 +0000936 log.info("igmpproxy config received. {}", config);
ke han81a38b92017-03-10 18:41:44 +0800937 reconfigureNetwork(config);
938 }
939 }
940
941 if (event.configClass().equals(IGMPPROXY_SSM_CONFIG_CLASS)) {
942 IgmpproxySsmTranslateConfig config = networkConfig.getConfig(appId, IGMPPROXY_SSM_CONFIG_CLASS);
943 if (config != null) {
944 reconfigureSsmTable(config);
945 }
946 }
947
948 if (event.configClass().equals(MCAST_CONFIG_CLASS)) {
949 McastConfig config = networkConfig.getConfig(coreAppId, MCAST_CONFIG_CLASS);
950 if (config != null && mvlan != config.egressVlan().toShort()) {
951 mvlan = config.egressVlan().toShort();
Deepa Vaddireddy88134a42017-07-05 12:16:03 +0530952 IgmpSender.getInstance().setMvlan(mvlan);
ke han81a38b92017-03-10 18:41:44 +0800953 groupMemberMap.values().forEach(m -> leaveAction(m));
954 }
955 }
956
957 log.info("Reconfigured");
958 break;
959 case CONFIG_REGISTERED:
960 case CONFIG_UNREGISTERED:
961 break;
962 case CONFIG_REMOVED:
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800963 // NOTE how to know if something has changed in sadis?
ke han81a38b92017-03-10 18:41:44 +0800964 default:
965 break;
966 }
967 }
968 }
969
ke han81a38b92017-03-10 18:41:44 +0800970 private void provisionUplinkFlows(DeviceId deviceId) {
971 if (connectPointMode) {
972 return;
973 }
974
Esin Karaman00e16b72020-02-21 10:32:39 +0000975 Optional<PortNumber> upLink = getDeviceUplink(deviceId);
976 if (upLink.isPresent()) {
977 processFilterObjective(deviceId, upLink.get(), false);
978 }
ke han81a38b92017-03-10 18:41:44 +0800979 }
980
981 private void provisionUplinkFlows() {
982 if (connectPointMode) {
983 return;
984 }
Esin Karaman00e16b72020-02-21 10:32:39 +0000985 deviceService.getAvailableDevices().forEach(device -> {
986 Optional<SubscriberAndDeviceInformation> accessDevice = getSubscriberAndDeviceInformation(device.id());
987 if (accessDevice.isPresent()) {
988 provisionUplinkFlows(device.id());
989 }
990 });
ke han81a38b92017-03-10 18:41:44 +0800991 }
Esin Karaman00e16b72020-02-21 10:32:39 +0000992
ke han81a38b92017-03-10 18:41:44 +0800993 private void unprovisionUplinkFlows() {
Esin Karaman00e16b72020-02-21 10:32:39 +0000994 deviceService.getAvailableDevices().forEach(device -> {
995 Optional<SubscriberAndDeviceInformation> accessDevices = getSubscriberAndDeviceInformation(device.id());
996 if (accessDevices.isPresent()) {
997 Optional<PortNumber> upLink = getDeviceUplink(device.id());
998 if (upLink.isPresent()) {
999 processFilterObjective(device.id(), upLink.get(), true);
1000 }
1001 }
1002 });
ke han81a38b92017-03-10 18:41:44 +08001003 }
1004
1005 private void provisionConnectPointFlows() {
1006 if ((!connectPointMode) || connectPoint == null) {
1007 return;
1008 }
1009
1010 processFilterObjective(connectPoint.deviceId(), connectPoint.port(), false);
1011 }
1012 private void unprovisionConnectPointFlows() {
1013 if (connectPoint == null) {
1014 return;
1015 }
1016 processFilterObjective(connectPoint.deviceId(), connectPoint.port(), true);
1017 }
Shubham Sharma47f2caf2020-02-18 12:13:40 +00001018
ke han81a38b92017-03-10 18:41:44 +08001019}