blob: 298d60ab5dd57ca1c551064bf98c65b191c437dd [file] [log] [blame]
Hyunsun Moone7e4bb32016-05-16 04:32:45 -07001/*
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 */
Hyunsun Moon5401aaa2016-06-12 17:40:34 -070016package org.opencord.cordvtn.impl.handler;
Hyunsun Moone7e4bb32016-05-16 04:32:45 -070017
18import com.google.common.base.Strings;
19import com.google.common.collect.Sets;
20import org.apache.felix.scr.annotations.Activate;
21import org.apache.felix.scr.annotations.Component;
22import org.apache.felix.scr.annotations.Deactivate;
23import org.apache.felix.scr.annotations.Reference;
24import org.apache.felix.scr.annotations.ReferenceCardinality;
25import org.apache.felix.scr.annotations.Service;
26import org.onlab.packet.Ethernet;
27import org.onlab.packet.IpAddress;
28import org.onlab.packet.IpPrefix;
29import org.onlab.packet.MacAddress;
30import org.onlab.packet.VlanId;
Hyunsun Moon5401aaa2016-06-12 17:40:34 -070031import org.opencord.cordvtn.api.InstanceService;
Hyunsun Moon60a10672016-06-12 17:39:12 -070032import org.opencord.cordvtn.impl.AbstractInstanceHandler;
alshabibb4d31712016-06-01 18:51:03 -070033import org.opencord.cordvtn.api.Instance;
34import org.opencord.cordvtn.api.InstanceHandler;
Hyunsun Moone7e4bb32016-05-16 04:32:45 -070035import org.onosproject.net.DefaultAnnotations;
36import org.onosproject.net.HostId;
37import org.onosproject.net.PortNumber;
38import org.onosproject.net.flow.DefaultFlowRule;
39import org.onosproject.net.flow.DefaultTrafficSelector;
40import org.onosproject.net.flow.DefaultTrafficTreatment;
41import org.onosproject.net.flow.FlowRule;
42import org.onosproject.net.flow.FlowRuleService;
43import org.onosproject.net.flow.TrafficSelector;
44import org.onosproject.net.flow.TrafficTreatment;
45import org.onosproject.net.flow.criteria.Criterion;
46import org.onosproject.net.flow.criteria.IPCriterion;
47import org.onosproject.net.flow.instructions.Instruction;
48import org.onosproject.net.flow.instructions.Instructions;
49import org.onosproject.net.flow.instructions.L2ModificationInstruction;
50import org.onosproject.net.host.DefaultHostDescription;
51import org.onosproject.net.host.HostDescription;
52import org.onosproject.xosclient.api.VtnPort;
Hyunsun Moon5401aaa2016-06-12 17:40:34 -070053import org.opencord.cordvtn.impl.CordVtnNodeManager;
alshabibb4d31712016-06-01 18:51:03 -070054import org.opencord.cordvtn.impl.CordVtnPipeline;
Hyunsun Moone7e4bb32016-05-16 04:32:45 -070055
56import java.util.Map;
Hyunsun Moon60a10672016-06-12 17:39:12 -070057import java.util.Optional;
Hyunsun Moone7e4bb32016-05-16 04:32:45 -070058import java.util.Set;
59
60import static com.google.common.base.Preconditions.checkNotNull;
Hyunsun Moone7e4bb32016-05-16 04:32:45 -070061import static org.onosproject.net.flow.criteria.Criterion.Type.IPV4_DST;
62import static org.onosproject.net.flow.instructions.L2ModificationInstruction.L2SubType.VLAN_PUSH;
Hyunsun Moon60a10672016-06-12 17:39:12 -070063import static org.onosproject.xosclient.api.VtnServiceApi.ServiceType.VSG;
Hyunsun Moone7e4bb32016-05-16 04:32:45 -070064
65/**
66 * Provides network connectivity for vSG instances.
67 */
68@Component(immediate = true)
69@Service(value = VsgInstanceHandler.class)
Hyunsun Moon60a10672016-06-12 17:39:12 -070070public final class VsgInstanceHandler extends AbstractInstanceHandler implements InstanceHandler {
Hyunsun Moone7e4bb32016-05-16 04:32:45 -070071
Hyunsun Moon5401aaa2016-06-12 17:40:34 -070072 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
73 protected CordVtnPipeline pipeline;
74
75 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
76 protected CordVtnNodeManager nodeManager;
77
Hyunsun Moone7e4bb32016-05-16 04:32:45 -070078 private static final String STAG = "stag";
79 private static final String VSG_VM = "vsgVm";
80
81 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
82 protected FlowRuleService flowRuleService;
83
84 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Hyunsun Moon5401aaa2016-06-12 17:40:34 -070085 protected InstanceService instanceService;
Hyunsun Moone7e4bb32016-05-16 04:32:45 -070086
87 @Activate
88 protected void activate() {
Hyunsun Moon60a10672016-06-12 17:39:12 -070089 serviceType = Optional.of(VSG);
Hyunsun Moone7e4bb32016-05-16 04:32:45 -070090 super.activate();
91 }
92
93 @Deactivate
94 protected void deactivate() {
95 super.deactivate();
96 }
97
98 @Override
99 public void instanceDetected(Instance instance) {
100 if (isVsgContainer(instance)) {
101 log.info("vSG container detected {}", instance);
102
103 // find vsg vm for this vsg container
104 String vsgVmId = instance.getAnnotation(VSG_VM);
105 if (Strings.isNullOrEmpty(vsgVmId)) {
Hyunsun Moon60a10672016-06-12 17:39:12 -0700106 log.warn("Failed to find vSG VM for {}", instance);
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700107 return;
108 }
109
110 Instance vsgVm = Instance.of(hostService.getHost(HostId.hostId(vsgVmId)));
111 VtnPort vtnPort = getVtnPort(vsgVm);
112 if (vtnPort == null || getStag(vtnPort) == null) {
Hyunsun Moon5401aaa2016-06-12 17:40:34 -0700113 log.warn("Failed to get vSG information {}", vsgVm);
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700114 return;
115 }
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700116 populateVsgRules(vsgVm, getStag(vtnPort),
Hyunsun Moon3ef52492016-06-15 14:56:31 -0700117 nodeManager.dataPort(vsgVm.deviceId()),
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700118 vtnPort.addressPairs().keySet(),
119 true);
120
121 } else {
122 VtnPort vtnPort = getVtnPort(instance);
123 if (vtnPort == null || getStag(vtnPort) == null) {
124 return;
125 }
Hyunsun Moon60a10672016-06-12 17:39:12 -0700126 log.info("vSG VM detected {}", instance);
127
128 // insert vSG containers inside the vSG VM as a host
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700129 vtnPort.addressPairs().entrySet().stream()
130 .forEach(pair -> addVsgContainer(
131 instance,
132 pair.getKey(),
133 pair.getValue(),
134 getStag(vtnPort).toString()
135 ));
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700136 }
137 }
138
139 @Override
140 public void instanceRemoved(Instance instance) {
Hyunsun Moon60a10672016-06-12 17:39:12 -0700141 if (!isVsgContainer(instance)) {
142 // nothing to do for the vSG VM itself
143 return;
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700144 }
Hyunsun Moon60a10672016-06-12 17:39:12 -0700145
146 log.info("vSG container vanished {}", instance);
147
148 // find vsg vm for this vsg container
149 String vsgVmId = instance.getAnnotation(VSG_VM);
150 if (Strings.isNullOrEmpty(vsgVmId)) {
151 log.warn("Failed to find vSG VM for {}", instance);
152 return;
153 }
154
155 Instance vsgVm = Instance.of(hostService.getHost(HostId.hostId(vsgVmId)));
156 VtnPort vtnPort = getVtnPort(vsgVm);
157 if (vtnPort == null || getStag(vtnPort) == null) {
158 return;
159 }
160
161 populateVsgRules(vsgVm, getStag(vtnPort),
Hyunsun Moon3ef52492016-06-15 14:56:31 -0700162 nodeManager.dataPort(vsgVm.deviceId()),
Hyunsun Moon60a10672016-06-12 17:39:12 -0700163 vtnPort.addressPairs().keySet(),
164 false);
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700165 }
166
167 /**
168 * Updates set of vSGs in a given vSG VM.
169 *
170 * @param vsgVmId vsg vm host id
171 * @param stag stag
172 * @param vsgInstances full set of vsg wan ip and mac address pairs in this vsg vm
173 */
174 public void updateVsgInstances(HostId vsgVmId, String stag, Map<IpAddress, MacAddress> vsgInstances) {
175 if (hostService.getHost(vsgVmId) == null) {
176 log.debug("vSG VM {} is not added yet, ignore this update", vsgVmId);
177 return;
178 }
179
180 Instance vsgVm = Instance.of(hostService.getHost(vsgVmId));
181 if (vsgVm == null) {
182 log.warn("Failed to find existing vSG VM for STAG: {}", stag);
183 return;
184 }
185
186 log.info("Updates vSGs in {} with STAG: {}", vsgVm, stag);
187
188 // adds vSGs in the address pair
189 vsgInstances.entrySet().stream()
190 .filter(addr -> hostService.getHostsByMac(addr.getValue()).isEmpty())
191 .forEach(addr -> addVsgContainer(
192 vsgVm,
193 addr.getKey(),
194 addr.getValue(),
195 stag));
196
197 // removes vSGs not listed in the address pair
198 hostService.getConnectedHosts(vsgVm.host().location()).stream()
199 .filter(host -> !host.mac().equals(vsgVm.mac()))
200 .filter(host -> !vsgInstances.values().contains(host.mac()))
201 .forEach(host -> {
202 log.info("Removed vSG {}", host.toString());
Hyunsun Moon5401aaa2016-06-12 17:40:34 -0700203 instanceService.removeNestedInstance(host.id());
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700204 });
205 }
206
207 private boolean isVsgContainer(Instance instance) {
208 return !Strings.isNullOrEmpty(instance.host().annotations().value(STAG));
209 }
210
211 private void addVsgContainer(Instance vsgVm, IpAddress vsgWanIp, MacAddress vsgMac,
212 String stag) {
213 HostId hostId = HostId.hostId(vsgMac);
214 DefaultAnnotations.Builder annotations = DefaultAnnotations.builder()
alshabibb4d31712016-06-01 18:51:03 -0700215 .set(Instance.SERVICE_TYPE, vsgVm.serviceType().toString())
216 .set(Instance.SERVICE_ID, vsgVm.serviceId().id())
217 .set(Instance.PORT_ID, vsgVm.portId().id())
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700218 .set(STAG, stag)
219 .set(VSG_VM, vsgVm.host().id().toString())
alshabibb4d31712016-06-01 18:51:03 -0700220 .set(Instance.CREATE_TIME, String.valueOf(System.currentTimeMillis()));
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700221
222 HostDescription hostDesc = new DefaultHostDescription(
223 vsgMac,
224 VlanId.NONE,
225 vsgVm.host().location(),
226 Sets.newHashSet(vsgWanIp),
227 annotations.build());
228
Hyunsun Moon5401aaa2016-06-12 17:40:34 -0700229 instanceService.addNestedInstance(hostId, hostDesc);
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700230 }
231
Hyunsun Moon3ef52492016-06-15 14:56:31 -0700232 private void populateVsgRules(Instance vsgVm, VlanId stag, PortNumber dataPort,
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700233 Set<IpAddress> vsgWanIps, boolean install) {
234 // for traffics with s-tag, strip the tag and take through the vSG VM
235 TrafficSelector selector = DefaultTrafficSelector.builder()
Hyunsun Moon3ef52492016-06-15 14:56:31 -0700236 .matchInPort(dataPort)
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700237 .matchVlanId(stag)
238 .build();
239
240 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
241 .setOutput(vsgVm.portNumber())
242 .build();
243
244 FlowRule flowRule = DefaultFlowRule.builder()
245 .fromApp(appId)
246 .withSelector(selector)
247 .withTreatment(treatment)
alshabibb4d31712016-06-01 18:51:03 -0700248 .withPriority(CordVtnPipeline.PRIORITY_DEFAULT)
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700249 .forDevice(vsgVm.deviceId())
alshabibb4d31712016-06-01 18:51:03 -0700250 .forTable(CordVtnPipeline.TABLE_VLAN)
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700251 .makePermanent()
252 .build();
253
254 pipeline.processFlowRule(install, flowRule);
255
256 // for traffics with customer vlan, tag with the service vlan based on input port with
257 // lower priority to avoid conflict with WAN tag
258 selector = DefaultTrafficSelector.builder()
259 .matchInPort(vsgVm.portNumber())
260 .matchVlanId(stag)
261 .build();
262
263 treatment = DefaultTrafficTreatment.builder()
Hyunsun Moon3ef52492016-06-15 14:56:31 -0700264 .setOutput(dataPort)
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700265 .build();
266
267 flowRule = DefaultFlowRule.builder()
268 .fromApp(appId)
269 .withSelector(selector)
270 .withTreatment(treatment)
alshabibb4d31712016-06-01 18:51:03 -0700271 .withPriority(CordVtnPipeline.PRIORITY_DEFAULT)
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700272 .forDevice(vsgVm.deviceId())
alshabibb4d31712016-06-01 18:51:03 -0700273 .forTable(CordVtnPipeline.TABLE_VLAN)
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700274 .makePermanent()
275 .build();
276
277 pipeline.processFlowRule(install, flowRule);
278
279 // for traffic coming from WAN, tag 500 and take through the vSG VM
280 // based on destination ip
281 vsgWanIps.stream().forEach(ip -> {
282 TrafficSelector downstream = DefaultTrafficSelector.builder()
283 .matchEthType(Ethernet.TYPE_IPV4)
284 .matchIPDst(ip.toIpPrefix())
285 .build();
286
287 TrafficTreatment downstreamTreatment = DefaultTrafficTreatment.builder()
288 .pushVlan()
alshabibb4d31712016-06-01 18:51:03 -0700289 .setVlanId(CordVtnPipeline.VLAN_WAN)
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700290 .setEthDst(vsgVm.mac())
291 .setOutput(vsgVm.portNumber())
292 .build();
293
294 FlowRule downstreamFlowRule = DefaultFlowRule.builder()
295 .fromApp(appId)
296 .withSelector(downstream)
297 .withTreatment(downstreamTreatment)
alshabibb4d31712016-06-01 18:51:03 -0700298 .withPriority(CordVtnPipeline.PRIORITY_DEFAULT)
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700299 .forDevice(vsgVm.deviceId())
Hyunsun Moon3a7bf9e2016-06-22 17:35:35 -0700300 .forTable(CordVtnPipeline.TABLE_DST)
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700301 .makePermanent()
302 .build();
303
304 pipeline.processFlowRule(install, downstreamFlowRule);
305 });
306
307 // remove downstream flow rules for the vSG not shown in vsgWanIps
308 for (FlowRule rule : flowRuleService.getFlowRulesById(appId)) {
309 if (!rule.deviceId().equals(vsgVm.deviceId())) {
310 continue;
311 }
312 PortNumber output = getOutputFromTreatment(rule);
313 if (output == null || !output.equals(vsgVm.portNumber()) ||
314 !isVlanPushFromTreatment(rule)) {
315 continue;
316 }
317
318 IpPrefix dstIp = getDstIpFromSelector(rule);
319 if (dstIp != null && !vsgWanIps.contains(dstIp.address())) {
320 pipeline.processFlowRule(false, rule);
321 }
322 }
323 }
324
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700325 // TODO get stag from XOS when XOS provides it, extract if from port name for now
326 private VlanId getStag(VtnPort vtnPort) {
327 checkNotNull(vtnPort);
328
329 String portName = vtnPort.name();
330 if (portName != null && portName.startsWith(STAG)) {
331 return VlanId.vlanId(portName.split("-")[1]);
332 } else {
333 return null;
334 }
335 }
336
337 private PortNumber getOutputFromTreatment(FlowRule flowRule) {
338 Instruction instruction = flowRule.treatment().allInstructions().stream()
339 .filter(inst -> inst instanceof Instructions.OutputInstruction)
340 .findFirst()
341 .orElse(null);
342 if (instruction == null) {
343 return null;
344 }
345 return ((Instructions.OutputInstruction) instruction).port();
346 }
347
348 private IpPrefix getDstIpFromSelector(FlowRule flowRule) {
349 Criterion criterion = flowRule.selector().getCriterion(IPV4_DST);
350 if (criterion != null && criterion instanceof IPCriterion) {
351 IPCriterion ip = (IPCriterion) criterion;
352 return ip.ip();
353 } else {
354 return null;
355 }
356 }
357
358 private boolean isVlanPushFromTreatment(FlowRule flowRule) {
359 Instruction instruction = flowRule.treatment().allInstructions().stream()
360 .filter(inst -> inst instanceof L2ModificationInstruction)
361 .filter(inst -> ((L2ModificationInstruction) inst).subtype().equals(VLAN_PUSH))
362 .findAny()
363 .orElse(null);
364 return instruction != null;
365 }
366}