blob: 97e97cc557731e456161d3bbaa1147fdfdb0441e [file] [log] [blame]
David K. Bainbridged77028f2017-08-01 12:47:55 -07001/*
Brian O'Connor4d084702017-08-03 22:45:58 -07002 * Copyright 2017-present Open Networking Foundation
David K. Bainbridged77028f2017-08-01 12:47:55 -07003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
developere400c582020-03-24 19:42:08 +010016package org.opencord.igmpproxy.impl;
ke han81a38b92017-03-10 18:41:44 +080017
Esin Karamaneff10392019-06-27 18:09:13 +000018import com.google.common.collect.Sets;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -080019import org.onosproject.net.Device;
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +000020import org.opencord.igmpproxy.IgmpLeadershipService;
developere400c582020-03-24 19:42:08 +010021import org.opencord.igmpproxy.IgmpStatisticsService;
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +000022import org.opencord.igmpproxy.impl.store.groupmember.GroupMember;
23import org.opencord.igmpproxy.GroupMemberId;
24import org.opencord.igmpproxy.impl.store.groupmember.GroupMemberStore;
25import org.opencord.igmpproxy.statemachine.StateMachineService;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -080026import org.opencord.sadis.BaseInformationService;
27import org.opencord.sadis.SadisService;
28import org.opencord.sadis.SubscriberAndDeviceInformation;
Carmelo Casconebef302e2019-11-14 19:58:20 -080029import org.osgi.service.component.annotations.Activate;
30import org.osgi.service.component.annotations.Component;
31import org.osgi.service.component.annotations.Deactivate;
32import org.osgi.service.component.annotations.Reference;
33import org.osgi.service.component.annotations.ReferenceCardinality;
ke han81a38b92017-03-10 18:41:44 +080034import org.onlab.packet.EthType;
35import org.onlab.packet.Ethernet;
36import org.onlab.packet.IGMP;
37import org.onlab.packet.IGMPGroup;
38import org.onlab.packet.IGMPMembership;
39import org.onlab.packet.IGMPQuery;
40import org.onlab.packet.IPv4;
41import org.onlab.packet.Ip4Address;
42import org.onlab.packet.IpAddress;
43import org.onlab.packet.VlanId;
44import org.onosproject.core.ApplicationId;
45import org.onosproject.core.CoreService;
ke han81a38b92017-03-10 18:41:44 +080046import org.onosproject.mastership.MastershipService;
47import org.onosproject.net.AnnotationKeys;
48import org.onosproject.net.ConnectPoint;
49import org.onosproject.net.DeviceId;
50import org.onosproject.net.Port;
51import org.onosproject.net.PortNumber;
52import org.onosproject.net.config.ConfigFactory;
53import org.onosproject.net.config.NetworkConfigEvent;
54import org.onosproject.net.config.NetworkConfigListener;
55import org.onosproject.net.config.NetworkConfigRegistry;
Jonathan Hart488e1142018-05-02 17:30:05 -070056import org.onosproject.net.config.basics.McastConfig;
ke han81a38b92017-03-10 18:41:44 +080057import org.onosproject.net.config.basics.SubjectFactories;
58import org.onosproject.net.device.DeviceEvent;
59import org.onosproject.net.device.DeviceListener;
60import org.onosproject.net.device.DeviceService;
61import org.onosproject.net.flow.DefaultTrafficTreatment;
62import org.onosproject.net.flow.FlowRuleService;
63import org.onosproject.net.flow.criteria.Criteria;
64import org.onosproject.net.flowobjective.DefaultFilteringObjective;
65import org.onosproject.net.flowobjective.FilteringObjective;
66import org.onosproject.net.flowobjective.FlowObjectiveService;
67import org.onosproject.net.flowobjective.Objective;
68import org.onosproject.net.flowobjective.ObjectiveContext;
69import org.onosproject.net.flowobjective.ObjectiveError;
Esin Karamaneff10392019-06-27 18:09:13 +000070import org.onosproject.mcast.api.McastRoute;
71import org.onosproject.mcast.api.MulticastRouteService;
ke han81a38b92017-03-10 18:41:44 +080072import org.onosproject.net.packet.InboundPacket;
73import org.onosproject.net.packet.PacketContext;
74import org.onosproject.net.packet.PacketProcessor;
75import org.onosproject.net.packet.PacketService;
ke han81a38b92017-03-10 18:41:44 +080076import org.slf4j.Logger;
77import org.slf4j.LoggerFactory;
78
79import java.util.ArrayList;
Sonal Kasliwalf11c0672020-03-18 11:11:50 +000080import java.util.Arrays;
81import java.util.List;
ke han81a38b92017-03-10 18:41:44 +080082import java.util.Collection;
Esin Karamaneff10392019-06-27 18:09:13 +000083import java.util.HashSet;
ke han81a38b92017-03-10 18:41:44 +080084import java.util.Iterator;
ke han81a38b92017-03-10 18:41:44 +080085import java.util.Map;
Esin Karamaneff10392019-06-27 18:09:13 +000086import java.util.Optional;
ke han81a38b92017-03-10 18:41:44 +080087import java.util.Set;
88import java.util.TimerTask;
89import java.util.concurrent.ConcurrentHashMap;
Esin Karamanb38700c2019-09-17 13:01:25 +000090import java.util.concurrent.ExecutorService;
ke han81a38b92017-03-10 18:41:44 +080091import java.util.concurrent.Executors;
92import java.util.concurrent.ScheduledExecutorService;
93import java.util.concurrent.TimeUnit;
94
Sonal Kasliwalf11c0672020-03-18 11:11:50 +000095import static org.onlab.packet.IGMPMembership.MODE_IS_INCLUDE;
96import static org.onlab.packet.IGMPMembership.MODE_IS_EXCLUDE;
97import static org.onlab.packet.IGMPMembership.CHANGE_TO_INCLUDE_MODE;
98import static org.onlab.packet.IGMPMembership.CHANGE_TO_EXCLUDE_MODE;
99import static org.onlab.packet.IGMPMembership.ALLOW_NEW_SOURCES;
100import static org.onlab.packet.IGMPMembership.BLOCK_OLD_SOURCES;
101
Esin Karamanb38700c2019-09-17 13:01:25 +0000102import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
103import static org.onlab.util.Tools.groupedThreads;
104
ke han81a38b92017-03-10 18:41:44 +0800105/**
106 * Igmp process application, use proxy mode, support first join/ last leave , fast leave
107 * period query and keep alive, packet out igmp message to uplink port features.
108 */
109@Component(immediate = true)
110public class IgmpManager {
111
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800112 private static final String APP_NAME = "org.opencord.igmpproxy";
113
ke han81a38b92017-03-10 18:41:44 +0800114 private static final Class<IgmpproxyConfig> IGMPPROXY_CONFIG_CLASS =
115 IgmpproxyConfig.class;
116 private static final Class<IgmpproxySsmTranslateConfig> IGMPPROXY_SSM_CONFIG_CLASS =
117 IgmpproxySsmTranslateConfig.class;
118 private static final Class<McastConfig> MCAST_CONFIG_CLASS =
119 McastConfig.class;
Esin Karamaneff10392019-06-27 18:09:13 +0000120
ke han81a38b92017-03-10 18:41:44 +0800121 private static ApplicationId appId;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800122
ke han81a38b92017-03-10 18:41:44 +0800123 private static int unSolicitedTimeout = 3; // unit is 1 sec
124 private static int keepAliveCount = 3;
125 private static int lastQueryInterval = 2; //unit is 1 sec
126 private static int lastQueryCount = 2;
127 private static boolean fastLeave = true;
128 private static boolean withRAUplink = true;
129 private static boolean withRADownlink = false;
130 private static boolean periodicQuery = true;
131 private static short mvlan = 4000;
132 private static byte igmpCos = 7;
133 public static boolean connectPointMode = true;
134 public static ConnectPoint connectPoint = null;
Esin Karamaneff10392019-06-27 18:09:13 +0000135 private static ConnectPoint sourceDeviceAndPort = null;
136 private static boolean enableIgmpProvisioning = false;
Esin Karamanb38700c2019-09-17 13:01:25 +0000137 private static boolean igmpOnPodBasis = false;
Arjun E Kb0018fd2020-04-07 13:26:40 +0000138 private static boolean outgoingIgmpWithV3 = true;
Esin Karamaneff10392019-06-27 18:09:13 +0000139
140 private static final Integer MAX_PRIORITY = 10000;
141 private static final String INSTALLED = "installed";
142 private static final String REMOVED = "removed";
143 private static final String INSTALLATION = "installation";
144 private static final String REMOVAL = "removal";
Esin Karaman00e16b72020-02-21 10:32:39 +0000145 private static final String NNI_PREFIX = "nni";
ke han81a38b92017-03-10 18:41:44 +0800146
ke han29af27b2017-09-08 10:29:12 +0800147 private static boolean pimSSmInterworking = false;
148 private static final String DEFAULT_PIMSSM_HOST = "127.0.0.1";
ke han81a38b92017-03-10 18:41:44 +0800149 private final ScheduledExecutorService scheduledExecutorService =
150 Executors.newScheduledThreadPool(1);
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800151
Carmelo Casconebef302e2019-11-14 19:58:20 -0800152 @Reference(cardinality = ReferenceCardinality.MANDATORY)
ke han81a38b92017-03-10 18:41:44 +0800153 protected CoreService coreService;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800154
Carmelo Casconebef302e2019-11-14 19:58:20 -0800155 @Reference(cardinality = ReferenceCardinality.MANDATORY)
ke han81a38b92017-03-10 18:41:44 +0800156 protected PacketService packetService;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800157
Carmelo Casconebef302e2019-11-14 19:58:20 -0800158 @Reference(cardinality = ReferenceCardinality.MANDATORY)
ke han81a38b92017-03-10 18:41:44 +0800159 protected MastershipService mastershipService;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800160
Carmelo Casconebef302e2019-11-14 19:58:20 -0800161 @Reference(cardinality = ReferenceCardinality.MANDATORY)
ke han81a38b92017-03-10 18:41:44 +0800162 protected FlowRuleService flowRuleService;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800163
Carmelo Casconebef302e2019-11-14 19:58:20 -0800164 @Reference(cardinality = ReferenceCardinality.MANDATORY)
ke han81a38b92017-03-10 18:41:44 +0800165 protected DeviceService deviceService;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800166
Carmelo Casconebef302e2019-11-14 19:58:20 -0800167 @Reference(cardinality = ReferenceCardinality.MANDATORY)
ke han81a38b92017-03-10 18:41:44 +0800168 protected FlowObjectiveService flowObjectiveService;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800169
Carmelo Casconebef302e2019-11-14 19:58:20 -0800170 @Reference(cardinality = ReferenceCardinality.MANDATORY)
ke han81a38b92017-03-10 18:41:44 +0800171 protected NetworkConfigRegistry networkConfig;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800172
Carmelo Casconebef302e2019-11-14 19:58:20 -0800173 @Reference(cardinality = ReferenceCardinality.MANDATORY)
ke han81a38b92017-03-10 18:41:44 +0800174 protected MulticastRouteService multicastService;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800175
176 @Reference(cardinality = ReferenceCardinality.MANDATORY)
177 protected SadisService sadisService;
178
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000179 @Reference(cardinality = ReferenceCardinality.MANDATORY)
180 protected IgmpStatisticsService igmpStatisticsManager;
181
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000182 @Reference(cardinality = ReferenceCardinality.MANDATORY)
183 protected GroupMemberStore groupMemberStore;
184
185 @Reference(cardinality = ReferenceCardinality.MANDATORY)
186 protected StateMachineService stateMachineService;
187
188 @Reference(cardinality = ReferenceCardinality.MANDATORY)
189 protected IgmpLeadershipService igmpLeadershipService;
190
ke han81a38b92017-03-10 18:41:44 +0800191 private IgmpPacketProcessor processor = new IgmpPacketProcessor();
192 private Logger log = LoggerFactory.getLogger(getClass());
193 private ApplicationId coreAppId;
194 private Map<Ip4Address, Ip4Address> ssmTranslateTable = new ConcurrentHashMap<>();
Esin Karamaneff10392019-06-27 18:09:13 +0000195
ke han81a38b92017-03-10 18:41:44 +0800196 private InternalNetworkConfigListener configListener =
197 new InternalNetworkConfigListener();
198 private DeviceListener deviceListener = new InternalDeviceListener();
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800199
ke han81a38b92017-03-10 18:41:44 +0800200 private ConfigFactory<ApplicationId, IgmpproxyConfig> igmpproxyConfigFactory =
201 new ConfigFactory<ApplicationId, IgmpproxyConfig>(
202 SubjectFactories.APP_SUBJECT_FACTORY, IGMPPROXY_CONFIG_CLASS, "igmpproxy") {
203 @Override
204 public IgmpproxyConfig createConfig() {
205 return new IgmpproxyConfig();
206 }
207 };
208 private ConfigFactory<ApplicationId, IgmpproxySsmTranslateConfig> igmpproxySsmConfigFactory =
209 new ConfigFactory<ApplicationId, IgmpproxySsmTranslateConfig>(
210 SubjectFactories.APP_SUBJECT_FACTORY, IGMPPROXY_SSM_CONFIG_CLASS, "ssmTranslate", true) {
211 @Override
212 public IgmpproxySsmTranslateConfig createConfig() {
213 return new IgmpproxySsmTranslateConfig();
214 }
215 };
Esin Karamaneff10392019-06-27 18:09:13 +0000216
ke han81a38b92017-03-10 18:41:44 +0800217 private int maxResp = 10; //unit is 1 sec
218 private int keepAliveInterval = 120; //unit is 1 sec
219
Esin Karamanb38700c2019-09-17 13:01:25 +0000220 private ExecutorService eventExecutor;
221
ke han81a38b92017-03-10 18:41:44 +0800222 public static int getUnsolicitedTimeout() {
223 return unSolicitedTimeout;
224 }
225
Arjun E Kb0018fd2020-04-07 13:26:40 +0000226 public static boolean outgoingIgmpWithV3() {
227 return outgoingIgmpWithV3;
228 }
229
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800230 protected BaseInformationService<SubscriberAndDeviceInformation> subsService;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800231
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000232 private List<Byte> validMembershipModes = Arrays.asList(MODE_IS_INCLUDE, MODE_IS_EXCLUDE, CHANGE_TO_INCLUDE_MODE,
233 CHANGE_TO_EXCLUDE_MODE, ALLOW_NEW_SOURCES, BLOCK_OLD_SOURCES);
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000234
ke han81a38b92017-03-10 18:41:44 +0800235 @Activate
236 protected void activate() {
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800237 appId = coreService.registerApplication(APP_NAME);
ke han81a38b92017-03-10 18:41:44 +0800238 coreAppId = coreService.registerApplication(CoreService.CORE_APP_NAME);
239 packetService.addProcessor(processor, PacketProcessor.director(4));
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000240 IgmpSender.init(packetService, igmpLeadershipService, igmpStatisticsManager);
ke han81a38b92017-03-10 18:41:44 +0800241
ke han81a38b92017-03-10 18:41:44 +0800242 networkConfig.registerConfigFactory(igmpproxySsmConfigFactory);
243 networkConfig.registerConfigFactory(igmpproxyConfigFactory);
244 networkConfig.addListener(configListener);
245
246 configListener.reconfigureNetwork(networkConfig.getConfig(appId, IGMPPROXY_CONFIG_CLASS));
247 configListener.reconfigureSsmTable(networkConfig.getConfig(appId, IGMPPROXY_SSM_CONFIG_CLASS));
248
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800249 subsService = sadisService.getSubscriberInfoService();
ke han81a38b92017-03-10 18:41:44 +0800250 if (connectPointMode) {
251 provisionConnectPointFlows();
252 } else {
253 provisionUplinkFlows();
254 }
255
256 McastConfig config = networkConfig.getConfig(coreAppId, MCAST_CONFIG_CLASS);
257 if (config != null) {
258 mvlan = config.egressVlan().toShort();
Deepa Vaddireddy88134a42017-07-05 12:16:03 +0530259 IgmpSender.getInstance().setMvlan(mvlan);
ke han81a38b92017-03-10 18:41:44 +0800260 }
261 deviceService.addListener(deviceListener);
Sonal Kasliwalddc3ff22019-11-18 11:52:49 +0000262 scheduledExecutorService.scheduleAtFixedRate(new IgmpProxyTimerTask(), 1000, 1000, TimeUnit.MILLISECONDS);
Esin Karamanb38700c2019-09-17 13:01:25 +0000263 eventExecutor = newSingleThreadScheduledExecutor(groupedThreads("cord/igmpproxy",
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000264 "events-igmp-%d", log));
ke han81a38b92017-03-10 18:41:44 +0800265 log.info("Started");
266 }
267
268 @Deactivate
269 protected void deactivate() {
270 scheduledExecutorService.shutdown();
Esin Karamanb38700c2019-09-17 13:01:25 +0000271 eventExecutor.shutdown();
ke han81a38b92017-03-10 18:41:44 +0800272
273 // de-register and null our handler
274 networkConfig.removeListener(configListener);
ke han81a38b92017-03-10 18:41:44 +0800275 networkConfig.unregisterConfigFactory(igmpproxyConfigFactory);
276 networkConfig.unregisterConfigFactory(igmpproxySsmConfigFactory);
277 deviceService.removeListener(deviceListener);
278 packetService.removeProcessor(processor);
279 flowRuleService.removeFlowRulesById(appId);
ke han81a38b92017-03-10 18:41:44 +0800280 log.info("Stopped");
281 }
282
283 protected Ip4Address getDeviceIp(DeviceId ofDeviceId) {
284 try {
285 String[] mgmtAddress = deviceService.getDevice(ofDeviceId)
286 .annotations().value(AnnotationKeys.MANAGEMENT_ADDRESS).split(":");
287 return Ip4Address.valueOf(mgmtAddress[0]);
288 } catch (Exception ex) {
289 log.info("No valid Ipaddress for " + ofDeviceId.toString());
290 return null;
291 }
292 }
293
294 private void processIgmpQuery(IGMPQuery igmpQuery, ConnectPoint cp, int maxResp) {
295
296 DeviceId deviceId = cp.deviceId();
297 Ip4Address gAddr = igmpQuery.getGaddr().getIp4Address();
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000298 maxResp = calculateMaxResp(maxResp);
299 if (gAddr != null && !gAddr.isZero()) {
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000300 stateMachineService.specialQuery(deviceId, gAddr, maxResp);
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000301 igmpStatisticsManager.getIgmpStats().increaseIgmpGrpSpecificMembershipQuery();
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000302 } else {
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000303 stateMachineService.generalQuery(deviceId, maxResp);
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000304 igmpStatisticsManager.getIgmpStats().increaseIgmpGeneralMembershipQuery();
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000305 }
306 }
ke han81a38b92017-03-10 18:41:44 +0800307
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000308 private void processIgmpConnectPointQuery(IGMPQuery igmpQuery, ConnectPoint cp, int maxResp) {
309
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000310 Ip4Address gAddr = igmpQuery.getGaddr().getIp4Address();
Esin Karaman00e16b72020-02-21 10:32:39 +0000311 final int maxResponseTime = calculateMaxResp(maxResp);
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000312 //The query is received on the ConnectPoint
313 // send query accordingly to the registered OLT devices.
314 if (gAddr != null && !gAddr.isZero()) {
Esin Karaman00e16b72020-02-21 10:32:39 +0000315 deviceService.getAvailableDevices().forEach(device -> {
316 Optional<SubscriberAndDeviceInformation> accessDevice = getSubscriberAndDeviceInformation(device.id());
317 if (accessDevice.isPresent()) {
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000318 stateMachineService.specialQuery(device.id(), gAddr, maxResponseTime);
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000319 igmpStatisticsManager.getIgmpStats().increaseIgmpGrpAndSrcSpecificMembershipQuery();
Esin Karaman00e16b72020-02-21 10:32:39 +0000320 }
321 });
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000322 igmpStatisticsManager.getIgmpStats().increaseCurrentGrpNumCounter();
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000323 } else {
324 //Don't know which group is targeted by the query
325 //So query all the members(in all the OLTs) and proxy their reports
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000326 stateMachineService.generalQuery(maxResponseTime);
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000327 igmpStatisticsManager.getIgmpStats().increaseIgmpGeneralMembershipQuery();
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000328 }
329 }
330
331
332 private int calculateMaxResp(int maxResp) {
ke han81a38b92017-03-10 18:41:44 +0800333 if (maxResp >= 128) {
334 int mant = maxResp & 0xf;
335 int exp = (maxResp >> 4) & 0x7;
336 maxResp = (mant | 0x10) << (exp + 3);
337 }
338
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000339 return (maxResp + 5) / 10;
ke han81a38b92017-03-10 18:41:44 +0800340 }
341
342 private Ip4Address ssmTranslateRoute(IpAddress group) {
343 return ssmTranslateTable.get(group);
344 }
345
346 private void processIgmpReport(IGMPMembership igmpGroup, VlanId vlan, ConnectPoint cp, byte igmpType) {
347 DeviceId deviceId = cp.deviceId();
348 PortNumber portNumber = cp.port();
349
350 Ip4Address groupIp = igmpGroup.getGaddr().getIp4Address();
351 if (!groupIp.isMulticast()) {
352 log.info(groupIp.toString() + " is not a valid group address");
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000353 igmpStatisticsManager.getIgmpStats().increaseFailJoinReqUnknownMulticastIpCounter();
ke han81a38b92017-03-10 18:41:44 +0800354 return;
355 }
356 Ip4Address srcIp = getDeviceIp(deviceId);
357
358 byte recordType = igmpGroup.getRecordType();
359 boolean join = false;
360
361 ArrayList<Ip4Address> sourceList = new ArrayList<>();
362
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000363 if (!validMembershipModes.contains(recordType)) {
364 igmpStatisticsManager.getIgmpStats().increaseReportsRxWithWrongModeCounter();
365 }
ke han81a38b92017-03-10 18:41:44 +0800366 if (igmpGroup.getSources().size() > 0) {
367 igmpGroup.getSources().forEach(source -> sourceList.add(source.getIp4Address()));
368 if (recordType == IGMPMembership.CHANGE_TO_EXCLUDE_MODE ||
369 recordType == IGMPMembership.MODE_IS_EXCLUDE ||
370 recordType == IGMPMembership.BLOCK_OLD_SOURCES) {
371 join = false;
372 } else if (recordType == IGMPMembership.CHANGE_TO_INCLUDE_MODE ||
373 recordType == IGMPMembership.MODE_IS_INCLUDE ||
374 recordType == IGMPMembership.ALLOW_NEW_SOURCES) {
375 join = true;
376 }
377 } else {
ke han29af27b2017-09-08 10:29:12 +0800378 IpAddress src = null;
379 if (pimSSmInterworking) {
380 src = ssmTranslateRoute(groupIp);
381 if (src == null) {
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000382 log.info("no ssm translate for group {}", groupIp);
ke han29af27b2017-09-08 10:29:12 +0800383 return;
384 }
385 } else {
386 src = IpAddress.valueOf(DEFAULT_PIMSSM_HOST);
ke han81a38b92017-03-10 18:41:44 +0800387 }
388 sourceList.add(src.getIp4Address());
389 if (recordType == IGMPMembership.CHANGE_TO_EXCLUDE_MODE ||
390 recordType == IGMPMembership.MODE_IS_EXCLUDE ||
391 recordType == IGMPMembership.BLOCK_OLD_SOURCES) {
392 join = true;
393 } else if (recordType == IGMPMembership.CHANGE_TO_INCLUDE_MODE ||
394 recordType == IGMPMembership.MODE_IS_INCLUDE ||
395 recordType == IGMPMembership.ALLOW_NEW_SOURCES) {
396 join = false;
397 }
398 }
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000399 GroupMemberId groupMemberKey = GroupMemberId.of(groupIp, deviceId, portNumber);
400 GroupMember groupMember = groupMemberStore.getGroupMember(groupMemberKey);
ke han81a38b92017-03-10 18:41:44 +0800401
402 if (join) {
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000403 igmpStatisticsManager.getIgmpStats().increaseIgmpJoinReq();
ke han81a38b92017-03-10 18:41:44 +0800404 if (groupMember == null) {
Esin Karamaneff10392019-06-27 18:09:13 +0000405 Optional<ConnectPoint> sourceConfigured = getSource();
406 if (!sourceConfigured.isPresent()) {
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000407 igmpStatisticsManager.getIgmpStats().increaseIgmpFailJoinReq();
Esin Karamaneff10392019-06-27 18:09:13 +0000408 log.warn("Unable to process IGMP Join from {} since no source " +
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000409 "configuration is found.", deviceId);
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000410 igmpStatisticsManager.getIgmpStats().increaseFailJoinReqInsuffPermissionAccessCounter();
Esin Karamaneff10392019-06-27 18:09:13 +0000411 return;
412 }
Esin Karaman00e16b72020-02-21 10:32:39 +0000413
414 Optional<PortNumber> deviceUplink = getDeviceUplink(deviceId);
415 if (deviceUplink.isEmpty()) {
416 log.warn("Unable to process IGMP Join since uplink port " +
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000417 "of the device {} is not found.", deviceId);
Esin Karaman00e16b72020-02-21 10:32:39 +0000418 return;
419 }
420
421 if (igmpType == IGMP.TYPE_IGMPV2_MEMBERSHIP_REPORT) {
422 groupMember = new GroupMember(groupIp, vlan, deviceId, portNumber, true);
423 } else {
424 groupMember = new GroupMember(groupIp, vlan, deviceId, portNumber, false);
425 }
426
Esin Karamaneff10392019-06-27 18:09:13 +0000427 HashSet<ConnectPoint> sourceConnectPoints = Sets.newHashSet(sourceConfigured.get());
428
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000429 boolean isJoined = stateMachineService.join(deviceId, groupIp, srcIp, deviceUplink.get());
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000430 if (isJoined) {
431 igmpStatisticsManager.getIgmpStats().increaseIgmpSuccessJoinRejoinReq();
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000432 igmpStatisticsManager.getIgmpStats().increaseIgmpChannelJoinCounter();
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000433 } else {
434 igmpStatisticsManager.getIgmpStats().increaseIgmpFailJoinReq();
435 }
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000436 groupMemberStore.putGroupMember(groupMember);
437 log.debug("Group member created with id: {}", groupMember.getGroupMemberId());
ke han81a38b92017-03-10 18:41:44 +0800438 groupMember.updateList(recordType, sourceList);
Esin Karamaneff10392019-06-27 18:09:13 +0000439 groupMember.getSourceList().forEach(source -> {
440 McastRoute route = new McastRoute(source, groupIp, McastRoute.Type.IGMP);
441 //add route
442 multicastService.add(route);
443 //add source to the route
444 multicastService.addSources(route, Sets.newHashSet(sourceConnectPoints));
445 //add sink to the route
446 multicastService.addSinks(route, Sets.newHashSet(cp));
447 });
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000448 igmpStatisticsManager.getIgmpStats().increaseUnconfiguredGroupCounter();
Esin Karamaneff10392019-06-27 18:09:13 +0000449
ke han81a38b92017-03-10 18:41:44 +0800450 }
451 groupMember.resetAllTimers();
452 groupMember.updateList(recordType, sourceList);
453 groupMember.setLeave(false);
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000454 //put updated member to the store
455 groupMemberStore.putGroupMember(groupMember);
ke han81a38b92017-03-10 18:41:44 +0800456 } else {
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000457 igmpStatisticsManager.getIgmpStats().increaseIgmpLeaveReq();
ke han81a38b92017-03-10 18:41:44 +0800458 if (groupMember == null) {
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000459 log.info("receive leave but no instance, group {} device: {} port:{}",
460 groupIp, deviceId, portNumber);
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000461 igmpStatisticsManager.getIgmpStats().increaseUnconfiguredGroupCounter();
ke han81a38b92017-03-10 18:41:44 +0800462 return;
463 } else {
464 groupMember.setLeave(true);
465 if (fastLeave) {
466 leaveAction(groupMember);
467 } else {
468 sendQuery(groupMember);
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000469 //put modified group member object to the store
470 groupMemberStore.updateGroupMember(groupMember);
ke han81a38b92017-03-10 18:41:44 +0800471 }
472 }
473 }
474 }
475
476 private void leaveAction(GroupMember groupMember) {
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000477 igmpStatisticsManager.getIgmpStats().increaseIgmpDisconnect();
ke han81a38b92017-03-10 18:41:44 +0800478 ConnectPoint cp = new ConnectPoint(groupMember.getDeviceId(), groupMember.getPortNumber());
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000479 stateMachineService.leave(groupMember.getDeviceId(), groupMember.getGroupIp());
Esin Karamaneff10392019-06-27 18:09:13 +0000480 groupMember.getSourceList().forEach(source -> multicastService.removeSinks(
ke han81a38b92017-03-10 18:41:44 +0800481 new McastRoute(source, groupMember.getGroupIp(),
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000482 McastRoute.Type.IGMP), Sets.newHashSet(cp)));
483 groupMemberStore.removeGroupMember(groupMember.getGroupMemberId());
ke han81a38b92017-03-10 18:41:44 +0800484 }
485
486 private void sendQuery(GroupMember groupMember) {
487 Ethernet ethpkt;
488 Ip4Address srcIp = getDeviceIp(groupMember.getDeviceId());
489 if (groupMember.getv2()) {
490 ethpkt = IgmpSender.getInstance().buildIgmpV2Query(groupMember.getGroupIp(), srcIp);
491 } else {
492 ethpkt = IgmpSender.getInstance().buildIgmpV3Query(groupMember.getGroupIp(), srcIp);
493 }
494 IgmpSender.getInstance().sendIgmpPacket(ethpkt, groupMember.getDeviceId(), groupMember.getPortNumber());
495 }
496
Esin Karamaneff10392019-06-27 18:09:13 +0000497 /**
498 * @return connect point of the source if configured; and empty Optional otherwise.
499 */
500 public static Optional<ConnectPoint> getSource() {
501 return sourceDeviceAndPort == null ? Optional.empty() :
502 Optional.of(sourceDeviceAndPort);
ke han81a38b92017-03-10 18:41:44 +0800503 }
504
505 /**
506 * Packet processor responsible for forwarding packets along their paths.
507 */
508 private class IgmpPacketProcessor implements PacketProcessor {
509 @Override
510 public void process(PacketContext context) {
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000511
Esin Karamanb38700c2019-09-17 13:01:25 +0000512 eventExecutor.execute(() -> {
513 try {
514 InboundPacket pkt = context.inPacket();
515 Ethernet ethPkt = pkt.parsed();
516 if (ethPkt == null) {
517 return;
518 }
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000519 igmpStatisticsManager.getIgmpStats().increaseTotalMsgReceived();
ke han81a38b92017-03-10 18:41:44 +0800520
Esin Karamanb38700c2019-09-17 13:01:25 +0000521 if (ethPkt.getEtherType() != Ethernet.TYPE_IPV4) {
522 return;
523 }
ke han81a38b92017-03-10 18:41:44 +0800524
Esin Karamanb38700c2019-09-17 13:01:25 +0000525 IPv4 ipv4Pkt = (IPv4) ethPkt.getPayload();
ke han81a38b92017-03-10 18:41:44 +0800526
Esin Karamanb38700c2019-09-17 13:01:25 +0000527 if (ipv4Pkt.getProtocol() != IPv4.PROTOCOL_IGMP) {
528 return;
529 }
ke han81a38b92017-03-10 18:41:44 +0800530
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000531 igmpStatisticsManager.getIgmpStats().increaseIgmpValidChecksumCounter();
Esin Karamanb38700c2019-09-17 13:01:25 +0000532 short vlan = ethPkt.getVlanID();
533 DeviceId deviceId = pkt.receivedFrom().deviceId();
ke han81a38b92017-03-10 18:41:44 +0800534
Esin Karaman00e16b72020-02-21 10:32:39 +0000535 if (!isConnectPoint(deviceId, pkt.receivedFrom().port()) &&
536 !getSubscriberAndDeviceInformation(deviceId).isPresent()) {
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000537 log.error("Device not registered in netcfg : {}", deviceId);
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000538 igmpStatisticsManager.getIgmpStats().increaseFailJoinReqInsuffPermissionAccessCounter();
Esin Karamanb38700c2019-09-17 13:01:25 +0000539 return;
540 }
ke han81a38b92017-03-10 18:41:44 +0800541
Esin Karamanb38700c2019-09-17 13:01:25 +0000542 IGMP igmp = (IGMP) ipv4Pkt.getPayload();
Esin Karamance5ce512020-02-25 15:58:14 +0000543
544 Optional<PortNumber> deviceUpLinkOpt = getDeviceUplink(deviceId);
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000545 PortNumber upLinkPort = deviceUpLinkOpt.isPresent() ? deviceUpLinkOpt.get() : null;
Esin Karamanb38700c2019-09-17 13:01:25 +0000546 switch (igmp.getIgmpType()) {
547 case IGMP.TYPE_IGMPV3_MEMBERSHIP_QUERY:
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000548 igmpStatisticsManager.getIgmpStats().increaseIgmpv3MembershipQuery();
Esin Karamanb38700c2019-09-17 13:01:25 +0000549 //Discard Query from OLT’s non-uplink port’s
Esin Karamance5ce512020-02-25 15:58:14 +0000550 if (!pkt.receivedFrom().port().equals(upLinkPort)) {
Esin Karamanb38700c2019-09-17 13:01:25 +0000551 if (isConnectPoint(deviceId, pkt.receivedFrom().port())) {
552 log.info("IGMP Picked up query from connectPoint");
553 //OK to process packet
554 processIgmpConnectPointQuery((IGMPQuery) igmp.getGroups().get(0),
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000555 pkt.receivedFrom(),
556 0xff & igmp.getMaxRespField());
Esin Karamanb38700c2019-09-17 13:01:25 +0000557 break;
558 } else {
559 //Not OK to process packet
Esin Karamance5ce512020-02-25 15:58:14 +0000560 log.warn("IGMP Picked up query from non-uplink port {}", upLinkPort);
Esin Karamanb38700c2019-09-17 13:01:25 +0000561 return;
562 }
563 }
ke han81a38b92017-03-10 18:41:44 +0800564
Esin Karamanb38700c2019-09-17 13:01:25 +0000565 processIgmpQuery((IGMPQuery) igmp.getGroups().get(0), pkt.receivedFrom(),
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000566 0xff & igmp.getMaxRespField());
Esin Karamanb38700c2019-09-17 13:01:25 +0000567 break;
568 case IGMP.TYPE_IGMPV1_MEMBERSHIP_REPORT:
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000569 igmpStatisticsManager.getIgmpStats().increaseIgmpv1MembershipReport();
Esin Karamanb38700c2019-09-17 13:01:25 +0000570 log.debug("IGMP version 1 message types are not currently supported.");
571 break;
572 case IGMP.TYPE_IGMPV3_MEMBERSHIP_REPORT:
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000573 igmpStatisticsManager.getIgmpStats().increaseIgmpv3MembershipReport();
574 processIgmpMessage(pkt, igmp, upLinkPort, vlan);
575 break;
Esin Karamanb38700c2019-09-17 13:01:25 +0000576 case IGMP.TYPE_IGMPV2_MEMBERSHIP_REPORT:
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000577 igmpStatisticsManager.getIgmpStats().increaseIgmpv2MembershipReport();
578 processIgmpMessage(pkt, igmp, upLinkPort, vlan);
579 break;
Esin Karamanb38700c2019-09-17 13:01:25 +0000580 case IGMP.TYPE_IGMPV2_LEAVE_GROUP:
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000581 igmpStatisticsManager.getIgmpStats().increaseIgmpv2LeaveGroup();
582 processIgmpMessage(pkt, igmp, upLinkPort, vlan);
Esin Karamanb38700c2019-09-17 13:01:25 +0000583 break;
ke han81a38b92017-03-10 18:41:44 +0800584
Esin Karamanb38700c2019-09-17 13:01:25 +0000585 default:
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000586 log.warn("Unknown IGMP message type: {}", igmp.getIgmpType());
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000587 igmpStatisticsManager.getIgmpStats().increaseInvalidIgmpMsgReceived();
Sonal Kasliwalf11c0672020-03-18 11:11:50 +0000588 igmpStatisticsManager.getIgmpStats().increaseUnknownIgmpTypePacketsRxCounter();
Esin Karamanb38700c2019-09-17 13:01:25 +0000589 break;
590 }
591
592 } catch (Exception ex) {
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000593 log.error("igmp process error : ", ex);
ke han81a38b92017-03-10 18:41:44 +0800594 }
Esin Karamanb38700c2019-09-17 13:01:25 +0000595 });
ke han81a38b92017-03-10 18:41:44 +0800596 }
597 }
598
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000599 private void processIgmpMessage(InboundPacket pkt, IGMP igmp, PortNumber upLinkPort, short vlan) {
600 //Discard join/leave from OLT’s uplink port’s
601 if (pkt.receivedFrom().port().equals(upLinkPort) ||
602 isConnectPoint(pkt.receivedFrom().deviceId(), pkt.receivedFrom().port())) {
603 log.info("IGMP Picked up join/leave from uplink/connectPoint port");
604 return;
605 }
606
607 Iterator<IGMPGroup> itr = igmp.getGroups().iterator();
608 while (itr.hasNext()) {
609 IGMPGroup group = itr.next();
610 if (group instanceof IGMPMembership) {
611 processIgmpReport((IGMPMembership) group, VlanId.vlanId(vlan),
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000612 pkt.receivedFrom(), igmp.getIgmpType());
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000613 } else {
614 IGMPMembership mgroup = new IGMPMembership(group.getGaddr().getIp4Address());
615 mgroup.setRecordType(igmp.getIgmpType() == IGMP.TYPE_IGMPV2_MEMBERSHIP_REPORT ?
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000616 IGMPMembership.MODE_IS_EXCLUDE :
617 IGMPMembership.MODE_IS_INCLUDE);
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000618 processIgmpReport(mgroup, VlanId.vlanId(vlan),
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000619 pkt.receivedFrom(), igmp.getIgmpType());
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000620 }
621 }
622
623 }
624
ke han81a38b92017-03-10 18:41:44 +0800625 private class IgmpProxyTimerTask extends TimerTask {
626 public void run() {
627 try {
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000628 stateMachineService.timeOut1s();
ke han81a38b92017-03-10 18:41:44 +0800629 queryMembers();
630 } catch (Exception ex) {
631 log.warn("Igmp timer task error : {}", ex.getMessage());
632 }
633 }
634
635 private void queryMembers() {
636 GroupMember groupMember;
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000637 Set<GroupMemberId> keySet = groupMemberStore.getAllGroupMemberIds();
638 for (GroupMemberId key : keySet) {
639 groupMember = groupMemberStore.getGroupMember(key);
640 if (groupMember == null) {
641 continue;
642 }
ke han81a38b92017-03-10 18:41:44 +0800643 DeviceId did = groupMember.getDeviceId();
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000644 if (igmpLeadershipService.isLocalLeader(did)) {
ke han81a38b92017-03-10 18:41:44 +0800645 if (groupMember.isLeave()) {
646 lastQuery(groupMember);
647 } else if (periodicQuery) {
648 periodicQuery(groupMember);
649 }
650 }
651 }
652 }
653
654 private void lastQuery(GroupMember groupMember) {
655 if (groupMember.getLastQueryInterval() < lastQueryInterval) {
656 groupMember.lastQueryInterval(true); // count times
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000657 //put modified group member object to the store
658 groupMemberStore.updateGroupMember(groupMember);
ke han81a38b92017-03-10 18:41:44 +0800659 } else if (groupMember.getLastQueryCount() < lastQueryCount - 1) {
660 sendQuery(groupMember);
661 groupMember.lastQueryInterval(false); // reset count number
662 groupMember.lastQueryCount(true); //count times
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000663 //put modified group member object to the store
664 groupMemberStore.updateGroupMember(groupMember);
ke han81a38b92017-03-10 18:41:44 +0800665 } else if (groupMember.getLastQueryCount() == lastQueryCount - 1) {
666 leaveAction(groupMember);
667 }
668 }
669
670 private void periodicQuery(GroupMember groupMember) {
671 if (groupMember.getKeepAliveQueryInterval() < keepAliveInterval) {
672 groupMember.keepAliveInterval(true);
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000673 //put modified group member object to the store
674 groupMemberStore.updateGroupMember(groupMember);
ke han81a38b92017-03-10 18:41:44 +0800675 } else if (groupMember.getKeepAliveQueryCount() < keepAliveCount) {
676 sendQuery(groupMember);
677 groupMember.keepAliveInterval(false);
678 groupMember.keepAliveQueryCount(true);
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000679 //put modified group member object to the store
680 groupMemberStore.updateGroupMember(groupMember);
ke han81a38b92017-03-10 18:41:44 +0800681 } else if (groupMember.getKeepAliveQueryCount() == keepAliveCount) {
682 leaveAction(groupMember);
683 }
684 }
685
686 }
687
Esin Karaman00e16b72020-02-21 10:32:39 +0000688 public Optional<PortNumber> getDeviceUplink(DeviceId devId) {
689 Device device = deviceService.getDevice(devId);
690 if (device == null || device.serialNumber() == null) {
691 return Optional.empty();
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000692 }
Esin Karaman00e16b72020-02-21 10:32:39 +0000693 Optional<SubscriberAndDeviceInformation> olt = getSubscriberAndDeviceInformation(device.serialNumber());
694 if (olt.isEmpty()) {
695 return Optional.empty();
696 }
697 PortNumber portNumber = PortNumber.portNumber(olt.get().uplinkPort());
698 return validateUpLinkPort(device.id(), portNumber) ?
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000699 Optional.of(portNumber) : Optional.empty();
Esin Karaman00e16b72020-02-21 10:32:39 +0000700 }
701
702 /**
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000703 * @param deviceId device id
Esin Karaman00e16b72020-02-21 10:32:39 +0000704 * @param portNumber port number
705 * @return true if the port name starts with NNI_PREFIX; false otherwise.
706 */
707 public boolean validateUpLinkPort(DeviceId deviceId, PortNumber portNumber) {
708 Port port = deviceService.getPort(deviceId, portNumber);
709 if (port == null) {
710 //port is not discovered by ONOS; so cannot validate it.
711 return false;
712 }
Esin Karamance5ce512020-02-25 15:58:14 +0000713 boolean isValid = port.annotations().value(AnnotationKeys.PORT_NAME) != null &&
Esin Karaman00e16b72020-02-21 10:32:39 +0000714 port.annotations().value(AnnotationKeys.PORT_NAME).startsWith(NNI_PREFIX);
Esin Karamance5ce512020-02-25 15:58:14 +0000715 if (!isValid) {
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000716 log.warn("Port cannot be validated; it is not configured as an NNI port. Device/port: {}/{}",
717 deviceId, portNumber);
Esin Karamance5ce512020-02-25 15:58:14 +0000718 }
719 return isValid;
ke han81a38b92017-03-10 18:41:44 +0800720 }
721
Esin Karamanb38700c2019-09-17 13:01:25 +0000722 public static boolean isIgmpOnPodBasis() {
723 return igmpOnPodBasis;
724 }
725
ke han81a38b92017-03-10 18:41:44 +0800726 private void processFilterObjective(DeviceId devId, PortNumber port, boolean remove) {
Esin Karamaneff10392019-06-27 18:09:13 +0000727 if (!enableIgmpProvisioning) {
728 log.debug("IGMP trap rules won't be installed since enableIgmpProvisioning flag is not set");
729 return;
730 }
ke han81a38b92017-03-10 18:41:44 +0800731 //TODO migrate to packet requests when packet service uses filtering objectives
732 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
733
734 builder = remove ? builder.deny() : builder.permit();
735
736 FilteringObjective igmp = builder
737 .withKey(Criteria.matchInPort(port))
738 .addCondition(Criteria.matchEthType(EthType.EtherType.IPV4.ethType()))
739 .addCondition(Criteria.matchIPProtocol(IPv4.PROTOCOL_IGMP))
740 .withMeta(DefaultTrafficTreatment.builder().setOutput(PortNumber.CONTROLLER).build())
741 .fromApp(appId)
Esin Karamaneff10392019-06-27 18:09:13 +0000742 .withPriority(MAX_PRIORITY)
ke han81a38b92017-03-10 18:41:44 +0800743 .add(new ObjectiveContext() {
744 @Override
745 public void onSuccess(Objective objective) {
Esin Karamaneff10392019-06-27 18:09:13 +0000746 log.info("Igmp filter for {} on {} {}.",
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000747 devId, port, (remove) ? REMOVED : INSTALLED);
ke han81a38b92017-03-10 18:41:44 +0800748 }
749
750 @Override
751 public void onError(Objective objective, ObjectiveError error) {
Esin Karamaneff10392019-06-27 18:09:13 +0000752 log.info("Igmp filter {} for device {} on port {} failed because of {}",
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000753 (remove) ? INSTALLATION : REMOVAL, devId, port,
754 error);
ke han81a38b92017-03-10 18:41:44 +0800755 }
756 });
757
758 flowObjectiveService.filter(devId, igmp);
Esin Karamaneff10392019-06-27 18:09:13 +0000759
ke han81a38b92017-03-10 18:41:44 +0800760 }
761
762 private boolean isConnectPoint(DeviceId device, PortNumber port) {
Deepa Vaddireddy01f33b42017-07-02 13:26:53 +0530763 if (connectPoint != null) {
764 return (connectPointMode && connectPoint.deviceId().equals(device)
765 && connectPoint.port().equals(port));
766 } else {
767 log.info("connectPoint not configured for device {}", device);
768 return false;
769 }
ke han81a38b92017-03-10 18:41:44 +0800770 }
Deepa Vaddireddy01f33b42017-07-02 13:26:53 +0530771
ke han81a38b92017-03-10 18:41:44 +0800772 private boolean isUplink(DeviceId device, PortNumber port) {
Esin Karamance5ce512020-02-25 15:58:14 +0000773 if (connectPointMode) {
774 return false;
775 }
776 Optional<PortNumber> upLinkPort = getDeviceUplink(device);
777 return upLinkPort.isPresent() && upLinkPort.get().equals(port);
ke han81a38b92017-03-10 18:41:44 +0800778 }
779
Esin Karaman00e16b72020-02-21 10:32:39 +0000780 /**
781 * Fetches device information associated with the device serial number from SADIS.
782 *
783 * @param serialNumber serial number of a device
784 * @return device information; an empty Optional otherwise.
785 */
786 private Optional<SubscriberAndDeviceInformation> getSubscriberAndDeviceInformation(String serialNumber) {
787 long start = System.currentTimeMillis();
788 try {
789 return Optional.ofNullable(sadisService.getSubscriberInfoService().get(serialNumber));
790 } finally {
791 if (log.isDebugEnabled()) {
792 // SADIS can call remote systems to fetch device data and this calls can take a long time.
793 // This measurement is just for monitoring these kinds of situations.
794 log.debug("Device fetched from SADIS. Elapsed {} msec", System.currentTimeMillis() - start);
795 }
796
797 }
798 }
799
800 /**
801 * Fetches device information associated with the device serial number from SADIS.
802 *
803 * @param deviceId device id
804 * @return device information; an empty Optional otherwise.
805 */
806 private Optional<SubscriberAndDeviceInformation> getSubscriberAndDeviceInformation(DeviceId deviceId) {
807 Device device = deviceService.getDevice(deviceId);
808 if (device == null || device.serialNumber() == null) {
809 return Optional.empty();
810 }
811 return getSubscriberAndDeviceInformation(device.serialNumber());
812 }
813
ke han81a38b92017-03-10 18:41:44 +0800814 private class InternalDeviceListener implements DeviceListener {
815 @Override
816 public void event(DeviceEvent event) {
817 DeviceId devId = event.subject().id();
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +0000818 Port p = event.port();
Esin Karaman00e16b72020-02-21 10:32:39 +0000819 if (getSubscriberAndDeviceInformation(devId).isEmpty() &&
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +0000820 !(p != null && isConnectPoint(devId, p.number()))) {
ke han81a38b92017-03-10 18:41:44 +0800821 return;
822 }
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +0000823 PortNumber port;
824
ke han81a38b92017-03-10 18:41:44 +0800825 switch (event.type()) {
826
827 case DEVICE_ADDED:
828 case DEVICE_UPDATED:
829 case DEVICE_REMOVED:
830 case DEVICE_SUSPENDED:
831 case DEVICE_AVAILABILITY_CHANGED:
832 case PORT_STATS_UPDATED:
833 break;
834 case PORT_ADDED:
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +0000835 port = p.number();
Esin Karaman00e16b72020-02-21 10:32:39 +0000836 if (getSubscriberAndDeviceInformation(devId).isPresent() &&
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000837 !isUplink(devId, port) && !isConnectPoint(devId, port)) {
ke han81a38b92017-03-10 18:41:44 +0800838 processFilterObjective(devId, port, false);
839 } else if (isUplink(devId, port)) {
840 provisionUplinkFlows();
841 } else if (isConnectPoint(devId, port)) {
842 provisionConnectPointFlows();
843 }
844 break;
845 case PORT_UPDATED:
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +0000846 port = p.number();
Esin Karaman00e16b72020-02-21 10:32:39 +0000847 if (getSubscriberAndDeviceInformation(devId).isPresent() &&
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000848 !isUplink(devId, port) && !isConnectPoint(devId, port)) {
ke han81a38b92017-03-10 18:41:44 +0800849 if (event.port().isEnabled()) {
850 processFilterObjective(devId, port, false);
851 } else {
852 processFilterObjective(devId, port, true);
853 }
854 } else if (isUplink(devId, port)) {
855 if (event.port().isEnabled()) {
856 provisionUplinkFlows(devId);
857 } else {
858 processFilterObjective(devId, port, true);
859 }
860 } else if (isConnectPoint(devId, port)) {
861 if (event.port().isEnabled()) {
862 provisionConnectPointFlows();
863 } else {
864 unprovisionConnectPointFlows();
865 }
866 }
867 break;
868 case PORT_REMOVED:
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +0000869 port = p.number();
ke han81a38b92017-03-10 18:41:44 +0800870 processFilterObjective(devId, port, true);
871 break;
872 default:
873 log.info("Unknown device event {}", event.type());
874 break;
875 }
876 }
877
878 @Override
879 public boolean isRelevant(DeviceEvent event) {
880 return true;
881 }
882 }
883
884 private class InternalNetworkConfigListener implements NetworkConfigListener {
885
886 private void reconfigureNetwork(IgmpproxyConfig cfg) {
Esin Karamaneff10392019-06-27 18:09:13 +0000887 IgmpproxyConfig newCfg = cfg == null ? new IgmpproxyConfig() : cfg;
ke han81a38b92017-03-10 18:41:44 +0800888
889 unSolicitedTimeout = newCfg.unsolicitedTimeOut();
890 maxResp = newCfg.maxResp();
891 keepAliveInterval = newCfg.keepAliveInterval();
892 keepAliveCount = newCfg.keepAliveCount();
893 lastQueryInterval = newCfg.lastQueryInterval();
894 lastQueryCount = newCfg.lastQueryCount();
895 withRAUplink = newCfg.withRAUplink();
896 withRADownlink = newCfg.withRADownlink();
897 igmpCos = newCfg.igmpCos();
898 periodicQuery = newCfg.periodicQuery();
899 fastLeave = newCfg.fastLeave();
ke han29af27b2017-09-08 10:29:12 +0800900 pimSSmInterworking = newCfg.pimSsmInterworking();
Esin Karamaneff10392019-06-27 18:09:13 +0000901 enableIgmpProvisioning = newCfg.enableIgmpProvisioning();
Esin Karamanb38700c2019-09-17 13:01:25 +0000902 igmpOnPodBasis = newCfg.igmpOnPodBasis();
Arjun E Kb0018fd2020-04-07 13:26:40 +0000903 if (newCfg.outgoingIgmpWithV3() != null &&
904 outgoingIgmpWithV3 != newCfg.outgoingIgmpWithV3()) {
905 outgoingIgmpWithV3 = newCfg.outgoingIgmpWithV3();
906 }
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +0000907
908 if (connectPointMode != newCfg.connectPointMode() ||
909 connectPoint != newCfg.connectPoint()) {
ke han81a38b92017-03-10 18:41:44 +0800910 connectPointMode = newCfg.connectPointMode();
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +0000911 connectPoint = newCfg.connectPoint();
ke han81a38b92017-03-10 18:41:44 +0800912 if (connectPointMode) {
913 unprovisionUplinkFlows();
914 provisionConnectPointFlows();
915 } else {
916 unprovisionConnectPointFlows();
917 provisionUplinkFlows();
918 }
919 }
920 if (connectPoint != null) {
Esin Karamaneff10392019-06-27 18:09:13 +0000921 log.info("connect point : {}", connectPoint);
ke han81a38b92017-03-10 18:41:44 +0800922 }
Esin Karamaneff10392019-06-27 18:09:13 +0000923 log.info("mode: {}", connectPointMode);
924
925 getSourceConnectPoint(newCfg);
ke han81a38b92017-03-10 18:41:44 +0800926
927 IgmpSender.getInstance().setIgmpCos(igmpCos);
928 IgmpSender.getInstance().setMaxResp(maxResp);
929 IgmpSender.getInstance().setMvlan(mvlan);
930 IgmpSender.getInstance().setWithRADownlink(withRADownlink);
931 IgmpSender.getInstance().setWithRAUplink(withRAUplink);
Esin Karamaneff10392019-06-27 18:09:13 +0000932 }
ke han81a38b92017-03-10 18:41:44 +0800933
Esin Karamaneff10392019-06-27 18:09:13 +0000934 void getSourceConnectPoint(IgmpproxyConfig cfg) {
935 sourceDeviceAndPort = cfg.getSourceDeviceAndPort();
936 if (sourceDeviceAndPort != null) {
937 log.debug("source parameter configured to {}", sourceDeviceAndPort);
938 }
ke han81a38b92017-03-10 18:41:44 +0800939 }
940
941 public void reconfigureSsmTable(IgmpproxySsmTranslateConfig cfg) {
942 if (cfg == null) {
943 return;
944 }
945 Collection<McastRoute> translations = cfg.getSsmTranslations();
946 for (McastRoute route : translations) {
Esin Karamaneff10392019-06-27 18:09:13 +0000947 ssmTranslateTable.put(route.group().getIp4Address(), route.source().get().getIp4Address());
ke han81a38b92017-03-10 18:41:44 +0800948 }
949 }
950
951 @Override
952 public void event(NetworkConfigEvent event) {
953 switch (event.type()) {
954 case CONFIG_ADDED:
955 case CONFIG_UPDATED:
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800956 // NOTE how to know if something has changed in sadis?
ke han81a38b92017-03-10 18:41:44 +0800957
958 if (event.configClass().equals(IGMPPROXY_CONFIG_CLASS)) {
959 IgmpproxyConfig config = networkConfig.getConfig(appId, IGMPPROXY_CONFIG_CLASS);
960 if (config != null) {
Esin Karamaneff10392019-06-27 18:09:13 +0000961 log.info("igmpproxy config received. {}", config);
ke han81a38b92017-03-10 18:41:44 +0800962 reconfigureNetwork(config);
963 }
964 }
965
966 if (event.configClass().equals(IGMPPROXY_SSM_CONFIG_CLASS)) {
967 IgmpproxySsmTranslateConfig config = networkConfig.getConfig(appId, IGMPPROXY_SSM_CONFIG_CLASS);
968 if (config != null) {
969 reconfigureSsmTable(config);
970 }
971 }
972
973 if (event.configClass().equals(MCAST_CONFIG_CLASS)) {
974 McastConfig config = networkConfig.getConfig(coreAppId, MCAST_CONFIG_CLASS);
975 if (config != null && mvlan != config.egressVlan().toShort()) {
976 mvlan = config.egressVlan().toShort();
Deepa Vaddireddy88134a42017-07-05 12:16:03 +0530977 IgmpSender.getInstance().setMvlan(mvlan);
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +0000978 groupMemberStore.getAllGroupMembers().forEach(m -> leaveAction(m));
ke han81a38b92017-03-10 18:41:44 +0800979 }
980 }
981
982 log.info("Reconfigured");
983 break;
984 case CONFIG_REGISTERED:
985 case CONFIG_UNREGISTERED:
986 break;
987 case CONFIG_REMOVED:
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800988 // NOTE how to know if something has changed in sadis?
ke han81a38b92017-03-10 18:41:44 +0800989 default:
990 break;
991 }
992 }
993 }
994
ke han81a38b92017-03-10 18:41:44 +0800995 private void provisionUplinkFlows(DeviceId deviceId) {
996 if (connectPointMode) {
997 return;
998 }
999
Esin Karaman00e16b72020-02-21 10:32:39 +00001000 Optional<PortNumber> upLink = getDeviceUplink(deviceId);
1001 if (upLink.isPresent()) {
1002 processFilterObjective(deviceId, upLink.get(), false);
1003 }
ke han81a38b92017-03-10 18:41:44 +08001004 }
1005
1006 private void provisionUplinkFlows() {
1007 if (connectPointMode) {
1008 return;
1009 }
Esin Karaman00e16b72020-02-21 10:32:39 +00001010 deviceService.getAvailableDevices().forEach(device -> {
1011 Optional<SubscriberAndDeviceInformation> accessDevice = getSubscriberAndDeviceInformation(device.id());
1012 if (accessDevice.isPresent()) {
1013 provisionUplinkFlows(device.id());
1014 }
1015 });
ke han81a38b92017-03-10 18:41:44 +08001016 }
Esin Karaman00e16b72020-02-21 10:32:39 +00001017
ke han81a38b92017-03-10 18:41:44 +08001018 private void unprovisionUplinkFlows() {
Esin Karaman00e16b72020-02-21 10:32:39 +00001019 deviceService.getAvailableDevices().forEach(device -> {
1020 Optional<SubscriberAndDeviceInformation> accessDevices = getSubscriberAndDeviceInformation(device.id());
1021 if (accessDevices.isPresent()) {
1022 Optional<PortNumber> upLink = getDeviceUplink(device.id());
1023 if (upLink.isPresent()) {
1024 processFilterObjective(device.id(), upLink.get(), true);
1025 }
1026 }
1027 });
ke han81a38b92017-03-10 18:41:44 +08001028 }
1029
1030 private void provisionConnectPointFlows() {
1031 if ((!connectPointMode) || connectPoint == null) {
1032 return;
1033 }
1034
1035 processFilterObjective(connectPoint.deviceId(), connectPoint.port(), false);
1036 }
Ilayda Ozdemir4c5947c2020-05-05 13:14:32 +00001037
ke han81a38b92017-03-10 18:41:44 +08001038 private void unprovisionConnectPointFlows() {
1039 if (connectPoint == null) {
1040 return;
1041 }
1042 processFilterObjective(connectPoint.deviceId(), connectPoint.port(), true);
1043 }
Shubham Sharma47f2caf2020-02-18 12:13:40 +00001044
ke han81a38b92017-03-10 18:41:44 +08001045}