blob: a42d41bf288a0c4af3a28804213d7bedeb760e84 [file] [log] [blame]
David K. Bainbridged77028f2017-08-01 12:47:55 -07001/*
Brian O'Connor4d084702017-08-03 22:45:58 -07002 * Copyright 2017-present Open Networking Foundation
David K. Bainbridged77028f2017-08-01 12:47:55 -07003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
ke han81a38b92017-03-10 18:41:44 +080016package org.opencord.igmpproxy;
17
18import com.google.common.collect.Maps;
Esin Karamaneff10392019-06-27 18:09:13 +000019import com.google.common.collect.Sets;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -080020import org.onosproject.net.Device;
21import org.opencord.sadis.BaseInformationService;
22import org.opencord.sadis.SadisService;
23import org.opencord.sadis.SubscriberAndDeviceInformation;
Carmelo Casconebef302e2019-11-14 19:58:20 -080024import org.osgi.service.component.annotations.Activate;
25import org.osgi.service.component.annotations.Component;
26import org.osgi.service.component.annotations.Deactivate;
27import org.osgi.service.component.annotations.Reference;
28import org.osgi.service.component.annotations.ReferenceCardinality;
ke han81a38b92017-03-10 18:41:44 +080029import org.onlab.packet.EthType;
30import org.onlab.packet.Ethernet;
31import org.onlab.packet.IGMP;
32import org.onlab.packet.IGMPGroup;
33import org.onlab.packet.IGMPMembership;
34import org.onlab.packet.IGMPQuery;
35import org.onlab.packet.IPv4;
36import org.onlab.packet.Ip4Address;
37import org.onlab.packet.IpAddress;
38import org.onlab.packet.VlanId;
39import org.onosproject.core.ApplicationId;
40import org.onosproject.core.CoreService;
ke han81a38b92017-03-10 18:41:44 +080041import org.onosproject.mastership.MastershipService;
42import org.onosproject.net.AnnotationKeys;
43import org.onosproject.net.ConnectPoint;
44import org.onosproject.net.DeviceId;
45import org.onosproject.net.Port;
46import org.onosproject.net.PortNumber;
47import org.onosproject.net.config.ConfigFactory;
48import org.onosproject.net.config.NetworkConfigEvent;
49import org.onosproject.net.config.NetworkConfigListener;
50import org.onosproject.net.config.NetworkConfigRegistry;
Jonathan Hart488e1142018-05-02 17:30:05 -070051import org.onosproject.net.config.basics.McastConfig;
ke han81a38b92017-03-10 18:41:44 +080052import org.onosproject.net.config.basics.SubjectFactories;
53import org.onosproject.net.device.DeviceEvent;
54import org.onosproject.net.device.DeviceListener;
55import org.onosproject.net.device.DeviceService;
56import org.onosproject.net.flow.DefaultTrafficTreatment;
57import org.onosproject.net.flow.FlowRuleService;
58import org.onosproject.net.flow.criteria.Criteria;
59import org.onosproject.net.flowobjective.DefaultFilteringObjective;
60import org.onosproject.net.flowobjective.FilteringObjective;
61import org.onosproject.net.flowobjective.FlowObjectiveService;
62import org.onosproject.net.flowobjective.Objective;
63import org.onosproject.net.flowobjective.ObjectiveContext;
64import org.onosproject.net.flowobjective.ObjectiveError;
Esin Karamaneff10392019-06-27 18:09:13 +000065import org.onosproject.mcast.api.McastRoute;
66import org.onosproject.mcast.api.MulticastRouteService;
ke han81a38b92017-03-10 18:41:44 +080067import org.onosproject.net.packet.InboundPacket;
68import org.onosproject.net.packet.PacketContext;
69import org.onosproject.net.packet.PacketProcessor;
70import org.onosproject.net.packet.PacketService;
ke han81a38b92017-03-10 18:41:44 +080071import org.slf4j.Logger;
72import org.slf4j.LoggerFactory;
73
74import java.util.ArrayList;
75import java.util.Collection;
Esin Karamaneff10392019-06-27 18:09:13 +000076import java.util.HashSet;
ke han81a38b92017-03-10 18:41:44 +080077import java.util.Iterator;
ke han81a38b92017-03-10 18:41:44 +080078import java.util.Map;
Esin Karamaneff10392019-06-27 18:09:13 +000079import java.util.Optional;
ke han81a38b92017-03-10 18:41:44 +080080import java.util.Set;
81import java.util.TimerTask;
82import java.util.concurrent.ConcurrentHashMap;
Esin Karamanb38700c2019-09-17 13:01:25 +000083import java.util.concurrent.ExecutorService;
ke han81a38b92017-03-10 18:41:44 +080084import java.util.concurrent.Executors;
85import java.util.concurrent.ScheduledExecutorService;
86import java.util.concurrent.TimeUnit;
87
Esin Karamanb38700c2019-09-17 13:01:25 +000088import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
89import static org.onlab.util.Tools.groupedThreads;
90
ke han81a38b92017-03-10 18:41:44 +080091/**
92 * Igmp process application, use proxy mode, support first join/ last leave , fast leave
93 * period query and keep alive, packet out igmp message to uplink port features.
94 */
95@Component(immediate = true)
96public class IgmpManager {
97
Matteo Scandolo7482bbe2020-02-12 14:37:09 -080098 private static final String APP_NAME = "org.opencord.igmpproxy";
99
ke han81a38b92017-03-10 18:41:44 +0800100 private static final Class<IgmpproxyConfig> IGMPPROXY_CONFIG_CLASS =
101 IgmpproxyConfig.class;
102 private static final Class<IgmpproxySsmTranslateConfig> IGMPPROXY_SSM_CONFIG_CLASS =
103 IgmpproxySsmTranslateConfig.class;
104 private static final Class<McastConfig> MCAST_CONFIG_CLASS =
105 McastConfig.class;
Esin Karamaneff10392019-06-27 18:09:13 +0000106
ke han81a38b92017-03-10 18:41:44 +0800107 public static Map<String, GroupMember> groupMemberMap = Maps.newConcurrentMap();
108 private static ApplicationId appId;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800109
ke han81a38b92017-03-10 18:41:44 +0800110 private static int unSolicitedTimeout = 3; // unit is 1 sec
111 private static int keepAliveCount = 3;
112 private static int lastQueryInterval = 2; //unit is 1 sec
113 private static int lastQueryCount = 2;
114 private static boolean fastLeave = true;
115 private static boolean withRAUplink = true;
116 private static boolean withRADownlink = false;
117 private static boolean periodicQuery = true;
118 private static short mvlan = 4000;
119 private static byte igmpCos = 7;
120 public static boolean connectPointMode = true;
121 public static ConnectPoint connectPoint = null;
Esin Karamaneff10392019-06-27 18:09:13 +0000122 private static ConnectPoint sourceDeviceAndPort = null;
123 private static boolean enableIgmpProvisioning = false;
Esin Karamanb38700c2019-09-17 13:01:25 +0000124 private static boolean igmpOnPodBasis = false;
Esin Karamaneff10392019-06-27 18:09:13 +0000125
126 private static final Integer MAX_PRIORITY = 10000;
127 private static final String INSTALLED = "installed";
128 private static final String REMOVED = "removed";
129 private static final String INSTALLATION = "installation";
130 private static final String REMOVAL = "removal";
Esin Karaman00e16b72020-02-21 10:32:39 +0000131 private static final String NNI_PREFIX = "nni";
ke han81a38b92017-03-10 18:41:44 +0800132
ke han29af27b2017-09-08 10:29:12 +0800133 private static boolean pimSSmInterworking = false;
134 private static final String DEFAULT_PIMSSM_HOST = "127.0.0.1";
ke han81a38b92017-03-10 18:41:44 +0800135 private final ScheduledExecutorService scheduledExecutorService =
136 Executors.newScheduledThreadPool(1);
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800137
Carmelo Casconebef302e2019-11-14 19:58:20 -0800138 @Reference(cardinality = ReferenceCardinality.MANDATORY)
ke han81a38b92017-03-10 18:41:44 +0800139 protected CoreService coreService;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800140
Carmelo Casconebef302e2019-11-14 19:58:20 -0800141 @Reference(cardinality = ReferenceCardinality.MANDATORY)
ke han81a38b92017-03-10 18:41:44 +0800142 protected PacketService packetService;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800143
Carmelo Casconebef302e2019-11-14 19:58:20 -0800144 @Reference(cardinality = ReferenceCardinality.MANDATORY)
ke han81a38b92017-03-10 18:41:44 +0800145 protected MastershipService mastershipService;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800146
Carmelo Casconebef302e2019-11-14 19:58:20 -0800147 @Reference(cardinality = ReferenceCardinality.MANDATORY)
ke han81a38b92017-03-10 18:41:44 +0800148 protected FlowRuleService flowRuleService;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800149
Carmelo Casconebef302e2019-11-14 19:58:20 -0800150 @Reference(cardinality = ReferenceCardinality.MANDATORY)
ke han81a38b92017-03-10 18:41:44 +0800151 protected DeviceService deviceService;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800152
Carmelo Casconebef302e2019-11-14 19:58:20 -0800153 @Reference(cardinality = ReferenceCardinality.MANDATORY)
ke han81a38b92017-03-10 18:41:44 +0800154 protected FlowObjectiveService flowObjectiveService;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800155
Carmelo Casconebef302e2019-11-14 19:58:20 -0800156 @Reference(cardinality = ReferenceCardinality.MANDATORY)
ke han81a38b92017-03-10 18:41:44 +0800157 protected NetworkConfigRegistry networkConfig;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800158
Carmelo Casconebef302e2019-11-14 19:58:20 -0800159 @Reference(cardinality = ReferenceCardinality.MANDATORY)
ke han81a38b92017-03-10 18:41:44 +0800160 protected MulticastRouteService multicastService;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800161
162 @Reference(cardinality = ReferenceCardinality.MANDATORY)
163 protected SadisService sadisService;
164
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000165 @Reference(cardinality = ReferenceCardinality.MANDATORY)
166 protected IgmpStatisticsService igmpStatisticsManager;
167
ke han81a38b92017-03-10 18:41:44 +0800168 private IgmpPacketProcessor processor = new IgmpPacketProcessor();
169 private Logger log = LoggerFactory.getLogger(getClass());
170 private ApplicationId coreAppId;
171 private Map<Ip4Address, Ip4Address> ssmTranslateTable = new ConcurrentHashMap<>();
Esin Karamaneff10392019-06-27 18:09:13 +0000172
ke han81a38b92017-03-10 18:41:44 +0800173 private InternalNetworkConfigListener configListener =
174 new InternalNetworkConfigListener();
175 private DeviceListener deviceListener = new InternalDeviceListener();
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800176
ke han81a38b92017-03-10 18:41:44 +0800177 private ConfigFactory<ApplicationId, IgmpproxyConfig> igmpproxyConfigFactory =
178 new ConfigFactory<ApplicationId, IgmpproxyConfig>(
179 SubjectFactories.APP_SUBJECT_FACTORY, IGMPPROXY_CONFIG_CLASS, "igmpproxy") {
180 @Override
181 public IgmpproxyConfig createConfig() {
182 return new IgmpproxyConfig();
183 }
184 };
185 private ConfigFactory<ApplicationId, IgmpproxySsmTranslateConfig> igmpproxySsmConfigFactory =
186 new ConfigFactory<ApplicationId, IgmpproxySsmTranslateConfig>(
187 SubjectFactories.APP_SUBJECT_FACTORY, IGMPPROXY_SSM_CONFIG_CLASS, "ssmTranslate", true) {
188 @Override
189 public IgmpproxySsmTranslateConfig createConfig() {
190 return new IgmpproxySsmTranslateConfig();
191 }
192 };
Esin Karamaneff10392019-06-27 18:09:13 +0000193
ke han81a38b92017-03-10 18:41:44 +0800194 private int maxResp = 10; //unit is 1 sec
195 private int keepAliveInterval = 120; //unit is 1 sec
196
Esin Karamanb38700c2019-09-17 13:01:25 +0000197 private ExecutorService eventExecutor;
198
ke han81a38b92017-03-10 18:41:44 +0800199 public static int getUnsolicitedTimeout() {
200 return unSolicitedTimeout;
201 }
202
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800203 protected BaseInformationService<SubscriberAndDeviceInformation> subsService;
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800204
ke han81a38b92017-03-10 18:41:44 +0800205 @Activate
206 protected void activate() {
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800207 appId = coreService.registerApplication(APP_NAME);
ke han81a38b92017-03-10 18:41:44 +0800208 coreAppId = coreService.registerApplication(CoreService.CORE_APP_NAME);
209 packetService.addProcessor(processor, PacketProcessor.director(4));
210 IgmpSender.init(packetService, mastershipService);
211
ke han81a38b92017-03-10 18:41:44 +0800212 networkConfig.registerConfigFactory(igmpproxySsmConfigFactory);
213 networkConfig.registerConfigFactory(igmpproxyConfigFactory);
214 networkConfig.addListener(configListener);
215
216 configListener.reconfigureNetwork(networkConfig.getConfig(appId, IGMPPROXY_CONFIG_CLASS));
217 configListener.reconfigureSsmTable(networkConfig.getConfig(appId, IGMPPROXY_SSM_CONFIG_CLASS));
218
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800219 subsService = sadisService.getSubscriberInfoService();
ke han81a38b92017-03-10 18:41:44 +0800220 if (connectPointMode) {
221 provisionConnectPointFlows();
222 } else {
223 provisionUplinkFlows();
224 }
225
226 McastConfig config = networkConfig.getConfig(coreAppId, MCAST_CONFIG_CLASS);
227 if (config != null) {
228 mvlan = config.egressVlan().toShort();
Deepa Vaddireddy88134a42017-07-05 12:16:03 +0530229 IgmpSender.getInstance().setMvlan(mvlan);
ke han81a38b92017-03-10 18:41:44 +0800230 }
231 deviceService.addListener(deviceListener);
Sonal Kasliwalddc3ff22019-11-18 11:52:49 +0000232 scheduledExecutorService.scheduleAtFixedRate(new IgmpProxyTimerTask(), 1000, 1000, TimeUnit.MILLISECONDS);
Esin Karamanb38700c2019-09-17 13:01:25 +0000233 eventExecutor = newSingleThreadScheduledExecutor(groupedThreads("cord/igmpproxy",
234 "events-igmp-%d", log));
ke han81a38b92017-03-10 18:41:44 +0800235 log.info("Started");
236 }
237
238 @Deactivate
239 protected void deactivate() {
240 scheduledExecutorService.shutdown();
Esin Karamanb38700c2019-09-17 13:01:25 +0000241 eventExecutor.shutdown();
ke han81a38b92017-03-10 18:41:44 +0800242
243 // de-register and null our handler
244 networkConfig.removeListener(configListener);
ke han81a38b92017-03-10 18:41:44 +0800245 networkConfig.unregisterConfigFactory(igmpproxyConfigFactory);
246 networkConfig.unregisterConfigFactory(igmpproxySsmConfigFactory);
247 deviceService.removeListener(deviceListener);
248 packetService.removeProcessor(processor);
249 flowRuleService.removeFlowRulesById(appId);
ke han81a38b92017-03-10 18:41:44 +0800250 log.info("Stopped");
251 }
252
253 protected Ip4Address getDeviceIp(DeviceId ofDeviceId) {
254 try {
255 String[] mgmtAddress = deviceService.getDevice(ofDeviceId)
256 .annotations().value(AnnotationKeys.MANAGEMENT_ADDRESS).split(":");
257 return Ip4Address.valueOf(mgmtAddress[0]);
258 } catch (Exception ex) {
259 log.info("No valid Ipaddress for " + ofDeviceId.toString());
260 return null;
261 }
262 }
263
264 private void processIgmpQuery(IGMPQuery igmpQuery, ConnectPoint cp, int maxResp) {
265
266 DeviceId deviceId = cp.deviceId();
267 Ip4Address gAddr = igmpQuery.getGaddr().getIp4Address();
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000268 maxResp = calculateMaxResp(maxResp);
269 if (gAddr != null && !gAddr.isZero()) {
270 StateMachine.specialQuery(deviceId, gAddr, maxResp);
271 } else {
272 StateMachine.generalQuery(deviceId, maxResp);
273 }
274 }
ke han81a38b92017-03-10 18:41:44 +0800275
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000276 private void processIgmpConnectPointQuery(IGMPQuery igmpQuery, ConnectPoint cp, int maxResp) {
277
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000278 Ip4Address gAddr = igmpQuery.getGaddr().getIp4Address();
Esin Karaman00e16b72020-02-21 10:32:39 +0000279 final int maxResponseTime = calculateMaxResp(maxResp);
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000280 //The query is received on the ConnectPoint
281 // send query accordingly to the registered OLT devices.
282 if (gAddr != null && !gAddr.isZero()) {
Esin Karaman00e16b72020-02-21 10:32:39 +0000283 deviceService.getAvailableDevices().forEach(device -> {
284 Optional<SubscriberAndDeviceInformation> accessDevice = getSubscriberAndDeviceInformation(device.id());
285 if (accessDevice.isPresent()) {
286 StateMachine.specialQuery(device.id(), gAddr, maxResponseTime);
287 }
288 });
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000289 } else {
290 //Don't know which group is targeted by the query
291 //So query all the members(in all the OLTs) and proxy their reports
Esin Karaman00e16b72020-02-21 10:32:39 +0000292 StateMachine.generalQuery(maxResponseTime);
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000293 }
294 }
295
296
297 private int calculateMaxResp(int maxResp) {
ke han81a38b92017-03-10 18:41:44 +0800298 if (maxResp >= 128) {
299 int mant = maxResp & 0xf;
300 int exp = (maxResp >> 4) & 0x7;
301 maxResp = (mant | 0x10) << (exp + 3);
302 }
303
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000304 return (maxResp + 5) / 10;
ke han81a38b92017-03-10 18:41:44 +0800305 }
306
307 private Ip4Address ssmTranslateRoute(IpAddress group) {
308 return ssmTranslateTable.get(group);
309 }
310
311 private void processIgmpReport(IGMPMembership igmpGroup, VlanId vlan, ConnectPoint cp, byte igmpType) {
312 DeviceId deviceId = cp.deviceId();
313 PortNumber portNumber = cp.port();
314
315 Ip4Address groupIp = igmpGroup.getGaddr().getIp4Address();
316 if (!groupIp.isMulticast()) {
317 log.info(groupIp.toString() + " is not a valid group address");
318 return;
319 }
320 Ip4Address srcIp = getDeviceIp(deviceId);
321
322 byte recordType = igmpGroup.getRecordType();
323 boolean join = false;
324
325 ArrayList<Ip4Address> sourceList = new ArrayList<>();
326
327 if (igmpGroup.getSources().size() > 0) {
328 igmpGroup.getSources().forEach(source -> sourceList.add(source.getIp4Address()));
329 if (recordType == IGMPMembership.CHANGE_TO_EXCLUDE_MODE ||
330 recordType == IGMPMembership.MODE_IS_EXCLUDE ||
331 recordType == IGMPMembership.BLOCK_OLD_SOURCES) {
332 join = false;
333 } else if (recordType == IGMPMembership.CHANGE_TO_INCLUDE_MODE ||
334 recordType == IGMPMembership.MODE_IS_INCLUDE ||
335 recordType == IGMPMembership.ALLOW_NEW_SOURCES) {
336 join = true;
337 }
338 } else {
ke han29af27b2017-09-08 10:29:12 +0800339 IpAddress src = null;
340 if (pimSSmInterworking) {
341 src = ssmTranslateRoute(groupIp);
342 if (src == null) {
343 log.info("no ssm translate for group " + groupIp.toString());
344 return;
345 }
346 } else {
347 src = IpAddress.valueOf(DEFAULT_PIMSSM_HOST);
ke han81a38b92017-03-10 18:41:44 +0800348 }
349 sourceList.add(src.getIp4Address());
350 if (recordType == IGMPMembership.CHANGE_TO_EXCLUDE_MODE ||
351 recordType == IGMPMembership.MODE_IS_EXCLUDE ||
352 recordType == IGMPMembership.BLOCK_OLD_SOURCES) {
353 join = true;
354 } else if (recordType == IGMPMembership.CHANGE_TO_INCLUDE_MODE ||
355 recordType == IGMPMembership.MODE_IS_INCLUDE ||
356 recordType == IGMPMembership.ALLOW_NEW_SOURCES) {
357 join = false;
358 }
359 }
360 String groupMemberKey = GroupMember.getkey(groupIp, deviceId, portNumber);
361 GroupMember groupMember = groupMemberMap.get(groupMemberKey);
362
363 if (join) {
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000364 igmpStatisticsManager.getIgmpStats().increaseIgmpJoinReq();
ke han81a38b92017-03-10 18:41:44 +0800365 if (groupMember == null) {
Esin Karamaneff10392019-06-27 18:09:13 +0000366 Optional<ConnectPoint> sourceConfigured = getSource();
367 if (!sourceConfigured.isPresent()) {
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000368 igmpStatisticsManager.getIgmpStats().increaseIgmpFailJoinReq();
Esin Karamaneff10392019-06-27 18:09:13 +0000369 log.warn("Unable to process IGMP Join from {} since no source " +
370 "configuration is found.", deviceId);
371 return;
372 }
Esin Karaman00e16b72020-02-21 10:32:39 +0000373
374 Optional<PortNumber> deviceUplink = getDeviceUplink(deviceId);
375 if (deviceUplink.isEmpty()) {
376 log.warn("Unable to process IGMP Join since uplink port " +
377 "of the device {} is not found.", deviceId);
378 return;
379 }
380
381 if (igmpType == IGMP.TYPE_IGMPV2_MEMBERSHIP_REPORT) {
382 groupMember = new GroupMember(groupIp, vlan, deviceId, portNumber, true);
383 } else {
384 groupMember = new GroupMember(groupIp, vlan, deviceId, portNumber, false);
385 }
386
Esin Karamaneff10392019-06-27 18:09:13 +0000387 HashSet<ConnectPoint> sourceConnectPoints = Sets.newHashSet(sourceConfigured.get());
388
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000389 boolean isJoined = StateMachine.join(deviceId, groupIp, srcIp, deviceUplink.get());
390 if (isJoined) {
391 igmpStatisticsManager.getIgmpStats().increaseIgmpSuccessJoinRejoinReq();
392 } else {
393 igmpStatisticsManager.getIgmpStats().increaseIgmpFailJoinReq();
394 }
ke han81a38b92017-03-10 18:41:44 +0800395 groupMemberMap.put(groupMemberKey, groupMember);
396 groupMember.updateList(recordType, sourceList);
Esin Karamaneff10392019-06-27 18:09:13 +0000397 groupMember.getSourceList().forEach(source -> {
398 McastRoute route = new McastRoute(source, groupIp, McastRoute.Type.IGMP);
399 //add route
400 multicastService.add(route);
401 //add source to the route
402 multicastService.addSources(route, Sets.newHashSet(sourceConnectPoints));
403 //add sink to the route
404 multicastService.addSinks(route, Sets.newHashSet(cp));
405 });
406
ke han81a38b92017-03-10 18:41:44 +0800407 }
408 groupMember.resetAllTimers();
409 groupMember.updateList(recordType, sourceList);
410 groupMember.setLeave(false);
411 } else {
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000412 igmpStatisticsManager.getIgmpStats().increaseIgmpLeaveReq();
ke han81a38b92017-03-10 18:41:44 +0800413 if (groupMember == null) {
414 log.info("receive leave but no instance, group " + groupIp.toString() +
415 " device:" + deviceId.toString() + " port:" + portNumber.toString());
416 return;
417 } else {
418 groupMember.setLeave(true);
419 if (fastLeave) {
420 leaveAction(groupMember);
421 } else {
422 sendQuery(groupMember);
423 }
424 }
425 }
426 }
427
428 private void leaveAction(GroupMember groupMember) {
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000429 igmpStatisticsManager.getIgmpStats().increaseIgmpDisconnect();
ke han81a38b92017-03-10 18:41:44 +0800430 ConnectPoint cp = new ConnectPoint(groupMember.getDeviceId(), groupMember.getPortNumber());
431 StateMachine.leave(groupMember.getDeviceId(), groupMember.getGroupIp());
Esin Karamaneff10392019-06-27 18:09:13 +0000432 groupMember.getSourceList().forEach(source -> multicastService.removeSinks(
ke han81a38b92017-03-10 18:41:44 +0800433 new McastRoute(source, groupMember.getGroupIp(),
Esin Karamaneff10392019-06-27 18:09:13 +0000434 McastRoute.Type.IGMP), Sets.newHashSet(cp)));
ke han81a38b92017-03-10 18:41:44 +0800435 groupMemberMap.remove(groupMember.getId());
436 }
437
438 private void sendQuery(GroupMember groupMember) {
439 Ethernet ethpkt;
440 Ip4Address srcIp = getDeviceIp(groupMember.getDeviceId());
441 if (groupMember.getv2()) {
442 ethpkt = IgmpSender.getInstance().buildIgmpV2Query(groupMember.getGroupIp(), srcIp);
443 } else {
444 ethpkt = IgmpSender.getInstance().buildIgmpV3Query(groupMember.getGroupIp(), srcIp);
445 }
446 IgmpSender.getInstance().sendIgmpPacket(ethpkt, groupMember.getDeviceId(), groupMember.getPortNumber());
447 }
448
Esin Karamaneff10392019-06-27 18:09:13 +0000449 /**
450 * @return connect point of the source if configured; and empty Optional otherwise.
451 */
452 public static Optional<ConnectPoint> getSource() {
453 return sourceDeviceAndPort == null ? Optional.empty() :
454 Optional.of(sourceDeviceAndPort);
ke han81a38b92017-03-10 18:41:44 +0800455 }
456
457 /**
458 * Packet processor responsible for forwarding packets along their paths.
459 */
460 private class IgmpPacketProcessor implements PacketProcessor {
461 @Override
462 public void process(PacketContext context) {
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000463
Esin Karamanb38700c2019-09-17 13:01:25 +0000464 eventExecutor.execute(() -> {
465 try {
466 InboundPacket pkt = context.inPacket();
467 Ethernet ethPkt = pkt.parsed();
468 if (ethPkt == null) {
469 return;
470 }
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000471 igmpStatisticsManager.getIgmpStats().increaseTotalMsgReceived();
ke han81a38b92017-03-10 18:41:44 +0800472
Esin Karamanb38700c2019-09-17 13:01:25 +0000473 if (ethPkt.getEtherType() != Ethernet.TYPE_IPV4) {
474 return;
475 }
ke han81a38b92017-03-10 18:41:44 +0800476
Esin Karamanb38700c2019-09-17 13:01:25 +0000477 IPv4 ipv4Pkt = (IPv4) ethPkt.getPayload();
ke han81a38b92017-03-10 18:41:44 +0800478
Esin Karamanb38700c2019-09-17 13:01:25 +0000479 if (ipv4Pkt.getProtocol() != IPv4.PROTOCOL_IGMP) {
480 return;
481 }
ke han81a38b92017-03-10 18:41:44 +0800482
Esin Karamanb38700c2019-09-17 13:01:25 +0000483 short vlan = ethPkt.getVlanID();
484 DeviceId deviceId = pkt.receivedFrom().deviceId();
ke han81a38b92017-03-10 18:41:44 +0800485
Esin Karaman00e16b72020-02-21 10:32:39 +0000486 if (!isConnectPoint(deviceId, pkt.receivedFrom().port()) &&
487 !getSubscriberAndDeviceInformation(deviceId).isPresent()) {
Esin Karamanb38700c2019-09-17 13:01:25 +0000488 log.error("Device not registered in netcfg :" + deviceId.toString());
489 return;
490 }
ke han81a38b92017-03-10 18:41:44 +0800491
Esin Karamanb38700c2019-09-17 13:01:25 +0000492 IGMP igmp = (IGMP) ipv4Pkt.getPayload();
Esin Karamance5ce512020-02-25 15:58:14 +0000493
494 Optional<PortNumber> deviceUpLinkOpt = getDeviceUplink(deviceId);
495 PortNumber upLinkPort = deviceUpLinkOpt.isPresent() ? deviceUpLinkOpt.get() : null;
Esin Karamanb38700c2019-09-17 13:01:25 +0000496 switch (igmp.getIgmpType()) {
497 case IGMP.TYPE_IGMPV3_MEMBERSHIP_QUERY:
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000498 igmpStatisticsManager.getIgmpStats().increaseIgmpv3MembershipQuery();
Esin Karamanb38700c2019-09-17 13:01:25 +0000499 //Discard Query from OLT’s non-uplink port’s
Esin Karamance5ce512020-02-25 15:58:14 +0000500 if (!pkt.receivedFrom().port().equals(upLinkPort)) {
Esin Karamanb38700c2019-09-17 13:01:25 +0000501 if (isConnectPoint(deviceId, pkt.receivedFrom().port())) {
502 log.info("IGMP Picked up query from connectPoint");
503 //OK to process packet
504 processIgmpConnectPointQuery((IGMPQuery) igmp.getGroups().get(0),
505 pkt.receivedFrom(),
506 0xff & igmp.getMaxRespField());
507 break;
508 } else {
509 //Not OK to process packet
Esin Karamance5ce512020-02-25 15:58:14 +0000510 log.warn("IGMP Picked up query from non-uplink port {}", upLinkPort);
Esin Karamanb38700c2019-09-17 13:01:25 +0000511 return;
512 }
513 }
ke han81a38b92017-03-10 18:41:44 +0800514
Esin Karamanb38700c2019-09-17 13:01:25 +0000515 processIgmpQuery((IGMPQuery) igmp.getGroups().get(0), pkt.receivedFrom(),
516 0xff & igmp.getMaxRespField());
517 break;
518 case IGMP.TYPE_IGMPV1_MEMBERSHIP_REPORT:
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000519 igmpStatisticsManager.getIgmpStats().increaseIgmpv1MembershipReport();
Esin Karamanb38700c2019-09-17 13:01:25 +0000520 log.debug("IGMP version 1 message types are not currently supported.");
521 break;
522 case IGMP.TYPE_IGMPV3_MEMBERSHIP_REPORT:
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000523 igmpStatisticsManager.getIgmpStats().increaseIgmpv3MembershipReport();
524 processIgmpMessage(pkt, igmp, upLinkPort, vlan);
525 break;
Esin Karamanb38700c2019-09-17 13:01:25 +0000526 case IGMP.TYPE_IGMPV2_MEMBERSHIP_REPORT:
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000527 igmpStatisticsManager.getIgmpStats().increaseIgmpv2MembershipReport();
528 processIgmpMessage(pkt, igmp, upLinkPort, vlan);
529 break;
Esin Karamanb38700c2019-09-17 13:01:25 +0000530 case IGMP.TYPE_IGMPV2_LEAVE_GROUP:
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000531 igmpStatisticsManager.getIgmpStats().increaseIgmpv2LeaveGroup();
532 processIgmpMessage(pkt, igmp, upLinkPort, vlan);
Esin Karamanb38700c2019-09-17 13:01:25 +0000533 break;
ke han81a38b92017-03-10 18:41:44 +0800534
Esin Karamanb38700c2019-09-17 13:01:25 +0000535 default:
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000536 log.warn("wrong IGMP v3 type:" + igmp.getIgmpType());
537 igmpStatisticsManager.getIgmpStats().increaseInvalidIgmpMsgReceived();
Esin Karamanb38700c2019-09-17 13:01:25 +0000538 break;
539 }
540
541 } catch (Exception ex) {
542 log.error("igmp process error : {} ", ex);
ke han81a38b92017-03-10 18:41:44 +0800543 }
Esin Karamanb38700c2019-09-17 13:01:25 +0000544 });
ke han81a38b92017-03-10 18:41:44 +0800545 }
546 }
547
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000548 private void processIgmpMessage(InboundPacket pkt, IGMP igmp, PortNumber upLinkPort, short vlan) {
549 //Discard join/leave from OLT’s uplink port’s
550 if (pkt.receivedFrom().port().equals(upLinkPort) ||
551 isConnectPoint(pkt.receivedFrom().deviceId(), pkt.receivedFrom().port())) {
552 log.info("IGMP Picked up join/leave from uplink/connectPoint port");
553 return;
554 }
555
556 Iterator<IGMPGroup> itr = igmp.getGroups().iterator();
557 while (itr.hasNext()) {
558 IGMPGroup group = itr.next();
559 if (group instanceof IGMPMembership) {
560 processIgmpReport((IGMPMembership) group, VlanId.vlanId(vlan),
561 pkt.receivedFrom(), igmp.getIgmpType());
562 } else {
563 IGMPMembership mgroup = new IGMPMembership(group.getGaddr().getIp4Address());
564 mgroup.setRecordType(igmp.getIgmpType() == IGMP.TYPE_IGMPV2_MEMBERSHIP_REPORT ?
565 IGMPMembership.MODE_IS_EXCLUDE :
566 IGMPMembership.MODE_IS_INCLUDE);
567 processIgmpReport(mgroup, VlanId.vlanId(vlan),
568 pkt.receivedFrom(), igmp.getIgmpType());
569 }
570 }
571
572 }
573
ke han81a38b92017-03-10 18:41:44 +0800574 private class IgmpProxyTimerTask extends TimerTask {
575 public void run() {
576 try {
577 IgmpTimer.timeOut1s();
578 queryMembers();
579 } catch (Exception ex) {
580 log.warn("Igmp timer task error : {}", ex.getMessage());
581 }
582 }
583
584 private void queryMembers() {
585 GroupMember groupMember;
586 Set groupMemberSet = groupMemberMap.entrySet();
587 Iterator itr = groupMemberSet.iterator();
588 while (itr.hasNext()) {
589 Map.Entry entry = (Map.Entry) itr.next();
590 groupMember = (GroupMember) entry.getValue();
591 DeviceId did = groupMember.getDeviceId();
592 if (mastershipService.isLocalMaster(did)) {
593 if (groupMember.isLeave()) {
594 lastQuery(groupMember);
595 } else if (periodicQuery) {
596 periodicQuery(groupMember);
597 }
598 }
599 }
600 }
601
602 private void lastQuery(GroupMember groupMember) {
603 if (groupMember.getLastQueryInterval() < lastQueryInterval) {
604 groupMember.lastQueryInterval(true); // count times
605 } else if (groupMember.getLastQueryCount() < lastQueryCount - 1) {
606 sendQuery(groupMember);
607 groupMember.lastQueryInterval(false); // reset count number
608 groupMember.lastQueryCount(true); //count times
609 } else if (groupMember.getLastQueryCount() == lastQueryCount - 1) {
610 leaveAction(groupMember);
611 }
612 }
613
614 private void periodicQuery(GroupMember groupMember) {
615 if (groupMember.getKeepAliveQueryInterval() < keepAliveInterval) {
616 groupMember.keepAliveInterval(true);
617 } else if (groupMember.getKeepAliveQueryCount() < keepAliveCount) {
618 sendQuery(groupMember);
619 groupMember.keepAliveInterval(false);
620 groupMember.keepAliveQueryCount(true);
621 } else if (groupMember.getKeepAliveQueryCount() == keepAliveCount) {
622 leaveAction(groupMember);
623 }
624 }
625
626 }
627
Esin Karaman00e16b72020-02-21 10:32:39 +0000628 public Optional<PortNumber> getDeviceUplink(DeviceId devId) {
629 Device device = deviceService.getDevice(devId);
630 if (device == null || device.serialNumber() == null) {
631 return Optional.empty();
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000632 }
Esin Karaman00e16b72020-02-21 10:32:39 +0000633 Optional<SubscriberAndDeviceInformation> olt = getSubscriberAndDeviceInformation(device.serialNumber());
634 if (olt.isEmpty()) {
635 return Optional.empty();
636 }
637 PortNumber portNumber = PortNumber.portNumber(olt.get().uplinkPort());
638 return validateUpLinkPort(device.id(), portNumber) ?
639 Optional.of(portNumber) : Optional.empty();
640 }
641
642 /**
643 *
644 * @param deviceId device id
645 * @param portNumber port number
646 * @return true if the port name starts with NNI_PREFIX; false otherwise.
647 */
648 public boolean validateUpLinkPort(DeviceId deviceId, PortNumber portNumber) {
649 Port port = deviceService.getPort(deviceId, portNumber);
650 if (port == null) {
651 //port is not discovered by ONOS; so cannot validate it.
652 return false;
653 }
Esin Karamance5ce512020-02-25 15:58:14 +0000654 boolean isValid = port.annotations().value(AnnotationKeys.PORT_NAME) != null &&
Esin Karaman00e16b72020-02-21 10:32:39 +0000655 port.annotations().value(AnnotationKeys.PORT_NAME).startsWith(NNI_PREFIX);
Esin Karamance5ce512020-02-25 15:58:14 +0000656 if (!isValid) {
657 log.warn("Port cannot be validated; it is not configured as an NNI port." +
658 "Device/port: {}/{}", deviceId, portNumber);
659 }
660 return isValid;
ke han81a38b92017-03-10 18:41:44 +0800661 }
662
Esin Karamanb38700c2019-09-17 13:01:25 +0000663 public static boolean isIgmpOnPodBasis() {
664 return igmpOnPodBasis;
665 }
666
ke han81a38b92017-03-10 18:41:44 +0800667 private void processFilterObjective(DeviceId devId, PortNumber port, boolean remove) {
Esin Karamaneff10392019-06-27 18:09:13 +0000668 if (!enableIgmpProvisioning) {
669 log.debug("IGMP trap rules won't be installed since enableIgmpProvisioning flag is not set");
670 return;
671 }
ke han81a38b92017-03-10 18:41:44 +0800672 //TODO migrate to packet requests when packet service uses filtering objectives
673 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
674
675 builder = remove ? builder.deny() : builder.permit();
676
677 FilteringObjective igmp = builder
678 .withKey(Criteria.matchInPort(port))
679 .addCondition(Criteria.matchEthType(EthType.EtherType.IPV4.ethType()))
680 .addCondition(Criteria.matchIPProtocol(IPv4.PROTOCOL_IGMP))
681 .withMeta(DefaultTrafficTreatment.builder().setOutput(PortNumber.CONTROLLER).build())
682 .fromApp(appId)
Esin Karamaneff10392019-06-27 18:09:13 +0000683 .withPriority(MAX_PRIORITY)
ke han81a38b92017-03-10 18:41:44 +0800684 .add(new ObjectiveContext() {
685 @Override
686 public void onSuccess(Objective objective) {
Esin Karamaneff10392019-06-27 18:09:13 +0000687 log.info("Igmp filter for {} on {} {}.",
688 devId, port, (remove) ? REMOVED : INSTALLED);
ke han81a38b92017-03-10 18:41:44 +0800689 }
690
691 @Override
692 public void onError(Objective objective, ObjectiveError error) {
Esin Karamaneff10392019-06-27 18:09:13 +0000693 log.info("Igmp filter {} for device {} on port {} failed because of {}",
694 (remove) ? INSTALLATION : REMOVAL, devId, port,
695 error);
ke han81a38b92017-03-10 18:41:44 +0800696 }
697 });
698
699 flowObjectiveService.filter(devId, igmp);
Esin Karamaneff10392019-06-27 18:09:13 +0000700
ke han81a38b92017-03-10 18:41:44 +0800701 }
702
703 private boolean isConnectPoint(DeviceId device, PortNumber port) {
Deepa Vaddireddy01f33b42017-07-02 13:26:53 +0530704 if (connectPoint != null) {
705 return (connectPointMode && connectPoint.deviceId().equals(device)
706 && connectPoint.port().equals(port));
707 } else {
708 log.info("connectPoint not configured for device {}", device);
709 return false;
710 }
ke han81a38b92017-03-10 18:41:44 +0800711 }
Deepa Vaddireddy01f33b42017-07-02 13:26:53 +0530712
ke han81a38b92017-03-10 18:41:44 +0800713 private boolean isUplink(DeviceId device, PortNumber port) {
Esin Karamance5ce512020-02-25 15:58:14 +0000714 if (connectPointMode) {
715 return false;
716 }
717 Optional<PortNumber> upLinkPort = getDeviceUplink(device);
718 return upLinkPort.isPresent() && upLinkPort.get().equals(port);
ke han81a38b92017-03-10 18:41:44 +0800719 }
720
Esin Karaman00e16b72020-02-21 10:32:39 +0000721 /**
722 * Fetches device information associated with the device serial number from SADIS.
723 *
724 * @param serialNumber serial number of a device
725 * @return device information; an empty Optional otherwise.
726 */
727 private Optional<SubscriberAndDeviceInformation> getSubscriberAndDeviceInformation(String serialNumber) {
728 long start = System.currentTimeMillis();
729 try {
730 return Optional.ofNullable(sadisService.getSubscriberInfoService().get(serialNumber));
731 } finally {
732 if (log.isDebugEnabled()) {
733 // SADIS can call remote systems to fetch device data and this calls can take a long time.
734 // This measurement is just for monitoring these kinds of situations.
735 log.debug("Device fetched from SADIS. Elapsed {} msec", System.currentTimeMillis() - start);
736 }
737
738 }
739 }
740
741 /**
742 * Fetches device information associated with the device serial number from SADIS.
743 *
744 * @param deviceId device id
745 * @return device information; an empty Optional otherwise.
746 */
747 private Optional<SubscriberAndDeviceInformation> getSubscriberAndDeviceInformation(DeviceId deviceId) {
748 Device device = deviceService.getDevice(deviceId);
749 if (device == null || device.serialNumber() == null) {
750 return Optional.empty();
751 }
752 return getSubscriberAndDeviceInformation(device.serialNumber());
753 }
754
ke han81a38b92017-03-10 18:41:44 +0800755 private class InternalDeviceListener implements DeviceListener {
756 @Override
757 public void event(DeviceEvent event) {
758 DeviceId devId = event.subject().id();
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +0000759 Port p = event.port();
Esin Karaman00e16b72020-02-21 10:32:39 +0000760 if (getSubscriberAndDeviceInformation(devId).isEmpty() &&
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +0000761 !(p != null && isConnectPoint(devId, p.number()))) {
ke han81a38b92017-03-10 18:41:44 +0800762 return;
763 }
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +0000764 PortNumber port;
765
ke han81a38b92017-03-10 18:41:44 +0800766 switch (event.type()) {
767
768 case DEVICE_ADDED:
769 case DEVICE_UPDATED:
770 case DEVICE_REMOVED:
771 case DEVICE_SUSPENDED:
772 case DEVICE_AVAILABILITY_CHANGED:
773 case PORT_STATS_UPDATED:
774 break;
775 case PORT_ADDED:
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +0000776 port = p.number();
Esin Karaman00e16b72020-02-21 10:32:39 +0000777 if (getSubscriberAndDeviceInformation(devId).isPresent() &&
778 !isUplink(devId, port) && !isConnectPoint(devId, port)) {
ke han81a38b92017-03-10 18:41:44 +0800779 processFilterObjective(devId, port, false);
780 } else if (isUplink(devId, port)) {
781 provisionUplinkFlows();
782 } else if (isConnectPoint(devId, port)) {
783 provisionConnectPointFlows();
784 }
785 break;
786 case PORT_UPDATED:
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +0000787 port = p.number();
Esin Karaman00e16b72020-02-21 10:32:39 +0000788 if (getSubscriberAndDeviceInformation(devId).isPresent() &&
789 !isUplink(devId, port) && !isConnectPoint(devId, port)) {
ke han81a38b92017-03-10 18:41:44 +0800790 if (event.port().isEnabled()) {
791 processFilterObjective(devId, port, false);
792 } else {
793 processFilterObjective(devId, port, true);
794 }
795 } else if (isUplink(devId, port)) {
796 if (event.port().isEnabled()) {
797 provisionUplinkFlows(devId);
798 } else {
799 processFilterObjective(devId, port, true);
800 }
801 } else if (isConnectPoint(devId, port)) {
802 if (event.port().isEnabled()) {
803 provisionConnectPointFlows();
804 } else {
805 unprovisionConnectPointFlows();
806 }
807 }
808 break;
809 case PORT_REMOVED:
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +0000810 port = p.number();
ke han81a38b92017-03-10 18:41:44 +0800811 processFilterObjective(devId, port, true);
812 break;
813 default:
814 log.info("Unknown device event {}", event.type());
815 break;
816 }
817 }
818
819 @Override
820 public boolean isRelevant(DeviceEvent event) {
821 return true;
822 }
823 }
824
825 private class InternalNetworkConfigListener implements NetworkConfigListener {
826
827 private void reconfigureNetwork(IgmpproxyConfig cfg) {
Esin Karamaneff10392019-06-27 18:09:13 +0000828 IgmpproxyConfig newCfg = cfg == null ? new IgmpproxyConfig() : cfg;
ke han81a38b92017-03-10 18:41:44 +0800829
830 unSolicitedTimeout = newCfg.unsolicitedTimeOut();
831 maxResp = newCfg.maxResp();
832 keepAliveInterval = newCfg.keepAliveInterval();
833 keepAliveCount = newCfg.keepAliveCount();
834 lastQueryInterval = newCfg.lastQueryInterval();
835 lastQueryCount = newCfg.lastQueryCount();
836 withRAUplink = newCfg.withRAUplink();
837 withRADownlink = newCfg.withRADownlink();
838 igmpCos = newCfg.igmpCos();
839 periodicQuery = newCfg.periodicQuery();
840 fastLeave = newCfg.fastLeave();
ke han29af27b2017-09-08 10:29:12 +0800841 pimSSmInterworking = newCfg.pimSsmInterworking();
Esin Karamaneff10392019-06-27 18:09:13 +0000842 enableIgmpProvisioning = newCfg.enableIgmpProvisioning();
Esin Karamanb38700c2019-09-17 13:01:25 +0000843 igmpOnPodBasis = newCfg.igmpOnPodBasis();
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +0000844
845 if (connectPointMode != newCfg.connectPointMode() ||
846 connectPoint != newCfg.connectPoint()) {
ke han81a38b92017-03-10 18:41:44 +0800847 connectPointMode = newCfg.connectPointMode();
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +0000848 connectPoint = newCfg.connectPoint();
ke han81a38b92017-03-10 18:41:44 +0800849 if (connectPointMode) {
850 unprovisionUplinkFlows();
851 provisionConnectPointFlows();
852 } else {
853 unprovisionConnectPointFlows();
854 provisionUplinkFlows();
855 }
856 }
857 if (connectPoint != null) {
Esin Karamaneff10392019-06-27 18:09:13 +0000858 log.info("connect point : {}", connectPoint);
ke han81a38b92017-03-10 18:41:44 +0800859 }
Esin Karamaneff10392019-06-27 18:09:13 +0000860 log.info("mode: {}", connectPointMode);
861
862 getSourceConnectPoint(newCfg);
ke han81a38b92017-03-10 18:41:44 +0800863
864 IgmpSender.getInstance().setIgmpCos(igmpCos);
865 IgmpSender.getInstance().setMaxResp(maxResp);
866 IgmpSender.getInstance().setMvlan(mvlan);
867 IgmpSender.getInstance().setWithRADownlink(withRADownlink);
868 IgmpSender.getInstance().setWithRAUplink(withRAUplink);
Esin Karamaneff10392019-06-27 18:09:13 +0000869 }
ke han81a38b92017-03-10 18:41:44 +0800870
Esin Karamaneff10392019-06-27 18:09:13 +0000871 void getSourceConnectPoint(IgmpproxyConfig cfg) {
872 sourceDeviceAndPort = cfg.getSourceDeviceAndPort();
873 if (sourceDeviceAndPort != null) {
874 log.debug("source parameter configured to {}", sourceDeviceAndPort);
875 }
ke han81a38b92017-03-10 18:41:44 +0800876 }
877
878 public void reconfigureSsmTable(IgmpproxySsmTranslateConfig cfg) {
879 if (cfg == null) {
880 return;
881 }
882 Collection<McastRoute> translations = cfg.getSsmTranslations();
883 for (McastRoute route : translations) {
Esin Karamaneff10392019-06-27 18:09:13 +0000884 ssmTranslateTable.put(route.group().getIp4Address(), route.source().get().getIp4Address());
ke han81a38b92017-03-10 18:41:44 +0800885 }
886 }
887
888 @Override
889 public void event(NetworkConfigEvent event) {
890 switch (event.type()) {
891 case CONFIG_ADDED:
892 case CONFIG_UPDATED:
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800893 // NOTE how to know if something has changed in sadis?
ke han81a38b92017-03-10 18:41:44 +0800894
895 if (event.configClass().equals(IGMPPROXY_CONFIG_CLASS)) {
896 IgmpproxyConfig config = networkConfig.getConfig(appId, IGMPPROXY_CONFIG_CLASS);
897 if (config != null) {
Esin Karamaneff10392019-06-27 18:09:13 +0000898 log.info("igmpproxy config received. {}", config);
ke han81a38b92017-03-10 18:41:44 +0800899 reconfigureNetwork(config);
900 }
901 }
902
903 if (event.configClass().equals(IGMPPROXY_SSM_CONFIG_CLASS)) {
904 IgmpproxySsmTranslateConfig config = networkConfig.getConfig(appId, IGMPPROXY_SSM_CONFIG_CLASS);
905 if (config != null) {
906 reconfigureSsmTable(config);
907 }
908 }
909
910 if (event.configClass().equals(MCAST_CONFIG_CLASS)) {
911 McastConfig config = networkConfig.getConfig(coreAppId, MCAST_CONFIG_CLASS);
912 if (config != null && mvlan != config.egressVlan().toShort()) {
913 mvlan = config.egressVlan().toShort();
Deepa Vaddireddy88134a42017-07-05 12:16:03 +0530914 IgmpSender.getInstance().setMvlan(mvlan);
ke han81a38b92017-03-10 18:41:44 +0800915 groupMemberMap.values().forEach(m -> leaveAction(m));
916 }
917 }
918
919 log.info("Reconfigured");
920 break;
921 case CONFIG_REGISTERED:
922 case CONFIG_UNREGISTERED:
923 break;
924 case CONFIG_REMOVED:
Matteo Scandolo7482bbe2020-02-12 14:37:09 -0800925 // NOTE how to know if something has changed in sadis?
ke han81a38b92017-03-10 18:41:44 +0800926 default:
927 break;
928 }
929 }
930 }
931
ke han81a38b92017-03-10 18:41:44 +0800932 private void provisionUplinkFlows(DeviceId deviceId) {
933 if (connectPointMode) {
934 return;
935 }
936
Esin Karaman00e16b72020-02-21 10:32:39 +0000937 Optional<PortNumber> upLink = getDeviceUplink(deviceId);
938 if (upLink.isPresent()) {
939 processFilterObjective(deviceId, upLink.get(), false);
940 }
ke han81a38b92017-03-10 18:41:44 +0800941 }
942
943 private void provisionUplinkFlows() {
944 if (connectPointMode) {
945 return;
946 }
Esin Karaman00e16b72020-02-21 10:32:39 +0000947 deviceService.getAvailableDevices().forEach(device -> {
948 Optional<SubscriberAndDeviceInformation> accessDevice = getSubscriberAndDeviceInformation(device.id());
949 if (accessDevice.isPresent()) {
950 provisionUplinkFlows(device.id());
951 }
952 });
ke han81a38b92017-03-10 18:41:44 +0800953 }
Esin Karaman00e16b72020-02-21 10:32:39 +0000954
ke han81a38b92017-03-10 18:41:44 +0800955 private void unprovisionUplinkFlows() {
Esin Karaman00e16b72020-02-21 10:32:39 +0000956 deviceService.getAvailableDevices().forEach(device -> {
957 Optional<SubscriberAndDeviceInformation> accessDevices = getSubscriberAndDeviceInformation(device.id());
958 if (accessDevices.isPresent()) {
959 Optional<PortNumber> upLink = getDeviceUplink(device.id());
960 if (upLink.isPresent()) {
961 processFilterObjective(device.id(), upLink.get(), true);
962 }
963 }
964 });
ke han81a38b92017-03-10 18:41:44 +0800965 }
966
967 private void provisionConnectPointFlows() {
968 if ((!connectPointMode) || connectPoint == null) {
969 return;
970 }
971
972 processFilterObjective(connectPoint.deviceId(), connectPoint.port(), false);
973 }
974 private void unprovisionConnectPointFlows() {
975 if (connectPoint == null) {
976 return;
977 }
978 processFilterObjective(connectPoint.deviceId(), connectPoint.port(), true);
979 }
Shubham Sharma47f2caf2020-02-18 12:13:40 +0000980
ke han81a38b92017-03-10 18:41:44 +0800981}