blob: d57c7312489ef73d2e9c1159afc7dfa6f79f8948 [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 Karamane4cbbf62019-06-27 18:09:13 +000019import com.google.common.collect.Sets;
Esin Karaman305908c2020-02-24 14:42:52 +000020import org.onosproject.net.Device;
21import org.opencord.sadis.BaseInformationService;
22import org.opencord.sadis.SadisService;
23import org.opencord.sadis.SubscriberAndDeviceInformation;
ke han81a38b92017-03-10 18:41:44 +080024import org.apache.felix.scr.annotations.Activate;
25import org.apache.felix.scr.annotations.Component;
26import org.apache.felix.scr.annotations.Deactivate;
27import org.apache.felix.scr.annotations.Reference;
28import org.apache.felix.scr.annotations.ReferenceCardinality;
29import 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 Karamane4cbbf62019-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 Karamane4cbbf62019-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 Karamane4cbbf62019-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 Karamana05342e2019-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 Karamana05342e2019-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
Esin Karaman305908c2020-02-24 14:42:52 +000098 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 Karamane4cbbf62019-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;
Esin Karaman305908c2020-02-24 14:42:52 +0000109
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 Karamane4cbbf62019-06-27 18:09:13 +0000122 private static ConnectPoint sourceDeviceAndPort = null;
123 private static boolean enableIgmpProvisioning = false;
Esin Karamana05342e2019-09-17 13:01:25 +0000124 private static boolean igmpOnPodBasis = false;
Esin Karamane4cbbf62019-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 Karaman305908c2020-02-24 14:42:52 +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);
137 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
138 protected CoreService coreService;
139 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
140 protected PacketService packetService;
141 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
142 protected MastershipService mastershipService;
143 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
144 protected FlowRuleService flowRuleService;
145 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
146 protected DeviceService deviceService;
147 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
148 protected FlowObjectiveService flowObjectiveService;
149 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
150 protected NetworkConfigRegistry networkConfig;
151 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
152 protected MulticastRouteService multicastService;
Esin Karaman305908c2020-02-24 14:42:52 +0000153
154 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
155 protected SadisService sadisService;
156
ke han81a38b92017-03-10 18:41:44 +0800157 private IgmpPacketProcessor processor = new IgmpPacketProcessor();
158 private Logger log = LoggerFactory.getLogger(getClass());
159 private ApplicationId coreAppId;
160 private Map<Ip4Address, Ip4Address> ssmTranslateTable = new ConcurrentHashMap<>();
Esin Karamane4cbbf62019-06-27 18:09:13 +0000161
ke han81a38b92017-03-10 18:41:44 +0800162 private InternalNetworkConfigListener configListener =
163 new InternalNetworkConfigListener();
164 private DeviceListener deviceListener = new InternalDeviceListener();
Esin Karaman305908c2020-02-24 14:42:52 +0000165
ke han81a38b92017-03-10 18:41:44 +0800166 private ConfigFactory<ApplicationId, IgmpproxyConfig> igmpproxyConfigFactory =
167 new ConfigFactory<ApplicationId, IgmpproxyConfig>(
168 SubjectFactories.APP_SUBJECT_FACTORY, IGMPPROXY_CONFIG_CLASS, "igmpproxy") {
169 @Override
170 public IgmpproxyConfig createConfig() {
171 return new IgmpproxyConfig();
172 }
173 };
174 private ConfigFactory<ApplicationId, IgmpproxySsmTranslateConfig> igmpproxySsmConfigFactory =
175 new ConfigFactory<ApplicationId, IgmpproxySsmTranslateConfig>(
176 SubjectFactories.APP_SUBJECT_FACTORY, IGMPPROXY_SSM_CONFIG_CLASS, "ssmTranslate", true) {
177 @Override
178 public IgmpproxySsmTranslateConfig createConfig() {
179 return new IgmpproxySsmTranslateConfig();
180 }
181 };
Esin Karamane4cbbf62019-06-27 18:09:13 +0000182
ke han81a38b92017-03-10 18:41:44 +0800183 private int maxResp = 10; //unit is 1 sec
184 private int keepAliveInterval = 120; //unit is 1 sec
185
Esin Karamana05342e2019-09-17 13:01:25 +0000186 private ExecutorService eventExecutor;
187
ke han81a38b92017-03-10 18:41:44 +0800188 public static int getUnsolicitedTimeout() {
189 return unSolicitedTimeout;
190 }
191
Esin Karaman305908c2020-02-24 14:42:52 +0000192 protected BaseInformationService<SubscriberAndDeviceInformation> subsService;
193
ke han81a38b92017-03-10 18:41:44 +0800194 @Activate
195 protected void activate() {
Esin Karaman305908c2020-02-24 14:42:52 +0000196 appId = coreService.registerApplication(APP_NAME);
ke han81a38b92017-03-10 18:41:44 +0800197 coreAppId = coreService.registerApplication(CoreService.CORE_APP_NAME);
198 packetService.addProcessor(processor, PacketProcessor.director(4));
199 IgmpSender.init(packetService, mastershipService);
200
ke han81a38b92017-03-10 18:41:44 +0800201 networkConfig.registerConfigFactory(igmpproxySsmConfigFactory);
202 networkConfig.registerConfigFactory(igmpproxyConfigFactory);
203 networkConfig.addListener(configListener);
204
205 configListener.reconfigureNetwork(networkConfig.getConfig(appId, IGMPPROXY_CONFIG_CLASS));
206 configListener.reconfigureSsmTable(networkConfig.getConfig(appId, IGMPPROXY_SSM_CONFIG_CLASS));
207
Esin Karaman305908c2020-02-24 14:42:52 +0000208 subsService = sadisService.getSubscriberInfoService();
ke han81a38b92017-03-10 18:41:44 +0800209
ke han81a38b92017-03-10 18:41:44 +0800210 if (connectPointMode) {
211 provisionConnectPointFlows();
212 } else {
213 provisionUplinkFlows();
214 }
215
216 McastConfig config = networkConfig.getConfig(coreAppId, MCAST_CONFIG_CLASS);
217 if (config != null) {
218 mvlan = config.egressVlan().toShort();
Deepa Vaddireddy88134a42017-07-05 12:16:03 +0530219 IgmpSender.getInstance().setMvlan(mvlan);
ke han81a38b92017-03-10 18:41:44 +0800220 }
221 deviceService.addListener(deviceListener);
Vignesh Ethirajd1957c92019-11-18 11:52:49 +0000222 scheduledExecutorService.scheduleAtFixedRate(new IgmpProxyTimerTask(), 1000, 1000, TimeUnit.MILLISECONDS);
Esin Karamana05342e2019-09-17 13:01:25 +0000223 eventExecutor = newSingleThreadScheduledExecutor(groupedThreads("cord/igmpproxy",
224 "events-igmp-%d", log));
ke han81a38b92017-03-10 18:41:44 +0800225
226 log.info("Started");
227 }
228
229 @Deactivate
230 protected void deactivate() {
231 scheduledExecutorService.shutdown();
Esin Karamana05342e2019-09-17 13:01:25 +0000232 eventExecutor.shutdown();
ke han81a38b92017-03-10 18:41:44 +0800233
234 // de-register and null our handler
235 networkConfig.removeListener(configListener);
ke han81a38b92017-03-10 18:41:44 +0800236 networkConfig.unregisterConfigFactory(igmpproxyConfigFactory);
237 networkConfig.unregisterConfigFactory(igmpproxySsmConfigFactory);
238 deviceService.removeListener(deviceListener);
239 packetService.removeProcessor(processor);
240 flowRuleService.removeFlowRulesById(appId);
241
242 log.info("Stopped");
243 }
244
245 protected Ip4Address getDeviceIp(DeviceId ofDeviceId) {
246 try {
247 String[] mgmtAddress = deviceService.getDevice(ofDeviceId)
248 .annotations().value(AnnotationKeys.MANAGEMENT_ADDRESS).split(":");
249 return Ip4Address.valueOf(mgmtAddress[0]);
250 } catch (Exception ex) {
251 log.info("No valid Ipaddress for " + ofDeviceId.toString());
252 return null;
253 }
254 }
255
256 private void processIgmpQuery(IGMPQuery igmpQuery, ConnectPoint cp, int maxResp) {
257
258 DeviceId deviceId = cp.deviceId();
259 Ip4Address gAddr = igmpQuery.getGaddr().getIp4Address();
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000260 maxResp = calculateMaxResp(maxResp);
261 if (gAddr != null && !gAddr.isZero()) {
262 StateMachine.specialQuery(deviceId, gAddr, maxResp);
263 } else {
264 StateMachine.generalQuery(deviceId, maxResp);
265 }
266 }
ke han81a38b92017-03-10 18:41:44 +0800267
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000268 private void processIgmpConnectPointQuery(IGMPQuery igmpQuery, ConnectPoint cp, int maxResp) {
269
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000270 Ip4Address gAddr = igmpQuery.getGaddr().getIp4Address();
Esin Karaman305908c2020-02-24 14:42:52 +0000271 final int maxResponseTime = calculateMaxResp(maxResp);
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000272 //The query is received on the ConnectPoint
273 // send query accordingly to the registered OLT devices.
274 if (gAddr != null && !gAddr.isZero()) {
Esin Karaman305908c2020-02-24 14:42:52 +0000275 deviceService.getAvailableDevices().forEach(device -> {
276 Optional<SubscriberAndDeviceInformation> accessDevice = getSubscriberAndDeviceInformation(device.id());
277 if (accessDevice.isPresent()) {
278 StateMachine.specialQuery(device.id(), gAddr, maxResponseTime);
279 }
280 });
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000281 } else {
282 //Don't know which group is targeted by the query
283 //So query all the members(in all the OLTs) and proxy their reports
Esin Karaman305908c2020-02-24 14:42:52 +0000284 StateMachine.generalQuery(maxResponseTime);
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000285 }
286 }
287
288
289 private int calculateMaxResp(int maxResp) {
ke han81a38b92017-03-10 18:41:44 +0800290 if (maxResp >= 128) {
291 int mant = maxResp & 0xf;
292 int exp = (maxResp >> 4) & 0x7;
293 maxResp = (mant | 0x10) << (exp + 3);
294 }
295
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000296 return (maxResp + 5) / 10;
ke han81a38b92017-03-10 18:41:44 +0800297 }
298
299 private Ip4Address ssmTranslateRoute(IpAddress group) {
300 return ssmTranslateTable.get(group);
301 }
302
303 private void processIgmpReport(IGMPMembership igmpGroup, VlanId vlan, ConnectPoint cp, byte igmpType) {
304 DeviceId deviceId = cp.deviceId();
305 PortNumber portNumber = cp.port();
306
307 Ip4Address groupIp = igmpGroup.getGaddr().getIp4Address();
308 if (!groupIp.isMulticast()) {
309 log.info(groupIp.toString() + " is not a valid group address");
310 return;
311 }
312 Ip4Address srcIp = getDeviceIp(deviceId);
313
314 byte recordType = igmpGroup.getRecordType();
315 boolean join = false;
316
317 ArrayList<Ip4Address> sourceList = new ArrayList<>();
318
319 if (igmpGroup.getSources().size() > 0) {
320 igmpGroup.getSources().forEach(source -> sourceList.add(source.getIp4Address()));
321 if (recordType == IGMPMembership.CHANGE_TO_EXCLUDE_MODE ||
322 recordType == IGMPMembership.MODE_IS_EXCLUDE ||
323 recordType == IGMPMembership.BLOCK_OLD_SOURCES) {
324 join = false;
325 } else if (recordType == IGMPMembership.CHANGE_TO_INCLUDE_MODE ||
326 recordType == IGMPMembership.MODE_IS_INCLUDE ||
327 recordType == IGMPMembership.ALLOW_NEW_SOURCES) {
328 join = true;
329 }
330 } else {
ke han29af27b2017-09-08 10:29:12 +0800331 IpAddress src = null;
332 if (pimSSmInterworking) {
333 src = ssmTranslateRoute(groupIp);
334 if (src == null) {
335 log.info("no ssm translate for group " + groupIp.toString());
336 return;
337 }
338 } else {
339 src = IpAddress.valueOf(DEFAULT_PIMSSM_HOST);
ke han81a38b92017-03-10 18:41:44 +0800340 }
341 sourceList.add(src.getIp4Address());
342 if (recordType == IGMPMembership.CHANGE_TO_EXCLUDE_MODE ||
343 recordType == IGMPMembership.MODE_IS_EXCLUDE ||
344 recordType == IGMPMembership.BLOCK_OLD_SOURCES) {
345 join = true;
346 } else if (recordType == IGMPMembership.CHANGE_TO_INCLUDE_MODE ||
347 recordType == IGMPMembership.MODE_IS_INCLUDE ||
348 recordType == IGMPMembership.ALLOW_NEW_SOURCES) {
349 join = false;
350 }
351 }
352 String groupMemberKey = GroupMember.getkey(groupIp, deviceId, portNumber);
353 GroupMember groupMember = groupMemberMap.get(groupMemberKey);
354
355 if (join) {
356 if (groupMember == null) {
Esin Karamane4cbbf62019-06-27 18:09:13 +0000357 Optional<ConnectPoint> sourceConfigured = getSource();
358 if (!sourceConfigured.isPresent()) {
359 log.warn("Unable to process IGMP Join from {} since no source " +
360 "configuration is found.", deviceId);
361 return;
362 }
Esin Karaman305908c2020-02-24 14:42:52 +0000363
364 Optional<PortNumber> deviceUplink = getDeviceUplink(deviceId);
365 if (!deviceUplink.isPresent()) {
366 log.warn("Unable to process IGMP Join since uplink port " +
367 "of the device {} is not found.", deviceId);
368 return;
369 }
370
371 if (igmpType == IGMP.TYPE_IGMPV2_MEMBERSHIP_REPORT) {
372 groupMember = new GroupMember(groupIp, vlan, deviceId, portNumber, true);
373 } else {
374 groupMember = new GroupMember(groupIp, vlan, deviceId, portNumber, false);
375 }
376
Esin Karamane4cbbf62019-06-27 18:09:13 +0000377 HashSet<ConnectPoint> sourceConnectPoints = Sets.newHashSet(sourceConfigured.get());
378
Esin Karaman305908c2020-02-24 14:42:52 +0000379 StateMachine.join(deviceId, groupIp, srcIp, deviceUplink.get());
ke han81a38b92017-03-10 18:41:44 +0800380 groupMemberMap.put(groupMemberKey, groupMember);
381 groupMember.updateList(recordType, sourceList);
Esin Karamane4cbbf62019-06-27 18:09:13 +0000382 groupMember.getSourceList().forEach(source -> {
383 McastRoute route = new McastRoute(source, groupIp, McastRoute.Type.IGMP);
384 //add route
385 multicastService.add(route);
386 //add source to the route
387 multicastService.addSources(route, Sets.newHashSet(sourceConnectPoints));
388 //add sink to the route
389 multicastService.addSinks(route, Sets.newHashSet(cp));
390 });
391
ke han81a38b92017-03-10 18:41:44 +0800392 }
393 groupMember.resetAllTimers();
394 groupMember.updateList(recordType, sourceList);
395 groupMember.setLeave(false);
396 } else {
397 if (groupMember == null) {
398 log.info("receive leave but no instance, group " + groupIp.toString() +
399 " device:" + deviceId.toString() + " port:" + portNumber.toString());
400 return;
401 } else {
402 groupMember.setLeave(true);
403 if (fastLeave) {
404 leaveAction(groupMember);
405 } else {
406 sendQuery(groupMember);
407 }
408 }
409 }
410 }
411
412 private void leaveAction(GroupMember groupMember) {
413 ConnectPoint cp = new ConnectPoint(groupMember.getDeviceId(), groupMember.getPortNumber());
414 StateMachine.leave(groupMember.getDeviceId(), groupMember.getGroupIp());
Esin Karamane4cbbf62019-06-27 18:09:13 +0000415 groupMember.getSourceList().forEach(source -> multicastService.removeSinks(
ke han81a38b92017-03-10 18:41:44 +0800416 new McastRoute(source, groupMember.getGroupIp(),
Esin Karamane4cbbf62019-06-27 18:09:13 +0000417 McastRoute.Type.IGMP), Sets.newHashSet(cp)));
ke han81a38b92017-03-10 18:41:44 +0800418 groupMemberMap.remove(groupMember.getId());
419 }
420
421 private void sendQuery(GroupMember groupMember) {
422 Ethernet ethpkt;
423 Ip4Address srcIp = getDeviceIp(groupMember.getDeviceId());
424 if (groupMember.getv2()) {
425 ethpkt = IgmpSender.getInstance().buildIgmpV2Query(groupMember.getGroupIp(), srcIp);
426 } else {
427 ethpkt = IgmpSender.getInstance().buildIgmpV3Query(groupMember.getGroupIp(), srcIp);
428 }
429 IgmpSender.getInstance().sendIgmpPacket(ethpkt, groupMember.getDeviceId(), groupMember.getPortNumber());
430 }
431
Esin Karamane4cbbf62019-06-27 18:09:13 +0000432 /**
433 * @return connect point of the source if configured; and empty Optional otherwise.
434 */
435 public static Optional<ConnectPoint> getSource() {
436 return sourceDeviceAndPort == null ? Optional.empty() :
437 Optional.of(sourceDeviceAndPort);
ke han81a38b92017-03-10 18:41:44 +0800438 }
439
440 /**
441 * Packet processor responsible for forwarding packets along their paths.
442 */
443 private class IgmpPacketProcessor implements PacketProcessor {
444 @Override
445 public void process(PacketContext context) {
Esin Karamana05342e2019-09-17 13:01:25 +0000446 eventExecutor.execute(() -> {
Esin Karaman305908c2020-02-24 14:42:52 +0000447 try {
448 InboundPacket pkt = context.inPacket();
449 Ethernet ethPkt = pkt.parsed();
450 if (ethPkt == null) {
451 return;
452 }
ke han81a38b92017-03-10 18:41:44 +0800453
Esin Karaman305908c2020-02-24 14:42:52 +0000454 if (ethPkt.getEtherType() != Ethernet.TYPE_IPV4) {
455 return;
456 }
ke han81a38b92017-03-10 18:41:44 +0800457
Esin Karaman305908c2020-02-24 14:42:52 +0000458 IPv4 ipv4Pkt = (IPv4) ethPkt.getPayload();
ke han81a38b92017-03-10 18:41:44 +0800459
Esin Karaman305908c2020-02-24 14:42:52 +0000460 if (ipv4Pkt.getProtocol() != IPv4.PROTOCOL_IGMP) {
461 return;
462 }
ke han81a38b92017-03-10 18:41:44 +0800463
Esin Karaman305908c2020-02-24 14:42:52 +0000464 short vlan = ethPkt.getVlanID();
465 DeviceId deviceId = pkt.receivedFrom().deviceId();
ke han81a38b92017-03-10 18:41:44 +0800466
Esin Karaman305908c2020-02-24 14:42:52 +0000467 if (!isConnectPoint(deviceId, pkt.receivedFrom().port()) &&
468 !getSubscriberAndDeviceInformation(deviceId).isPresent()) {
469 log.error("Device not registered in netcfg :" + deviceId.toString());
470 return;
471 }
472
473 IGMP igmp = (IGMP) ipv4Pkt.getPayload();
474
475 Optional<PortNumber> deviceUpLinkOpt = getDeviceUplink(deviceId);
476 PortNumber upLinkPort = deviceUpLinkOpt.isPresent() ? deviceUpLinkOpt.get() : null;
477 switch (igmp.getIgmpType()) {
478 case IGMP.TYPE_IGMPV3_MEMBERSHIP_QUERY:
479 //Discard Query from OLT’s non-uplink port’s
480 if (!pkt.receivedFrom().port().equals(upLinkPort)) {
481 if (isConnectPoint(deviceId, pkt.receivedFrom().port())) {
482 log.info("IGMP Picked up query from connectPoint");
483 //OK to process packet
484 processIgmpConnectPointQuery((IGMPQuery) igmp.getGroups().get(0),
485 pkt.receivedFrom(),
486 0xff & igmp.getMaxRespField());
487 break;
488 } else {
489 //Not OK to process packet
490 log.warn("IGMP Picked up query from non-uplink port {}", upLinkPort);
491 return;
492 }
493 }
494
495 processIgmpQuery((IGMPQuery) igmp.getGroups().get(0), pkt.receivedFrom(),
496 0xff & igmp.getMaxRespField());
497 break;
498 case IGMP.TYPE_IGMPV1_MEMBERSHIP_REPORT:
499 log.debug("IGMP version 1 message types are not currently supported.");
500 break;
501 case IGMP.TYPE_IGMPV3_MEMBERSHIP_REPORT:
502 case IGMP.TYPE_IGMPV2_MEMBERSHIP_REPORT:
503 case IGMP.TYPE_IGMPV2_LEAVE_GROUP:
504 //Discard join/leave from OLT’s uplink port’s
505 if (pkt.receivedFrom().port().equals(upLinkPort) ||
506 isConnectPoint(deviceId, pkt.receivedFrom().port())) {
507 log.info("IGMP Picked up join/leave from uplink/connectPoint port");
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000508 return;
509 }
ke han81a38b92017-03-10 18:41:44 +0800510
Esin Karaman305908c2020-02-24 14:42:52 +0000511 Iterator<IGMPGroup> itr = igmp.getGroups().iterator();
512 while (itr.hasNext()) {
513 IGMPGroup group = itr.next();
514 if (group instanceof IGMPMembership) {
515 processIgmpReport((IGMPMembership) group, VlanId.vlanId(vlan),
516 pkt.receivedFrom(), igmp.getIgmpType());
517 } else if (group instanceof IGMPQuery) {
518 IGMPMembership mgroup;
519 mgroup = new IGMPMembership(group.getGaddr().getIp4Address());
520 mgroup.setRecordType(igmp.getIgmpType() == IGMP.TYPE_IGMPV2_MEMBERSHIP_REPORT ?
521 IGMPMembership.MODE_IS_EXCLUDE :
522 IGMPMembership.MODE_IS_INCLUDE);
523 processIgmpReport(mgroup, VlanId.vlanId(vlan),
524 pkt.receivedFrom(), igmp.getIgmpType());
525 }
ke han81a38b92017-03-10 18:41:44 +0800526 }
Esin Karaman305908c2020-02-24 14:42:52 +0000527 break;
ke han81a38b92017-03-10 18:41:44 +0800528
Esin Karaman305908c2020-02-24 14:42:52 +0000529 default:
530 log.info("wrong IGMP v3 type:" + igmp.getIgmpType());
531 break;
532 }
533
534 } catch (Exception ex) {
535 log.error("igmp process error : {} ", ex);
536 ex.printStackTrace();
ke han81a38b92017-03-10 18:41:44 +0800537 }
Esin Karamana05342e2019-09-17 13:01:25 +0000538 });
ke han81a38b92017-03-10 18:41:44 +0800539 }
540 }
541
542 private class IgmpProxyTimerTask extends TimerTask {
543 public void run() {
544 try {
545 IgmpTimer.timeOut1s();
546 queryMembers();
547 } catch (Exception ex) {
548 log.warn("Igmp timer task error : {}", ex.getMessage());
549 }
550 }
551
552 private void queryMembers() {
553 GroupMember groupMember;
554 Set groupMemberSet = groupMemberMap.entrySet();
555 Iterator itr = groupMemberSet.iterator();
556 while (itr.hasNext()) {
557 Map.Entry entry = (Map.Entry) itr.next();
558 groupMember = (GroupMember) entry.getValue();
559 DeviceId did = groupMember.getDeviceId();
560 if (mastershipService.isLocalMaster(did)) {
561 if (groupMember.isLeave()) {
562 lastQuery(groupMember);
563 } else if (periodicQuery) {
564 periodicQuery(groupMember);
565 }
566 }
567 }
568 }
569
570 private void lastQuery(GroupMember groupMember) {
571 if (groupMember.getLastQueryInterval() < lastQueryInterval) {
572 groupMember.lastQueryInterval(true); // count times
573 } else if (groupMember.getLastQueryCount() < lastQueryCount - 1) {
574 sendQuery(groupMember);
575 groupMember.lastQueryInterval(false); // reset count number
576 groupMember.lastQueryCount(true); //count times
577 } else if (groupMember.getLastQueryCount() == lastQueryCount - 1) {
578 leaveAction(groupMember);
579 }
580 }
581
582 private void periodicQuery(GroupMember groupMember) {
583 if (groupMember.getKeepAliveQueryInterval() < keepAliveInterval) {
584 groupMember.keepAliveInterval(true);
585 } else if (groupMember.getKeepAliveQueryCount() < keepAliveCount) {
586 sendQuery(groupMember);
587 groupMember.keepAliveInterval(false);
588 groupMember.keepAliveQueryCount(true);
589 } else if (groupMember.getKeepAliveQueryCount() == keepAliveCount) {
590 leaveAction(groupMember);
591 }
592 }
593
594 }
595
Esin Karaman305908c2020-02-24 14:42:52 +0000596 public Optional<PortNumber> getDeviceUplink(DeviceId devId) {
597 Device device = deviceService.getDevice(devId);
598 if (device == null || device.serialNumber() == null) {
599 return Optional.empty();
Deepa Vaddireddybcd52352017-09-21 05:04:48 +0000600 }
Esin Karaman305908c2020-02-24 14:42:52 +0000601 Optional<SubscriberAndDeviceInformation> olt = getSubscriberAndDeviceInformation(device.serialNumber());
602
603 if (!olt.isPresent()) {
604 return Optional.empty();
605 }
606 PortNumber portNumber = PortNumber.portNumber(olt.get().uplinkPort());
607 return validateUpLinkPort(device.id(), portNumber) ?
608 Optional.of(portNumber) : Optional.empty();
609 }
610
611 /**
612 *
613 * @param deviceId device id
614 * @param portNumber port number
615 * @return true if the port name starts with NNI_PREFIX; false otherwise.
616 */
617 public boolean validateUpLinkPort(DeviceId deviceId, PortNumber portNumber) {
618 Port port = deviceService.getPort(deviceId, portNumber);
619 if (port == null) {
620 //port is not discovered by ONOS; so cannot validate it.
621 return false;
622 }
623 boolean isValid = port.annotations().value(AnnotationKeys.PORT_NAME) != null &&
624 port.annotations().value(AnnotationKeys.PORT_NAME).startsWith(NNI_PREFIX);
625 if (!isValid) {
626 log.warn("Port cannot be validated; it is not configured as an NNI port." +
627 "Device/port: {}/{}", deviceId, portNumber);
628 }
629 return isValid;
ke han81a38b92017-03-10 18:41:44 +0800630 }
631
Esin Karamana05342e2019-09-17 13:01:25 +0000632 public static boolean isIgmpOnPodBasis() {
633 return igmpOnPodBasis;
634 }
635
ke han81a38b92017-03-10 18:41:44 +0800636 private void processFilterObjective(DeviceId devId, PortNumber port, boolean remove) {
Esin Karamane4cbbf62019-06-27 18:09:13 +0000637 if (!enableIgmpProvisioning) {
638 log.debug("IGMP trap rules won't be installed since enableIgmpProvisioning flag is not set");
639 return;
640 }
ke han81a38b92017-03-10 18:41:44 +0800641 //TODO migrate to packet requests when packet service uses filtering objectives
642 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
643
644 builder = remove ? builder.deny() : builder.permit();
645
646 FilteringObjective igmp = builder
647 .withKey(Criteria.matchInPort(port))
648 .addCondition(Criteria.matchEthType(EthType.EtherType.IPV4.ethType()))
649 .addCondition(Criteria.matchIPProtocol(IPv4.PROTOCOL_IGMP))
650 .withMeta(DefaultTrafficTreatment.builder().setOutput(PortNumber.CONTROLLER).build())
651 .fromApp(appId)
Esin Karamane4cbbf62019-06-27 18:09:13 +0000652 .withPriority(MAX_PRIORITY)
ke han81a38b92017-03-10 18:41:44 +0800653 .add(new ObjectiveContext() {
654 @Override
655 public void onSuccess(Objective objective) {
Esin Karamane4cbbf62019-06-27 18:09:13 +0000656 log.info("Igmp filter for {} on {} {}.",
657 devId, port, (remove) ? REMOVED : INSTALLED);
ke han81a38b92017-03-10 18:41:44 +0800658 }
659
660 @Override
661 public void onError(Objective objective, ObjectiveError error) {
Esin Karamane4cbbf62019-06-27 18:09:13 +0000662 log.info("Igmp filter {} for device {} on port {} failed because of {}",
663 (remove) ? INSTALLATION : REMOVAL, devId, port,
664 error);
ke han81a38b92017-03-10 18:41:44 +0800665 }
666 });
667
668 flowObjectiveService.filter(devId, igmp);
Esin Karamane4cbbf62019-06-27 18:09:13 +0000669
ke han81a38b92017-03-10 18:41:44 +0800670 }
671
672 private boolean isConnectPoint(DeviceId device, PortNumber port) {
Deepa Vaddireddy01f33b42017-07-02 13:26:53 +0530673 if (connectPoint != null) {
674 return (connectPointMode && connectPoint.deviceId().equals(device)
675 && connectPoint.port().equals(port));
676 } else {
677 log.info("connectPoint not configured for device {}", device);
678 return false;
679 }
ke han81a38b92017-03-10 18:41:44 +0800680 }
Deepa Vaddireddy01f33b42017-07-02 13:26:53 +0530681
ke han81a38b92017-03-10 18:41:44 +0800682 private boolean isUplink(DeviceId device, PortNumber port) {
Esin Karaman305908c2020-02-24 14:42:52 +0000683 if (connectPointMode) {
684 return false;
685 }
686 Optional<PortNumber> upLinkPort = getDeviceUplink(device);
687 return upLinkPort.isPresent() && upLinkPort.get().equals(port);
688 }
689
690 /**
691 * Fetches device information associated with the device serial number from SADIS.
692 *
693 * @param serialNumber serial number of a device
694 * @return device information; an empty Optional otherwise.
695 */
696 private Optional<SubscriberAndDeviceInformation> getSubscriberAndDeviceInformation(String serialNumber) {
697 long start = System.currentTimeMillis();
698 try {
699 return Optional.ofNullable(sadisService.getSubscriberInfoService().get(serialNumber));
700 } finally {
701 if (log.isDebugEnabled()) {
702 // SADIS can call remote systems to fetch device data and this calls can take a long time.
703 // This measurement is just for monitoring these kinds of situations.
704 log.debug("Device fetched from SADIS. Elapsed {} msec", System.currentTimeMillis() - start);
705 }
706
707 }
708 }
709
710 /**
711 * Fetches device information associated with the device serial number from SADIS.
712 *
713 * @param deviceId device id
714 * @return device information; an empty Optional otherwise.
715 */
716 private Optional<SubscriberAndDeviceInformation> getSubscriberAndDeviceInformation(DeviceId deviceId) {
717 Device device = deviceService.getDevice(deviceId);
718 if (device == null || device.serialNumber() == null) {
719 return Optional.empty();
720 }
721 return getSubscriberAndDeviceInformation(device.serialNumber());
ke han81a38b92017-03-10 18:41:44 +0800722 }
723
724 private class InternalDeviceListener implements DeviceListener {
725 @Override
726 public void event(DeviceEvent event) {
727 DeviceId devId = event.subject().id();
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +0000728 Port p = event.port();
Esin Karaman305908c2020-02-24 14:42:52 +0000729 if (!getSubscriberAndDeviceInformation(devId).isPresent() &&
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +0000730 !(p != null && isConnectPoint(devId, p.number()))) {
ke han81a38b92017-03-10 18:41:44 +0800731 return;
732 }
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +0000733 PortNumber port;
734
ke han81a38b92017-03-10 18:41:44 +0800735 switch (event.type()) {
736
737 case DEVICE_ADDED:
738 case DEVICE_UPDATED:
739 case DEVICE_REMOVED:
740 case DEVICE_SUSPENDED:
741 case DEVICE_AVAILABILITY_CHANGED:
742 case PORT_STATS_UPDATED:
743 break;
744 case PORT_ADDED:
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +0000745 port = p.number();
Esin Karaman305908c2020-02-24 14:42:52 +0000746 if (getSubscriberAndDeviceInformation(devId).isPresent() &&
747 !isUplink(devId, port) && !isConnectPoint(devId, port)) {
ke han81a38b92017-03-10 18:41:44 +0800748 processFilterObjective(devId, port, false);
749 } else if (isUplink(devId, port)) {
750 provisionUplinkFlows();
751 } else if (isConnectPoint(devId, port)) {
752 provisionConnectPointFlows();
753 }
754 break;
755 case PORT_UPDATED:
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +0000756 port = p.number();
Esin Karaman305908c2020-02-24 14:42:52 +0000757 if (getSubscriberAndDeviceInformation(devId).isPresent() &&
758 !isUplink(devId, port) && !isConnectPoint(devId, port)) {
ke han81a38b92017-03-10 18:41:44 +0800759 if (event.port().isEnabled()) {
760 processFilterObjective(devId, port, false);
761 } else {
762 processFilterObjective(devId, port, true);
763 }
764 } else if (isUplink(devId, port)) {
765 if (event.port().isEnabled()) {
766 provisionUplinkFlows(devId);
767 } else {
768 processFilterObjective(devId, port, true);
769 }
770 } else if (isConnectPoint(devId, port)) {
771 if (event.port().isEnabled()) {
772 provisionConnectPointFlows();
773 } else {
774 unprovisionConnectPointFlows();
775 }
776 }
777 break;
778 case PORT_REMOVED:
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +0000779 port = p.number();
ke han81a38b92017-03-10 18:41:44 +0800780 processFilterObjective(devId, port, true);
781 break;
782 default:
783 log.info("Unknown device event {}", event.type());
784 break;
785 }
786 }
787
788 @Override
789 public boolean isRelevant(DeviceEvent event) {
790 return true;
791 }
792 }
793
794 private class InternalNetworkConfigListener implements NetworkConfigListener {
795
796 private void reconfigureNetwork(IgmpproxyConfig cfg) {
Esin Karamane4cbbf62019-06-27 18:09:13 +0000797 IgmpproxyConfig newCfg = cfg == null ? new IgmpproxyConfig() : cfg;
ke han81a38b92017-03-10 18:41:44 +0800798
799 unSolicitedTimeout = newCfg.unsolicitedTimeOut();
800 maxResp = newCfg.maxResp();
801 keepAliveInterval = newCfg.keepAliveInterval();
802 keepAliveCount = newCfg.keepAliveCount();
803 lastQueryInterval = newCfg.lastQueryInterval();
804 lastQueryCount = newCfg.lastQueryCount();
805 withRAUplink = newCfg.withRAUplink();
806 withRADownlink = newCfg.withRADownlink();
807 igmpCos = newCfg.igmpCos();
808 periodicQuery = newCfg.periodicQuery();
809 fastLeave = newCfg.fastLeave();
ke han29af27b2017-09-08 10:29:12 +0800810 pimSSmInterworking = newCfg.pimSsmInterworking();
Esin Karamane4cbbf62019-06-27 18:09:13 +0000811 enableIgmpProvisioning = newCfg.enableIgmpProvisioning();
Esin Karamana05342e2019-09-17 13:01:25 +0000812 igmpOnPodBasis = newCfg.igmpOnPodBasis();
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +0000813
814 if (connectPointMode != newCfg.connectPointMode() ||
815 connectPoint != newCfg.connectPoint()) {
ke han81a38b92017-03-10 18:41:44 +0800816 connectPointMode = newCfg.connectPointMode();
Deepa Vaddireddyca7b25d2017-09-28 13:47:18 +0000817 connectPoint = newCfg.connectPoint();
ke han81a38b92017-03-10 18:41:44 +0800818 if (connectPointMode) {
819 unprovisionUplinkFlows();
820 provisionConnectPointFlows();
821 } else {
822 unprovisionConnectPointFlows();
823 provisionUplinkFlows();
824 }
825 }
826 if (connectPoint != null) {
Esin Karamane4cbbf62019-06-27 18:09:13 +0000827 log.info("connect point : {}", connectPoint);
ke han81a38b92017-03-10 18:41:44 +0800828 }
Esin Karamane4cbbf62019-06-27 18:09:13 +0000829 log.info("mode: {}", connectPointMode);
830
831 getSourceConnectPoint(newCfg);
ke han81a38b92017-03-10 18:41:44 +0800832
833 IgmpSender.getInstance().setIgmpCos(igmpCos);
834 IgmpSender.getInstance().setMaxResp(maxResp);
835 IgmpSender.getInstance().setMvlan(mvlan);
836 IgmpSender.getInstance().setWithRADownlink(withRADownlink);
837 IgmpSender.getInstance().setWithRAUplink(withRAUplink);
Esin Karamane4cbbf62019-06-27 18:09:13 +0000838 }
ke han81a38b92017-03-10 18:41:44 +0800839
Esin Karamane4cbbf62019-06-27 18:09:13 +0000840 void getSourceConnectPoint(IgmpproxyConfig cfg) {
841 sourceDeviceAndPort = cfg.getSourceDeviceAndPort();
842 if (sourceDeviceAndPort != null) {
843 log.debug("source parameter configured to {}", sourceDeviceAndPort);
844 }
ke han81a38b92017-03-10 18:41:44 +0800845 }
846
847 public void reconfigureSsmTable(IgmpproxySsmTranslateConfig cfg) {
848 if (cfg == null) {
849 return;
850 }
851 Collection<McastRoute> translations = cfg.getSsmTranslations();
852 for (McastRoute route : translations) {
Esin Karamane4cbbf62019-06-27 18:09:13 +0000853 ssmTranslateTable.put(route.group().getIp4Address(), route.source().get().getIp4Address());
ke han81a38b92017-03-10 18:41:44 +0800854 }
855 }
856
857 @Override
858 public void event(NetworkConfigEvent event) {
859 switch (event.type()) {
860 case CONFIG_ADDED:
861 case CONFIG_UPDATED:
ke han81a38b92017-03-10 18:41:44 +0800862
863 if (event.configClass().equals(IGMPPROXY_CONFIG_CLASS)) {
864 IgmpproxyConfig config = networkConfig.getConfig(appId, IGMPPROXY_CONFIG_CLASS);
865 if (config != null) {
Esin Karamane4cbbf62019-06-27 18:09:13 +0000866 log.info("igmpproxy config received. {}", config);
ke han81a38b92017-03-10 18:41:44 +0800867 reconfigureNetwork(config);
868 }
869 }
870
871 if (event.configClass().equals(IGMPPROXY_SSM_CONFIG_CLASS)) {
872 IgmpproxySsmTranslateConfig config = networkConfig.getConfig(appId, IGMPPROXY_SSM_CONFIG_CLASS);
873 if (config != null) {
874 reconfigureSsmTable(config);
875 }
876 }
877
878 if (event.configClass().equals(MCAST_CONFIG_CLASS)) {
879 McastConfig config = networkConfig.getConfig(coreAppId, MCAST_CONFIG_CLASS);
880 if (config != null && mvlan != config.egressVlan().toShort()) {
881 mvlan = config.egressVlan().toShort();
Deepa Vaddireddy88134a42017-07-05 12:16:03 +0530882 IgmpSender.getInstance().setMvlan(mvlan);
ke han81a38b92017-03-10 18:41:44 +0800883 groupMemberMap.values().forEach(m -> leaveAction(m));
884 }
885 }
886
887 log.info("Reconfigured");
888 break;
889 case CONFIG_REGISTERED:
890 case CONFIG_UNREGISTERED:
ke han81a38b92017-03-10 18:41:44 +0800891 case CONFIG_REMOVED:
ke han81a38b92017-03-10 18:41:44 +0800892 default:
893 break;
894 }
895 }
896 }
897
ke han81a38b92017-03-10 18:41:44 +0800898 private void provisionUplinkFlows(DeviceId deviceId) {
899 if (connectPointMode) {
900 return;
901 }
902
Esin Karaman305908c2020-02-24 14:42:52 +0000903 Optional<PortNumber> upLink = getDeviceUplink(deviceId);
904 if (upLink.isPresent()) {
905 processFilterObjective(deviceId, upLink.get(), false);
906 }
ke han81a38b92017-03-10 18:41:44 +0800907 }
908
909 private void provisionUplinkFlows() {
910 if (connectPointMode) {
911 return;
912 }
Esin Karaman305908c2020-02-24 14:42:52 +0000913 deviceService.getAvailableDevices().forEach(device -> {
914 Optional<SubscriberAndDeviceInformation> accessDevice = getSubscriberAndDeviceInformation(device.id());
915 if (accessDevice.isPresent()) {
916 provisionUplinkFlows(device.id());
917 }
918 });
ke han81a38b92017-03-10 18:41:44 +0800919 }
Esin Karaman305908c2020-02-24 14:42:52 +0000920
ke han81a38b92017-03-10 18:41:44 +0800921 private void unprovisionUplinkFlows() {
Esin Karaman305908c2020-02-24 14:42:52 +0000922 deviceService.getAvailableDevices().forEach(device -> {
923 Optional<SubscriberAndDeviceInformation> accessDevices = getSubscriberAndDeviceInformation(device.id());
924 if (accessDevices.isPresent()) {
925 Optional<PortNumber> upLink = getDeviceUplink(device.id());
926 if (upLink.isPresent()) {
927 processFilterObjective(device.id(), upLink.get(), true);
928 }
929 }
930 });
ke han81a38b92017-03-10 18:41:44 +0800931 }
932
933 private void provisionConnectPointFlows() {
934 if ((!connectPointMode) || connectPoint == null) {
935 return;
936 }
937
938 processFilterObjective(connectPoint.deviceId(), connectPoint.port(), false);
939 }
940 private void unprovisionConnectPointFlows() {
941 if (connectPoint == null) {
942 return;
943 }
944 processFilterObjective(connectPoint.deviceId(), connectPoint.port(), true);
945 }
946}