blob: d5677f0bcd3ae530b5ac0bcd1a90175a215542b4 [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;
Sonal Kasliwalf11c0672020-03-18 11:11:50 +000075import java.util.Arrays;
76import java.util.List;
ke han81a38b92017-03-10 18:41:44 +080077import java.util.Collection;
Esin Karamaneff10392019-06-27 18:09:13 +000078import java.util.HashSet;
ke han81a38b92017-03-10 18:41:44 +080079import java.util.Iterator;
ke han81a38b92017-03-10 18:41:44 +080080import java.util.Map;
Esin Karamaneff10392019-06-27 18:09:13 +000081import java.util.Optional;
ke han81a38b92017-03-10 18:41:44 +080082import java.util.Set;
83import java.util.TimerTask;
84import java.util.concurrent.ConcurrentHashMap;
Esin Karamanb38700c2019-09-17 13:01:25 +000085import java.util.concurrent.ExecutorService;
ke han81a38b92017-03-10 18:41:44 +080086import java.util.concurrent.Executors;
87import java.util.concurrent.ScheduledExecutorService;
88import java.util.concurrent.TimeUnit;
89
Sonal Kasliwalf11c0672020-03-18 11:11:50 +000090import static org.onlab.packet.IGMPMembership.MODE_IS_INCLUDE;
91import static org.onlab.packet.IGMPMembership.MODE_IS_EXCLUDE;
92import static org.onlab.packet.IGMPMembership.CHANGE_TO_INCLUDE_MODE;
93import static org.onlab.packet.IGMPMembership.CHANGE_TO_EXCLUDE_MODE;
94import static org.onlab.packet.IGMPMembership.ALLOW_NEW_SOURCES;
95import static org.onlab.packet.IGMPMembership.BLOCK_OLD_SOURCES;
96
Esin Karamanb38700c2019-09-17 13:01:25 +000097import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
98import static org.onlab.util.Tools.groupedThreads;
99
ke han81a38b92017-03-10 18:41:44 +0800100/**
101 * Igmp process application, use proxy mode, support first join/ last leave , fast leave
102 * period query and keep alive, packet out igmp message to uplink port features.
103 */
104@Component(immediate = true)
105public class IgmpManager {
106
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800107 private static final String APP_NAME = "org.opencord.igmpproxy";
108
ke han81a38b92017-03-10 18:41:44 +0800109 private static final Class<IgmpproxyConfig> IGMPPROXY_CONFIG_CLASS =
110 IgmpproxyConfig.class;
111 private static final Class<IgmpproxySsmTranslateConfig> IGMPPROXY_SSM_CONFIG_CLASS =
112 IgmpproxySsmTranslateConfig.class;
113 private static final Class<McastConfig> MCAST_CONFIG_CLASS =
114 McastConfig.class;
Esin Karamaneff10392019-06-27 18:09:13 +0000115
ke han81a38b92017-03-10 18:41:44 +0800116 public static Map<String, GroupMember> groupMemberMap = Maps.newConcurrentMap();
117 private static ApplicationId appId;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800118
ke han81a38b92017-03-10 18:41:44 +0800119 private static int unSolicitedTimeout = 3; // unit is 1 sec
120 private static int keepAliveCount = 3;
121 private static int lastQueryInterval = 2; //unit is 1 sec
122 private static int lastQueryCount = 2;
123 private static boolean fastLeave = true;
124 private static boolean withRAUplink = true;
125 private static boolean withRADownlink = false;
126 private static boolean periodicQuery = true;
127 private static short mvlan = 4000;
128 private static byte igmpCos = 7;
129 public static boolean connectPointMode = true;
130 public static ConnectPoint connectPoint = null;
Esin Karamaneff10392019-06-27 18:09:13 +0000131 private static ConnectPoint sourceDeviceAndPort = null;
132 private static boolean enableIgmpProvisioning = false;
Esin Karamanb38700c2019-09-17 13:01:25 +0000133 private static boolean igmpOnPodBasis = false;
Esin Karamaneff10392019-06-27 18:09:13 +0000134
135 private static final Integer MAX_PRIORITY = 10000;
136 private static final String INSTALLED = "installed";
137 private static final String REMOVED = "removed";
138 private static final String INSTALLATION = "installation";
139 private static final String REMOVAL = "removal";
Esin Karaman00e16b72020-02-21 10:32:39 +0000140 private static final String NNI_PREFIX = "nni";
ke han81a38b92017-03-10 18:41:44 +0800141
ke han29af27b2017-09-08 10:29:12 +0800142 private static boolean pimSSmInterworking = false;
143 private static final String DEFAULT_PIMSSM_HOST = "127.0.0.1";
ke han81a38b92017-03-10 18:41:44 +0800144 private final ScheduledExecutorService scheduledExecutorService =
145 Executors.newScheduledThreadPool(1);
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 CoreService coreService;
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 PacketService packetService;
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 MastershipService mastershipService;
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 FlowRuleService flowRuleService;
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 DeviceService deviceService;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800161
Carmelo Casconebef302e2019-11-14 19:58:20 -0800162 @Reference(cardinality = ReferenceCardinality.MANDATORY)
ke han81a38b92017-03-10 18:41:44 +0800163 protected FlowObjectiveService flowObjectiveService;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800164
Carmelo Casconebef302e2019-11-14 19:58:20 -0800165 @Reference(cardinality = ReferenceCardinality.MANDATORY)
ke han81a38b92017-03-10 18:41:44 +0800166 protected NetworkConfigRegistry networkConfig;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800167
Carmelo Casconebef302e2019-11-14 19:58:20 -0800168 @Reference(cardinality = ReferenceCardinality.MANDATORY)
ke han81a38b92017-03-10 18:41:44 +0800169 protected MulticastRouteService multicastService;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800170
171 @Reference(cardinality = ReferenceCardinality.MANDATORY)
172 protected SadisService sadisService;
173
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000174 @Reference(cardinality = ReferenceCardinality.MANDATORY)
175 protected IgmpStatisticsService igmpStatisticsManager;
176
ke han81a38b92017-03-10 18:41:44 +0800177 private IgmpPacketProcessor processor = new IgmpPacketProcessor();
178 private Logger log = LoggerFactory.getLogger(getClass());
179 private ApplicationId coreAppId;
180 private Map<Ip4Address, Ip4Address> ssmTranslateTable = new ConcurrentHashMap<>();
Esin Karamaneff10392019-06-27 18:09:13 +0000181
ke han81a38b92017-03-10 18:41:44 +0800182 private InternalNetworkConfigListener configListener =
183 new InternalNetworkConfigListener();
184 private DeviceListener deviceListener = new InternalDeviceListener();
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800185
ke han81a38b92017-03-10 18:41:44 +0800186 private ConfigFactory<ApplicationId, IgmpproxyConfig> igmpproxyConfigFactory =
187 new ConfigFactory<ApplicationId, IgmpproxyConfig>(
188 SubjectFactories.APP_SUBJECT_FACTORY, IGMPPROXY_CONFIG_CLASS, "igmpproxy") {
189 @Override
190 public IgmpproxyConfig createConfig() {
191 return new IgmpproxyConfig();
192 }
193 };
194 private ConfigFactory<ApplicationId, IgmpproxySsmTranslateConfig> igmpproxySsmConfigFactory =
195 new ConfigFactory<ApplicationId, IgmpproxySsmTranslateConfig>(
196 SubjectFactories.APP_SUBJECT_FACTORY, IGMPPROXY_SSM_CONFIG_CLASS, "ssmTranslate", true) {
197 @Override
198 public IgmpproxySsmTranslateConfig createConfig() {
199 return new IgmpproxySsmTranslateConfig();
200 }
201 };
Esin Karamaneff10392019-06-27 18:09:13 +0000202
ke han81a38b92017-03-10 18:41:44 +0800203 private int maxResp = 10; //unit is 1 sec
204 private int keepAliveInterval = 120; //unit is 1 sec
205
Esin Karamanb38700c2019-09-17 13:01:25 +0000206 private ExecutorService eventExecutor;
207
ke han81a38b92017-03-10 18:41:44 +0800208 public static int getUnsolicitedTimeout() {
209 return unSolicitedTimeout;
210 }
211
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800212 protected BaseInformationService<SubscriberAndDeviceInformation> subsService;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800213
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000214 private List<Byte> validMembershipModes = Arrays.asList(MODE_IS_INCLUDE, MODE_IS_EXCLUDE, CHANGE_TO_INCLUDE_MODE,
215 CHANGE_TO_EXCLUDE_MODE, ALLOW_NEW_SOURCES, BLOCK_OLD_SOURCES);
216
ke han81a38b92017-03-10 18:41:44 +0800217 @Activate
218 protected void activate() {
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800219 appId = coreService.registerApplication(APP_NAME);
ke han81a38b92017-03-10 18:41:44 +0800220 coreAppId = coreService.registerApplication(CoreService.CORE_APP_NAME);
221 packetService.addProcessor(processor, PacketProcessor.director(4));
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000222 IgmpSender.init(packetService, mastershipService, igmpStatisticsManager);
ke han81a38b92017-03-10 18:41:44 +0800223
ke han81a38b92017-03-10 18:41:44 +0800224 networkConfig.registerConfigFactory(igmpproxySsmConfigFactory);
225 networkConfig.registerConfigFactory(igmpproxyConfigFactory);
226 networkConfig.addListener(configListener);
227
228 configListener.reconfigureNetwork(networkConfig.getConfig(appId, IGMPPROXY_CONFIG_CLASS));
229 configListener.reconfigureSsmTable(networkConfig.getConfig(appId, IGMPPROXY_SSM_CONFIG_CLASS));
230
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800231 subsService = sadisService.getSubscriberInfoService();
ke han81a38b92017-03-10 18:41:44 +0800232 if (connectPointMode) {
233 provisionConnectPointFlows();
234 } else {
235 provisionUplinkFlows();
236 }
237
238 McastConfig config = networkConfig.getConfig(coreAppId, MCAST_CONFIG_CLASS);
239 if (config != null) {
240 mvlan = config.egressVlan().toShort();
Deepa Vaddireddy88134a42017-07-05 12:16:03 +0530241 IgmpSender.getInstance().setMvlan(mvlan);
ke han81a38b92017-03-10 18:41:44 +0800242 }
243 deviceService.addListener(deviceListener);
Sonal Kasliwalddc3ff22019-11-18 11:52:49 +0000244 scheduledExecutorService.scheduleAtFixedRate(new IgmpProxyTimerTask(), 1000, 1000, TimeUnit.MILLISECONDS);
Esin Karamanb38700c2019-09-17 13:01:25 +0000245 eventExecutor = newSingleThreadScheduledExecutor(groupedThreads("cord/igmpproxy",
246 "events-igmp-%d", log));
ke han81a38b92017-03-10 18:41:44 +0800247 log.info("Started");
248 }
249
250 @Deactivate
251 protected void deactivate() {
252 scheduledExecutorService.shutdown();
Esin Karamanb38700c2019-09-17 13:01:25 +0000253 eventExecutor.shutdown();
ke han81a38b92017-03-10 18:41:44 +0800254
255 // de-register and null our handler
256 networkConfig.removeListener(configListener);
ke han81a38b92017-03-10 18:41:44 +0800257 networkConfig.unregisterConfigFactory(igmpproxyConfigFactory);
258 networkConfig.unregisterConfigFactory(igmpproxySsmConfigFactory);
259 deviceService.removeListener(deviceListener);
260 packetService.removeProcessor(processor);
261 flowRuleService.removeFlowRulesById(appId);
ke han81a38b92017-03-10 18:41:44 +0800262 log.info("Stopped");
263 }
264
265 protected Ip4Address getDeviceIp(DeviceId ofDeviceId) {
266 try {
267 String[] mgmtAddress = deviceService.getDevice(ofDeviceId)
268 .annotations().value(AnnotationKeys.MANAGEMENT_ADDRESS).split(":");
269 return Ip4Address.valueOf(mgmtAddress[0]);
270 } catch (Exception ex) {
271 log.info("No valid Ipaddress for " + ofDeviceId.toString());
272 return null;
273 }
274 }
275
276 private void processIgmpQuery(IGMPQuery igmpQuery, ConnectPoint cp, int maxResp) {
277
278 DeviceId deviceId = cp.deviceId();
279 Ip4Address gAddr = igmpQuery.getGaddr().getIp4Address();
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000280 maxResp = calculateMaxResp(maxResp);
281 if (gAddr != null && !gAddr.isZero()) {
282 StateMachine.specialQuery(deviceId, gAddr, maxResp);
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000283 igmpStatisticsManager.getIgmpStats().increaseIgmpGrpSpecificMembershipQuery();
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000284 } else {
285 StateMachine.generalQuery(deviceId, maxResp);
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000286 igmpStatisticsManager.getIgmpStats().increaseIgmpGeneralMembershipQuery();
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000287 }
288 }
ke han81a38b92017-03-10 18:41:44 +0800289
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000290 private void processIgmpConnectPointQuery(IGMPQuery igmpQuery, ConnectPoint cp, int maxResp) {
291
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000292 Ip4Address gAddr = igmpQuery.getGaddr().getIp4Address();
Esin Karaman00e16b72020-02-21 10:32:39 +0000293 final int maxResponseTime = calculateMaxResp(maxResp);
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000294 //The query is received on the ConnectPoint
295 // send query accordingly to the registered OLT devices.
296 if (gAddr != null && !gAddr.isZero()) {
Esin Karaman00e16b72020-02-21 10:32:39 +0000297 deviceService.getAvailableDevices().forEach(device -> {
298 Optional<SubscriberAndDeviceInformation> accessDevice = getSubscriberAndDeviceInformation(device.id());
299 if (accessDevice.isPresent()) {
300 StateMachine.specialQuery(device.id(), gAddr, maxResponseTime);
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000301 igmpStatisticsManager.getIgmpStats().increaseIgmpGrpAndSrcSpecificMembershipQuery();
Esin Karaman00e16b72020-02-21 10:32:39 +0000302 }
303 });
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000304 igmpStatisticsManager.getIgmpStats().increaseCurrentGrpNumCounter();
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000305 } else {
306 //Don't know which group is targeted by the query
307 //So query all the members(in all the OLTs) and proxy their reports
Esin Karaman00e16b72020-02-21 10:32:39 +0000308 StateMachine.generalQuery(maxResponseTime);
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000309 igmpStatisticsManager.getIgmpStats().increaseIgmpGeneralMembershipQuery();
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000310 }
311 }
312
313
314 private int calculateMaxResp(int maxResp) {
ke han81a38b92017-03-10 18:41:44 +0800315 if (maxResp >= 128) {
316 int mant = maxResp & 0xf;
317 int exp = (maxResp >> 4) & 0x7;
318 maxResp = (mant | 0x10) << (exp + 3);
319 }
320
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000321 return (maxResp + 5) / 10;
ke han81a38b92017-03-10 18:41:44 +0800322 }
323
324 private Ip4Address ssmTranslateRoute(IpAddress group) {
325 return ssmTranslateTable.get(group);
326 }
327
328 private void processIgmpReport(IGMPMembership igmpGroup, VlanId vlan, ConnectPoint cp, byte igmpType) {
329 DeviceId deviceId = cp.deviceId();
330 PortNumber portNumber = cp.port();
331
332 Ip4Address groupIp = igmpGroup.getGaddr().getIp4Address();
333 if (!groupIp.isMulticast()) {
334 log.info(groupIp.toString() + " is not a valid group address");
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000335 igmpStatisticsManager.getIgmpStats().increaseFailJoinReqUnknownMulticastIpCounter();
ke han81a38b92017-03-10 18:41:44 +0800336 return;
337 }
338 Ip4Address srcIp = getDeviceIp(deviceId);
339
340 byte recordType = igmpGroup.getRecordType();
341 boolean join = false;
342
343 ArrayList<Ip4Address> sourceList = new ArrayList<>();
344
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000345 if (!validMembershipModes.contains(recordType)) {
346 igmpStatisticsManager.getIgmpStats().increaseReportsRxWithWrongModeCounter();
347 }
ke han81a38b92017-03-10 18:41:44 +0800348 if (igmpGroup.getSources().size() > 0) {
349 igmpGroup.getSources().forEach(source -> sourceList.add(source.getIp4Address()));
350 if (recordType == IGMPMembership.CHANGE_TO_EXCLUDE_MODE ||
351 recordType == IGMPMembership.MODE_IS_EXCLUDE ||
352 recordType == IGMPMembership.BLOCK_OLD_SOURCES) {
353 join = false;
354 } else if (recordType == IGMPMembership.CHANGE_TO_INCLUDE_MODE ||
355 recordType == IGMPMembership.MODE_IS_INCLUDE ||
356 recordType == IGMPMembership.ALLOW_NEW_SOURCES) {
357 join = true;
358 }
359 } else {
ke han29af27b2017-09-08 10:29:12 +0800360 IpAddress src = null;
361 if (pimSSmInterworking) {
362 src = ssmTranslateRoute(groupIp);
363 if (src == null) {
364 log.info("no ssm translate for group " + groupIp.toString());
365 return;
366 }
367 } else {
368 src = IpAddress.valueOf(DEFAULT_PIMSSM_HOST);
ke han81a38b92017-03-10 18:41:44 +0800369 }
370 sourceList.add(src.getIp4Address());
371 if (recordType == IGMPMembership.CHANGE_TO_EXCLUDE_MODE ||
372 recordType == IGMPMembership.MODE_IS_EXCLUDE ||
373 recordType == IGMPMembership.BLOCK_OLD_SOURCES) {
374 join = true;
375 } else if (recordType == IGMPMembership.CHANGE_TO_INCLUDE_MODE ||
376 recordType == IGMPMembership.MODE_IS_INCLUDE ||
377 recordType == IGMPMembership.ALLOW_NEW_SOURCES) {
378 join = false;
379 }
380 }
381 String groupMemberKey = GroupMember.getkey(groupIp, deviceId, portNumber);
382 GroupMember groupMember = groupMemberMap.get(groupMemberKey);
383
384 if (join) {
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000385 igmpStatisticsManager.getIgmpStats().increaseIgmpJoinReq();
ke han81a38b92017-03-10 18:41:44 +0800386 if (groupMember == null) {
Esin Karamaneff10392019-06-27 18:09:13 +0000387 Optional<ConnectPoint> sourceConfigured = getSource();
388 if (!sourceConfigured.isPresent()) {
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000389 igmpStatisticsManager.getIgmpStats().increaseIgmpFailJoinReq();
Esin Karamaneff10392019-06-27 18:09:13 +0000390 log.warn("Unable to process IGMP Join from {} since no source " +
391 "configuration is found.", deviceId);
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000392 igmpStatisticsManager.getIgmpStats().increaseFailJoinReqInsuffPermissionAccessCounter();
Esin Karamaneff10392019-06-27 18:09:13 +0000393 return;
394 }
Esin Karaman00e16b72020-02-21 10:32:39 +0000395
396 Optional<PortNumber> deviceUplink = getDeviceUplink(deviceId);
397 if (deviceUplink.isEmpty()) {
398 log.warn("Unable to process IGMP Join since uplink port " +
399 "of the device {} is not found.", deviceId);
400 return;
401 }
402
403 if (igmpType == IGMP.TYPE_IGMPV2_MEMBERSHIP_REPORT) {
404 groupMember = new GroupMember(groupIp, vlan, deviceId, portNumber, true);
405 } else {
406 groupMember = new GroupMember(groupIp, vlan, deviceId, portNumber, false);
407 }
408
Esin Karamaneff10392019-06-27 18:09:13 +0000409 HashSet<ConnectPoint> sourceConnectPoints = Sets.newHashSet(sourceConfigured.get());
410
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000411 boolean isJoined = StateMachine.join(deviceId, groupIp, srcIp, deviceUplink.get());
412 if (isJoined) {
413 igmpStatisticsManager.getIgmpStats().increaseIgmpSuccessJoinRejoinReq();
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000414 igmpStatisticsManager.getIgmpStats().increaseIgmpChannelJoinCounter();
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000415 } else {
416 igmpStatisticsManager.getIgmpStats().increaseIgmpFailJoinReq();
417 }
ke han81a38b92017-03-10 18:41:44 +0800418 groupMemberMap.put(groupMemberKey, groupMember);
419 groupMember.updateList(recordType, sourceList);
Esin Karamaneff10392019-06-27 18:09:13 +0000420 groupMember.getSourceList().forEach(source -> {
421 McastRoute route = new McastRoute(source, groupIp, McastRoute.Type.IGMP);
422 //add route
423 multicastService.add(route);
424 //add source to the route
425 multicastService.addSources(route, Sets.newHashSet(sourceConnectPoints));
426 //add sink to the route
427 multicastService.addSinks(route, Sets.newHashSet(cp));
428 });
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000429 igmpStatisticsManager.getIgmpStats().increaseUnconfiguredGroupCounter();
Esin Karamaneff10392019-06-27 18:09:13 +0000430
ke han81a38b92017-03-10 18:41:44 +0800431 }
432 groupMember.resetAllTimers();
433 groupMember.updateList(recordType, sourceList);
434 groupMember.setLeave(false);
435 } else {
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000436 igmpStatisticsManager.getIgmpStats().increaseIgmpLeaveReq();
ke han81a38b92017-03-10 18:41:44 +0800437 if (groupMember == null) {
438 log.info("receive leave but no instance, group " + groupIp.toString() +
439 " device:" + deviceId.toString() + " port:" + portNumber.toString());
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000440 igmpStatisticsManager.getIgmpStats().increaseUnconfiguredGroupCounter();
ke han81a38b92017-03-10 18:41:44 +0800441 return;
442 } else {
443 groupMember.setLeave(true);
444 if (fastLeave) {
445 leaveAction(groupMember);
446 } else {
447 sendQuery(groupMember);
448 }
449 }
450 }
451 }
452
453 private void leaveAction(GroupMember groupMember) {
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000454 igmpStatisticsManager.getIgmpStats().increaseIgmpDisconnect();
ke han81a38b92017-03-10 18:41:44 +0800455 ConnectPoint cp = new ConnectPoint(groupMember.getDeviceId(), groupMember.getPortNumber());
456 StateMachine.leave(groupMember.getDeviceId(), groupMember.getGroupIp());
Esin Karamaneff10392019-06-27 18:09:13 +0000457 groupMember.getSourceList().forEach(source -> multicastService.removeSinks(
ke han81a38b92017-03-10 18:41:44 +0800458 new McastRoute(source, groupMember.getGroupIp(),
Esin Karamaneff10392019-06-27 18:09:13 +0000459 McastRoute.Type.IGMP), Sets.newHashSet(cp)));
ke han81a38b92017-03-10 18:41:44 +0800460 groupMemberMap.remove(groupMember.getId());
461 }
462
463 private void sendQuery(GroupMember groupMember) {
464 Ethernet ethpkt;
465 Ip4Address srcIp = getDeviceIp(groupMember.getDeviceId());
466 if (groupMember.getv2()) {
467 ethpkt = IgmpSender.getInstance().buildIgmpV2Query(groupMember.getGroupIp(), srcIp);
468 } else {
469 ethpkt = IgmpSender.getInstance().buildIgmpV3Query(groupMember.getGroupIp(), srcIp);
470 }
471 IgmpSender.getInstance().sendIgmpPacket(ethpkt, groupMember.getDeviceId(), groupMember.getPortNumber());
472 }
473
Esin Karamaneff10392019-06-27 18:09:13 +0000474 /**
475 * @return connect point of the source if configured; and empty Optional otherwise.
476 */
477 public static Optional<ConnectPoint> getSource() {
478 return sourceDeviceAndPort == null ? Optional.empty() :
479 Optional.of(sourceDeviceAndPort);
ke han81a38b92017-03-10 18:41:44 +0800480 }
481
482 /**
483 * Packet processor responsible for forwarding packets along their paths.
484 */
485 private class IgmpPacketProcessor implements PacketProcessor {
486 @Override
487 public void process(PacketContext context) {
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000488
Esin Karamanb38700c2019-09-17 13:01:25 +0000489 eventExecutor.execute(() -> {
490 try {
491 InboundPacket pkt = context.inPacket();
492 Ethernet ethPkt = pkt.parsed();
493 if (ethPkt == null) {
494 return;
495 }
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000496 igmpStatisticsManager.getIgmpStats().increaseTotalMsgReceived();
ke han81a38b92017-03-10 18:41:44 +0800497
Esin Karamanb38700c2019-09-17 13:01:25 +0000498 if (ethPkt.getEtherType() != Ethernet.TYPE_IPV4) {
499 return;
500 }
ke han81a38b92017-03-10 18:41:44 +0800501
Esin Karamanb38700c2019-09-17 13:01:25 +0000502 IPv4 ipv4Pkt = (IPv4) ethPkt.getPayload();
ke han81a38b92017-03-10 18:41:44 +0800503
Esin Karamanb38700c2019-09-17 13:01:25 +0000504 if (ipv4Pkt.getProtocol() != IPv4.PROTOCOL_IGMP) {
505 return;
506 }
ke han81a38b92017-03-10 18:41:44 +0800507
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000508 igmpStatisticsManager.getIgmpStats().increaseIgmpValidChecksumCounter();
Esin Karamanb38700c2019-09-17 13:01:25 +0000509 short vlan = ethPkt.getVlanID();
510 DeviceId deviceId = pkt.receivedFrom().deviceId();
ke han81a38b92017-03-10 18:41:44 +0800511
Esin Karaman00e16b72020-02-21 10:32:39 +0000512 if (!isConnectPoint(deviceId, pkt.receivedFrom().port()) &&
513 !getSubscriberAndDeviceInformation(deviceId).isPresent()) {
Esin Karamanb38700c2019-09-17 13:01:25 +0000514 log.error("Device not registered in netcfg :" + deviceId.toString());
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000515 igmpStatisticsManager.getIgmpStats().increaseFailJoinReqInsuffPermissionAccessCounter();
Esin Karamanb38700c2019-09-17 13:01:25 +0000516 return;
517 }
ke han81a38b92017-03-10 18:41:44 +0800518
Esin Karamanb38700c2019-09-17 13:01:25 +0000519 IGMP igmp = (IGMP) ipv4Pkt.getPayload();
Esin Karamance5ce512020-02-25 15:58:14 +0000520
521 Optional<PortNumber> deviceUpLinkOpt = getDeviceUplink(deviceId);
522 PortNumber upLinkPort = deviceUpLinkOpt.isPresent() ? deviceUpLinkOpt.get() : null;
Esin Karamanb38700c2019-09-17 13:01:25 +0000523 switch (igmp.getIgmpType()) {
524 case IGMP.TYPE_IGMPV3_MEMBERSHIP_QUERY:
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000525 igmpStatisticsManager.getIgmpStats().increaseIgmpv3MembershipQuery();
Esin Karamanb38700c2019-09-17 13:01:25 +0000526 //Discard Query from OLT’s non-uplink port’s
Esin Karamance5ce512020-02-25 15:58:14 +0000527 if (!pkt.receivedFrom().port().equals(upLinkPort)) {
Esin Karamanb38700c2019-09-17 13:01:25 +0000528 if (isConnectPoint(deviceId, pkt.receivedFrom().port())) {
529 log.info("IGMP Picked up query from connectPoint");
530 //OK to process packet
531 processIgmpConnectPointQuery((IGMPQuery) igmp.getGroups().get(0),
532 pkt.receivedFrom(),
533 0xff & igmp.getMaxRespField());
534 break;
535 } else {
536 //Not OK to process packet
Esin Karamance5ce512020-02-25 15:58:14 +0000537 log.warn("IGMP Picked up query from non-uplink port {}", upLinkPort);
Esin Karamanb38700c2019-09-17 13:01:25 +0000538 return;
539 }
540 }
ke han81a38b92017-03-10 18:41:44 +0800541
Esin Karamanb38700c2019-09-17 13:01:25 +0000542 processIgmpQuery((IGMPQuery) igmp.getGroups().get(0), pkt.receivedFrom(),
543 0xff & igmp.getMaxRespField());
544 break;
545 case IGMP.TYPE_IGMPV1_MEMBERSHIP_REPORT:
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000546 igmpStatisticsManager.getIgmpStats().increaseIgmpv1MembershipReport();
Esin Karamanb38700c2019-09-17 13:01:25 +0000547 log.debug("IGMP version 1 message types are not currently supported.");
548 break;
549 case IGMP.TYPE_IGMPV3_MEMBERSHIP_REPORT:
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000550 igmpStatisticsManager.getIgmpStats().increaseIgmpv3MembershipReport();
551 processIgmpMessage(pkt, igmp, upLinkPort, vlan);
552 break;
Esin Karamanb38700c2019-09-17 13:01:25 +0000553 case IGMP.TYPE_IGMPV2_MEMBERSHIP_REPORT:
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000554 igmpStatisticsManager.getIgmpStats().increaseIgmpv2MembershipReport();
555 processIgmpMessage(pkt, igmp, upLinkPort, vlan);
556 break;
Esin Karamanb38700c2019-09-17 13:01:25 +0000557 case IGMP.TYPE_IGMPV2_LEAVE_GROUP:
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000558 igmpStatisticsManager.getIgmpStats().increaseIgmpv2LeaveGroup();
559 processIgmpMessage(pkt, igmp, upLinkPort, vlan);
Esin Karamanb38700c2019-09-17 13:01:25 +0000560 break;
ke han81a38b92017-03-10 18:41:44 +0800561
Esin Karamanb38700c2019-09-17 13:01:25 +0000562 default:
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000563 log.warn("wrong IGMP v3 type:" + igmp.getIgmpType());
564 igmpStatisticsManager.getIgmpStats().increaseInvalidIgmpMsgReceived();
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000565 igmpStatisticsManager.getIgmpStats().increaseUnknownIgmpTypePacketsRxCounter();
Esin Karamanb38700c2019-09-17 13:01:25 +0000566 break;
567 }
568
569 } catch (Exception ex) {
570 log.error("igmp process error : {} ", ex);
ke han81a38b92017-03-10 18:41:44 +0800571 }
Esin Karamanb38700c2019-09-17 13:01:25 +0000572 });
ke han81a38b92017-03-10 18:41:44 +0800573 }
574 }
575
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000576 private void processIgmpMessage(InboundPacket pkt, IGMP igmp, PortNumber upLinkPort, short vlan) {
577 //Discard join/leave from OLT’s uplink port’s
578 if (pkt.receivedFrom().port().equals(upLinkPort) ||
579 isConnectPoint(pkt.receivedFrom().deviceId(), pkt.receivedFrom().port())) {
580 log.info("IGMP Picked up join/leave from uplink/connectPoint port");
581 return;
582 }
583
584 Iterator<IGMPGroup> itr = igmp.getGroups().iterator();
585 while (itr.hasNext()) {
586 IGMPGroup group = itr.next();
587 if (group instanceof IGMPMembership) {
588 processIgmpReport((IGMPMembership) group, VlanId.vlanId(vlan),
589 pkt.receivedFrom(), igmp.getIgmpType());
590 } else {
591 IGMPMembership mgroup = new IGMPMembership(group.getGaddr().getIp4Address());
592 mgroup.setRecordType(igmp.getIgmpType() == IGMP.TYPE_IGMPV2_MEMBERSHIP_REPORT ?
593 IGMPMembership.MODE_IS_EXCLUDE :
594 IGMPMembership.MODE_IS_INCLUDE);
595 processIgmpReport(mgroup, VlanId.vlanId(vlan),
596 pkt.receivedFrom(), igmp.getIgmpType());
597 }
598 }
599
600 }
601
ke han81a38b92017-03-10 18:41:44 +0800602 private class IgmpProxyTimerTask extends TimerTask {
603 public void run() {
604 try {
605 IgmpTimer.timeOut1s();
606 queryMembers();
607 } catch (Exception ex) {
608 log.warn("Igmp timer task error : {}", ex.getMessage());
609 }
610 }
611
612 private void queryMembers() {
613 GroupMember groupMember;
614 Set groupMemberSet = groupMemberMap.entrySet();
615 Iterator itr = groupMemberSet.iterator();
616 while (itr.hasNext()) {
617 Map.Entry entry = (Map.Entry) itr.next();
618 groupMember = (GroupMember) entry.getValue();
619 DeviceId did = groupMember.getDeviceId();
620 if (mastershipService.isLocalMaster(did)) {
621 if (groupMember.isLeave()) {
622 lastQuery(groupMember);
623 } else if (periodicQuery) {
624 periodicQuery(groupMember);
625 }
626 }
627 }
628 }
629
630 private void lastQuery(GroupMember groupMember) {
631 if (groupMember.getLastQueryInterval() < lastQueryInterval) {
632 groupMember.lastQueryInterval(true); // count times
633 } else if (groupMember.getLastQueryCount() < lastQueryCount - 1) {
634 sendQuery(groupMember);
635 groupMember.lastQueryInterval(false); // reset count number
636 groupMember.lastQueryCount(true); //count times
637 } else if (groupMember.getLastQueryCount() == lastQueryCount - 1) {
638 leaveAction(groupMember);
639 }
640 }
641
642 private void periodicQuery(GroupMember groupMember) {
643 if (groupMember.getKeepAliveQueryInterval() < keepAliveInterval) {
644 groupMember.keepAliveInterval(true);
645 } else if (groupMember.getKeepAliveQueryCount() < keepAliveCount) {
646 sendQuery(groupMember);
647 groupMember.keepAliveInterval(false);
648 groupMember.keepAliveQueryCount(true);
649 } else if (groupMember.getKeepAliveQueryCount() == keepAliveCount) {
650 leaveAction(groupMember);
651 }
652 }
653
654 }
655
Esin Karaman00e16b72020-02-21 10:32:39 +0000656 public Optional<PortNumber> getDeviceUplink(DeviceId devId) {
657 Device device = deviceService.getDevice(devId);
658 if (device == null || device.serialNumber() == null) {
659 return Optional.empty();
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000660 }
Esin Karaman00e16b72020-02-21 10:32:39 +0000661 Optional<SubscriberAndDeviceInformation> olt = getSubscriberAndDeviceInformation(device.serialNumber());
662 if (olt.isEmpty()) {
663 return Optional.empty();
664 }
665 PortNumber portNumber = PortNumber.portNumber(olt.get().uplinkPort());
666 return validateUpLinkPort(device.id(), portNumber) ?
667 Optional.of(portNumber) : Optional.empty();
668 }
669
670 /**
671 *
672 * @param deviceId device id
673 * @param portNumber port number
674 * @return true if the port name starts with NNI_PREFIX; false otherwise.
675 */
676 public boolean validateUpLinkPort(DeviceId deviceId, PortNumber portNumber) {
677 Port port = deviceService.getPort(deviceId, portNumber);
678 if (port == null) {
679 //port is not discovered by ONOS; so cannot validate it.
680 return false;
681 }
Esin Karamance5ce512020-02-25 15:58:14 +0000682 boolean isValid = port.annotations().value(AnnotationKeys.PORT_NAME) != null &&
Esin Karaman00e16b72020-02-21 10:32:39 +0000683 port.annotations().value(AnnotationKeys.PORT_NAME).startsWith(NNI_PREFIX);
Esin Karamance5ce512020-02-25 15:58:14 +0000684 if (!isValid) {
685 log.warn("Port cannot be validated; it is not configured as an NNI port." +
686 "Device/port: {}/{}", deviceId, portNumber);
687 }
688 return isValid;
ke han81a38b92017-03-10 18:41:44 +0800689 }
690
Esin Karamanb38700c2019-09-17 13:01:25 +0000691 public static boolean isIgmpOnPodBasis() {
692 return igmpOnPodBasis;
693 }
694
ke han81a38b92017-03-10 18:41:44 +0800695 private void processFilterObjective(DeviceId devId, PortNumber port, boolean remove) {
Esin Karamaneff10392019-06-27 18:09:13 +0000696 if (!enableIgmpProvisioning) {
697 log.debug("IGMP trap rules won't be installed since enableIgmpProvisioning flag is not set");
698 return;
699 }
ke han81a38b92017-03-10 18:41:44 +0800700 //TODO migrate to packet requests when packet service uses filtering objectives
701 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
702
703 builder = remove ? builder.deny() : builder.permit();
704
705 FilteringObjective igmp = builder
706 .withKey(Criteria.matchInPort(port))
707 .addCondition(Criteria.matchEthType(EthType.EtherType.IPV4.ethType()))
708 .addCondition(Criteria.matchIPProtocol(IPv4.PROTOCOL_IGMP))
709 .withMeta(DefaultTrafficTreatment.builder().setOutput(PortNumber.CONTROLLER).build())
710 .fromApp(appId)
Esin Karamaneff10392019-06-27 18:09:13 +0000711 .withPriority(MAX_PRIORITY)
ke han81a38b92017-03-10 18:41:44 +0800712 .add(new ObjectiveContext() {
713 @Override
714 public void onSuccess(Objective objective) {
Esin Karamaneff10392019-06-27 18:09:13 +0000715 log.info("Igmp filter for {} on {} {}.",
716 devId, port, (remove) ? REMOVED : INSTALLED);
ke han81a38b92017-03-10 18:41:44 +0800717 }
718
719 @Override
720 public void onError(Objective objective, ObjectiveError error) {
Esin Karamaneff10392019-06-27 18:09:13 +0000721 log.info("Igmp filter {} for device {} on port {} failed because of {}",
722 (remove) ? INSTALLATION : REMOVAL, devId, port,
723 error);
ke han81a38b92017-03-10 18:41:44 +0800724 }
725 });
726
727 flowObjectiveService.filter(devId, igmp);
Esin Karamaneff10392019-06-27 18:09:13 +0000728
ke han81a38b92017-03-10 18:41:44 +0800729 }
730
731 private boolean isConnectPoint(DeviceId device, PortNumber port) {
Deepa Vaddireddy01f33b42017-07-02 13:26:53 +0530732 if (connectPoint != null) {
733 return (connectPointMode && connectPoint.deviceId().equals(device)
734 && connectPoint.port().equals(port));
735 } else {
736 log.info("connectPoint not configured for device {}", device);
737 return false;
738 }
ke han81a38b92017-03-10 18:41:44 +0800739 }
Deepa Vaddireddy01f33b42017-07-02 13:26:53 +0530740
ke han81a38b92017-03-10 18:41:44 +0800741 private boolean isUplink(DeviceId device, PortNumber port) {
Esin Karamance5ce512020-02-25 15:58:14 +0000742 if (connectPointMode) {
743 return false;
744 }
745 Optional<PortNumber> upLinkPort = getDeviceUplink(device);
746 return upLinkPort.isPresent() && upLinkPort.get().equals(port);
ke han81a38b92017-03-10 18:41:44 +0800747 }
748
Esin Karaman00e16b72020-02-21 10:32:39 +0000749 /**
750 * Fetches device information associated with the device serial number from SADIS.
751 *
752 * @param serialNumber serial number of a device
753 * @return device information; an empty Optional otherwise.
754 */
755 private Optional<SubscriberAndDeviceInformation> getSubscriberAndDeviceInformation(String serialNumber) {
756 long start = System.currentTimeMillis();
757 try {
758 return Optional.ofNullable(sadisService.getSubscriberInfoService().get(serialNumber));
759 } finally {
760 if (log.isDebugEnabled()) {
761 // SADIS can call remote systems to fetch device data and this calls can take a long time.
762 // This measurement is just for monitoring these kinds of situations.
763 log.debug("Device fetched from SADIS. Elapsed {} msec", System.currentTimeMillis() - start);
764 }
765
766 }
767 }
768
769 /**
770 * Fetches device information associated with the device serial number from SADIS.
771 *
772 * @param deviceId device id
773 * @return device information; an empty Optional otherwise.
774 */
775 private Optional<SubscriberAndDeviceInformation> getSubscriberAndDeviceInformation(DeviceId deviceId) {
776 Device device = deviceService.getDevice(deviceId);
777 if (device == null || device.serialNumber() == null) {
778 return Optional.empty();
779 }
780 return getSubscriberAndDeviceInformation(device.serialNumber());
781 }
782
ke han81a38b92017-03-10 18:41:44 +0800783 private class InternalDeviceListener implements DeviceListener {
784 @Override
785 public void event(DeviceEvent event) {
786 DeviceId devId = event.subject().id();
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +0000787 Port p = event.port();
Esin Karaman00e16b72020-02-21 10:32:39 +0000788 if (getSubscriberAndDeviceInformation(devId).isEmpty() &&
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +0000789 !(p != null && isConnectPoint(devId, p.number()))) {
ke han81a38b92017-03-10 18:41:44 +0800790 return;
791 }
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +0000792 PortNumber port;
793
ke han81a38b92017-03-10 18:41:44 +0800794 switch (event.type()) {
795
796 case DEVICE_ADDED:
797 case DEVICE_UPDATED:
798 case DEVICE_REMOVED:
799 case DEVICE_SUSPENDED:
800 case DEVICE_AVAILABILITY_CHANGED:
801 case PORT_STATS_UPDATED:
802 break;
803 case PORT_ADDED:
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +0000804 port = p.number();
Esin Karaman00e16b72020-02-21 10:32:39 +0000805 if (getSubscriberAndDeviceInformation(devId).isPresent() &&
806 !isUplink(devId, port) && !isConnectPoint(devId, port)) {
ke han81a38b92017-03-10 18:41:44 +0800807 processFilterObjective(devId, port, false);
808 } else if (isUplink(devId, port)) {
809 provisionUplinkFlows();
810 } else if (isConnectPoint(devId, port)) {
811 provisionConnectPointFlows();
812 }
813 break;
814 case PORT_UPDATED:
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +0000815 port = p.number();
Esin Karaman00e16b72020-02-21 10:32:39 +0000816 if (getSubscriberAndDeviceInformation(devId).isPresent() &&
817 !isUplink(devId, port) && !isConnectPoint(devId, port)) {
ke han81a38b92017-03-10 18:41:44 +0800818 if (event.port().isEnabled()) {
819 processFilterObjective(devId, port, false);
820 } else {
821 processFilterObjective(devId, port, true);
822 }
823 } else if (isUplink(devId, port)) {
824 if (event.port().isEnabled()) {
825 provisionUplinkFlows(devId);
826 } else {
827 processFilterObjective(devId, port, true);
828 }
829 } else if (isConnectPoint(devId, port)) {
830 if (event.port().isEnabled()) {
831 provisionConnectPointFlows();
832 } else {
833 unprovisionConnectPointFlows();
834 }
835 }
836 break;
837 case PORT_REMOVED:
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +0000838 port = p.number();
ke han81a38b92017-03-10 18:41:44 +0800839 processFilterObjective(devId, port, true);
840 break;
841 default:
842 log.info("Unknown device event {}", event.type());
843 break;
844 }
845 }
846
847 @Override
848 public boolean isRelevant(DeviceEvent event) {
849 return true;
850 }
851 }
852
853 private class InternalNetworkConfigListener implements NetworkConfigListener {
854
855 private void reconfigureNetwork(IgmpproxyConfig cfg) {
Esin Karamaneff10392019-06-27 18:09:13 +0000856 IgmpproxyConfig newCfg = cfg == null ? new IgmpproxyConfig() : cfg;
ke han81a38b92017-03-10 18:41:44 +0800857
858 unSolicitedTimeout = newCfg.unsolicitedTimeOut();
859 maxResp = newCfg.maxResp();
860 keepAliveInterval = newCfg.keepAliveInterval();
861 keepAliveCount = newCfg.keepAliveCount();
862 lastQueryInterval = newCfg.lastQueryInterval();
863 lastQueryCount = newCfg.lastQueryCount();
864 withRAUplink = newCfg.withRAUplink();
865 withRADownlink = newCfg.withRADownlink();
866 igmpCos = newCfg.igmpCos();
867 periodicQuery = newCfg.periodicQuery();
868 fastLeave = newCfg.fastLeave();
ke han29af27b2017-09-08 10:29:12 +0800869 pimSSmInterworking = newCfg.pimSsmInterworking();
Esin Karamaneff10392019-06-27 18:09:13 +0000870 enableIgmpProvisioning = newCfg.enableIgmpProvisioning();
Esin Karamanb38700c2019-09-17 13:01:25 +0000871 igmpOnPodBasis = newCfg.igmpOnPodBasis();
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +0000872
873 if (connectPointMode != newCfg.connectPointMode() ||
874 connectPoint != newCfg.connectPoint()) {
ke han81a38b92017-03-10 18:41:44 +0800875 connectPointMode = newCfg.connectPointMode();
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +0000876 connectPoint = newCfg.connectPoint();
ke han81a38b92017-03-10 18:41:44 +0800877 if (connectPointMode) {
878 unprovisionUplinkFlows();
879 provisionConnectPointFlows();
880 } else {
881 unprovisionConnectPointFlows();
882 provisionUplinkFlows();
883 }
884 }
885 if (connectPoint != null) {
Esin Karamaneff10392019-06-27 18:09:13 +0000886 log.info("connect point : {}", connectPoint);
ke han81a38b92017-03-10 18:41:44 +0800887 }
Esin Karamaneff10392019-06-27 18:09:13 +0000888 log.info("mode: {}", connectPointMode);
889
890 getSourceConnectPoint(newCfg);
ke han81a38b92017-03-10 18:41:44 +0800891
892 IgmpSender.getInstance().setIgmpCos(igmpCos);
893 IgmpSender.getInstance().setMaxResp(maxResp);
894 IgmpSender.getInstance().setMvlan(mvlan);
895 IgmpSender.getInstance().setWithRADownlink(withRADownlink);
896 IgmpSender.getInstance().setWithRAUplink(withRAUplink);
Esin Karamaneff10392019-06-27 18:09:13 +0000897 }
ke han81a38b92017-03-10 18:41:44 +0800898
Esin Karamaneff10392019-06-27 18:09:13 +0000899 void getSourceConnectPoint(IgmpproxyConfig cfg) {
900 sourceDeviceAndPort = cfg.getSourceDeviceAndPort();
901 if (sourceDeviceAndPort != null) {
902 log.debug("source parameter configured to {}", sourceDeviceAndPort);
903 }
ke han81a38b92017-03-10 18:41:44 +0800904 }
905
906 public void reconfigureSsmTable(IgmpproxySsmTranslateConfig cfg) {
907 if (cfg == null) {
908 return;
909 }
910 Collection<McastRoute> translations = cfg.getSsmTranslations();
911 for (McastRoute route : translations) {
Esin Karamaneff10392019-06-27 18:09:13 +0000912 ssmTranslateTable.put(route.group().getIp4Address(), route.source().get().getIp4Address());
ke han81a38b92017-03-10 18:41:44 +0800913 }
914 }
915
916 @Override
917 public void event(NetworkConfigEvent event) {
918 switch (event.type()) {
919 case CONFIG_ADDED:
920 case CONFIG_UPDATED:
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800921 // NOTE how to know if something has changed in sadis?
ke han81a38b92017-03-10 18:41:44 +0800922
923 if (event.configClass().equals(IGMPPROXY_CONFIG_CLASS)) {
924 IgmpproxyConfig config = networkConfig.getConfig(appId, IGMPPROXY_CONFIG_CLASS);
925 if (config != null) {
Esin Karamaneff10392019-06-27 18:09:13 +0000926 log.info("igmpproxy config received. {}", config);
ke han81a38b92017-03-10 18:41:44 +0800927 reconfigureNetwork(config);
928 }
929 }
930
931 if (event.configClass().equals(IGMPPROXY_SSM_CONFIG_CLASS)) {
932 IgmpproxySsmTranslateConfig config = networkConfig.getConfig(appId, IGMPPROXY_SSM_CONFIG_CLASS);
933 if (config != null) {
934 reconfigureSsmTable(config);
935 }
936 }
937
938 if (event.configClass().equals(MCAST_CONFIG_CLASS)) {
939 McastConfig config = networkConfig.getConfig(coreAppId, MCAST_CONFIG_CLASS);
940 if (config != null && mvlan != config.egressVlan().toShort()) {
941 mvlan = config.egressVlan().toShort();
Deepa Vaddireddy88134a42017-07-05 12:16:03 +0530942 IgmpSender.getInstance().setMvlan(mvlan);
ke han81a38b92017-03-10 18:41:44 +0800943 groupMemberMap.values().forEach(m -> leaveAction(m));
944 }
945 }
946
947 log.info("Reconfigured");
948 break;
949 case CONFIG_REGISTERED:
950 case CONFIG_UNREGISTERED:
951 break;
952 case CONFIG_REMOVED:
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800953 // NOTE how to know if something has changed in sadis?
ke han81a38b92017-03-10 18:41:44 +0800954 default:
955 break;
956 }
957 }
958 }
959
ke han81a38b92017-03-10 18:41:44 +0800960 private void provisionUplinkFlows(DeviceId deviceId) {
961 if (connectPointMode) {
962 return;
963 }
964
Esin Karaman00e16b72020-02-21 10:32:39 +0000965 Optional<PortNumber> upLink = getDeviceUplink(deviceId);
966 if (upLink.isPresent()) {
967 processFilterObjective(deviceId, upLink.get(), false);
968 }
ke han81a38b92017-03-10 18:41:44 +0800969 }
970
971 private void provisionUplinkFlows() {
972 if (connectPointMode) {
973 return;
974 }
Esin Karaman00e16b72020-02-21 10:32:39 +0000975 deviceService.getAvailableDevices().forEach(device -> {
976 Optional<SubscriberAndDeviceInformation> accessDevice = getSubscriberAndDeviceInformation(device.id());
977 if (accessDevice.isPresent()) {
978 provisionUplinkFlows(device.id());
979 }
980 });
ke han81a38b92017-03-10 18:41:44 +0800981 }
Esin Karaman00e16b72020-02-21 10:32:39 +0000982
ke han81a38b92017-03-10 18:41:44 +0800983 private void unprovisionUplinkFlows() {
Esin Karaman00e16b72020-02-21 10:32:39 +0000984 deviceService.getAvailableDevices().forEach(device -> {
985 Optional<SubscriberAndDeviceInformation> accessDevices = getSubscriberAndDeviceInformation(device.id());
986 if (accessDevices.isPresent()) {
987 Optional<PortNumber> upLink = getDeviceUplink(device.id());
988 if (upLink.isPresent()) {
989 processFilterObjective(device.id(), upLink.get(), true);
990 }
991 }
992 });
ke han81a38b92017-03-10 18:41:44 +0800993 }
994
995 private void provisionConnectPointFlows() {
996 if ((!connectPointMode) || connectPoint == null) {
997 return;
998 }
999
1000 processFilterObjective(connectPoint.deviceId(), connectPoint.port(), false);
1001 }
1002 private void unprovisionConnectPointFlows() {
1003 if (connectPoint == null) {
1004 return;
1005 }
1006 processFilterObjective(connectPoint.deviceId(), connectPoint.port(), true);
1007 }
Shubham Sharma47f2caf2020-02-18 12:13:40 +00001008
ke han81a38b92017-03-10 18:41:44 +08001009}