Charles Chan
Committed by Gerrit Code Review

CORD-348 multicast support in SegmentRouting and vRouter

In this submission:
* Setup/teardown multicast route according to SinkAdded/SinkRemoved event
    - ingressVlan and egressVlan is configurable through network config
* Change behavior of OFDPA VLAN assignment
    - Always use the VLAN in metadata if present
* Bugfix of writing immutable object

NOT in this submission (coming soon):
* Error handling (e.g. link/device failure recovery)

Change-Id: I9be11af04eb2d6456b865c7e59e96cc02370f846
...@@ -29,11 +29,13 @@ import org.apache.felix.scr.annotations.ReferenceCardinality; ...@@ -29,11 +29,13 @@ import org.apache.felix.scr.annotations.ReferenceCardinality;
29 import org.onlab.packet.Ethernet; 29 import org.onlab.packet.Ethernet;
30 import org.onlab.packet.IpAddress; 30 import org.onlab.packet.IpAddress;
31 import org.onlab.packet.IpPrefix; 31 import org.onlab.packet.IpPrefix;
32 +import org.onlab.packet.MacAddress;
32 import org.onlab.packet.VlanId; 33 import org.onlab.packet.VlanId;
33 import org.onlab.util.Tools; 34 import org.onlab.util.Tools;
34 import org.onosproject.cfg.ComponentConfigService; 35 import org.onosproject.cfg.ComponentConfigService;
35 import org.onosproject.core.ApplicationId; 36 import org.onosproject.core.ApplicationId;
36 import org.onosproject.core.CoreService; 37 import org.onosproject.core.CoreService;
38 +import org.onosproject.incubator.net.config.basics.McastConfig;
37 import org.onosproject.incubator.net.intf.Interface; 39 import org.onosproject.incubator.net.intf.Interface;
38 import org.onosproject.incubator.net.intf.InterfaceEvent; 40 import org.onosproject.incubator.net.intf.InterfaceEvent;
39 import org.onosproject.incubator.net.intf.InterfaceListener; 41 import org.onosproject.incubator.net.intf.InterfaceListener;
...@@ -44,9 +46,12 @@ import org.onosproject.incubator.net.routing.RouteListener; ...@@ -44,9 +46,12 @@ import org.onosproject.incubator.net.routing.RouteListener;
44 import org.onosproject.incubator.net.routing.RouteService; 46 import org.onosproject.incubator.net.routing.RouteService;
45 import org.onosproject.net.ConnectPoint; 47 import org.onosproject.net.ConnectPoint;
46 import org.onosproject.net.DeviceId; 48 import org.onosproject.net.DeviceId;
49 +import org.onosproject.net.config.ConfigFactory;
47 import org.onosproject.net.config.NetworkConfigEvent; 50 import org.onosproject.net.config.NetworkConfigEvent;
48 import org.onosproject.net.config.NetworkConfigListener; 51 import org.onosproject.net.config.NetworkConfigListener;
52 +import org.onosproject.net.config.NetworkConfigRegistry;
49 import org.onosproject.net.config.NetworkConfigService; 53 import org.onosproject.net.config.NetworkConfigService;
54 +import org.onosproject.net.config.basics.SubjectFactories;
50 import org.onosproject.net.device.DeviceEvent; 55 import org.onosproject.net.device.DeviceEvent;
51 import org.onosproject.net.device.DeviceListener; 56 import org.onosproject.net.device.DeviceListener;
52 import org.onosproject.net.device.DeviceService; 57 import org.onosproject.net.device.DeviceService;
...@@ -102,6 +107,9 @@ public class SingleSwitchFibInstaller { ...@@ -102,6 +107,9 @@ public class SingleSwitchFibInstaller {
102 protected NetworkConfigService networkConfigService; 107 protected NetworkConfigService networkConfigService;
103 108
104 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) 109 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
110 + protected NetworkConfigRegistry networkConfigRegistry;
111 +
112 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
105 protected ComponentConfigService componentConfigService; 113 protected ComponentConfigService componentConfigService;
106 114
107 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) 115 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
...@@ -123,6 +131,7 @@ public class SingleSwitchFibInstaller { ...@@ -123,6 +131,7 @@ public class SingleSwitchFibInstaller {
123 131
124 private List<String> interfaces; 132 private List<String> interfaces;
125 133
134 + private ApplicationId coreAppId;
126 private ApplicationId routerAppId; 135 private ApplicationId routerAppId;
127 136
128 // Reference count for how many times a next hop is used by a route 137 // Reference count for how many times a next hop is used by a route
...@@ -138,13 +147,25 @@ public class SingleSwitchFibInstaller { ...@@ -138,13 +147,25 @@ public class SingleSwitchFibInstaller {
138 private InternalInterfaceListener internalInterfaceList = new InternalInterfaceListener(); 147 private InternalInterfaceListener internalInterfaceList = new InternalInterfaceListener();
139 private InternalRouteListener routeListener = new InternalRouteListener(); 148 private InternalRouteListener routeListener = new InternalRouteListener();
140 149
150 + private ConfigFactory<ApplicationId, McastConfig> mcastConfigFactory =
151 + new ConfigFactory<ApplicationId, McastConfig>(SubjectFactories.APP_SUBJECT_FACTORY,
152 + McastConfig.class, "multicast") {
153 + @Override
154 + public McastConfig createConfig() {
155 + return new McastConfig();
156 + }
157 + };
158 +
141 @Activate 159 @Activate
142 protected void activate(ComponentContext context) { 160 protected void activate(ComponentContext context) {
143 componentConfigService.registerProperties(getClass()); 161 componentConfigService.registerProperties(getClass());
144 modified(context); 162 modified(context);
145 163
164 + coreAppId = coreService.registerApplication(CoreService.CORE_APP_NAME);
146 routerAppId = coreService.registerApplication(RoutingService.ROUTER_APP_ID); 165 routerAppId = coreService.registerApplication(RoutingService.ROUTER_APP_ID);
147 166
167 + networkConfigRegistry.registerConfigFactory(mcastConfigFactory);
168 +
148 deviceListener = new InternalDeviceListener(); 169 deviceListener = new InternalDeviceListener();
149 deviceService.addListener(deviceListener); 170 deviceService.addListener(deviceListener);
150 171
...@@ -368,6 +389,7 @@ public class SingleSwitchFibInstaller { ...@@ -368,6 +389,7 @@ public class SingleSwitchFibInstaller {
368 } 389 }
369 390
370 createFilteringObjective(install, intf); 391 createFilteringObjective(install, intf);
392 + createMcastFilteringObjective(install, intf);
371 } 393 }
372 } 394 }
373 395
...@@ -380,10 +402,14 @@ public class SingleSwitchFibInstaller { ...@@ -380,10 +402,14 @@ public class SingleSwitchFibInstaller {
380 } 402 }
381 403
382 createFilteringObjective(install, intf); 404 createFilteringObjective(install, intf);
405 + createMcastFilteringObjective(install, intf);
383 } 406 }
384 407
385 //create filtering objective for interface 408 //create filtering objective for interface
386 private void createFilteringObjective(boolean install, Interface intf) { 409 private void createFilteringObjective(boolean install, Interface intf) {
410 + VlanId assignedVlan = (egressVlan().equals(VlanId.NONE)) ?
411 + VlanId.vlanId(ASSIGNED_VLAN) :
412 + egressVlan();
387 413
388 FilteringObjective.Builder fob = DefaultFilteringObjective.builder(); 414 FilteringObjective.Builder fob = DefaultFilteringObjective.builder();
389 // first add filter for the interface 415 // first add filter for the interface
...@@ -393,12 +419,12 @@ public class SingleSwitchFibInstaller { ...@@ -393,12 +419,12 @@ public class SingleSwitchFibInstaller {
393 fob.withPriority(PRIORITY_OFFSET); 419 fob.withPriority(PRIORITY_OFFSET);
394 if (intf.vlan() == VlanId.NONE) { 420 if (intf.vlan() == VlanId.NONE) {
395 TrafficTreatment tt = DefaultTrafficTreatment.builder() 421 TrafficTreatment tt = DefaultTrafficTreatment.builder()
396 - .pushVlan().setVlanId(VlanId.vlanId(ASSIGNED_VLAN)).build(); 422 + .pushVlan().setVlanId(assignedVlan).build();
397 fob.withMeta(tt); 423 fob.withMeta(tt);
398 } 424 }
399 -
400 fob.permit().fromApp(routerAppId); 425 fob.permit().fromApp(routerAppId);
401 sendFilteringObjective(install, fob, intf); 426 sendFilteringObjective(install, fob, intf);
427 +
402 if (controlPlaneConnectPoint != null) { 428 if (controlPlaneConnectPoint != null) {
403 // then add the same mac/vlan filters for control-plane connect point 429 // then add the same mac/vlan filters for control-plane connect point
404 fob.withKey(Criteria.matchInPort(controlPlaneConnectPoint.port())); 430 fob.withKey(Criteria.matchInPort(controlPlaneConnectPoint.port()));
...@@ -406,6 +432,27 @@ public class SingleSwitchFibInstaller { ...@@ -406,6 +432,27 @@ public class SingleSwitchFibInstaller {
406 } 432 }
407 } 433 }
408 434
435 + //create filtering objective for multicast traffic
436 + private void createMcastFilteringObjective(boolean install, Interface intf) {
437 + VlanId assignedVlan = (egressVlan().equals(VlanId.NONE)) ?
438 + VlanId.vlanId(ASSIGNED_VLAN) :
439 + egressVlan();
440 +
441 + FilteringObjective.Builder fob = DefaultFilteringObjective.builder();
442 + // first add filter for the interface
443 + fob.withKey(Criteria.matchInPort(intf.connectPoint().port()))
444 + .addCondition(Criteria.matchEthDstMasked(MacAddress.IPV4_MULTICAST,
445 + MacAddress.IPV4_MULTICAST_MASK))
446 + .addCondition(Criteria.matchVlanId(ingressVlan()));
447 + fob.withPriority(PRIORITY_OFFSET);
448 + TrafficTreatment tt = DefaultTrafficTreatment.builder()
449 + .pushVlan().setVlanId(assignedVlan).build();
450 + fob.withMeta(tt);
451 +
452 + fob.permit().fromApp(routerAppId);
453 + sendFilteringObjective(install, fob, intf);
454 + }
455 +
409 private void sendFilteringObjective(boolean install, FilteringObjective.Builder fob, 456 private void sendFilteringObjective(boolean install, FilteringObjective.Builder fob,
410 Interface intf) { 457 Interface intf) {
411 458
...@@ -419,6 +466,18 @@ public class SingleSwitchFibInstaller { ...@@ -419,6 +466,18 @@ public class SingleSwitchFibInstaller {
419 flowObjectiveService.filter(deviceId, filter); 466 flowObjectiveService.filter(deviceId, filter);
420 } 467 }
421 468
469 + private VlanId ingressVlan() {
470 + McastConfig mcastConfig =
471 + networkConfigService.getConfig(coreAppId, McastConfig.class);
472 + return (mcastConfig != null) ? mcastConfig.ingressVlan() : VlanId.NONE;
473 + }
474 +
475 + private VlanId egressVlan() {
476 + McastConfig mcastConfig =
477 + networkConfigService.getConfig(coreAppId, McastConfig.class);
478 + return (mcastConfig != null) ? mcastConfig.egressVlan() : VlanId.NONE;
479 + }
480 +
422 private class InternalRouteListener implements RouteListener { 481 private class InternalRouteListener implements RouteListener {
423 @Override 482 @Override
424 public void event(RouteEvent event) { 483 public void event(RouteEvent event) {
...@@ -490,7 +549,6 @@ public class SingleSwitchFibInstaller { ...@@ -490,7 +549,6 @@ public class SingleSwitchFibInstaller {
490 } 549 }
491 550
492 private class InternalInterfaceListener implements InterfaceListener { 551 private class InternalInterfaceListener implements InterfaceListener {
493 -
494 @Override 552 @Override
495 public void event(InterfaceEvent event) { 553 public void event(InterfaceEvent event) {
496 Interface intf = event.subject(); 554 Interface intf = event.subject();
......
...@@ -40,6 +40,7 @@ import org.onosproject.incubator.net.routing.RouteServiceAdapter; ...@@ -40,6 +40,7 @@ import org.onosproject.incubator.net.routing.RouteServiceAdapter;
40 import org.onosproject.net.ConnectPoint; 40 import org.onosproject.net.ConnectPoint;
41 import org.onosproject.net.DeviceId; 41 import org.onosproject.net.DeviceId;
42 import org.onosproject.net.PortNumber; 42 import org.onosproject.net.PortNumber;
43 +import org.onosproject.net.config.NetworkConfigRegistry;
43 import org.onosproject.net.config.NetworkConfigService; 44 import org.onosproject.net.config.NetworkConfigService;
44 import org.onosproject.net.device.DeviceListener; 45 import org.onosproject.net.device.DeviceListener;
45 import org.onosproject.net.device.DeviceService; 46 import org.onosproject.net.device.DeviceService;
...@@ -105,6 +106,7 @@ public class SingleSwitchFibInstallerTest { ...@@ -105,6 +106,7 @@ public class SingleSwitchFibInstallerTest {
105 private final Set<Interface> interfaces = Sets.newHashSet(); 106 private final Set<Interface> interfaces = Sets.newHashSet();
106 private InterfaceService interfaceService; 107 private InterfaceService interfaceService;
107 private NetworkConfigService networkConfigService; 108 private NetworkConfigService networkConfigService;
109 + private NetworkConfigRegistry networkConfigRegistry;
108 private FlowObjectiveService flowObjectiveService; 110 private FlowObjectiveService flowObjectiveService;
109 private DeviceService deviceService; 111 private DeviceService deviceService;
110 private static final ApplicationId APPID = TestApplicationId.create("foo"); 112 private static final ApplicationId APPID = TestApplicationId.create("foo");
...@@ -128,13 +130,15 @@ public class SingleSwitchFibInstallerTest { ...@@ -128,13 +130,15 @@ public class SingleSwitchFibInstallerTest {
128 interfaceService = createMock(InterfaceService.class); 130 interfaceService = createMock(InterfaceService.class);
129 131
130 networkConfigService = createMock(NetworkConfigService.class); 132 networkConfigService = createMock(NetworkConfigService.class);
133 + networkConfigRegistry = createMock(NetworkConfigRegistry.class);
131 flowObjectiveService = createMock(FlowObjectiveService.class); 134 flowObjectiveService = createMock(FlowObjectiveService.class);
132 deviceService = new TestDeviceService(); 135 deviceService = new TestDeviceService();
133 CoreService coreService = createNiceMock(CoreService.class); 136 CoreService coreService = createNiceMock(CoreService.class);
134 - expect(coreService.registerApplication(anyString())).andReturn(APPID); 137 + expect(coreService.registerApplication(anyString())).andReturn(APPID).anyTimes();
135 replay(coreService); 138 replay(coreService);
136 139
137 sSfibInstaller.networkConfigService = networkConfigService; 140 sSfibInstaller.networkConfigService = networkConfigService;
141 + sSfibInstaller.networkConfigRegistry = networkConfigRegistry;
138 sSfibInstaller.interfaceService = interfaceService; 142 sSfibInstaller.interfaceService = interfaceService;
139 sSfibInstaller.flowObjectiveService = flowObjectiveService; 143 sSfibInstaller.flowObjectiveService = flowObjectiveService;
140 sSfibInstaller.coreService = coreService; 144 sSfibInstaller.coreService = coreService;
......
...@@ -3,9 +3,9 @@ COMPILE_DEPS = [ ...@@ -3,9 +3,9 @@ COMPILE_DEPS = [
3 '//lib:org.apache.karaf.shell.console', 3 '//lib:org.apache.karaf.shell.console',
4 '//lib:javax.ws.rs-api', 4 '//lib:javax.ws.rs-api',
5 '//cli:onos-cli', 5 '//cli:onos-cli',
6 + '//core/store/serializers:onos-core-serializers',
6 '//incubator/api:onos-incubator-api', 7 '//incubator/api:onos-incubator-api',
7 '//utils/rest:onlab-rest', 8 '//utils/rest:onlab-rest',
8 - '//core/store/serializers:onos-core-serializers',
9 ] 9 ]
10 10
11 TEST_DEPS = [ 11 TEST_DEPS = [
......
...@@ -51,7 +51,11 @@ ...@@ -51,7 +51,11 @@
51 <artifactId>onos-cli</artifactId> 51 <artifactId>onos-cli</artifactId>
52 <version>${project.version}</version> 52 <version>${project.version}</version>
53 </dependency> 53 </dependency>
54 - 54 + <dependency>
55 + <groupId>org.onosproject</groupId>
56 + <artifactId>onos-core-serializers</artifactId>
57 + <version>${project.version}</version>
58 + </dependency>
55 <dependency> 59 <dependency>
56 <groupId>org.apache.karaf.shell</groupId> 60 <groupId>org.apache.karaf.shell</groupId>
57 <artifactId>org.apache.karaf.shell.console</artifactId> 61 <artifactId>org.apache.karaf.shell.console</artifactId>
...@@ -84,12 +88,10 @@ ...@@ -84,12 +88,10 @@
84 <groupId>com.fasterxml.jackson.core</groupId> 88 <groupId>com.fasterxml.jackson.core</groupId>
85 <artifactId>jackson-databind</artifactId> 89 <artifactId>jackson-databind</artifactId>
86 </dependency> 90 </dependency>
87 -
88 <dependency> 91 <dependency>
89 <groupId>com.fasterxml.jackson.core</groupId> 92 <groupId>com.fasterxml.jackson.core</groupId>
90 <artifactId>jackson-annotations</artifactId> 93 <artifactId>jackson-annotations</artifactId>
91 </dependency> 94 </dependency>
92 -
93 <dependency> 95 <dependency>
94 <groupId>org.osgi</groupId> 96 <groupId>org.osgi</groupId>
95 <artifactId>org.osgi.compendium</artifactId> 97 <artifactId>org.osgi.compendium</artifactId>
...@@ -103,7 +105,6 @@ ...@@ -103,7 +105,6 @@
103 <artifactId>onlab-junit</artifactId> 105 <artifactId>onlab-junit</artifactId>
104 <scope>test</scope> 106 <scope>test</scope>
105 </dependency> 107 </dependency>
106 -
107 <dependency> 108 <dependency>
108 <groupId>org.onosproject</groupId> 109 <groupId>org.onosproject</groupId>
109 <artifactId>onos-api</artifactId> 110 <artifactId>onos-api</artifactId>
......
1 +/*
2 + * Copyright 2016-present Open Networking Laboratory
3 + *
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 + */
16 +
17 +package org.onosproject.segmentrouting;
18 +
19 +import com.google.common.collect.ImmutableSet;
20 +import com.google.common.collect.Lists;
21 +import com.google.common.collect.Sets;
22 +import org.onlab.packet.Ethernet;
23 +import org.onlab.packet.IpAddress;
24 +import org.onlab.packet.IpPrefix;
25 +import org.onlab.packet.MacAddress;
26 +import org.onlab.packet.VlanId;
27 +import org.onlab.util.KryoNamespace;
28 +import org.onosproject.core.ApplicationId;
29 +import org.onosproject.core.CoreService;
30 +import org.onosproject.incubator.net.config.basics.McastConfig;
31 +import org.onosproject.net.ConnectPoint;
32 +import org.onosproject.net.DeviceId;
33 +import org.onosproject.net.Link;
34 +import org.onosproject.net.Path;
35 +import org.onosproject.net.PortNumber;
36 +import org.onosproject.net.flow.DefaultTrafficSelector;
37 +import org.onosproject.net.flow.DefaultTrafficTreatment;
38 +import org.onosproject.net.flow.TrafficSelector;
39 +import org.onosproject.net.flow.TrafficTreatment;
40 +import org.onosproject.net.flow.criteria.Criteria;
41 +import org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
42 +import org.onosproject.net.flowobjective.DefaultFilteringObjective;
43 +import org.onosproject.net.flowobjective.DefaultForwardingObjective;
44 +import org.onosproject.net.flowobjective.DefaultNextObjective;
45 +import org.onosproject.net.flowobjective.FilteringObjective;
46 +import org.onosproject.net.flowobjective.ForwardingObjective;
47 +import org.onosproject.net.flowobjective.NextObjective;
48 +import org.onosproject.net.mcast.McastEvent;
49 +import org.onosproject.net.mcast.McastRouteInfo;
50 +import org.onosproject.net.topology.TopologyService;
51 +import org.onosproject.segmentrouting.grouphandler.McastNextObjectiveStoreKey;
52 +import org.onosproject.store.serializers.KryoNamespaces;
53 +import org.onosproject.store.service.ConsistentMap;
54 +import org.onosproject.store.service.Serializer;
55 +import org.onosproject.store.service.StorageService;
56 +import org.slf4j.Logger;
57 +import org.slf4j.LoggerFactory;
58 +
59 +import java.util.Collection;
60 +import java.util.Collections;
61 +import java.util.List;
62 +import java.util.Optional;
63 +import java.util.Set;
64 +
65 +/**
66 + * Multicast event handler.
67 + */
68 +public class McastEventHandler {
69 + private static final Logger log = LoggerFactory.getLogger(McastEventHandler.class);
70 + private final SegmentRoutingManager srManager;
71 + private final ApplicationId coreAppId;
72 + private StorageService storageService;
73 + private TopologyService topologyService;
74 + private final KryoNamespace.Builder kryoBuilder;
75 + private final ConsistentMap<McastNextObjectiveStoreKey, NextObjective> mcastNextObjStore;
76 +
77 + /**
78 + * Constructs the McastEventHandler.
79 + *
80 + * @param srManager Segment Routing manager
81 + */
82 + public McastEventHandler(SegmentRoutingManager srManager) {
83 + coreAppId = srManager.coreService.getAppId(CoreService.CORE_APP_NAME);
84 +
85 + this.srManager = srManager;
86 + this.storageService = srManager.storageService;
87 + this.topologyService = srManager.topologyService;
88 +
89 + kryoBuilder = new KryoNamespace.Builder()
90 + .register(KryoNamespaces.API)
91 + .register(McastNextObjectiveStoreKey.class);
92 + mcastNextObjStore = storageService
93 + .<McastNextObjectiveStoreKey, NextObjective>consistentMapBuilder()
94 + .withName("onos-mcast-nextobj-store")
95 + .withSerializer(Serializer.using(kryoBuilder.build()))
96 + .build();
97 + }
98 +
99 + /**
100 + * Processes the SOURCE_ADDED event.
101 + *
102 + * @param event McastEvent with SOURCE_ADDED type
103 + */
104 + protected void processSourceAdded(McastEvent event) {
105 + log.info("processSourceAdded {}", event);
106 + McastRouteInfo mcastRouteInfo = event.subject();
107 + if (!mcastRouteInfo.isComplete()) {
108 + log.info("Incompleted McastRouteInfo. Abort.");
109 + return;
110 + }
111 + ConnectPoint source = mcastRouteInfo.source().orElse(null);
112 + Set<ConnectPoint> sinks = mcastRouteInfo.sinks();
113 + IpAddress mcastIp = mcastRouteInfo.route().group();
114 +
115 + sinks.forEach(sink -> {
116 + processSinkAddedInternal(source, sink, mcastIp);
117 + });
118 + }
119 +
120 + /**
121 + * Processes the SINK_ADDED event.
122 + *
123 + * @param event McastEvent with SINK_ADDED type
124 + */
125 + protected void processSinkAdded(McastEvent event) {
126 + log.info("processSinkAdded {}", event);
127 + McastRouteInfo mcastRouteInfo = event.subject();
128 + if (!mcastRouteInfo.isComplete()) {
129 + log.info("Incompleted McastRouteInfo. Abort.");
130 + return;
131 + }
132 + ConnectPoint source = mcastRouteInfo.source().orElse(null);
133 + ConnectPoint sink = mcastRouteInfo.sink().orElse(null);
134 + IpAddress mcastIp = mcastRouteInfo.route().group();
135 +
136 + processSinkAddedInternal(source, sink, mcastIp);
137 + }
138 +
139 + /**
140 + * Processes the SINK_REMOVED event.
141 + *
142 + * @param event McastEvent with SINK_REMOVED type
143 + */
144 + protected void processSinkRemoved(McastEvent event) {
145 + log.info("processSinkRemoved {}", event);
146 + McastRouteInfo mcastRouteInfo = event.subject();
147 + if (!mcastRouteInfo.isComplete()) {
148 + log.info("Incompleted McastRouteInfo. Abort.");
149 + return;
150 + }
151 + ConnectPoint source = mcastRouteInfo.source().orElse(null);
152 + ConnectPoint sink = mcastRouteInfo.sink().orElse(null);
153 + IpAddress mcastIp = mcastRouteInfo.route().group();
154 + VlanId assignedVlan = assignedVlan();
155 +
156 + // When source and sink are on the same device
157 + if (source.deviceId().equals(sink.deviceId())) {
158 + // Source and sink are on even the same port. There must be something wrong.
159 + if (source.port().equals(sink.port())) {
160 + log.warn("Sink is on the same port of source. Abort");
161 + return;
162 + }
163 + removePortFromDevice(sink.deviceId(), sink.port(), mcastIp, assignedVlan);
164 + return;
165 + }
166 +
167 + // Process the egress device
168 + boolean isLast = removePortFromDevice(sink.deviceId(), sink.port(), mcastIp, assignedVlan);
169 +
170 + // If this is the last sink on the device, also update upstream
171 + Optional<Path> mcastPath = getPath(source.deviceId(), sink.deviceId(), mcastIp);
172 + if (mcastPath.isPresent()) {
173 + List<Link> links = Lists.newArrayList(mcastPath.get().links());
174 + Collections.reverse(links);
175 + for (Link link : links) {
176 + if (isLast) {
177 + isLast = removePortFromDevice(link.src().deviceId(), link.src().port(),
178 + mcastIp, assignedVlan);
179 + }
180 + }
181 + }
182 + }
183 +
184 + /**
185 + * Establishes a path from source to sink for given multicast group.
186 + *
187 + * @param source connect point of the multicast source
188 + * @param sink connection point of the multicast sink
189 + * @param mcastIp multicast group IP address
190 + */
191 + private void processSinkAddedInternal(ConnectPoint source, ConnectPoint sink,
192 + IpAddress mcastIp) {
193 + VlanId assignedVlan = assignedVlan();
194 +
195 + // When source and sink are on the same device
196 + if (source.deviceId().equals(sink.deviceId())) {
197 + // Source and sink are on even the same port. There must be something wrong.
198 + if (source.port().equals(sink.port())) {
199 + log.warn("Sink is on the same port of source. Abort");
200 + return;
201 + }
202 + addPortToDevice(sink.deviceId(), sink.port(), mcastIp, assignedVlan);
203 + return;
204 + }
205 +
206 + // Process the ingress device
207 + addFilterToDevice(source.deviceId(), source.port(), assignedVlan);
208 +
209 + // Find a path. If present, create/update groups and flows for each hop
210 + Optional<Path> mcastPath = getPath(source.deviceId(), sink.deviceId(), mcastIp);
211 + if (mcastPath.isPresent()) {
212 + mcastPath.get().links().forEach(link -> {
213 + addFilterToDevice(link.dst().deviceId(), link.dst().port(), assignedVlan);
214 + addPortToDevice(link.src().deviceId(), link.src().port(), mcastIp, assignedVlan);
215 + });
216 + // Process the egress device
217 + addPortToDevice(sink.deviceId(), sink.port(), mcastIp, assignedVlan);
218 + }
219 + }
220 +
221 + /**
222 + * Adds filtering objective for given device and port.
223 + *
224 + * @param deviceId device ID
225 + * @param port ingress port number
226 + * @param assignedVlan assigned VLAN ID
227 + */
228 + private void addFilterToDevice(DeviceId deviceId, PortNumber port, VlanId assignedVlan) {
229 + // Do nothing if the port is configured as suppressed
230 + ConnectPoint connectPt = new ConnectPoint(deviceId, port);
231 + if (srManager.deviceConfiguration.suppressSubnet().contains(connectPt) ||
232 + srManager.deviceConfiguration.suppressHost().contains(connectPt)) {
233 + log.info("Ignore suppressed port {}", connectPt);
234 + return;
235 + }
236 +
237 + FilteringObjective.Builder filtObjBuilder =
238 + filterObjBuilder(deviceId, port, assignedVlan);
239 + srManager.flowObjectiveService.filter(deviceId, filtObjBuilder.add());
240 + // TODO add objective context
241 + }
242 +
243 + /**
244 + * Adds a port to given multicast group on given device. This involves the
245 + * update of L3 multicast group and multicast routing table entry.
246 + *
247 + * @param deviceId device ID
248 + * @param port port to be added
249 + * @param mcastIp multicast group
250 + * @param assignedVlan assigned VLAN ID
251 + */
252 + private void addPortToDevice(DeviceId deviceId, PortNumber port,
253 + IpAddress mcastIp, VlanId assignedVlan) {
254 + log.info("Add port {} to {}. mcastIp={}, assignedVlan={}",
255 + port, deviceId, mcastIp, assignedVlan);
256 + McastNextObjectiveStoreKey mcastNextObjectiveStoreKey =
257 + new McastNextObjectiveStoreKey(mcastIp, deviceId);
258 + ImmutableSet.Builder<PortNumber> portBuilder = ImmutableSet.builder();
259 + if (!mcastNextObjStore.containsKey(mcastNextObjectiveStoreKey)) {
260 + // First time someone request this mcast group via this device
261 + portBuilder.add(port);
262 + } else {
263 + // This device already serves some subscribers of this mcast group
264 + NextObjective nextObj = mcastNextObjStore.get(mcastNextObjectiveStoreKey).value();
265 + // Stop if the port is already in the nextobj
266 + Set<PortNumber> existingPorts = getPorts(nextObj.next());
267 + if (existingPorts.contains(port)) {
268 + log.info("NextObj for {}/{} already exists. Abort", deviceId, port);
269 + return;
270 + }
271 + portBuilder.addAll(existingPorts).add(port).build();
272 + }
273 + // Create, store and apply the new nextObj and fwdObj
274 + NextObjective newNextObj =
275 + nextObjBuilder(mcastIp, assignedVlan, portBuilder.build()).add();
276 + ForwardingObjective fwdObj =
277 + fwdObjBuilder(mcastIp, assignedVlan, newNextObj.id()).add();
278 + mcastNextObjStore.put(mcastNextObjectiveStoreKey, newNextObj);
279 + srManager.flowObjectiveService.next(deviceId, newNextObj);
280 + srManager.flowObjectiveService.forward(deviceId, fwdObj);
281 + // TODO add objective callback
282 + }
283 +
284 + /**
285 + * Removes a port from given multicast group on given device.
286 + * This involves the update of L3 multicast group and multicast routing
287 + * table entry.
288 + *
289 + * @param deviceId device ID
290 + * @param port port to be added
291 + * @param mcastIp multicast group
292 + * @param assignedVlan assigned VLAN ID
293 + * @return true if this is the last sink on this device
294 + */
295 + private boolean removePortFromDevice(DeviceId deviceId, PortNumber port,
296 + IpAddress mcastIp, VlanId assignedVlan) {
297 + log.info("Remove port {} from {}. mcastIp={}, assignedVlan={}",
298 + port, deviceId, mcastIp, assignedVlan);
299 + McastNextObjectiveStoreKey mcastNextObjectiveStoreKey =
300 + new McastNextObjectiveStoreKey(mcastIp, deviceId);
301 + // This device is not serving this multicast group
302 + if (!mcastNextObjStore.containsKey(mcastNextObjectiveStoreKey)) {
303 + log.warn("{} is not serving {} on port {}. Abort.", deviceId, mcastIp, port);
304 + return false;
305 + }
306 + NextObjective nextObj = mcastNextObjStore.get(mcastNextObjectiveStoreKey).value();
307 +
308 + Set<PortNumber> existingPorts = getPorts(nextObj.next());
309 + // This device does not serve this multicast group
310 + if (!existingPorts.contains(port)) {
311 + log.warn("{} is not serving {} on port {}. Abort.", deviceId, mcastIp, port);
312 + return false;
313 + }
314 + // Copy and modify the ImmutableSet
315 + existingPorts = Sets.newHashSet(existingPorts);
316 + existingPorts.remove(port);
317 +
318 + NextObjective newNextObj;
319 + ForwardingObjective fwdObj;
320 + if (existingPorts.isEmpty()) {
321 + // If this is the last sink, remove flows and groups
322 + // NOTE: Rely on GroupStore garbage collection rather than explicitly
323 + // remove L3MG since there might be other flows/groups refer to
324 + // the same L2IG
325 + fwdObj = fwdObjBuilder(mcastIp, assignedVlan, nextObj.id()).remove();
326 + mcastNextObjStore.remove(mcastNextObjectiveStoreKey);
327 + srManager.flowObjectiveService.forward(deviceId, fwdObj);
328 + } else {
329 + // If this is not the last sink, update flows and groups
330 + newNextObj = nextObjBuilder(mcastIp, assignedVlan, existingPorts).add();
331 + fwdObj = fwdObjBuilder(mcastIp, assignedVlan, newNextObj.id()).add();
332 + mcastNextObjStore.put(mcastNextObjectiveStoreKey, newNextObj);
333 + srManager.flowObjectiveService.next(deviceId, newNextObj);
334 + srManager.flowObjectiveService.forward(deviceId, fwdObj);
335 + }
336 + // TODO add objective callback
337 +
338 + return existingPorts.isEmpty();
339 + }
340 +
341 + /**
342 + * Creates a next objective builder for multicast.
343 + *
344 + * @param mcastIp multicast group
345 + * @param assignedVlan assigned VLAN ID
346 + * @param outPorts set of output port numbers
347 + * @return next objective builder
348 + */
349 + private NextObjective.Builder nextObjBuilder(IpAddress mcastIp,
350 + VlanId assignedVlan, Set<PortNumber> outPorts) {
351 + int nextId = srManager.flowObjectiveService.allocateNextId();
352 +
353 + TrafficSelector metadata =
354 + DefaultTrafficSelector.builder()
355 + .matchVlanId(assignedVlan)
356 + .matchIPDst(mcastIp.toIpPrefix())
357 + .build();
358 +
359 + NextObjective.Builder nextObjBuilder = DefaultNextObjective
360 + .builder().withId(nextId)
361 + .withType(NextObjective.Type.BROADCAST).fromApp(srManager.appId)
362 + .withMeta(metadata);
363 +
364 + outPorts.forEach(port -> {
365 + TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
366 + if (egressVlan().equals(VlanId.NONE)) {
367 + tBuilder.popVlan();
368 + }
369 + tBuilder.setOutput(port);
370 + nextObjBuilder.addTreatment(tBuilder.build());
371 + });
372 +
373 + return nextObjBuilder;
374 + }
375 +
376 + /**
377 + * Creates a forwarding objective builder for multicast.
378 + *
379 + * @param mcastIp multicast group
380 + * @param assignedVlan assigned VLAN ID
381 + * @param nextId next ID of the L3 multicast group
382 + * @return forwarding objective builder
383 + */
384 + private ForwardingObjective.Builder fwdObjBuilder(IpAddress mcastIp,
385 + VlanId assignedVlan, int nextId) {
386 + TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
387 + IpPrefix mcastPrefix = IpPrefix.valueOf(mcastIp, IpPrefix.MAX_INET_MASK_LENGTH);
388 + sbuilder.matchEthType(Ethernet.TYPE_IPV4);
389 + sbuilder.matchIPDst(mcastPrefix);
390 + TrafficSelector.Builder metabuilder = DefaultTrafficSelector.builder();
391 + metabuilder.matchVlanId(assignedVlan);
392 +
393 + ForwardingObjective.Builder fwdBuilder = DefaultForwardingObjective.builder();
394 + fwdBuilder.withSelector(sbuilder.build())
395 + .withMeta(metabuilder.build())
396 + .nextStep(nextId)
397 + .withFlag(ForwardingObjective.Flag.SPECIFIC)
398 + .fromApp(srManager.appId)
399 + .withPriority(SegmentRoutingService.DEFAULT_PRIORITY);
400 + return fwdBuilder;
401 + }
402 +
403 + /**
404 + * Creates a filtering objective builder for multicast.
405 + *
406 + * @param deviceId Device ID
407 + * @param ingressPort ingress port of the multicast stream
408 + * @param assignedVlan assigned VLAN ID
409 + * @return filtering objective builder
410 + */
411 + private FilteringObjective.Builder filterObjBuilder(DeviceId deviceId, PortNumber ingressPort,
412 + VlanId assignedVlan) {
413 + FilteringObjective.Builder filtBuilder = DefaultFilteringObjective.builder();
414 + filtBuilder.withKey(Criteria.matchInPort(ingressPort))
415 + .addCondition(Criteria.matchEthDstMasked(MacAddress.IPV4_MULTICAST,
416 + MacAddress.IPV4_MULTICAST_MASK))
417 + .addCondition(Criteria.matchVlanId(egressVlan()))
418 + .withPriority(SegmentRoutingService.DEFAULT_PRIORITY);
419 + // vlan assignment is valid only if this instance is master
420 + if (srManager.mastershipService.isLocalMaster(deviceId)) {
421 + TrafficTreatment tt = DefaultTrafficTreatment.builder()
422 + .pushVlan().setVlanId(assignedVlan).build();
423 + filtBuilder.withMeta(tt);
424 + }
425 + return filtBuilder.permit().fromApp(srManager.appId);
426 + }
427 +
428 + /**
429 + * Gets output ports information from treatments.
430 + *
431 + * @param treatments collection of traffic treatments
432 + * @return set of output port numbers
433 + */
434 + private Set<PortNumber> getPorts(Collection<TrafficTreatment> treatments) {
435 + ImmutableSet.Builder<PortNumber> builder = ImmutableSet.builder();
436 + treatments.forEach(treatment -> {
437 + treatment.allInstructions().stream()
438 + .filter(instr -> instr instanceof OutputInstruction)
439 + .forEach(instr -> {
440 + builder.add(((OutputInstruction) instr).port());
441 + });
442 + });
443 + return builder.build();
444 + }
445 +
446 + /**
447 + * Gets a path from src to dst.
448 + * If a path was allocated before, returns the allocated path.
449 + * Otherwise, randomly pick one from available paths.
450 + *
451 + * @param src source device ID
452 + * @param dst destination device ID
453 + * @param mcastIp multicast group
454 + * @return an optional path from src to dst
455 + */
456 + private Optional<Path> getPath(DeviceId src, DeviceId dst, IpAddress mcastIp) {
457 + List<Path> allPaths = Lists.newArrayList(
458 + topologyService.getPaths(topologyService.currentTopology(), src, dst));
459 + if (allPaths.isEmpty()) {
460 + log.warn("Fail to find a path from {} to {}. Abort.", src, dst);
461 + return Optional.empty();
462 + }
463 +
464 + // If one of the available path is used before, use the same path
465 + McastNextObjectiveStoreKey mcastNextObjectiveStoreKey =
466 + new McastNextObjectiveStoreKey(mcastIp, src);
467 + if (mcastNextObjStore.containsKey(mcastNextObjectiveStoreKey)) {
468 + NextObjective nextObj = mcastNextObjStore.get(mcastNextObjectiveStoreKey).value();
469 + Set<PortNumber> existingPorts = getPorts(nextObj.next());
470 + for (Path path : allPaths) {
471 + PortNumber srcPort = path.links().get(0).src().port();
472 + if (existingPorts.contains(srcPort)) {
473 + return Optional.of(path);
474 + }
475 + }
476 + }
477 + // Otherwise, randomly pick a path
478 + Collections.shuffle(allPaths);
479 + return allPaths.stream().findFirst();
480 + }
481 +
482 + /**
483 + * Gets egress VLAN from McastConfig.
484 + *
485 + * @return egress VLAN or VlanId.NONE if not configured
486 + */
487 + private VlanId egressVlan() {
488 + McastConfig mcastConfig =
489 + srManager.cfgService.getConfig(coreAppId, McastConfig.class);
490 + return (mcastConfig != null) ? mcastConfig.egressVlan() : VlanId.NONE;
491 + }
492 +
493 + /**
494 + * Gets assigned VLAN according to the value of egress VLAN.
495 + *
496 + * @return assigned VLAN
497 + */
498 + private VlanId assignedVlan() {
499 + return (egressVlan().equals(VlanId.NONE)) ?
500 + VlanId.vlanId(SegmentRoutingManager.ASSIGNED_VLAN_NO_SUBNET) :
501 + egressVlan();
502 + }
503 +}
...@@ -73,8 +73,8 @@ public class NetworkConfigEventHandler { ...@@ -73,8 +73,8 @@ public class NetworkConfigEventHandler {
73 SegmentRoutingAppConfig config = (SegmentRoutingAppConfig) event.config().get(); 73 SegmentRoutingAppConfig config = (SegmentRoutingAppConfig) event.config().get();
74 SegmentRoutingAppConfig prevConfig = (SegmentRoutingAppConfig) event.prevConfig().get(); 74 SegmentRoutingAppConfig prevConfig = (SegmentRoutingAppConfig) event.prevConfig().get();
75 deviceService.getAvailableDevices().forEach(device -> { 75 deviceService.getAvailableDevices().forEach(device -> {
76 - Set<MacAddress> macAddresses = getMacAddresses(config); 76 + Set<MacAddress> macAddresses = new HashSet<>(getMacAddresses(config));
77 - Set<MacAddress> prevMacAddresses = getMacAddresses(prevConfig); 77 + Set<MacAddress> prevMacAddresses = new HashSet<>(getMacAddresses(prevConfig));
78 // Avoid removing and re-adding unchanged MAC addresses since 78 // Avoid removing and re-adding unchanged MAC addresses since
79 // FlowObjective does not guarantee the execution order. 79 // FlowObjective does not guarantee the execution order.
80 Set<MacAddress> sameMacAddresses = new HashSet<>(macAddresses); 80 Set<MacAddress> sameMacAddresses = new HashSet<>(macAddresses);
......
...@@ -33,6 +33,7 @@ import org.onosproject.cfg.ComponentConfigService; ...@@ -33,6 +33,7 @@ import org.onosproject.cfg.ComponentConfigService;
33 import org.onosproject.core.ApplicationId; 33 import org.onosproject.core.ApplicationId;
34 import org.onosproject.core.CoreService; 34 import org.onosproject.core.CoreService;
35 import org.onosproject.event.Event; 35 import org.onosproject.event.Event;
36 +import org.onosproject.incubator.net.config.basics.McastConfig;
36 import org.onosproject.mastership.MastershipService; 37 import org.onosproject.mastership.MastershipService;
37 import org.onosproject.net.ConnectPoint; 38 import org.onosproject.net.ConnectPoint;
38 import org.onosproject.net.Device; 39 import org.onosproject.net.Device;
...@@ -60,23 +61,27 @@ import org.onosproject.net.flowobjective.ObjectiveContext; ...@@ -60,23 +61,27 @@ import org.onosproject.net.flowobjective.ObjectiveContext;
60 import org.onosproject.net.flowobjective.ObjectiveError; 61 import org.onosproject.net.flowobjective.ObjectiveError;
61 import org.onosproject.net.host.HostEvent; 62 import org.onosproject.net.host.HostEvent;
62 import org.onosproject.net.host.HostListener; 63 import org.onosproject.net.host.HostListener;
63 -import org.onosproject.net.host.HostService; 64 +import org.onosproject.net.mcast.McastEvent;
64 -import org.onosproject.net.link.LinkEvent; 65 +import org.onosproject.net.mcast.McastListener;
65 -import org.onosproject.net.link.LinkListener; 66 +import org.onosproject.net.mcast.MulticastRouteService;
66 -import org.onosproject.net.link.LinkService;
67 -import org.onosproject.net.packet.InboundPacket;
68 -import org.onosproject.net.packet.PacketContext;
69 import org.onosproject.net.packet.PacketPriority; 67 import org.onosproject.net.packet.PacketPriority;
70 -import org.onosproject.net.packet.PacketProcessor; 68 +import org.onosproject.net.topology.TopologyService;
71 -import org.onosproject.net.packet.PacketService;
72 import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException; 69 import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
73 import org.onosproject.segmentrouting.config.DeviceConfiguration; 70 import org.onosproject.segmentrouting.config.DeviceConfiguration;
74 -import org.onosproject.segmentrouting.config.SegmentRoutingAppConfig;
75 import org.onosproject.segmentrouting.config.SegmentRoutingDeviceConfig; 71 import org.onosproject.segmentrouting.config.SegmentRoutingDeviceConfig;
72 +import org.onosproject.segmentrouting.config.SegmentRoutingAppConfig;
76 import org.onosproject.segmentrouting.grouphandler.DefaultGroupHandler; 73 import org.onosproject.segmentrouting.grouphandler.DefaultGroupHandler;
77 import org.onosproject.segmentrouting.grouphandler.NeighborSet; 74 import org.onosproject.segmentrouting.grouphandler.NeighborSet;
78 import org.onosproject.segmentrouting.grouphandler.NeighborSetNextObjectiveStoreKey; 75 import org.onosproject.segmentrouting.grouphandler.NeighborSetNextObjectiveStoreKey;
79 import org.onosproject.segmentrouting.grouphandler.PortNextObjectiveStoreKey; 76 import org.onosproject.segmentrouting.grouphandler.PortNextObjectiveStoreKey;
77 +import org.onosproject.net.host.HostService;
78 +import org.onosproject.net.link.LinkEvent;
79 +import org.onosproject.net.link.LinkListener;
80 +import org.onosproject.net.link.LinkService;
81 +import org.onosproject.net.packet.InboundPacket;
82 +import org.onosproject.net.packet.PacketContext;
83 +import org.onosproject.net.packet.PacketProcessor;
84 +import org.onosproject.net.packet.PacketService;
80 import org.onosproject.segmentrouting.grouphandler.SubnetNextObjectiveStoreKey; 85 import org.onosproject.segmentrouting.grouphandler.SubnetNextObjectiveStoreKey;
81 import org.onosproject.segmentrouting.grouphandler.XConnectNextObjectiveStoreKey; 86 import org.onosproject.segmentrouting.grouphandler.XConnectNextObjectiveStoreKey;
82 import org.onosproject.store.serializers.KryoNamespaces; 87 import org.onosproject.store.serializers.KryoNamespaces;
...@@ -143,6 +148,12 @@ public class SegmentRoutingManager implements SegmentRoutingService { ...@@ -143,6 +148,12 @@ public class SegmentRoutingManager implements SegmentRoutingService {
143 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) 148 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
144 protected ComponentConfigService compCfgService; 149 protected ComponentConfigService compCfgService;
145 150
151 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
152 + protected MulticastRouteService multicastRouteService;
153 +
154 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
155 + protected TopologyService topologyService;
156 +
146 protected ArpHandler arpHandler = null; 157 protected ArpHandler arpHandler = null;
147 protected IcmpHandler icmpHandler = null; 158 protected IcmpHandler icmpHandler = null;
148 protected IpHandler ipHandler = null; 159 protected IpHandler ipHandler = null;
...@@ -157,8 +168,11 @@ public class SegmentRoutingManager implements SegmentRoutingService { ...@@ -157,8 +168,11 @@ public class SegmentRoutingManager implements SegmentRoutingService {
157 private InternalLinkListener linkListener = null; 168 private InternalLinkListener linkListener = null;
158 private InternalDeviceListener deviceListener = null; 169 private InternalDeviceListener deviceListener = null;
159 private NetworkConfigEventHandler netcfgHandler = null; 170 private NetworkConfigEventHandler netcfgHandler = null;
171 + private McastEventHandler mcastEventHandler = null;
160 private InternalEventHandler eventHandler = new InternalEventHandler(); 172 private InternalEventHandler eventHandler = new InternalEventHandler();
161 private final InternalHostListener hostListener = new InternalHostListener(); 173 private final InternalHostListener hostListener = new InternalHostListener();
174 + private final InternalConfigListener cfgListener = new InternalConfigListener(this);
175 + private final InternalMcastListener mcastListener = new InternalMcastListener();
162 176
163 private ScheduledExecutorService executorService = Executors 177 private ScheduledExecutorService executorService = Executors
164 .newScheduledThreadPool(1); 178 .newScheduledThreadPool(1);
...@@ -196,29 +210,32 @@ public class SegmentRoutingManager implements SegmentRoutingService { ...@@ -196,29 +210,32 @@ public class SegmentRoutingManager implements SegmentRoutingService {
196 private EventuallyConsistentMap<String, Tunnel> tunnelStore = null; 210 private EventuallyConsistentMap<String, Tunnel> tunnelStore = null;
197 private EventuallyConsistentMap<String, Policy> policyStore = null; 211 private EventuallyConsistentMap<String, Policy> policyStore = null;
198 212
199 - private final InternalConfigListener cfgListener = 213 + private final ConfigFactory<DeviceId, SegmentRoutingDeviceConfig> deviceConfigFactory =
200 - new InternalConfigListener(this);
201 -
202 - private final ConfigFactory<DeviceId, SegmentRoutingDeviceConfig> cfgDeviceFactory =
203 new ConfigFactory<DeviceId, SegmentRoutingDeviceConfig>(SubjectFactories.DEVICE_SUBJECT_FACTORY, 214 new ConfigFactory<DeviceId, SegmentRoutingDeviceConfig>(SubjectFactories.DEVICE_SUBJECT_FACTORY,
204 - SegmentRoutingDeviceConfig.class, 215 + SegmentRoutingDeviceConfig.class, "segmentrouting") {
205 - "segmentrouting") {
206 @Override 216 @Override
207 public SegmentRoutingDeviceConfig createConfig() { 217 public SegmentRoutingDeviceConfig createConfig() {
208 return new SegmentRoutingDeviceConfig(); 218 return new SegmentRoutingDeviceConfig();
209 } 219 }
210 }; 220 };
211 - 221 + private final ConfigFactory<ApplicationId, SegmentRoutingAppConfig> appConfigFactory =
212 - private final ConfigFactory<ApplicationId, SegmentRoutingAppConfig> cfgAppFactory =
213 new ConfigFactory<ApplicationId, SegmentRoutingAppConfig>(SubjectFactories.APP_SUBJECT_FACTORY, 222 new ConfigFactory<ApplicationId, SegmentRoutingAppConfig>(SubjectFactories.APP_SUBJECT_FACTORY,
214 - SegmentRoutingAppConfig.class, 223 + SegmentRoutingAppConfig.class, "segmentrouting") {
215 - "segmentrouting") {
216 @Override 224 @Override
217 public SegmentRoutingAppConfig createConfig() { 225 public SegmentRoutingAppConfig createConfig() {
218 return new SegmentRoutingAppConfig(); 226 return new SegmentRoutingAppConfig();
219 } 227 }
220 }; 228 };
221 229
230 + private ConfigFactory<ApplicationId, McastConfig> mcastConfigFactory =
231 + new ConfigFactory<ApplicationId, McastConfig>(SubjectFactories.APP_SUBJECT_FACTORY,
232 + McastConfig.class, "multicast") {
233 + @Override
234 + public McastConfig createConfig() {
235 + return new McastConfig();
236 + }
237 + };
238 +
222 private Object threadSchedulerLock = new Object(); 239 private Object threadSchedulerLock = new Object();
223 private static int numOfEventsQueued = 0; 240 private static int numOfEventsQueued = 0;
224 private static int numOfEventsExecuted = 0; 241 private static int numOfEventsExecuted = 0;
...@@ -312,14 +329,17 @@ public class SegmentRoutingManager implements SegmentRoutingService { ...@@ -312,14 +329,17 @@ public class SegmentRoutingManager implements SegmentRoutingService {
312 linkListener = new InternalLinkListener(); 329 linkListener = new InternalLinkListener();
313 deviceListener = new InternalDeviceListener(); 330 deviceListener = new InternalDeviceListener();
314 netcfgHandler = new NetworkConfigEventHandler(this); 331 netcfgHandler = new NetworkConfigEventHandler(this);
332 + mcastEventHandler = new McastEventHandler(this);
315 333
316 cfgService.addListener(cfgListener); 334 cfgService.addListener(cfgListener);
317 - cfgService.registerConfigFactory(cfgDeviceFactory); 335 + cfgService.registerConfigFactory(deviceConfigFactory);
318 - cfgService.registerConfigFactory(cfgAppFactory); 336 + cfgService.registerConfigFactory(appConfigFactory);
337 + cfgService.registerConfigFactory(mcastConfigFactory);
319 hostService.addListener(hostListener); 338 hostService.addListener(hostListener);
320 packetService.addProcessor(processor, PacketProcessor.director(2)); 339 packetService.addProcessor(processor, PacketProcessor.director(2));
321 linkService.addListener(linkListener); 340 linkService.addListener(linkListener);
322 deviceService.addListener(deviceListener); 341 deviceService.addListener(deviceListener);
342 + multicastRouteService.addListener(mcastListener);
323 343
324 // Request ARP packet-in 344 // Request ARP packet-in
325 TrafficSelector.Builder selector = DefaultTrafficSelector.builder(); 345 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
...@@ -351,8 +371,9 @@ public class SegmentRoutingManager implements SegmentRoutingService { ...@@ -351,8 +371,9 @@ public class SegmentRoutingManager implements SegmentRoutingService {
351 @Deactivate 371 @Deactivate
352 protected void deactivate() { 372 protected void deactivate() {
353 cfgService.removeListener(cfgListener); 373 cfgService.removeListener(cfgListener);
354 - cfgService.unregisterConfigFactory(cfgDeviceFactory); 374 + cfgService.unregisterConfigFactory(deviceConfigFactory);
355 - cfgService.unregisterConfigFactory(cfgAppFactory); 375 + cfgService.unregisterConfigFactory(appConfigFactory);
376 + cfgService.unregisterConfigFactory(mcastConfigFactory);
356 377
357 // Withdraw ARP packet-in 378 // Withdraw ARP packet-in
358 TrafficSelector.Builder selector = DefaultTrafficSelector.builder(); 379 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
...@@ -362,12 +383,20 @@ public class SegmentRoutingManager implements SegmentRoutingService { ...@@ -362,12 +383,20 @@ public class SegmentRoutingManager implements SegmentRoutingService {
362 packetService.removeProcessor(processor); 383 packetService.removeProcessor(processor);
363 linkService.removeListener(linkListener); 384 linkService.removeListener(linkListener);
364 deviceService.removeListener(deviceListener); 385 deviceService.removeListener(deviceListener);
386 + multicastRouteService.removeListener(mcastListener);
387 +
365 processor = null; 388 processor = null;
366 linkListener = null; 389 linkListener = null;
367 - deviceService = null; 390 + deviceListener = null;
368 -
369 groupHandlerMap.clear(); 391 groupHandlerMap.clear();
370 392
393 + nsNextObjStore.destroy();
394 + subnetNextObjStore.destroy();
395 + portNextObjStore.destroy();
396 + xConnectNextObjStore.destroy();
397 + tunnelStore.destroy();
398 + policyStore.destroy();
399 + subnetVidStore.destroy();
371 log.info("Stopped"); 400 log.info("Stopped");
372 } 401 }
373 402
...@@ -1186,6 +1215,27 @@ public class SegmentRoutingManager implements SegmentRoutingService { ...@@ -1186,6 +1215,27 @@ public class SegmentRoutingManager implements SegmentRoutingService {
1186 } 1215 }
1187 } 1216 }
1188 1217
1218 + private class InternalMcastListener implements McastListener {
1219 + @Override
1220 + public void event(McastEvent event) {
1221 + switch (event.type()) {
1222 + case SOURCE_ADDED:
1223 + mcastEventHandler.processSourceAdded(event);
1224 + break;
1225 + case SINK_ADDED:
1226 + mcastEventHandler.processSinkAdded(event);
1227 + break;
1228 + case SINK_REMOVED:
1229 + mcastEventHandler.processSinkRemoved(event);
1230 + break;
1231 + case ROUTE_ADDED:
1232 + case ROUTE_REMOVED:
1233 + default:
1234 + break;
1235 + }
1236 + }
1237 + }
1238 +
1189 private static class BridgingTableObjectiveContext implements ObjectiveContext { 1239 private static class BridgingTableObjectiveContext implements ObjectiveContext {
1190 final MacAddress mac; 1240 final MacAddress mac;
1191 final VlanId vlanId; 1241 final VlanId vlanId;
......
...@@ -496,12 +496,22 @@ public class DeviceConfiguration implements DeviceProperties { ...@@ -496,12 +496,22 @@ public class DeviceConfiguration implements DeviceProperties {
496 return srinfo != null && srinfo.adjacencySids.containsKey(sid); 496 return srinfo != null && srinfo.adjacencySids.containsKey(sid);
497 } 497 }
498 498
499 + /**
500 + * Gets connect points for which segment routing does not install subnet rules.
501 + *
502 + * @return set of connect points
503 + */
499 public Set<ConnectPoint> suppressSubnet() { 504 public Set<ConnectPoint> suppressSubnet() {
500 SegmentRoutingAppConfig appConfig = 505 SegmentRoutingAppConfig appConfig =
501 cfgService.getConfig(appId, SegmentRoutingAppConfig.class); 506 cfgService.getConfig(appId, SegmentRoutingAppConfig.class);
502 return (appConfig != null) ? appConfig.suppressSubnet() : ImmutableSet.of(); 507 return (appConfig != null) ? appConfig.suppressSubnet() : ImmutableSet.of();
503 } 508 }
504 509
510 + /**
511 + * Gets connect points for which segment routing does not install host rules.
512 + *
513 + * @return set of connect points
514 + */
505 public Set<ConnectPoint> suppressHost() { 515 public Set<ConnectPoint> suppressHost() {
506 SegmentRoutingAppConfig appConfig = 516 SegmentRoutingAppConfig appConfig =
507 cfgService.getConfig(appId, SegmentRoutingAppConfig.class); 517 cfgService.getConfig(appId, SegmentRoutingAppConfig.class);
......
1 +/*
2 + * Copyright 2015-present Open Networking Laboratory
3 + *
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 + */
16 +
17 +package org.onosproject.segmentrouting.grouphandler;
18 +
19 +import org.onlab.packet.IpAddress;
20 +import org.onosproject.net.DeviceId;
21 +import static com.google.common.base.MoreObjects.toStringHelper;
22 +import static com.google.common.base.Preconditions.checkArgument;
23 +import static com.google.common.base.Preconditions.checkNotNull;
24 +import java.util.Objects;
25 +
26 +/**
27 + * Key of multicast next objective store.
28 + */
29 +public class McastNextObjectiveStoreKey {
30 + private final IpAddress mcastIp;
31 + private final DeviceId deviceId;
32 +
33 + /**
34 + * Constructs the key of multicast next objective store.
35 + *
36 + * @param mcastIp multicast group IP address
37 + * @param deviceId device ID
38 + */
39 + public McastNextObjectiveStoreKey(IpAddress mcastIp, DeviceId deviceId) {
40 + checkNotNull(mcastIp, "mcastIp cannot be null");
41 + checkNotNull(deviceId, "deviceId cannot be null");
42 + checkArgument(mcastIp.isMulticast(), "mcastIp must be a multicast address");
43 + this.mcastIp = mcastIp;
44 + this.deviceId = deviceId;
45 + }
46 +
47 + /**
48 + * Returns the multicast IP address of this key.
49 + *
50 + * @return multicast IP
51 + */
52 + public IpAddress mcastIp() {
53 + return this.mcastIp;
54 + }
55 +
56 + /**
57 + * Returns the device ID of this key.
58 + *
59 + * @return device ID
60 + */
61 + public DeviceId deviceId() {
62 + return this.deviceId;
63 + }
64 +
65 + @Override
66 + public boolean equals(Object o) {
67 + if (this == o) {
68 + return true;
69 + }
70 + if (!(o instanceof McastNextObjectiveStoreKey)) {
71 + return false;
72 + }
73 + McastNextObjectiveStoreKey that =
74 + (McastNextObjectiveStoreKey) o;
75 + return (Objects.equals(this.mcastIp, that.mcastIp) &&
76 + Objects.equals(this.deviceId, that.deviceId));
77 + }
78 +
79 + @Override
80 + public int hashCode() {
81 + return Objects.hash(mcastIp, deviceId);
82 + }
83 +
84 + @Override
85 + public String toString() {
86 + return toStringHelper(getClass())
87 + .add("mcastIp", mcastIp)
88 + .add("deviceId", deviceId)
89 + .toString();
90 + }
91 +}
...@@ -22,6 +22,7 @@ import java.util.Collections; ...@@ -22,6 +22,7 @@ import java.util.Collections;
22 import java.util.Optional; 22 import java.util.Optional;
23 import java.util.Set; 23 import java.util.Set;
24 24
25 +import static com.google.common.base.MoreObjects.toStringHelper;
25 import static com.google.common.base.Preconditions.checkNotNull; 26 import static com.google.common.base.Preconditions.checkNotNull;
26 27
27 /** 28 /**
...@@ -102,4 +103,13 @@ public final class McastRouteInfo { ...@@ -102,4 +103,13 @@ public final class McastRouteInfo {
102 return sinks; 103 return sinks;
103 } 104 }
104 105
106 + @Override
107 + public String toString() {
108 + return toStringHelper(this)
109 + .add("route", route())
110 + .add("sink", sink())
111 + .add("source", source())
112 + .add("sinks", sinks())
113 + .toString();
114 + }
105 } 115 }
......
...@@ -22,8 +22,6 @@ import java.util.Collection; ...@@ -22,8 +22,6 @@ import java.util.Collection;
22 import java.util.Collections; 22 import java.util.Collections;
23 import java.util.Deque; 23 import java.util.Deque;
24 import java.util.List; 24 import java.util.List;
25 -import java.util.Set;
26 -import java.util.concurrent.ConcurrentHashMap;
27 25
28 import com.google.common.collect.ImmutableList; 26 import com.google.common.collect.ImmutableList;
29 import com.google.common.collect.ImmutableSet; 27 import com.google.common.collect.ImmutableSet;
...@@ -279,17 +277,6 @@ public class CpqdOfdpa2Pipeline extends Ofdpa2Pipeline { ...@@ -279,17 +277,6 @@ public class CpqdOfdpa2Pipeline extends Ofdpa2Pipeline {
279 } 277 }
280 278
281 for (PortNumber pnum : portnums) { 279 for (PortNumber pnum : portnums) {
282 - // update storage
283 - groupHandler.port2Vlan.put(pnum, storeVlan);
284 - Set<PortNumber> vlanPorts = groupHandler.vlan2Port.get(storeVlan);
285 - if (vlanPorts == null) {
286 - vlanPorts = Collections.newSetFromMap(
287 - new ConcurrentHashMap<PortNumber, Boolean>());
288 - vlanPorts.add(pnum);
289 - groupHandler.vlan2Port.put(storeVlan, vlanPorts);
290 - } else {
291 - vlanPorts.add(pnum);
292 - }
293 // create rest of flowrule 280 // create rest of flowrule
294 selector.matchInPort(pnum); 281 selector.matchInPort(pnum);
295 FlowRule rule = DefaultFlowRule.builder() 282 FlowRule rule = DefaultFlowRule.builder()
......
...@@ -65,7 +65,6 @@ import java.util.Collection; ...@@ -65,7 +65,6 @@ import java.util.Collection;
65 import java.util.Collections; 65 import java.util.Collections;
66 import java.util.Deque; 66 import java.util.Deque;
67 import java.util.List; 67 import java.util.List;
68 -import java.util.Map;
69 import java.util.Objects; 68 import java.util.Objects;
70 import java.util.Set; 69 import java.util.Set;
71 import java.util.concurrent.ConcurrentHashMap; 70 import java.util.concurrent.ConcurrentHashMap;
...@@ -123,10 +122,6 @@ public class Ofdpa2GroupHandler { ...@@ -123,10 +122,6 @@ public class Ofdpa2GroupHandler {
123 // index number for group creation 122 // index number for group creation
124 private AtomicCounter nextIndex; 123 private AtomicCounter nextIndex;
125 124
126 - // local stores for port-vlan mapping
127 - protected Map<PortNumber, VlanId> port2Vlan = new ConcurrentHashMap<>();
128 - protected Map<VlanId, Set<PortNumber>> vlan2Port = new ConcurrentHashMap<>();
129 -
130 // local store for pending bucketAdds - by design there can only be one 125 // local store for pending bucketAdds - by design there can only be one
131 // pending bucket for a group 126 // pending bucket for a group
132 protected ConcurrentHashMap<Integer, NextObjective> pendingBuckets = new ConcurrentHashMap<>(); 127 protected ConcurrentHashMap<Integer, NextObjective> pendingBuckets = new ConcurrentHashMap<>();
......
...@@ -313,31 +313,22 @@ public class Ofdpa2Pipeline extends AbstractHandlerBehaviour implements Pipeline ...@@ -313,31 +313,22 @@ public class Ofdpa2Pipeline extends AbstractHandlerBehaviour implements Pipeline
313 } 313 }
314 314
315 VlanId assignedVlan = null; 315 VlanId assignedVlan = null;
316 - // For VLAN cross-connect packets, use the configured VLAN
317 if (vidCriterion != null) { 316 if (vidCriterion != null) {
318 - if (vidCriterion.vlanId() != VlanId.NONE) { 317 + // Use the VLAN in metadata whenever a metadata is provided
318 + if (filt.meta() != null) {
319 + assignedVlan = readVlanFromTreatment(filt.meta());
320 + // Use the VLAN in criterion if metadata is not present and the traffic is tagged
321 + } else if (!vidCriterion.vlanId().equals(VlanId.NONE)) {
319 assignedVlan = vidCriterion.vlanId(); 322 assignedVlan = vidCriterion.vlanId();
323 + }
320 324
321 - // For untagged packets, assign a VLAN ID 325 + if (assignedVlan == null) {
322 - } else { 326 + log.error("Driver fails to extract VLAN information. "
323 - if (filt.meta() == null) { 327 + + "Not proccessing VLAN filters on device {}.", deviceId);
324 - log.error("Missing metadata in filtering objective required " + 328 + log.debug("VLAN ID in criterion={}, metadata={}",
325 - "for vlan assignment in dev {}", deviceId); 329 + readVlanFromTreatment(filt.meta()), vidCriterion.vlanId());
326 - fail(filt, ObjectiveError.BADPARAMS); 330 + fail(filt, ObjectiveError.BADPARAMS);
327 - return; 331 + return;
328 - }
329 - for (Instruction i : filt.meta().allInstructions()) {
330 - if (i instanceof ModVlanIdInstruction) {
331 - assignedVlan = ((ModVlanIdInstruction) i).vlanId();
332 - }
333 - }
334 - if (assignedVlan == null) {
335 - log.error("Driver requires an assigned vlan-id to tag incoming "
336 - + "untagged packets. Not processing vlan filters on "
337 - + "device {}", deviceId);
338 - fail(filt, ObjectiveError.BADPARAMS);
339 - return;
340 - }
341 } 332 }
342 } 333 }
343 334
...@@ -457,22 +448,14 @@ public class Ofdpa2Pipeline extends AbstractHandlerBehaviour implements Pipeline ...@@ -457,22 +448,14 @@ public class Ofdpa2Pipeline extends AbstractHandlerBehaviour implements Pipeline
457 TrafficSelector.Builder preSelector = null; 448 TrafficSelector.Builder preSelector = null;
458 TrafficTreatment.Builder preTreatment = null; 449 TrafficTreatment.Builder preTreatment = null;
459 450
460 -
461 treatment.transition(TMAC_TABLE); 451 treatment.transition(TMAC_TABLE);
462 452
463 - VlanId storeVlan = null;
464 if (vidCriterion.vlanId() == VlanId.NONE) { 453 if (vidCriterion.vlanId() == VlanId.NONE) {
465 // untagged packets are assigned vlans 454 // untagged packets are assigned vlans
466 OfdpaMatchVlanVid ofdpaMatchVlanVid = new OfdpaMatchVlanVid(VlanId.NONE); 455 OfdpaMatchVlanVid ofdpaMatchVlanVid = new OfdpaMatchVlanVid(VlanId.NONE);
467 selector.extension(ofdpaMatchVlanVid, deviceId); 456 selector.extension(ofdpaMatchVlanVid, deviceId);
468 OfdpaSetVlanVid ofdpaSetVlanVid = new OfdpaSetVlanVid(assignedVlan); 457 OfdpaSetVlanVid ofdpaSetVlanVid = new OfdpaSetVlanVid(assignedVlan);
469 treatment.extension(ofdpaSetVlanVid, deviceId); 458 treatment.extension(ofdpaSetVlanVid, deviceId);
470 - // ofdpa requires an additional vlan match rule for the assigned vlan
471 - // and it does not require the push when setting the assigned vlan.
472 - // It also requires the extra rule to be sent to the switch before we
473 - // send the untagged match rule.
474 - // None of this in compliance with OF standard.
475 - storeVlan = assignedVlan;
476 459
477 preSelector = DefaultTrafficSelector.builder(); 460 preSelector = DefaultTrafficSelector.builder();
478 OfdpaMatchVlanVid preOfdpaMatchVlanVid = new OfdpaMatchVlanVid(assignedVlan); 461 OfdpaMatchVlanVid preOfdpaMatchVlanVid = new OfdpaMatchVlanVid(assignedVlan);
...@@ -482,7 +465,11 @@ public class Ofdpa2Pipeline extends AbstractHandlerBehaviour implements Pipeline ...@@ -482,7 +465,11 @@ public class Ofdpa2Pipeline extends AbstractHandlerBehaviour implements Pipeline
482 } else { 465 } else {
483 OfdpaMatchVlanVid ofdpaMatchVlanVid = new OfdpaMatchVlanVid(vidCriterion.vlanId()); 466 OfdpaMatchVlanVid ofdpaMatchVlanVid = new OfdpaMatchVlanVid(vidCriterion.vlanId());
484 selector.extension(ofdpaMatchVlanVid, deviceId); 467 selector.extension(ofdpaMatchVlanVid, deviceId);
485 - storeVlan = vidCriterion.vlanId(); 468 +
469 + if (!assignedVlan.equals(vidCriterion.vlanId())) {
470 + OfdpaSetVlanVid ofdpaSetVlanVid = new OfdpaSetVlanVid(assignedVlan);
471 + treatment.extension(ofdpaSetVlanVid, deviceId);
472 + }
486 } 473 }
487 474
488 // ofdpa cannot match on ALL portnumber, so we need to use separate 475 // ofdpa cannot match on ALL portnumber, so we need to use separate
...@@ -499,17 +486,6 @@ public class Ofdpa2Pipeline extends AbstractHandlerBehaviour implements Pipeline ...@@ -499,17 +486,6 @@ public class Ofdpa2Pipeline extends AbstractHandlerBehaviour implements Pipeline
499 } 486 }
500 487
501 for (PortNumber pnum : portnums) { 488 for (PortNumber pnum : portnums) {
502 - // update storage
503 - groupHandler.port2Vlan.put(pnum, storeVlan);
504 - Set<PortNumber> vlanPorts = groupHandler.vlan2Port.get(storeVlan);
505 - if (vlanPorts == null) {
506 - vlanPorts = Collections.newSetFromMap(
507 - new ConcurrentHashMap<PortNumber, Boolean>());
508 - vlanPorts.add(pnum);
509 - groupHandler.vlan2Port.put(storeVlan, vlanPorts);
510 - } else {
511 - vlanPorts.add(pnum);
512 - }
513 // create rest of flowrule 489 // create rest of flowrule
514 selector.matchInPort(pnum); 490 selector.matchInPort(pnum);
515 FlowRule rule = DefaultFlowRule.builder() 491 FlowRule rule = DefaultFlowRule.builder()
...@@ -1112,4 +1088,13 @@ public class Ofdpa2Pipeline extends AbstractHandlerBehaviour implements Pipeline ...@@ -1112,4 +1088,13 @@ public class Ofdpa2Pipeline extends AbstractHandlerBehaviour implements Pipeline
1112 Criterion criterion = selector.getCriterion(Criterion.Type.IPV4_DST); 1088 Criterion criterion = selector.getCriterion(Criterion.Type.IPV4_DST);
1113 return (criterion == null) ? null : ((IPCriterion) criterion).ip(); 1089 return (criterion == null) ? null : ((IPCriterion) criterion).ip();
1114 } 1090 }
1091 +
1092 + private static VlanId readVlanFromTreatment(TrafficTreatment treatment) {
1093 + for (Instruction i : treatment.allInstructions()) {
1094 + if (i instanceof ModVlanIdInstruction) {
1095 + return ((ModVlanIdInstruction) i).vlanId();
1096 + }
1097 + }
1098 + return null;
1099 + }
1115 } 1100 }
......
1 +/*
2 + * Copyright 2016-present Open Networking Laboratory
3 + *
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 + */
16 +
17 +package org.onosproject.incubator.net.config.basics;
18 +
19 +import com.google.common.annotations.Beta;
20 +import org.onlab.packet.VlanId;
21 +import org.onosproject.core.ApplicationId;
22 +import org.onosproject.net.config.Config;
23 +
24 +/**
25 + * Configuration for multicast.
26 + */
27 +@Beta
28 +public class McastConfig extends Config<ApplicationId> {
29 + private static final String INGRESS_VLAN = "ingressVlan";
30 + private static final String EGRESS_VLAN = "egressVlan";
31 +
32 + @Override
33 + public boolean isValid() {
34 + return hasOnlyFields(INGRESS_VLAN, EGRESS_VLAN) &&
35 + ingressVlan() != null && egressVlan() != null;
36 + }
37 +
38 + /**
39 + * Gets ingress VLAN of multicast traffic.
40 + *
41 + * @return Ingress VLAN ID
42 + */
43 + public VlanId ingressVlan() {
44 + if (!object.has(INGRESS_VLAN)) {
45 + return VlanId.NONE;
46 + }
47 +
48 + try {
49 + return VlanId.vlanId(object.path(INGRESS_VLAN).asText());
50 + } catch (IllegalArgumentException e) {
51 + return null;
52 + }
53 + }
54 +
55 + /**
56 + * Sets ingress VLAN of multicast traffic.
57 + *
58 + * @param vlanId Ingress VLAN ID
59 + * @return this {@link McastConfig}
60 + */
61 + public McastConfig setIngressVlan(VlanId vlanId) {
62 + if (vlanId == null) {
63 + object.remove(INGRESS_VLAN);
64 + } else {
65 + object.put(INGRESS_VLAN, vlanId.toString());
66 + }
67 + return this;
68 + }
69 +
70 + /**
71 + * Gets egress VLAN of multicast traffic.
72 + *
73 + * @return Egress VLAN ID
74 + */
75 + public VlanId egressVlan() {
76 + if (!object.has(EGRESS_VLAN)) {
77 + return VlanId.NONE;
78 + }
79 +
80 + try {
81 + return VlanId.vlanId(object.path(EGRESS_VLAN).asText());
82 + } catch (IllegalArgumentException e) {
83 + return null;
84 + }
85 + }
86 +
87 + /**
88 + * Sets egress VLAN of multicast traffic.
89 + *
90 + * @param vlanId Egress VLAN ID
91 + * @return this {@link McastConfig}
92 + */
93 + public McastConfig setEgressVlan(VlanId vlanId) {
94 + if (vlanId == null) {
95 + object.remove(EGRESS_VLAN);
96 + } else {
97 + object.put(EGRESS_VLAN, vlanId.toString());
98 + }
99 + return this;
100 + }
101 +}
1 +/*
2 + * Copyright 2016-present Open Networking Laboratory
3 + *
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 + */
16 +
17 +package org.onosproject.incubator.net.config.basics;
18 +
19 +import com.fasterxml.jackson.databind.JsonNode;
20 +import com.fasterxml.jackson.databind.ObjectMapper;
21 +import com.google.common.annotations.Beta;
22 +import org.junit.Before;
23 +import org.junit.Test;
24 +import org.onlab.packet.VlanId;
25 +import org.onosproject.TestApplicationId;
26 +import org.onosproject.core.ApplicationId;
27 +import org.onosproject.core.CoreService;
28 +import org.onosproject.net.config.Config;
29 +import org.onosproject.net.config.ConfigApplyDelegate;
30 +
31 +import java.io.InputStream;
32 +
33 +import static org.hamcrest.Matchers.is;
34 +import static org.junit.Assert.*;
35 +
36 +/**
37 + * Tests for class {@link McastConfig}.
38 + */
39 +@Beta
40 +public class McastConfigTest {
41 + private static final TestApplicationId APP_ID =
42 + new TestApplicationId(CoreService.CORE_APP_NAME);
43 + private McastConfig config;
44 + private McastConfig invalidConfig;
45 +
46 + private static final VlanId INGRESS_VLAN_1 = VlanId.NONE;
47 + private static final VlanId EGRESS_VLAN_1 = VlanId.NONE;
48 + private static final VlanId INGRESS_VLAN_2 = VlanId.vlanId((short) 100);
49 + private static final VlanId EGRESS_VLAN_2 = VlanId.vlanId((short) 100);
50 +
51 + /**
52 + * Initialize test related variables.
53 + *
54 + * @throws Exception
55 + */
56 + @Before
57 + public void setUp() throws Exception {
58 + InputStream jsonStream = McastConfigTest.class
59 + .getResourceAsStream("/mcast-config.json");
60 + InputStream invalidJsonStream = McastConfigTest.class
61 + .getResourceAsStream("/mcast-config-invalid.json");
62 +
63 + ApplicationId subject = APP_ID;
64 + String key = CoreService.CORE_APP_NAME;
65 + ObjectMapper mapper = new ObjectMapper();
66 + JsonNode jsonNode = mapper.readTree(jsonStream);
67 + JsonNode invalidJsonNode = mapper.readTree(invalidJsonStream);
68 + ConfigApplyDelegate delegate = new MockDelegate();
69 +
70 + config = new McastConfig();
71 + config.init(subject, key, jsonNode, mapper, delegate);
72 + invalidConfig = new McastConfig();
73 + invalidConfig.init(subject, key, invalidJsonNode, mapper, delegate);
74 + }
75 +
76 + /**
77 + * Tests config validity.
78 + *
79 + * @throws Exception
80 + */
81 + @Test
82 + public void isValid() throws Exception {
83 + assertTrue(config.isValid());
84 + assertFalse(invalidConfig.isValid());
85 + }
86 +
87 + /**
88 + * Tests ingress VLAN getter.
89 + *
90 + * @throws Exception
91 + */
92 + @Test
93 + public void ingressVlan() throws Exception {
94 + VlanId ingressVlan = config.ingressVlan();
95 + assertNotNull("ingressVlan should not be null", ingressVlan);
96 + assertThat(ingressVlan, is(INGRESS_VLAN_1));
97 + }
98 +
99 + /**
100 + * Tests ingress VLAN setter.
101 + *
102 + * @throws Exception
103 + */
104 + @Test
105 + public void setIngressVlan() throws Exception {
106 + config.setIngressVlan(INGRESS_VLAN_2);
107 +
108 + VlanId ingressVlan = config.ingressVlan();
109 + assertNotNull("ingressVlan should not be null", ingressVlan);
110 + assertThat(ingressVlan, is(INGRESS_VLAN_2));
111 + }
112 +
113 + /**
114 + * Tests egress VLAN getter.
115 + *
116 + * @throws Exception
117 + */
118 + @Test
119 + public void egressVlan() throws Exception {
120 + VlanId egressVlan = config.egressVlan();
121 + assertNotNull("egressVlan should not be null", egressVlan);
122 + assertThat(egressVlan, is(EGRESS_VLAN_1));
123 + }
124 +
125 + /**
126 + * Tests egress VLAN setter.
127 + *
128 + * @throws Exception
129 + */
130 + @Test
131 + public void setEgressVlan() throws Exception {
132 + config.setEgressVlan(EGRESS_VLAN_2);
133 +
134 + VlanId egressVlan = config.egressVlan();
135 + assertNotNull("egressVlan should not be null", egressVlan);
136 + assertThat(egressVlan, is(EGRESS_VLAN_2));
137 + }
138 +
139 + private class MockDelegate implements ConfigApplyDelegate {
140 + @Override
141 + public void onApply(Config config) {
142 + }
143 + }
144 +}
...\ No newline at end of file ...\ No newline at end of file
1 +{
2 + "ingressVlan" : "None",
3 + "egressVlan" : "5000"
4 +}
...\ No newline at end of file ...\ No newline at end of file
1 +{
2 + "ingressVlan" : "None",
3 + "egressVlan" : "None"
4 +}
...\ No newline at end of file ...\ No newline at end of file