blob: 11e28e5966b8878262d900bd9b38b681904a65de [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;
Esin Karamaneff10392019-06-27 18:09:13 +0000135
136 private static final Integer MAX_PRIORITY = 10000;
137 private static final String INSTALLED = "installed";
138 private static final String REMOVED = "removed";
139 private static final String INSTALLATION = "installation";
140 private static final String REMOVAL = "removal";
Esin Karaman00e16b72020-02-21 10:32:39 +0000141 private static final String NNI_PREFIX = "nni";
ke han81a38b92017-03-10 18:41:44 +0800142
ke han29af27b2017-09-08 10:29:12 +0800143 private static boolean pimSSmInterworking = false;
144 private static final String DEFAULT_PIMSSM_HOST = "127.0.0.1";
ke han81a38b92017-03-10 18:41:44 +0800145 private final ScheduledExecutorService scheduledExecutorService =
146 Executors.newScheduledThreadPool(1);
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800147
Carmelo Casconebef302e2019-11-14 19:58:20 -0800148 @Reference(cardinality = ReferenceCardinality.MANDATORY)
ke han81a38b92017-03-10 18:41:44 +0800149 protected CoreService coreService;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800150
Carmelo Casconebef302e2019-11-14 19:58:20 -0800151 @Reference(cardinality = ReferenceCardinality.MANDATORY)
ke han81a38b92017-03-10 18:41:44 +0800152 protected PacketService packetService;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800153
Carmelo Casconebef302e2019-11-14 19:58:20 -0800154 @Reference(cardinality = ReferenceCardinality.MANDATORY)
ke han81a38b92017-03-10 18:41:44 +0800155 protected MastershipService mastershipService;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800156
Carmelo Casconebef302e2019-11-14 19:58:20 -0800157 @Reference(cardinality = ReferenceCardinality.MANDATORY)
ke han81a38b92017-03-10 18:41:44 +0800158 protected FlowRuleService flowRuleService;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800159
Carmelo Casconebef302e2019-11-14 19:58:20 -0800160 @Reference(cardinality = ReferenceCardinality.MANDATORY)
ke han81a38b92017-03-10 18:41:44 +0800161 protected DeviceService deviceService;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800162
Carmelo Casconebef302e2019-11-14 19:58:20 -0800163 @Reference(cardinality = ReferenceCardinality.MANDATORY)
ke han81a38b92017-03-10 18:41:44 +0800164 protected FlowObjectiveService flowObjectiveService;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800165
Carmelo Casconebef302e2019-11-14 19:58:20 -0800166 @Reference(cardinality = ReferenceCardinality.MANDATORY)
ke han81a38b92017-03-10 18:41:44 +0800167 protected NetworkConfigRegistry networkConfig;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800168
Carmelo Casconebef302e2019-11-14 19:58:20 -0800169 @Reference(cardinality = ReferenceCardinality.MANDATORY)
ke han81a38b92017-03-10 18:41:44 +0800170 protected MulticastRouteService multicastService;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800171
172 @Reference(cardinality = ReferenceCardinality.MANDATORY)
173 protected SadisService sadisService;
174
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000175 @Reference(cardinality = ReferenceCardinality.MANDATORY)
176 protected IgmpStatisticsService igmpStatisticsManager;
177
ke han81a38b92017-03-10 18:41:44 +0800178 private IgmpPacketProcessor processor = new IgmpPacketProcessor();
179 private Logger log = LoggerFactory.getLogger(getClass());
180 private ApplicationId coreAppId;
181 private Map<Ip4Address, Ip4Address> ssmTranslateTable = new ConcurrentHashMap<>();
Esin Karamaneff10392019-06-27 18:09:13 +0000182
ke han81a38b92017-03-10 18:41:44 +0800183 private InternalNetworkConfigListener configListener =
184 new InternalNetworkConfigListener();
185 private DeviceListener deviceListener = new InternalDeviceListener();
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800186
ke han81a38b92017-03-10 18:41:44 +0800187 private ConfigFactory<ApplicationId, IgmpproxyConfig> igmpproxyConfigFactory =
188 new ConfigFactory<ApplicationId, IgmpproxyConfig>(
189 SubjectFactories.APP_SUBJECT_FACTORY, IGMPPROXY_CONFIG_CLASS, "igmpproxy") {
190 @Override
191 public IgmpproxyConfig createConfig() {
192 return new IgmpproxyConfig();
193 }
194 };
195 private ConfigFactory<ApplicationId, IgmpproxySsmTranslateConfig> igmpproxySsmConfigFactory =
196 new ConfigFactory<ApplicationId, IgmpproxySsmTranslateConfig>(
197 SubjectFactories.APP_SUBJECT_FACTORY, IGMPPROXY_SSM_CONFIG_CLASS, "ssmTranslate", true) {
198 @Override
199 public IgmpproxySsmTranslateConfig createConfig() {
200 return new IgmpproxySsmTranslateConfig();
201 }
202 };
Esin Karamaneff10392019-06-27 18:09:13 +0000203
ke han81a38b92017-03-10 18:41:44 +0800204 private int maxResp = 10; //unit is 1 sec
205 private int keepAliveInterval = 120; //unit is 1 sec
206
Esin Karamanb38700c2019-09-17 13:01:25 +0000207 private ExecutorService eventExecutor;
208
ke han81a38b92017-03-10 18:41:44 +0800209 public static int getUnsolicitedTimeout() {
210 return unSolicitedTimeout;
211 }
212
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800213 protected BaseInformationService<SubscriberAndDeviceInformation> subsService;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800214
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000215 private List<Byte> validMembershipModes = Arrays.asList(MODE_IS_INCLUDE, MODE_IS_EXCLUDE, CHANGE_TO_INCLUDE_MODE,
216 CHANGE_TO_EXCLUDE_MODE, ALLOW_NEW_SOURCES, BLOCK_OLD_SOURCES);
217
ke han81a38b92017-03-10 18:41:44 +0800218 @Activate
219 protected void activate() {
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800220 appId = coreService.registerApplication(APP_NAME);
ke han81a38b92017-03-10 18:41:44 +0800221 coreAppId = coreService.registerApplication(CoreService.CORE_APP_NAME);
222 packetService.addProcessor(processor, PacketProcessor.director(4));
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000223 IgmpSender.init(packetService, mastershipService, igmpStatisticsManager);
ke han81a38b92017-03-10 18:41:44 +0800224
ke han81a38b92017-03-10 18:41:44 +0800225 networkConfig.registerConfigFactory(igmpproxySsmConfigFactory);
226 networkConfig.registerConfigFactory(igmpproxyConfigFactory);
227 networkConfig.addListener(configListener);
228
229 configListener.reconfigureNetwork(networkConfig.getConfig(appId, IGMPPROXY_CONFIG_CLASS));
230 configListener.reconfigureSsmTable(networkConfig.getConfig(appId, IGMPPROXY_SSM_CONFIG_CLASS));
231
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800232 subsService = sadisService.getSubscriberInfoService();
ke han81a38b92017-03-10 18:41:44 +0800233 if (connectPointMode) {
234 provisionConnectPointFlows();
235 } else {
236 provisionUplinkFlows();
237 }
238
239 McastConfig config = networkConfig.getConfig(coreAppId, MCAST_CONFIG_CLASS);
240 if (config != null) {
241 mvlan = config.egressVlan().toShort();
Deepa Vaddireddy88134a42017-07-05 12:16:03 +0530242 IgmpSender.getInstance().setMvlan(mvlan);
ke han81a38b92017-03-10 18:41:44 +0800243 }
244 deviceService.addListener(deviceListener);
Sonal Kasliwalddc3ff22019-11-18 11:52:49 +0000245 scheduledExecutorService.scheduleAtFixedRate(new IgmpProxyTimerTask(), 1000, 1000, TimeUnit.MILLISECONDS);
Esin Karamanb38700c2019-09-17 13:01:25 +0000246 eventExecutor = newSingleThreadScheduledExecutor(groupedThreads("cord/igmpproxy",
247 "events-igmp-%d", log));
ke han81a38b92017-03-10 18:41:44 +0800248 log.info("Started");
249 }
250
251 @Deactivate
252 protected void deactivate() {
253 scheduledExecutorService.shutdown();
Esin Karamanb38700c2019-09-17 13:01:25 +0000254 eventExecutor.shutdown();
ke han81a38b92017-03-10 18:41:44 +0800255
256 // de-register and null our handler
257 networkConfig.removeListener(configListener);
ke han81a38b92017-03-10 18:41:44 +0800258 networkConfig.unregisterConfigFactory(igmpproxyConfigFactory);
259 networkConfig.unregisterConfigFactory(igmpproxySsmConfigFactory);
260 deviceService.removeListener(deviceListener);
261 packetService.removeProcessor(processor);
262 flowRuleService.removeFlowRulesById(appId);
ke han81a38b92017-03-10 18:41:44 +0800263 log.info("Stopped");
264 }
265
266 protected Ip4Address getDeviceIp(DeviceId ofDeviceId) {
267 try {
268 String[] mgmtAddress = deviceService.getDevice(ofDeviceId)
269 .annotations().value(AnnotationKeys.MANAGEMENT_ADDRESS).split(":");
270 return Ip4Address.valueOf(mgmtAddress[0]);
271 } catch (Exception ex) {
272 log.info("No valid Ipaddress for " + ofDeviceId.toString());
273 return null;
274 }
275 }
276
277 private void processIgmpQuery(IGMPQuery igmpQuery, ConnectPoint cp, int maxResp) {
278
279 DeviceId deviceId = cp.deviceId();
280 Ip4Address gAddr = igmpQuery.getGaddr().getIp4Address();
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000281 maxResp = calculateMaxResp(maxResp);
282 if (gAddr != null && !gAddr.isZero()) {
283 StateMachine.specialQuery(deviceId, gAddr, maxResp);
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000284 igmpStatisticsManager.getIgmpStats().increaseIgmpGrpSpecificMembershipQuery();
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000285 } else {
286 StateMachine.generalQuery(deviceId, maxResp);
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000287 igmpStatisticsManager.getIgmpStats().increaseIgmpGeneralMembershipQuery();
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000288 }
289 }
ke han81a38b92017-03-10 18:41:44 +0800290
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000291 private void processIgmpConnectPointQuery(IGMPQuery igmpQuery, ConnectPoint cp, int maxResp) {
292
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000293 Ip4Address gAddr = igmpQuery.getGaddr().getIp4Address();
Esin Karaman00e16b72020-02-21 10:32:39 +0000294 final int maxResponseTime = calculateMaxResp(maxResp);
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000295 //The query is received on the ConnectPoint
296 // send query accordingly to the registered OLT devices.
297 if (gAddr != null && !gAddr.isZero()) {
Esin Karaman00e16b72020-02-21 10:32:39 +0000298 deviceService.getAvailableDevices().forEach(device -> {
299 Optional<SubscriberAndDeviceInformation> accessDevice = getSubscriberAndDeviceInformation(device.id());
300 if (accessDevice.isPresent()) {
301 StateMachine.specialQuery(device.id(), gAddr, maxResponseTime);
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000302 igmpStatisticsManager.getIgmpStats().increaseIgmpGrpAndSrcSpecificMembershipQuery();
Esin Karaman00e16b72020-02-21 10:32:39 +0000303 }
304 });
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000305 igmpStatisticsManager.getIgmpStats().increaseCurrentGrpNumCounter();
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000306 } else {
307 //Don't know which group is targeted by the query
308 //So query all the members(in all the OLTs) and proxy their reports
Esin Karaman00e16b72020-02-21 10:32:39 +0000309 StateMachine.generalQuery(maxResponseTime);
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000310 igmpStatisticsManager.getIgmpStats().increaseIgmpGeneralMembershipQuery();
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000311 }
312 }
313
314
315 private int calculateMaxResp(int maxResp) {
ke han81a38b92017-03-10 18:41:44 +0800316 if (maxResp >= 128) {
317 int mant = maxResp & 0xf;
318 int exp = (maxResp >> 4) & 0x7;
319 maxResp = (mant | 0x10) << (exp + 3);
320 }
321
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000322 return (maxResp + 5) / 10;
ke han81a38b92017-03-10 18:41:44 +0800323 }
324
325 private Ip4Address ssmTranslateRoute(IpAddress group) {
326 return ssmTranslateTable.get(group);
327 }
328
329 private void processIgmpReport(IGMPMembership igmpGroup, VlanId vlan, ConnectPoint cp, byte igmpType) {
330 DeviceId deviceId = cp.deviceId();
331 PortNumber portNumber = cp.port();
332
333 Ip4Address groupIp = igmpGroup.getGaddr().getIp4Address();
334 if (!groupIp.isMulticast()) {
335 log.info(groupIp.toString() + " is not a valid group address");
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000336 igmpStatisticsManager.getIgmpStats().increaseFailJoinReqUnknownMulticastIpCounter();
ke han81a38b92017-03-10 18:41:44 +0800337 return;
338 }
339 Ip4Address srcIp = getDeviceIp(deviceId);
340
341 byte recordType = igmpGroup.getRecordType();
342 boolean join = false;
343
344 ArrayList<Ip4Address> sourceList = new ArrayList<>();
345
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000346 if (!validMembershipModes.contains(recordType)) {
347 igmpStatisticsManager.getIgmpStats().increaseReportsRxWithWrongModeCounter();
348 }
ke han81a38b92017-03-10 18:41:44 +0800349 if (igmpGroup.getSources().size() > 0) {
350 igmpGroup.getSources().forEach(source -> sourceList.add(source.getIp4Address()));
351 if (recordType == IGMPMembership.CHANGE_TO_EXCLUDE_MODE ||
352 recordType == IGMPMembership.MODE_IS_EXCLUDE ||
353 recordType == IGMPMembership.BLOCK_OLD_SOURCES) {
354 join = false;
355 } else if (recordType == IGMPMembership.CHANGE_TO_INCLUDE_MODE ||
356 recordType == IGMPMembership.MODE_IS_INCLUDE ||
357 recordType == IGMPMembership.ALLOW_NEW_SOURCES) {
358 join = true;
359 }
360 } else {
ke han29af27b2017-09-08 10:29:12 +0800361 IpAddress src = null;
362 if (pimSSmInterworking) {
363 src = ssmTranslateRoute(groupIp);
364 if (src == null) {
365 log.info("no ssm translate for group " + groupIp.toString());
366 return;
367 }
368 } else {
369 src = IpAddress.valueOf(DEFAULT_PIMSSM_HOST);
ke han81a38b92017-03-10 18:41:44 +0800370 }
371 sourceList.add(src.getIp4Address());
372 if (recordType == IGMPMembership.CHANGE_TO_EXCLUDE_MODE ||
373 recordType == IGMPMembership.MODE_IS_EXCLUDE ||
374 recordType == IGMPMembership.BLOCK_OLD_SOURCES) {
375 join = true;
376 } else if (recordType == IGMPMembership.CHANGE_TO_INCLUDE_MODE ||
377 recordType == IGMPMembership.MODE_IS_INCLUDE ||
378 recordType == IGMPMembership.ALLOW_NEW_SOURCES) {
379 join = false;
380 }
381 }
382 String groupMemberKey = GroupMember.getkey(groupIp, deviceId, portNumber);
383 GroupMember groupMember = groupMemberMap.get(groupMemberKey);
384
385 if (join) {
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000386 igmpStatisticsManager.getIgmpStats().increaseIgmpJoinReq();
ke han81a38b92017-03-10 18:41:44 +0800387 if (groupMember == null) {
Esin Karamaneff10392019-06-27 18:09:13 +0000388 Optional<ConnectPoint> sourceConfigured = getSource();
389 if (!sourceConfigured.isPresent()) {
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000390 igmpStatisticsManager.getIgmpStats().increaseIgmpFailJoinReq();
Esin Karamaneff10392019-06-27 18:09:13 +0000391 log.warn("Unable to process IGMP Join from {} since no source " +
392 "configuration is found.", deviceId);
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000393 igmpStatisticsManager.getIgmpStats().increaseFailJoinReqInsuffPermissionAccessCounter();
Esin Karamaneff10392019-06-27 18:09:13 +0000394 return;
395 }
Esin Karaman00e16b72020-02-21 10:32:39 +0000396
397 Optional<PortNumber> deviceUplink = getDeviceUplink(deviceId);
398 if (deviceUplink.isEmpty()) {
399 log.warn("Unable to process IGMP Join since uplink port " +
400 "of the device {} is not found.", deviceId);
401 return;
402 }
403
404 if (igmpType == IGMP.TYPE_IGMPV2_MEMBERSHIP_REPORT) {
405 groupMember = new GroupMember(groupIp, vlan, deviceId, portNumber, true);
406 } else {
407 groupMember = new GroupMember(groupIp, vlan, deviceId, portNumber, false);
408 }
409
Esin Karamaneff10392019-06-27 18:09:13 +0000410 HashSet<ConnectPoint> sourceConnectPoints = Sets.newHashSet(sourceConfigured.get());
411
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000412 boolean isJoined = StateMachine.join(deviceId, groupIp, srcIp, deviceUplink.get());
413 if (isJoined) {
414 igmpStatisticsManager.getIgmpStats().increaseIgmpSuccessJoinRejoinReq();
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000415 igmpStatisticsManager.getIgmpStats().increaseIgmpChannelJoinCounter();
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000416 } else {
417 igmpStatisticsManager.getIgmpStats().increaseIgmpFailJoinReq();
418 }
ke han81a38b92017-03-10 18:41:44 +0800419 groupMemberMap.put(groupMemberKey, groupMember);
420 groupMember.updateList(recordType, sourceList);
Esin Karamaneff10392019-06-27 18:09:13 +0000421 groupMember.getSourceList().forEach(source -> {
422 McastRoute route = new McastRoute(source, groupIp, McastRoute.Type.IGMP);
423 //add route
424 multicastService.add(route);
425 //add source to the route
426 multicastService.addSources(route, Sets.newHashSet(sourceConnectPoints));
427 //add sink to the route
428 multicastService.addSinks(route, Sets.newHashSet(cp));
429 });
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000430 igmpStatisticsManager.getIgmpStats().increaseUnconfiguredGroupCounter();
Esin Karamaneff10392019-06-27 18:09:13 +0000431
ke han81a38b92017-03-10 18:41:44 +0800432 }
433 groupMember.resetAllTimers();
434 groupMember.updateList(recordType, sourceList);
435 groupMember.setLeave(false);
436 } else {
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000437 igmpStatisticsManager.getIgmpStats().increaseIgmpLeaveReq();
ke han81a38b92017-03-10 18:41:44 +0800438 if (groupMember == null) {
439 log.info("receive leave but no instance, group " + groupIp.toString() +
440 " device:" + deviceId.toString() + " port:" + portNumber.toString());
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000441 igmpStatisticsManager.getIgmpStats().increaseUnconfiguredGroupCounter();
ke han81a38b92017-03-10 18:41:44 +0800442 return;
443 } else {
444 groupMember.setLeave(true);
445 if (fastLeave) {
446 leaveAction(groupMember);
447 } else {
448 sendQuery(groupMember);
449 }
450 }
451 }
452 }
453
454 private void leaveAction(GroupMember groupMember) {
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000455 igmpStatisticsManager.getIgmpStats().increaseIgmpDisconnect();
ke han81a38b92017-03-10 18:41:44 +0800456 ConnectPoint cp = new ConnectPoint(groupMember.getDeviceId(), groupMember.getPortNumber());
457 StateMachine.leave(groupMember.getDeviceId(), groupMember.getGroupIp());
Esin Karamaneff10392019-06-27 18:09:13 +0000458 groupMember.getSourceList().forEach(source -> multicastService.removeSinks(
ke han81a38b92017-03-10 18:41:44 +0800459 new McastRoute(source, groupMember.getGroupIp(),
Esin Karamaneff10392019-06-27 18:09:13 +0000460 McastRoute.Type.IGMP), Sets.newHashSet(cp)));
ke han81a38b92017-03-10 18:41:44 +0800461 groupMemberMap.remove(groupMember.getId());
462 }
463
464 private void sendQuery(GroupMember groupMember) {
465 Ethernet ethpkt;
466 Ip4Address srcIp = getDeviceIp(groupMember.getDeviceId());
467 if (groupMember.getv2()) {
468 ethpkt = IgmpSender.getInstance().buildIgmpV2Query(groupMember.getGroupIp(), srcIp);
469 } else {
470 ethpkt = IgmpSender.getInstance().buildIgmpV3Query(groupMember.getGroupIp(), srcIp);
471 }
472 IgmpSender.getInstance().sendIgmpPacket(ethpkt, groupMember.getDeviceId(), groupMember.getPortNumber());
473 }
474
Esin Karamaneff10392019-06-27 18:09:13 +0000475 /**
476 * @return connect point of the source if configured; and empty Optional otherwise.
477 */
478 public static Optional<ConnectPoint> getSource() {
479 return sourceDeviceAndPort == null ? Optional.empty() :
480 Optional.of(sourceDeviceAndPort);
ke han81a38b92017-03-10 18:41:44 +0800481 }
482
483 /**
484 * Packet processor responsible for forwarding packets along their paths.
485 */
486 private class IgmpPacketProcessor implements PacketProcessor {
487 @Override
488 public void process(PacketContext context) {
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000489
Esin Karamanb38700c2019-09-17 13:01:25 +0000490 eventExecutor.execute(() -> {
491 try {
492 InboundPacket pkt = context.inPacket();
493 Ethernet ethPkt = pkt.parsed();
494 if (ethPkt == null) {
495 return;
496 }
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000497 igmpStatisticsManager.getIgmpStats().increaseTotalMsgReceived();
ke han81a38b92017-03-10 18:41:44 +0800498
Esin Karamanb38700c2019-09-17 13:01:25 +0000499 if (ethPkt.getEtherType() != Ethernet.TYPE_IPV4) {
500 return;
501 }
ke han81a38b92017-03-10 18:41:44 +0800502
Esin Karamanb38700c2019-09-17 13:01:25 +0000503 IPv4 ipv4Pkt = (IPv4) ethPkt.getPayload();
ke han81a38b92017-03-10 18:41:44 +0800504
Esin Karamanb38700c2019-09-17 13:01:25 +0000505 if (ipv4Pkt.getProtocol() != IPv4.PROTOCOL_IGMP) {
506 return;
507 }
ke han81a38b92017-03-10 18:41:44 +0800508
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000509 igmpStatisticsManager.getIgmpStats().increaseIgmpValidChecksumCounter();
Esin Karamanb38700c2019-09-17 13:01:25 +0000510 short vlan = ethPkt.getVlanID();
511 DeviceId deviceId = pkt.receivedFrom().deviceId();
ke han81a38b92017-03-10 18:41:44 +0800512
Esin Karaman00e16b72020-02-21 10:32:39 +0000513 if (!isConnectPoint(deviceId, pkt.receivedFrom().port()) &&
514 !getSubscriberAndDeviceInformation(deviceId).isPresent()) {
Esin Karamanb38700c2019-09-17 13:01:25 +0000515 log.error("Device not registered in netcfg :" + deviceId.toString());
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000516 igmpStatisticsManager.getIgmpStats().increaseFailJoinReqInsuffPermissionAccessCounter();
Esin Karamanb38700c2019-09-17 13:01:25 +0000517 return;
518 }
ke han81a38b92017-03-10 18:41:44 +0800519
Esin Karamanb38700c2019-09-17 13:01:25 +0000520 IGMP igmp = (IGMP) ipv4Pkt.getPayload();
Esin Karamance5ce512020-02-25 15:58:14 +0000521
522 Optional<PortNumber> deviceUpLinkOpt = getDeviceUplink(deviceId);
523 PortNumber upLinkPort = deviceUpLinkOpt.isPresent() ? deviceUpLinkOpt.get() : null;
Esin Karamanb38700c2019-09-17 13:01:25 +0000524 switch (igmp.getIgmpType()) {
525 case IGMP.TYPE_IGMPV3_MEMBERSHIP_QUERY:
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000526 igmpStatisticsManager.getIgmpStats().increaseIgmpv3MembershipQuery();
Esin Karamanb38700c2019-09-17 13:01:25 +0000527 //Discard Query from OLT’s non-uplink port’s
Esin Karamance5ce512020-02-25 15:58:14 +0000528 if (!pkt.receivedFrom().port().equals(upLinkPort)) {
Esin Karamanb38700c2019-09-17 13:01:25 +0000529 if (isConnectPoint(deviceId, pkt.receivedFrom().port())) {
530 log.info("IGMP Picked up query from connectPoint");
531 //OK to process packet
532 processIgmpConnectPointQuery((IGMPQuery) igmp.getGroups().get(0),
533 pkt.receivedFrom(),
534 0xff & igmp.getMaxRespField());
535 break;
536 } else {
537 //Not OK to process packet
Esin Karamance5ce512020-02-25 15:58:14 +0000538 log.warn("IGMP Picked up query from non-uplink port {}", upLinkPort);
Esin Karamanb38700c2019-09-17 13:01:25 +0000539 return;
540 }
541 }
ke han81a38b92017-03-10 18:41:44 +0800542
Esin Karamanb38700c2019-09-17 13:01:25 +0000543 processIgmpQuery((IGMPQuery) igmp.getGroups().get(0), pkt.receivedFrom(),
544 0xff & igmp.getMaxRespField());
545 break;
546 case IGMP.TYPE_IGMPV1_MEMBERSHIP_REPORT:
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000547 igmpStatisticsManager.getIgmpStats().increaseIgmpv1MembershipReport();
Esin Karamanb38700c2019-09-17 13:01:25 +0000548 log.debug("IGMP version 1 message types are not currently supported.");
549 break;
550 case IGMP.TYPE_IGMPV3_MEMBERSHIP_REPORT:
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000551 igmpStatisticsManager.getIgmpStats().increaseIgmpv3MembershipReport();
552 processIgmpMessage(pkt, igmp, upLinkPort, vlan);
553 break;
Esin Karamanb38700c2019-09-17 13:01:25 +0000554 case IGMP.TYPE_IGMPV2_MEMBERSHIP_REPORT:
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000555 igmpStatisticsManager.getIgmpStats().increaseIgmpv2MembershipReport();
556 processIgmpMessage(pkt, igmp, upLinkPort, vlan);
557 break;
Esin Karamanb38700c2019-09-17 13:01:25 +0000558 case IGMP.TYPE_IGMPV2_LEAVE_GROUP:
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000559 igmpStatisticsManager.getIgmpStats().increaseIgmpv2LeaveGroup();
560 processIgmpMessage(pkt, igmp, upLinkPort, vlan);
Esin Karamanb38700c2019-09-17 13:01:25 +0000561 break;
ke han81a38b92017-03-10 18:41:44 +0800562
Esin Karamanb38700c2019-09-17 13:01:25 +0000563 default:
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000564 log.warn("wrong IGMP v3 type:" + igmp.getIgmpType());
565 igmpStatisticsManager.getIgmpStats().increaseInvalidIgmpMsgReceived();
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000566 igmpStatisticsManager.getIgmpStats().increaseUnknownIgmpTypePacketsRxCounter();
Esin Karamanb38700c2019-09-17 13:01:25 +0000567 break;
568 }
569
570 } catch (Exception ex) {
571 log.error("igmp process error : {} ", ex);
ke han81a38b92017-03-10 18:41:44 +0800572 }
Esin Karamanb38700c2019-09-17 13:01:25 +0000573 });
ke han81a38b92017-03-10 18:41:44 +0800574 }
575 }
576
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000577 private void processIgmpMessage(InboundPacket pkt, IGMP igmp, PortNumber upLinkPort, short vlan) {
578 //Discard join/leave from OLT’s uplink port’s
579 if (pkt.receivedFrom().port().equals(upLinkPort) ||
580 isConnectPoint(pkt.receivedFrom().deviceId(), pkt.receivedFrom().port())) {
581 log.info("IGMP Picked up join/leave from uplink/connectPoint port");
582 return;
583 }
584
585 Iterator<IGMPGroup> itr = igmp.getGroups().iterator();
586 while (itr.hasNext()) {
587 IGMPGroup group = itr.next();
588 if (group instanceof IGMPMembership) {
589 processIgmpReport((IGMPMembership) group, VlanId.vlanId(vlan),
590 pkt.receivedFrom(), igmp.getIgmpType());
591 } else {
592 IGMPMembership mgroup = new IGMPMembership(group.getGaddr().getIp4Address());
593 mgroup.setRecordType(igmp.getIgmpType() == IGMP.TYPE_IGMPV2_MEMBERSHIP_REPORT ?
594 IGMPMembership.MODE_IS_EXCLUDE :
595 IGMPMembership.MODE_IS_INCLUDE);
596 processIgmpReport(mgroup, VlanId.vlanId(vlan),
597 pkt.receivedFrom(), igmp.getIgmpType());
598 }
599 }
600
601 }
602
ke han81a38b92017-03-10 18:41:44 +0800603 private class IgmpProxyTimerTask extends TimerTask {
604 public void run() {
605 try {
606 IgmpTimer.timeOut1s();
607 queryMembers();
608 } catch (Exception ex) {
609 log.warn("Igmp timer task error : {}", ex.getMessage());
610 }
611 }
612
613 private void queryMembers() {
614 GroupMember groupMember;
615 Set groupMemberSet = groupMemberMap.entrySet();
616 Iterator itr = groupMemberSet.iterator();
617 while (itr.hasNext()) {
618 Map.Entry entry = (Map.Entry) itr.next();
619 groupMember = (GroupMember) entry.getValue();
620 DeviceId did = groupMember.getDeviceId();
621 if (mastershipService.isLocalMaster(did)) {
622 if (groupMember.isLeave()) {
623 lastQuery(groupMember);
624 } else if (periodicQuery) {
625 periodicQuery(groupMember);
626 }
627 }
628 }
629 }
630
631 private void lastQuery(GroupMember groupMember) {
632 if (groupMember.getLastQueryInterval() < lastQueryInterval) {
633 groupMember.lastQueryInterval(true); // count times
634 } else if (groupMember.getLastQueryCount() < lastQueryCount - 1) {
635 sendQuery(groupMember);
636 groupMember.lastQueryInterval(false); // reset count number
637 groupMember.lastQueryCount(true); //count times
638 } else if (groupMember.getLastQueryCount() == lastQueryCount - 1) {
639 leaveAction(groupMember);
640 }
641 }
642
643 private void periodicQuery(GroupMember groupMember) {
644 if (groupMember.getKeepAliveQueryInterval() < keepAliveInterval) {
645 groupMember.keepAliveInterval(true);
646 } else if (groupMember.getKeepAliveQueryCount() < keepAliveCount) {
647 sendQuery(groupMember);
648 groupMember.keepAliveInterval(false);
649 groupMember.keepAliveQueryCount(true);
650 } else if (groupMember.getKeepAliveQueryCount() == keepAliveCount) {
651 leaveAction(groupMember);
652 }
653 }
654
655 }
656
Esin Karaman00e16b72020-02-21 10:32:39 +0000657 public Optional<PortNumber> getDeviceUplink(DeviceId devId) {
658 Device device = deviceService.getDevice(devId);
659 if (device == null || device.serialNumber() == null) {
660 return Optional.empty();
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000661 }
Esin Karaman00e16b72020-02-21 10:32:39 +0000662 Optional<SubscriberAndDeviceInformation> olt = getSubscriberAndDeviceInformation(device.serialNumber());
663 if (olt.isEmpty()) {
664 return Optional.empty();
665 }
666 PortNumber portNumber = PortNumber.portNumber(olt.get().uplinkPort());
667 return validateUpLinkPort(device.id(), portNumber) ?
668 Optional.of(portNumber) : Optional.empty();
669 }
670
671 /**
672 *
673 * @param deviceId device id
674 * @param portNumber port number
675 * @return true if the port name starts with NNI_PREFIX; false otherwise.
676 */
677 public boolean validateUpLinkPort(DeviceId deviceId, PortNumber portNumber) {
678 Port port = deviceService.getPort(deviceId, portNumber);
679 if (port == null) {
680 //port is not discovered by ONOS; so cannot validate it.
681 return false;
682 }
Esin Karamance5ce512020-02-25 15:58:14 +0000683 boolean isValid = port.annotations().value(AnnotationKeys.PORT_NAME) != null &&
Esin Karaman00e16b72020-02-21 10:32:39 +0000684 port.annotations().value(AnnotationKeys.PORT_NAME).startsWith(NNI_PREFIX);
Esin Karamance5ce512020-02-25 15:58:14 +0000685 if (!isValid) {
686 log.warn("Port cannot be validated; it is not configured as an NNI port." +
687 "Device/port: {}/{}", deviceId, portNumber);
688 }
689 return isValid;
ke han81a38b92017-03-10 18:41:44 +0800690 }
691
Esin Karamanb38700c2019-09-17 13:01:25 +0000692 public static boolean isIgmpOnPodBasis() {
693 return igmpOnPodBasis;
694 }
695
ke han81a38b92017-03-10 18:41:44 +0800696 private void processFilterObjective(DeviceId devId, PortNumber port, boolean remove) {
Esin Karamaneff10392019-06-27 18:09:13 +0000697 if (!enableIgmpProvisioning) {
698 log.debug("IGMP trap rules won't be installed since enableIgmpProvisioning flag is not set");
699 return;
700 }
ke han81a38b92017-03-10 18:41:44 +0800701 //TODO migrate to packet requests when packet service uses filtering objectives
702 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
703
704 builder = remove ? builder.deny() : builder.permit();
705
706 FilteringObjective igmp = builder
707 .withKey(Criteria.matchInPort(port))
708 .addCondition(Criteria.matchEthType(EthType.EtherType.IPV4.ethType()))
709 .addCondition(Criteria.matchIPProtocol(IPv4.PROTOCOL_IGMP))
710 .withMeta(DefaultTrafficTreatment.builder().setOutput(PortNumber.CONTROLLER).build())
711 .fromApp(appId)
Esin Karamaneff10392019-06-27 18:09:13 +0000712 .withPriority(MAX_PRIORITY)
ke han81a38b92017-03-10 18:41:44 +0800713 .add(new ObjectiveContext() {
714 @Override
715 public void onSuccess(Objective objective) {
Esin Karamaneff10392019-06-27 18:09:13 +0000716 log.info("Igmp filter for {} on {} {}.",
717 devId, port, (remove) ? REMOVED : INSTALLED);
ke han81a38b92017-03-10 18:41:44 +0800718 }
719
720 @Override
721 public void onError(Objective objective, ObjectiveError error) {
Esin Karamaneff10392019-06-27 18:09:13 +0000722 log.info("Igmp filter {} for device {} on port {} failed because of {}",
723 (remove) ? INSTALLATION : REMOVAL, devId, port,
724 error);
ke han81a38b92017-03-10 18:41:44 +0800725 }
726 });
727
728 flowObjectiveService.filter(devId, igmp);
Esin Karamaneff10392019-06-27 18:09:13 +0000729
ke han81a38b92017-03-10 18:41:44 +0800730 }
731
732 private boolean isConnectPoint(DeviceId device, PortNumber port) {
Deepa Vaddireddy01f33b42017-07-02 13:26:53 +0530733 if (connectPoint != null) {
734 return (connectPointMode && connectPoint.deviceId().equals(device)
735 && connectPoint.port().equals(port));
736 } else {
737 log.info("connectPoint not configured for device {}", device);
738 return false;
739 }
ke han81a38b92017-03-10 18:41:44 +0800740 }
Deepa Vaddireddy01f33b42017-07-02 13:26:53 +0530741
ke han81a38b92017-03-10 18:41:44 +0800742 private boolean isUplink(DeviceId device, PortNumber port) {
Esin Karamance5ce512020-02-25 15:58:14 +0000743 if (connectPointMode) {
744 return false;
745 }
746 Optional<PortNumber> upLinkPort = getDeviceUplink(device);
747 return upLinkPort.isPresent() && upLinkPort.get().equals(port);
ke han81a38b92017-03-10 18:41:44 +0800748 }
749
Esin Karaman00e16b72020-02-21 10:32:39 +0000750 /**
751 * Fetches device information associated with the device serial number from SADIS.
752 *
753 * @param serialNumber serial number of a device
754 * @return device information; an empty Optional otherwise.
755 */
756 private Optional<SubscriberAndDeviceInformation> getSubscriberAndDeviceInformation(String serialNumber) {
757 long start = System.currentTimeMillis();
758 try {
759 return Optional.ofNullable(sadisService.getSubscriberInfoService().get(serialNumber));
760 } finally {
761 if (log.isDebugEnabled()) {
762 // SADIS can call remote systems to fetch device data and this calls can take a long time.
763 // This measurement is just for monitoring these kinds of situations.
764 log.debug("Device fetched from SADIS. Elapsed {} msec", System.currentTimeMillis() - start);
765 }
766
767 }
768 }
769
770 /**
771 * Fetches device information associated with the device serial number from SADIS.
772 *
773 * @param deviceId device id
774 * @return device information; an empty Optional otherwise.
775 */
776 private Optional<SubscriberAndDeviceInformation> getSubscriberAndDeviceInformation(DeviceId deviceId) {
777 Device device = deviceService.getDevice(deviceId);
778 if (device == null || device.serialNumber() == null) {
779 return Optional.empty();
780 }
781 return getSubscriberAndDeviceInformation(device.serialNumber());
782 }
783
ke han81a38b92017-03-10 18:41:44 +0800784 private class InternalDeviceListener implements DeviceListener {
785 @Override
786 public void event(DeviceEvent event) {
787 DeviceId devId = event.subject().id();
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +0000788 Port p = event.port();
Esin Karaman00e16b72020-02-21 10:32:39 +0000789 if (getSubscriberAndDeviceInformation(devId).isEmpty() &&
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +0000790 !(p != null && isConnectPoint(devId, p.number()))) {
ke han81a38b92017-03-10 18:41:44 +0800791 return;
792 }
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +0000793 PortNumber port;
794
ke han81a38b92017-03-10 18:41:44 +0800795 switch (event.type()) {
796
797 case DEVICE_ADDED:
798 case DEVICE_UPDATED:
799 case DEVICE_REMOVED:
800 case DEVICE_SUSPENDED:
801 case DEVICE_AVAILABILITY_CHANGED:
802 case PORT_STATS_UPDATED:
803 break;
804 case PORT_ADDED:
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +0000805 port = p.number();
Esin Karaman00e16b72020-02-21 10:32:39 +0000806 if (getSubscriberAndDeviceInformation(devId).isPresent() &&
807 !isUplink(devId, port) && !isConnectPoint(devId, port)) {
ke han81a38b92017-03-10 18:41:44 +0800808 processFilterObjective(devId, port, false);
809 } else if (isUplink(devId, port)) {
810 provisionUplinkFlows();
811 } else if (isConnectPoint(devId, port)) {
812 provisionConnectPointFlows();
813 }
814 break;
815 case PORT_UPDATED:
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +0000816 port = p.number();
Esin Karaman00e16b72020-02-21 10:32:39 +0000817 if (getSubscriberAndDeviceInformation(devId).isPresent() &&
818 !isUplink(devId, port) && !isConnectPoint(devId, port)) {
ke han81a38b92017-03-10 18:41:44 +0800819 if (event.port().isEnabled()) {
820 processFilterObjective(devId, port, false);
821 } else {
822 processFilterObjective(devId, port, true);
823 }
824 } else if (isUplink(devId, port)) {
825 if (event.port().isEnabled()) {
826 provisionUplinkFlows(devId);
827 } else {
828 processFilterObjective(devId, port, true);
829 }
830 } else if (isConnectPoint(devId, port)) {
831 if (event.port().isEnabled()) {
832 provisionConnectPointFlows();
833 } else {
834 unprovisionConnectPointFlows();
835 }
836 }
837 break;
838 case PORT_REMOVED:
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +0000839 port = p.number();
ke han81a38b92017-03-10 18:41:44 +0800840 processFilterObjective(devId, port, true);
841 break;
842 default:
843 log.info("Unknown device event {}", event.type());
844 break;
845 }
846 }
847
848 @Override
849 public boolean isRelevant(DeviceEvent event) {
850 return true;
851 }
852 }
853
854 private class InternalNetworkConfigListener implements NetworkConfigListener {
855
856 private void reconfigureNetwork(IgmpproxyConfig cfg) {
Esin Karamaneff10392019-06-27 18:09:13 +0000857 IgmpproxyConfig newCfg = cfg == null ? new IgmpproxyConfig() : cfg;
ke han81a38b92017-03-10 18:41:44 +0800858
859 unSolicitedTimeout = newCfg.unsolicitedTimeOut();
860 maxResp = newCfg.maxResp();
861 keepAliveInterval = newCfg.keepAliveInterval();
862 keepAliveCount = newCfg.keepAliveCount();
863 lastQueryInterval = newCfg.lastQueryInterval();
864 lastQueryCount = newCfg.lastQueryCount();
865 withRAUplink = newCfg.withRAUplink();
866 withRADownlink = newCfg.withRADownlink();
867 igmpCos = newCfg.igmpCos();
868 periodicQuery = newCfg.periodicQuery();
869 fastLeave = newCfg.fastLeave();
ke han29af27b2017-09-08 10:29:12 +0800870 pimSSmInterworking = newCfg.pimSsmInterworking();
Esin Karamaneff10392019-06-27 18:09:13 +0000871 enableIgmpProvisioning = newCfg.enableIgmpProvisioning();
Esin Karamanb38700c2019-09-17 13:01:25 +0000872 igmpOnPodBasis = newCfg.igmpOnPodBasis();
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +0000873
874 if (connectPointMode != newCfg.connectPointMode() ||
875 connectPoint != newCfg.connectPoint()) {
ke han81a38b92017-03-10 18:41:44 +0800876 connectPointMode = newCfg.connectPointMode();
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +0000877 connectPoint = newCfg.connectPoint();
ke han81a38b92017-03-10 18:41:44 +0800878 if (connectPointMode) {
879 unprovisionUplinkFlows();
880 provisionConnectPointFlows();
881 } else {
882 unprovisionConnectPointFlows();
883 provisionUplinkFlows();
884 }
885 }
886 if (connectPoint != null) {
Esin Karamaneff10392019-06-27 18:09:13 +0000887 log.info("connect point : {}", connectPoint);
ke han81a38b92017-03-10 18:41:44 +0800888 }
Esin Karamaneff10392019-06-27 18:09:13 +0000889 log.info("mode: {}", connectPointMode);
890
891 getSourceConnectPoint(newCfg);
ke han81a38b92017-03-10 18:41:44 +0800892
893 IgmpSender.getInstance().setIgmpCos(igmpCos);
894 IgmpSender.getInstance().setMaxResp(maxResp);
895 IgmpSender.getInstance().setMvlan(mvlan);
896 IgmpSender.getInstance().setWithRADownlink(withRADownlink);
897 IgmpSender.getInstance().setWithRAUplink(withRAUplink);
Esin Karamaneff10392019-06-27 18:09:13 +0000898 }
ke han81a38b92017-03-10 18:41:44 +0800899
Esin Karamaneff10392019-06-27 18:09:13 +0000900 void getSourceConnectPoint(IgmpproxyConfig cfg) {
901 sourceDeviceAndPort = cfg.getSourceDeviceAndPort();
902 if (sourceDeviceAndPort != null) {
903 log.debug("source parameter configured to {}", sourceDeviceAndPort);
904 }
ke han81a38b92017-03-10 18:41:44 +0800905 }
906
907 public void reconfigureSsmTable(IgmpproxySsmTranslateConfig cfg) {
908 if (cfg == null) {
909 return;
910 }
911 Collection<McastRoute> translations = cfg.getSsmTranslations();
912 for (McastRoute route : translations) {
Esin Karamaneff10392019-06-27 18:09:13 +0000913 ssmTranslateTable.put(route.group().getIp4Address(), route.source().get().getIp4Address());
ke han81a38b92017-03-10 18:41:44 +0800914 }
915 }
916
917 @Override
918 public void event(NetworkConfigEvent event) {
919 switch (event.type()) {
920 case CONFIG_ADDED:
921 case CONFIG_UPDATED:
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800922 // NOTE how to know if something has changed in sadis?
ke han81a38b92017-03-10 18:41:44 +0800923
924 if (event.configClass().equals(IGMPPROXY_CONFIG_CLASS)) {
925 IgmpproxyConfig config = networkConfig.getConfig(appId, IGMPPROXY_CONFIG_CLASS);
926 if (config != null) {
Esin Karamaneff10392019-06-27 18:09:13 +0000927 log.info("igmpproxy config received. {}", config);
ke han81a38b92017-03-10 18:41:44 +0800928 reconfigureNetwork(config);
929 }
930 }
931
932 if (event.configClass().equals(IGMPPROXY_SSM_CONFIG_CLASS)) {
933 IgmpproxySsmTranslateConfig config = networkConfig.getConfig(appId, IGMPPROXY_SSM_CONFIG_CLASS);
934 if (config != null) {
935 reconfigureSsmTable(config);
936 }
937 }
938
939 if (event.configClass().equals(MCAST_CONFIG_CLASS)) {
940 McastConfig config = networkConfig.getConfig(coreAppId, MCAST_CONFIG_CLASS);
941 if (config != null && mvlan != config.egressVlan().toShort()) {
942 mvlan = config.egressVlan().toShort();
Deepa Vaddireddy88134a42017-07-05 12:16:03 +0530943 IgmpSender.getInstance().setMvlan(mvlan);
ke han81a38b92017-03-10 18:41:44 +0800944 groupMemberMap.values().forEach(m -> leaveAction(m));
945 }
946 }
947
948 log.info("Reconfigured");
949 break;
950 case CONFIG_REGISTERED:
951 case CONFIG_UNREGISTERED:
952 break;
953 case CONFIG_REMOVED:
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800954 // NOTE how to know if something has changed in sadis?
ke han81a38b92017-03-10 18:41:44 +0800955 default:
956 break;
957 }
958 }
959 }
960
ke han81a38b92017-03-10 18:41:44 +0800961 private void provisionUplinkFlows(DeviceId deviceId) {
962 if (connectPointMode) {
963 return;
964 }
965
Esin Karaman00e16b72020-02-21 10:32:39 +0000966 Optional<PortNumber> upLink = getDeviceUplink(deviceId);
967 if (upLink.isPresent()) {
968 processFilterObjective(deviceId, upLink.get(), false);
969 }
ke han81a38b92017-03-10 18:41:44 +0800970 }
971
972 private void provisionUplinkFlows() {
973 if (connectPointMode) {
974 return;
975 }
Esin Karaman00e16b72020-02-21 10:32:39 +0000976 deviceService.getAvailableDevices().forEach(device -> {
977 Optional<SubscriberAndDeviceInformation> accessDevice = getSubscriberAndDeviceInformation(device.id());
978 if (accessDevice.isPresent()) {
979 provisionUplinkFlows(device.id());
980 }
981 });
ke han81a38b92017-03-10 18:41:44 +0800982 }
Esin Karaman00e16b72020-02-21 10:32:39 +0000983
ke han81a38b92017-03-10 18:41:44 +0800984 private void unprovisionUplinkFlows() {
Esin Karaman00e16b72020-02-21 10:32:39 +0000985 deviceService.getAvailableDevices().forEach(device -> {
986 Optional<SubscriberAndDeviceInformation> accessDevices = getSubscriberAndDeviceInformation(device.id());
987 if (accessDevices.isPresent()) {
988 Optional<PortNumber> upLink = getDeviceUplink(device.id());
989 if (upLink.isPresent()) {
990 processFilterObjective(device.id(), upLink.get(), true);
991 }
992 }
993 });
ke han81a38b92017-03-10 18:41:44 +0800994 }
995
996 private void provisionConnectPointFlows() {
997 if ((!connectPointMode) || connectPoint == null) {
998 return;
999 }
1000
1001 processFilterObjective(connectPoint.deviceId(), connectPoint.port(), false);
1002 }
1003 private void unprovisionConnectPointFlows() {
1004 if (connectPoint == null) {
1005 return;
1006 }
1007 processFilterObjective(connectPoint.deviceId(), connectPoint.port(), true);
1008 }
Shubham Sharma47f2caf2020-02-18 12:13:40 +00001009
ke han81a38b92017-03-10 18:41:44 +08001010}