blob: 1b4285bf33bb7eb84a5ea8dce538565781e7d68c [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;
19import org.apache.felix.scr.annotations.Activate;
20import org.apache.felix.scr.annotations.Component;
21import org.apache.felix.scr.annotations.Deactivate;
22import org.apache.felix.scr.annotations.Reference;
23import org.apache.felix.scr.annotations.ReferenceCardinality;
24import org.onlab.packet.EthType;
25import org.onlab.packet.Ethernet;
26import org.onlab.packet.IGMP;
27import org.onlab.packet.IGMPGroup;
28import org.onlab.packet.IGMPMembership;
29import org.onlab.packet.IGMPQuery;
30import org.onlab.packet.IPv4;
31import org.onlab.packet.Ip4Address;
32import org.onlab.packet.IpAddress;
33import org.onlab.packet.VlanId;
34import org.onosproject.core.ApplicationId;
35import org.onosproject.core.CoreService;
36import org.onosproject.incubator.net.config.basics.McastConfig;
37import org.onosproject.mastership.MastershipService;
38import org.onosproject.net.AnnotationKeys;
39import org.onosproject.net.ConnectPoint;
40import org.onosproject.net.DeviceId;
41import org.onosproject.net.Port;
42import org.onosproject.net.PortNumber;
43import org.onosproject.net.config.ConfigFactory;
44import org.onosproject.net.config.NetworkConfigEvent;
45import org.onosproject.net.config.NetworkConfigListener;
46import org.onosproject.net.config.NetworkConfigRegistry;
47import org.onosproject.net.config.basics.SubjectFactories;
48import org.onosproject.net.device.DeviceEvent;
49import org.onosproject.net.device.DeviceListener;
50import org.onosproject.net.device.DeviceService;
51import org.onosproject.net.flow.DefaultTrafficTreatment;
52import org.onosproject.net.flow.FlowRuleService;
53import org.onosproject.net.flow.criteria.Criteria;
54import org.onosproject.net.flowobjective.DefaultFilteringObjective;
55import org.onosproject.net.flowobjective.FilteringObjective;
56import org.onosproject.net.flowobjective.FlowObjectiveService;
57import org.onosproject.net.flowobjective.Objective;
58import org.onosproject.net.flowobjective.ObjectiveContext;
59import org.onosproject.net.flowobjective.ObjectiveError;
60import org.onosproject.net.mcast.McastRoute;
61import org.onosproject.net.mcast.MulticastRouteService;
62import org.onosproject.net.packet.InboundPacket;
63import org.onosproject.net.packet.PacketContext;
64import org.onosproject.net.packet.PacketProcessor;
65import org.onosproject.net.packet.PacketService;
66import org.opencord.cordconfig.access.AccessDeviceConfig;
67import org.opencord.cordconfig.access.AccessDeviceData;
68import org.slf4j.Logger;
69import org.slf4j.LoggerFactory;
70
71import java.util.ArrayList;
72import java.util.Collection;
73import java.util.Iterator;
74import java.util.List;
75import java.util.Map;
76import java.util.Set;
77import java.util.TimerTask;
78import java.util.concurrent.ConcurrentHashMap;
79import java.util.concurrent.Executors;
80import java.util.concurrent.ScheduledExecutorService;
81import java.util.concurrent.TimeUnit;
82
83/**
84 * Igmp process application, use proxy mode, support first join/ last leave , fast leave
85 * period query and keep alive, packet out igmp message to uplink port features.
86 */
87@Component(immediate = true)
88public class IgmpManager {
89
90 private static final Class<AccessDeviceConfig> CONFIG_CLASS =
91 AccessDeviceConfig.class;
92 private static final Class<IgmpproxyConfig> IGMPPROXY_CONFIG_CLASS =
93 IgmpproxyConfig.class;
94 private static final Class<IgmpproxySsmTranslateConfig> IGMPPROXY_SSM_CONFIG_CLASS =
95 IgmpproxySsmTranslateConfig.class;
96 private static final Class<McastConfig> MCAST_CONFIG_CLASS =
97 McastConfig.class;
98 public static Map<String, GroupMember> groupMemberMap = Maps.newConcurrentMap();
99 private static ApplicationId appId;
100 private static Map<DeviceId, AccessDeviceData> oltData = new ConcurrentHashMap<>();
101 private static int unSolicitedTimeout = 3; // unit is 1 sec
102 private static int keepAliveCount = 3;
103 private static int lastQueryInterval = 2; //unit is 1 sec
104 private static int lastQueryCount = 2;
105 private static boolean fastLeave = true;
106 private static boolean withRAUplink = true;
107 private static boolean withRADownlink = false;
108 private static boolean periodicQuery = true;
109 private static short mvlan = 4000;
110 private static byte igmpCos = 7;
111 public static boolean connectPointMode = true;
112 public static ConnectPoint connectPoint = null;
113
114 private final ScheduledExecutorService scheduledExecutorService =
115 Executors.newScheduledThreadPool(1);
116 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
117 protected CoreService coreService;
118 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
119 protected PacketService packetService;
120 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
121 protected MastershipService mastershipService;
122 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
123 protected FlowRuleService flowRuleService;
124 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
125 protected DeviceService deviceService;
126 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
127 protected FlowObjectiveService flowObjectiveService;
128 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
129 protected NetworkConfigRegistry networkConfig;
130 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
131 protected MulticastRouteService multicastService;
132 private IgmpPacketProcessor processor = new IgmpPacketProcessor();
133 private Logger log = LoggerFactory.getLogger(getClass());
134 private ApplicationId coreAppId;
135 private Map<Ip4Address, Ip4Address> ssmTranslateTable = new ConcurrentHashMap<>();
136 private InternalNetworkConfigListener configListener =
137 new InternalNetworkConfigListener();
138 private DeviceListener deviceListener = new InternalDeviceListener();
139 private ConfigFactory<DeviceId, AccessDeviceConfig> configFactory = null;
140 private ConfigFactory<ApplicationId, IgmpproxyConfig> igmpproxyConfigFactory =
141 new ConfigFactory<ApplicationId, IgmpproxyConfig>(
142 SubjectFactories.APP_SUBJECT_FACTORY, IGMPPROXY_CONFIG_CLASS, "igmpproxy") {
143 @Override
144 public IgmpproxyConfig createConfig() {
145 return new IgmpproxyConfig();
146 }
147 };
148 private ConfigFactory<ApplicationId, IgmpproxySsmTranslateConfig> igmpproxySsmConfigFactory =
149 new ConfigFactory<ApplicationId, IgmpproxySsmTranslateConfig>(
150 SubjectFactories.APP_SUBJECT_FACTORY, IGMPPROXY_SSM_CONFIG_CLASS, "ssmTranslate", true) {
151 @Override
152 public IgmpproxySsmTranslateConfig createConfig() {
153 return new IgmpproxySsmTranslateConfig();
154 }
155 };
156 private int maxResp = 10; //unit is 1 sec
157 private int keepAliveInterval = 120; //unit is 1 sec
158
159 public static int getUnsolicitedTimeout() {
160 return unSolicitedTimeout;
161 }
162
163 @Activate
164 protected void activate() {
165 appId = coreService.registerApplication("org.opencord.igmpproxy");
166 coreAppId = coreService.registerApplication(CoreService.CORE_APP_NAME);
167 packetService.addProcessor(processor, PacketProcessor.director(4));
168 IgmpSender.init(packetService, mastershipService);
169
170 if (networkConfig.getConfigFactory(CONFIG_CLASS) == null) {
171 configFactory =
172 new ConfigFactory<DeviceId, AccessDeviceConfig>(
173 SubjectFactories.DEVICE_SUBJECT_FACTORY, CONFIG_CLASS, "accessDevice") {
174 @Override
175 public AccessDeviceConfig createConfig() {
176 return new AccessDeviceConfig();
177 }
178 };
179 networkConfig.registerConfigFactory(configFactory);
180 }
181 networkConfig.registerConfigFactory(igmpproxySsmConfigFactory);
182 networkConfig.registerConfigFactory(igmpproxyConfigFactory);
183 networkConfig.addListener(configListener);
184
185 configListener.reconfigureNetwork(networkConfig.getConfig(appId, IGMPPROXY_CONFIG_CLASS));
186 configListener.reconfigureSsmTable(networkConfig.getConfig(appId, IGMPPROXY_SSM_CONFIG_CLASS));
187
188 networkConfig.getSubjects(DeviceId.class, AccessDeviceConfig.class).forEach(
189 subject -> {
190 AccessDeviceConfig config = networkConfig.getConfig(subject,
191 AccessDeviceConfig.class);
192 if (config != null) {
193 AccessDeviceData data = config.getAccessDevice();
194 oltData.put(data.deviceId(), data);
195 }
196 }
197 );
198
David K. Bainbridge4809c0e2017-08-17 09:54:40 -0700199 oltData.keySet().forEach(d -> provisionDefaultFlows(d));
ke han81a38b92017-03-10 18:41:44 +0800200 if (connectPointMode) {
201 provisionConnectPointFlows();
202 } else {
203 provisionUplinkFlows();
204 }
205
206 McastConfig config = networkConfig.getConfig(coreAppId, MCAST_CONFIG_CLASS);
207 if (config != null) {
208 mvlan = config.egressVlan().toShort();
Deepa Vaddireddy88134a42017-07-05 12:16:03 +0530209 IgmpSender.getInstance().setMvlan(mvlan);
ke han81a38b92017-03-10 18:41:44 +0800210 }
211 deviceService.addListener(deviceListener);
212 scheduledExecutorService.scheduleAtFixedRate(new IgmpProxyTimerTask(), 0, 1000, TimeUnit.MILLISECONDS);
213
214 log.info("Started");
215 }
216
217 @Deactivate
218 protected void deactivate() {
219 scheduledExecutorService.shutdown();
220
221 // de-register and null our handler
222 networkConfig.removeListener(configListener);
223 if (configFactory != null) {
224 networkConfig.unregisterConfigFactory(configFactory);
225 }
226 networkConfig.unregisterConfigFactory(igmpproxyConfigFactory);
227 networkConfig.unregisterConfigFactory(igmpproxySsmConfigFactory);
228 deviceService.removeListener(deviceListener);
229 packetService.removeProcessor(processor);
230 flowRuleService.removeFlowRulesById(appId);
231
232 log.info("Stopped");
233 }
234
235 protected Ip4Address getDeviceIp(DeviceId ofDeviceId) {
236 try {
237 String[] mgmtAddress = deviceService.getDevice(ofDeviceId)
238 .annotations().value(AnnotationKeys.MANAGEMENT_ADDRESS).split(":");
239 return Ip4Address.valueOf(mgmtAddress[0]);
240 } catch (Exception ex) {
241 log.info("No valid Ipaddress for " + ofDeviceId.toString());
242 return null;
243 }
244 }
245
246 private void processIgmpQuery(IGMPQuery igmpQuery, ConnectPoint cp, int maxResp) {
247
248 DeviceId deviceId = cp.deviceId();
249 Ip4Address gAddr = igmpQuery.getGaddr().getIp4Address();
250
251 if (maxResp >= 128) {
252 int mant = maxResp & 0xf;
253 int exp = (maxResp >> 4) & 0x7;
254 maxResp = (mant | 0x10) << (exp + 3);
255 }
256
257 maxResp = (maxResp + 5) / 10;
258
259 if (gAddr != null && !gAddr.isZero()) {
260 StateMachine.specialQuery(deviceId, gAddr, maxResp);
261 } else {
262 StateMachine.generalQuery(deviceId, maxResp);
263 }
264
265 }
266
267 private Ip4Address ssmTranslateRoute(IpAddress group) {
268 return ssmTranslateTable.get(group);
269 }
270
271 private void processIgmpReport(IGMPMembership igmpGroup, VlanId vlan, ConnectPoint cp, byte igmpType) {
272 DeviceId deviceId = cp.deviceId();
273 PortNumber portNumber = cp.port();
274
275 Ip4Address groupIp = igmpGroup.getGaddr().getIp4Address();
276 if (!groupIp.isMulticast()) {
277 log.info(groupIp.toString() + " is not a valid group address");
278 return;
279 }
280 Ip4Address srcIp = getDeviceIp(deviceId);
281
282 byte recordType = igmpGroup.getRecordType();
283 boolean join = false;
284
285 ArrayList<Ip4Address> sourceList = new ArrayList<>();
286
287 if (igmpGroup.getSources().size() > 0) {
288 igmpGroup.getSources().forEach(source -> sourceList.add(source.getIp4Address()));
289 if (recordType == IGMPMembership.CHANGE_TO_EXCLUDE_MODE ||
290 recordType == IGMPMembership.MODE_IS_EXCLUDE ||
291 recordType == IGMPMembership.BLOCK_OLD_SOURCES) {
292 join = false;
293 } else if (recordType == IGMPMembership.CHANGE_TO_INCLUDE_MODE ||
294 recordType == IGMPMembership.MODE_IS_INCLUDE ||
295 recordType == IGMPMembership.ALLOW_NEW_SOURCES) {
296 join = true;
297 }
298 } else {
299 IpAddress src = ssmTranslateRoute(groupIp);
300 if (src == null) {
301 log.info("no ssm translate for group " + groupIp.toString());
302 return;
303 }
304 sourceList.add(src.getIp4Address());
305 if (recordType == IGMPMembership.CHANGE_TO_EXCLUDE_MODE ||
306 recordType == IGMPMembership.MODE_IS_EXCLUDE ||
307 recordType == IGMPMembership.BLOCK_OLD_SOURCES) {
308 join = true;
309 } else if (recordType == IGMPMembership.CHANGE_TO_INCLUDE_MODE ||
310 recordType == IGMPMembership.MODE_IS_INCLUDE ||
311 recordType == IGMPMembership.ALLOW_NEW_SOURCES) {
312 join = false;
313 }
314 }
315 String groupMemberKey = GroupMember.getkey(groupIp, deviceId, portNumber);
316 GroupMember groupMember = groupMemberMap.get(groupMemberKey);
317
318 if (join) {
319 if (groupMember == null) {
320 if (igmpType == IGMP.TYPE_IGMPV2_MEMBERSHIP_REPORT) {
321 groupMember = new GroupMember(groupIp, vlan, deviceId, portNumber, true);
322 } else {
323 groupMember = new GroupMember(groupIp, vlan, deviceId, portNumber, false);
324 }
325 StateMachine.join(deviceId, groupIp, srcIp);
326 groupMemberMap.put(groupMemberKey, groupMember);
327 groupMember.updateList(recordType, sourceList);
328 groupMember.getSourceList().forEach(source -> multicastService.addSink(
329 new McastRoute(source, groupIp, McastRoute.Type.IGMP), cp));
330 }
331 groupMember.resetAllTimers();
332 groupMember.updateList(recordType, sourceList);
333 groupMember.setLeave(false);
334 } else {
335 if (groupMember == null) {
336 log.info("receive leave but no instance, group " + groupIp.toString() +
337 " device:" + deviceId.toString() + " port:" + portNumber.toString());
338 return;
339 } else {
340 groupMember.setLeave(true);
341 if (fastLeave) {
342 leaveAction(groupMember);
343 } else {
344 sendQuery(groupMember);
345 }
346 }
347 }
348 }
349
350 private void leaveAction(GroupMember groupMember) {
351 ConnectPoint cp = new ConnectPoint(groupMember.getDeviceId(), groupMember.getPortNumber());
352 StateMachine.leave(groupMember.getDeviceId(), groupMember.getGroupIp());
353 groupMember.getSourceList().forEach(source -> multicastService.removeSink(
354 new McastRoute(source, groupMember.getGroupIp(),
355 McastRoute.Type.IGMP), cp));
356 groupMemberMap.remove(groupMember.getId());
357 }
358
359 private void sendQuery(GroupMember groupMember) {
360 Ethernet ethpkt;
361 Ip4Address srcIp = getDeviceIp(groupMember.getDeviceId());
362 if (groupMember.getv2()) {
363 ethpkt = IgmpSender.getInstance().buildIgmpV2Query(groupMember.getGroupIp(), srcIp);
364 } else {
365 ethpkt = IgmpSender.getInstance().buildIgmpV3Query(groupMember.getGroupIp(), srcIp);
366 }
367 IgmpSender.getInstance().sendIgmpPacket(ethpkt, groupMember.getDeviceId(), groupMember.getPortNumber());
368 }
369
370 private void processFilterObjective(DeviceId devId, Port port, boolean remove) {
371
372 //TODO migrate to packet requests when packet service uses filtering objectives
373 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
374
375 builder = remove ? builder.deny() : builder.permit();
376
377 FilteringObjective igmp = builder
378 .withKey(Criteria.matchInPort(port.number()))
379 .addCondition(Criteria.matchEthType(EthType.EtherType.IPV4.ethType()))
380 .addCondition(Criteria.matchIPProtocol(IPv4.PROTOCOL_IGMP))
381 .withMeta(DefaultTrafficTreatment.builder().setOutput(PortNumber.CONTROLLER).build())
382 .fromApp(appId)
383 .withPriority(10000)
384 .add(new ObjectiveContext() {
385 @Override
386 public void onSuccess(Objective objective) {
387 log.info("IgmpProxy filter for {} on {} installed.",
388 devId, port);
389 }
390
391 @Override
392 public void onError(Objective objective, ObjectiveError error) {
393 log.info("IgmpProxy filter for {} on {} failed because {}.",
394 devId, port, error);
395 }
396 });
397
398 flowObjectiveService.filter(devId, igmp);
399 }
400
401 /**
402 * Packet processor responsible for forwarding packets along their paths.
403 */
404 private class IgmpPacketProcessor implements PacketProcessor {
405 @Override
406 public void process(PacketContext context) {
407
408 try {
409 InboundPacket pkt = context.inPacket();
410 Ethernet ethPkt = pkt.parsed();
411 if (ethPkt == null) {
412 return;
413 }
414
415 if (ethPkt.getEtherType() != Ethernet.TYPE_IPV4) {
416 return;
417 }
418
419 IPv4 ipv4Pkt = (IPv4) ethPkt.getPayload();
420
421 if (ipv4Pkt.getProtocol() != IPv4.PROTOCOL_IGMP) {
422 return;
423 }
424
425 short vlan = ethPkt.getVlanID();
426 DeviceId deviceId = pkt.receivedFrom().deviceId();
427
428 if (oltData.get(deviceId) == null) {
429 log.error("Device not registered in netcfg :" + deviceId.toString());
430 return;
431 }
432
433 IGMP igmp = (IGMP) ipv4Pkt.getPayload();
434 switch (igmp.getIgmpType()) {
435 case IGMP.TYPE_IGMPV3_MEMBERSHIP_QUERY:
436 //Discard Query from OLT’s non-uplink port’s
437 if (!pkt.receivedFrom().port().equals(getDeviceUplink(deviceId))) {
438 log.info("IGMP Picked up query from non-uplink port");
439 return;
440 }
441
442 processIgmpQuery((IGMPQuery) igmp.getGroups().get(0), pkt.receivedFrom(),
443 0xff & igmp.getMaxRespField());
444 break;
445 case IGMP.TYPE_IGMPV1_MEMBERSHIP_REPORT:
446 log.debug("IGMP version 1 message types are not currently supported.");
447 break;
448 case IGMP.TYPE_IGMPV3_MEMBERSHIP_REPORT:
449 case IGMP.TYPE_IGMPV2_MEMBERSHIP_REPORT:
450 case IGMP.TYPE_IGMPV2_LEAVE_GROUP:
451 //Discard join/leave from OLT’s uplink port’s
452 if (pkt.receivedFrom().port().equals(getDeviceUplink(deviceId))) {
453 log.info("IGMP Picked up join/leave from the olt uplink port");
454 return;
455 }
456
457 Iterator<IGMPGroup> itr = igmp.getGroups().iterator();
458 while (itr.hasNext()) {
459 IGMPGroup group = itr.next();
460 if (group instanceof IGMPMembership) {
461 processIgmpReport((IGMPMembership) group, VlanId.vlanId(vlan),
462 pkt.receivedFrom(), igmp.getIgmpType());
463 } else if (group instanceof IGMPQuery) {
464 IGMPMembership mgroup;
465 mgroup = new IGMPMembership(group.getGaddr().getIp4Address());
466 mgroup.setRecordType(igmp.getIgmpType() == IGMP.TYPE_IGMPV2_MEMBERSHIP_REPORT ?
467 IGMPMembership.MODE_IS_EXCLUDE : IGMPMembership.MODE_IS_INCLUDE);
468 processIgmpReport(mgroup, VlanId.vlanId(vlan),
469 pkt.receivedFrom(), igmp.getIgmpType());
470 }
471 }
472 break;
473
474 default:
475 log.info("wrong IGMP v3 type:" + igmp.getIgmpType());
476 break;
477 }
478
479 } catch (Exception ex) {
480 log.error("igmp process error : " + ex.toString());
481 ex.printStackTrace();
482 }
483 }
484 }
485
486 private class IgmpProxyTimerTask extends TimerTask {
487 public void run() {
488 try {
489 IgmpTimer.timeOut1s();
490 queryMembers();
491 } catch (Exception ex) {
492 log.warn("Igmp timer task error : {}", ex.getMessage());
493 }
494 }
495
496 private void queryMembers() {
497 GroupMember groupMember;
498 Set groupMemberSet = groupMemberMap.entrySet();
499 Iterator itr = groupMemberSet.iterator();
500 while (itr.hasNext()) {
501 Map.Entry entry = (Map.Entry) itr.next();
502 groupMember = (GroupMember) entry.getValue();
503 DeviceId did = groupMember.getDeviceId();
504 if (mastershipService.isLocalMaster(did)) {
505 if (groupMember.isLeave()) {
506 lastQuery(groupMember);
507 } else if (periodicQuery) {
508 periodicQuery(groupMember);
509 }
510 }
511 }
512 }
513
514 private void lastQuery(GroupMember groupMember) {
515 if (groupMember.getLastQueryInterval() < lastQueryInterval) {
516 groupMember.lastQueryInterval(true); // count times
517 } else if (groupMember.getLastQueryCount() < lastQueryCount - 1) {
518 sendQuery(groupMember);
519 groupMember.lastQueryInterval(false); // reset count number
520 groupMember.lastQueryCount(true); //count times
521 } else if (groupMember.getLastQueryCount() == lastQueryCount - 1) {
522 leaveAction(groupMember);
523 }
524 }
525
526 private void periodicQuery(GroupMember groupMember) {
527 if (groupMember.getKeepAliveQueryInterval() < keepAliveInterval) {
528 groupMember.keepAliveInterval(true);
529 } else if (groupMember.getKeepAliveQueryCount() < keepAliveCount) {
530 sendQuery(groupMember);
531 groupMember.keepAliveInterval(false);
532 groupMember.keepAliveQueryCount(true);
533 } else if (groupMember.getKeepAliveQueryCount() == keepAliveCount) {
534 leaveAction(groupMember);
535 }
536 }
537
538 }
539
540 public static PortNumber getDeviceUplink(DeviceId devId) {
541 return oltData.get(devId).uplink();
542 }
543
544 private void processFilterObjective(DeviceId devId, PortNumber port, boolean remove) {
545 //TODO migrate to packet requests when packet service uses filtering objectives
546 DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
547
548 builder = remove ? builder.deny() : builder.permit();
549
550 FilteringObjective igmp = builder
551 .withKey(Criteria.matchInPort(port))
552 .addCondition(Criteria.matchEthType(EthType.EtherType.IPV4.ethType()))
553 .addCondition(Criteria.matchIPProtocol(IPv4.PROTOCOL_IGMP))
554 .withMeta(DefaultTrafficTreatment.builder().setOutput(PortNumber.CONTROLLER).build())
555 .fromApp(appId)
556 .withPriority(10000)
557 .add(new ObjectiveContext() {
558 @Override
559 public void onSuccess(Objective objective) {
560 log.info("IgmpProxy filter for {} on {} installed.",
561 devId, port);
562 }
563
564 @Override
565 public void onError(Objective objective, ObjectiveError error) {
566 log.info("IgmpProxy filter for {} on {} failed because {}.",
567 devId, port, error);
568 }
569 });
570
571 flowObjectiveService.filter(devId, igmp);
572 }
573
574 private boolean isConnectPoint(DeviceId device, PortNumber port) {
Deepa Vaddireddy01f33b42017-07-02 13:26:53 +0530575 if (connectPoint != null) {
576 return (connectPointMode && connectPoint.deviceId().equals(device)
577 && connectPoint.port().equals(port));
578 } else {
579 log.info("connectPoint not configured for device {}", device);
580 return false;
581 }
ke han81a38b92017-03-10 18:41:44 +0800582 }
Deepa Vaddireddy01f33b42017-07-02 13:26:53 +0530583
ke han81a38b92017-03-10 18:41:44 +0800584 private boolean isUplink(DeviceId device, PortNumber port) {
585 return ((!connectPointMode) && oltData.containsKey(device)
586 && oltData.get(device).uplink().equals(port));
587 }
588
589 private class InternalDeviceListener implements DeviceListener {
590 @Override
591 public void event(DeviceEvent event) {
592 DeviceId devId = event.subject().id();
593 PortNumber port;
594 if (oltData.get(devId) == null) {
595 return;
596 }
597 switch (event.type()) {
598
599 case DEVICE_ADDED:
600 case DEVICE_UPDATED:
601 case DEVICE_REMOVED:
602 case DEVICE_SUSPENDED:
603 case DEVICE_AVAILABILITY_CHANGED:
604 case PORT_STATS_UPDATED:
605 break;
606 case PORT_ADDED:
607 port = event.port().number();
608 if (oltData.containsKey(devId) && !isUplink(devId, port) && !isConnectPoint(devId, port)) {
609 processFilterObjective(devId, port, false);
610 } else if (isUplink(devId, port)) {
611 provisionUplinkFlows();
612 } else if (isConnectPoint(devId, port)) {
613 provisionConnectPointFlows();
614 }
615 break;
616 case PORT_UPDATED:
617 port = event.port().number();
618 if (oltData.containsKey(devId) && !isUplink(devId, port) && !isConnectPoint(devId, port)) {
619 if (event.port().isEnabled()) {
620 processFilterObjective(devId, port, false);
621 } else {
622 processFilterObjective(devId, port, true);
623 }
624 } else if (isUplink(devId, port)) {
625 if (event.port().isEnabled()) {
626 provisionUplinkFlows(devId);
627 } else {
628 processFilterObjective(devId, port, true);
629 }
630 } else if (isConnectPoint(devId, port)) {
631 if (event.port().isEnabled()) {
632 provisionConnectPointFlows();
633 } else {
634 unprovisionConnectPointFlows();
635 }
636 }
637 break;
638 case PORT_REMOVED:
639 port = event.port().number();
640 processFilterObjective(devId, port, true);
641 break;
642 default:
643 log.info("Unknown device event {}", event.type());
644 break;
645 }
646 }
647
648 @Override
649 public boolean isRelevant(DeviceEvent event) {
650 return true;
651 }
652 }
653
654 private class InternalNetworkConfigListener implements NetworkConfigListener {
655
656 private void reconfigureNetwork(IgmpproxyConfig cfg) {
657 IgmpproxyConfig newCfg;
658 if (cfg == null) {
659 newCfg = new IgmpproxyConfig();
660 } else {
661 newCfg = cfg;
662 }
663
664 unSolicitedTimeout = newCfg.unsolicitedTimeOut();
665 maxResp = newCfg.maxResp();
666 keepAliveInterval = newCfg.keepAliveInterval();
667 keepAliveCount = newCfg.keepAliveCount();
668 lastQueryInterval = newCfg.lastQueryInterval();
669 lastQueryCount = newCfg.lastQueryCount();
670 withRAUplink = newCfg.withRAUplink();
671 withRADownlink = newCfg.withRADownlink();
672 igmpCos = newCfg.igmpCos();
673 periodicQuery = newCfg.periodicQuery();
674 fastLeave = newCfg.fastLeave();
675
676 connectPoint = newCfg.connectPoint();
677 if (connectPointMode != newCfg.connectPointMode()) {
678 connectPointMode = newCfg.connectPointMode();
679 if (connectPointMode) {
680 unprovisionUplinkFlows();
681 provisionConnectPointFlows();
682 } else {
683 unprovisionConnectPointFlows();
684 provisionUplinkFlows();
685 }
686 }
687 if (connectPoint != null) {
688 log.info("connect point :" + connectPoint.toString());
689 }
690 log.info(" mode: " + connectPointMode);
691
692 IgmpSender.getInstance().setIgmpCos(igmpCos);
693 IgmpSender.getInstance().setMaxResp(maxResp);
694 IgmpSender.getInstance().setMvlan(mvlan);
695 IgmpSender.getInstance().setWithRADownlink(withRADownlink);
696 IgmpSender.getInstance().setWithRAUplink(withRAUplink);
697
698 }
699
700 public void reconfigureSsmTable(IgmpproxySsmTranslateConfig cfg) {
701 if (cfg == null) {
702 return;
703 }
704 Collection<McastRoute> translations = cfg.getSsmTranslations();
705 for (McastRoute route : translations) {
706 ssmTranslateTable.put(route.group().getIp4Address(), route.source().getIp4Address());
707 }
708 }
709
710 @Override
711 public void event(NetworkConfigEvent event) {
712 switch (event.type()) {
713 case CONFIG_ADDED:
714 case CONFIG_UPDATED:
715 if (event.configClass().equals(CONFIG_CLASS)) {
716 AccessDeviceConfig config =
717 networkConfig.getConfig((DeviceId) event.subject(), CONFIG_CLASS);
718 if (config != null) {
719 oltData.put(config.getAccessDevice().deviceId(), config.getAccessDevice());
720 provisionDefaultFlows((DeviceId) event.subject());
721 provisionUplinkFlows((DeviceId) event.subject());
722 }
723 }
724
725 if (event.configClass().equals(IGMPPROXY_CONFIG_CLASS)) {
726 IgmpproxyConfig config = networkConfig.getConfig(appId, IGMPPROXY_CONFIG_CLASS);
727 if (config != null) {
728 reconfigureNetwork(config);
729 }
730 }
731
732 if (event.configClass().equals(IGMPPROXY_SSM_CONFIG_CLASS)) {
733 IgmpproxySsmTranslateConfig config = networkConfig.getConfig(appId, IGMPPROXY_SSM_CONFIG_CLASS);
734 if (config != null) {
735 reconfigureSsmTable(config);
736 }
737 }
738
739 if (event.configClass().equals(MCAST_CONFIG_CLASS)) {
740 McastConfig config = networkConfig.getConfig(coreAppId, MCAST_CONFIG_CLASS);
741 if (config != null && mvlan != config.egressVlan().toShort()) {
742 mvlan = config.egressVlan().toShort();
Deepa Vaddireddy88134a42017-07-05 12:16:03 +0530743 IgmpSender.getInstance().setMvlan(mvlan);
ke han81a38b92017-03-10 18:41:44 +0800744 groupMemberMap.values().forEach(m -> leaveAction(m));
745 }
746 }
747
748 log.info("Reconfigured");
749 break;
750 case CONFIG_REGISTERED:
751 case CONFIG_UNREGISTERED:
752 break;
753 case CONFIG_REMOVED:
754 if (event.configClass().equals(CONFIG_CLASS)) {
755 oltData.remove(event.subject());
756 }
757
758 default:
759 break;
760 }
761 }
762 }
763
764 private void provisionDefaultFlows(DeviceId deviceId) {
765 List<Port> ports = deviceService.getPorts(deviceId);
766 ports.stream()
767 .filter(p -> (!oltData.get(p.element().id()).uplink().equals(p.number()) && p.isEnabled()))
768 .forEach(p -> processFilterObjective((DeviceId) p.element().id(), p.number(), false));
769 }
770
771 private void provisionUplinkFlows(DeviceId deviceId) {
772 if (connectPointMode) {
773 return;
774 }
775
776 processFilterObjective(deviceId, oltData.get(deviceId).uplink(), false);
777 }
778
779 private void provisionUplinkFlows() {
780 if (connectPointMode) {
781 return;
782 }
783
David K. Bainbridge4809c0e2017-08-17 09:54:40 -0700784 oltData.keySet().forEach(deviceId -> provisionUplinkFlows(deviceId));
ke han81a38b92017-03-10 18:41:44 +0800785 }
786 private void unprovisionUplinkFlows() {
787 oltData.keySet().forEach(deviceId ->
788 processFilterObjective(deviceId, oltData.get(deviceId).uplink(), true));
789 }
790
791 private void provisionConnectPointFlows() {
792 if ((!connectPointMode) || connectPoint == null) {
793 return;
794 }
795
796 processFilterObjective(connectPoint.deviceId(), connectPoint.port(), false);
797 }
798 private void unprovisionConnectPointFlows() {
799 if (connectPoint == null) {
800 return;
801 }
802 processFilterObjective(connectPoint.deviceId(), connectPoint.port(), true);
803 }
804}