blob: 8d5c780b3f0e30a32f232a22bc1334e4c9864be8 [file] [log] [blame]
sanghoshin94872a12015-10-16 18:04:34 +09001/*
2* Copyright 2015 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
17package org.onosproject.openstackswitching;
18
19import org.onlab.packet.Ethernet;
sanghoshin94872a12015-10-16 18:04:34 +090020import org.onlab.packet.Ip4Address;
sanghoshin94872a12015-10-16 18:04:34 +090021import org.onlab.packet.MacAddress;
sanghoshin94872a12015-10-16 18:04:34 +090022import org.onosproject.core.ApplicationId;
sanghoshinf25d2e02015-11-11 23:07:17 +090023import org.onosproject.net.Device;
sanghoshin94872a12015-10-16 18:04:34 +090024import org.onosproject.net.DeviceId;
25import org.onosproject.net.Port;
26import org.onosproject.net.PortNumber;
sanghoshinf25d2e02015-11-11 23:07:17 +090027import org.onosproject.net.behaviour.ExtensionTreatmentResolver;
28import org.onosproject.net.device.DeviceService;
29import org.onosproject.net.driver.DefaultDriverData;
30import org.onosproject.net.driver.DefaultDriverHandler;
31import org.onosproject.net.driver.Driver;
32import org.onosproject.net.driver.DriverHandler;
33import org.onosproject.net.driver.DriverService;
sanghoshin94872a12015-10-16 18:04:34 +090034import org.onosproject.net.flow.DefaultTrafficSelector;
35import org.onosproject.net.flow.DefaultTrafficTreatment;
36import org.onosproject.net.flow.TrafficSelector;
37import org.onosproject.net.flow.TrafficTreatment;
sanghoshinf25d2e02015-11-11 23:07:17 +090038import org.onosproject.net.flow.instructions.ExtensionTreatment;
39import org.onosproject.net.flow.instructions.ExtensionPropertyException;
40import org.onosproject.net.flow.instructions.ExtensionTreatmentType;
sanghoshin94872a12015-10-16 18:04:34 +090041import org.onosproject.net.flowobjective.DefaultForwardingObjective;
42import org.onosproject.net.flowobjective.FlowObjectiveService;
43import org.onosproject.net.flowobjective.ForwardingObjective;
44import org.slf4j.Logger;
45import org.slf4j.LoggerFactory;
46
sanghoshinf25d2e02015-11-11 23:07:17 +090047import java.util.Collection;
48
sanghoshin94872a12015-10-16 18:04:34 +090049/**
sanghoshin46297d22015-11-03 17:51:24 +090050 * Populates switching flow rules.
sanghoshin94872a12015-10-16 18:04:34 +090051 */
52public class OpenstackSwitchingRulePopulator {
53
54 private static Logger log = LoggerFactory
55 .getLogger(OpenstackSwitchingRulePopulator.class);
daniel2a2bd7b2015-12-02 13:53:58 +090056 private static final int SWITCHING_RULE_PRIORITY = 30000;
57 private static final int EAST_WEST_ROUTING_RULE_PRIORITY = 29000;
58 private static final int TUNNELTAG_RULE_PRIORITY = 30000;
sanghoshin94872a12015-10-16 18:04:34 +090059
60 private FlowObjectiveService flowObjectiveService;
sanghoshinf25d2e02015-11-11 23:07:17 +090061 private DriverService driverService;
62 private DeviceService deviceService;
63 private OpenstackRestHandler restHandler;
sanghoshin94872a12015-10-16 18:04:34 +090064 private ApplicationId appId;
65
sanghoshinf25d2e02015-11-11 23:07:17 +090066 private Collection<OpenstackNetwork> openstackNetworkList;
67 private Collection<OpenstackPort> openstackPortList;
68
sanghoshin94872a12015-10-16 18:04:34 +090069 /**
Ray Milkey87b16b02015-10-30 11:50:00 -070070 * Creates OpenstackSwitchingRulPopulator.
71 *
72 * @param appId application id
sanghoshin94872a12015-10-16 18:04:34 +090073 * @param flowObjectiveService FlowObjectiveService reference
sanghoshinf25d2e02015-11-11 23:07:17 +090074 * @param deviceService DeviceService reference
75 * @param driverService DriverService reference
sanghoshin94872a12015-10-16 18:04:34 +090076 */
77 public OpenstackSwitchingRulePopulator(ApplicationId appId,
sanghoshinf25d2e02015-11-11 23:07:17 +090078 FlowObjectiveService flowObjectiveService,
79 DeviceService deviceService,
80 OpenstackRestHandler restHandler,
81 DriverService driverService) {
sanghoshin94872a12015-10-16 18:04:34 +090082 this.flowObjectiveService = flowObjectiveService;
sanghoshinf25d2e02015-11-11 23:07:17 +090083 this.deviceService = deviceService;
84 this.driverService = driverService;
85 this.restHandler = restHandler;
sanghoshin94872a12015-10-16 18:04:34 +090086 this.appId = appId;
sanghoshinf25d2e02015-11-11 23:07:17 +090087
88 openstackNetworkList = restHandler.getNetworks();
89 openstackPortList = restHandler.getPorts();
sanghoshin94872a12015-10-16 18:04:34 +090090 }
91
daniel2a2bd7b2015-12-02 13:53:58 +090092
sanghoshin94872a12015-10-16 18:04:34 +090093 /**
sanghoshinf25d2e02015-11-11 23:07:17 +090094 * Populates flow rules for the VM created.
sanghoshin94872a12015-10-16 18:04:34 +090095 *
sanghoshinf25d2e02015-11-11 23:07:17 +090096 * @param device device to populate rules to
97 * @param port port for the VM created
sanghoshin94872a12015-10-16 18:04:34 +090098 */
sanghoshinf25d2e02015-11-11 23:07:17 +090099 public void populateSwitchingRules(Device device, Port port) {
Hyunsun Moon74ec4062015-12-09 10:58:32 -0800100 populateFlowRulesForTunnelTag(device, port);
sanghoshinf25d2e02015-11-11 23:07:17 +0900101 populateFlowRulesForTrafficToSameCnode(device, port);
102 populateFlowRulesForTrafficToDifferentCnode(device, port);
sanghoshin94872a12015-10-16 18:04:34 +0900103 }
104
105 /**
daniel2a2bd7b2015-12-02 13:53:58 +0900106 * Populate the flow rules for tagging tunnelId according to which inport is came from.
107 *
108 * @param device device to put the rules
109 * @param port port info of the VM
110 */
Hyunsun Moon74ec4062015-12-09 10:58:32 -0800111 private void populateFlowRulesForTunnelTag(Device device, Port port) {
daniel2a2bd7b2015-12-02 13:53:58 +0900112 Ip4Address vmIp = getFixedIpAddressForPort(port.annotations().value("portName"));
113 String portName = port.annotations().value("portName");
114 String vni = getVniForPort(portName);
115
116 if (vmIp != null) {
Hyunsun Moon74ec4062015-12-09 10:58:32 -0800117 setFlowRuleForTunnelTag(device.id(), port, vni);
daniel2a2bd7b2015-12-02 13:53:58 +0900118 }
119 }
120
121 /**
sanghoshin075e3e72015-11-25 16:34:29 +0900122 * Returns OpenstackPort object for the Port reference given.
123 *
124 * @param port Port object
125 * @return OpenstackPort reference, or null when not found
126 */
127 public OpenstackPort openstackPort(Port port) {
128 String uuid = port.annotations().value("portName").substring(3);
129 return openstackPortList.stream().filter(p -> p.id().startsWith(uuid))
130 .findAny().orElse(null);
131 }
132
133 /**
sanghoshin65723ae2015-11-17 22:07:21 +0900134 * Remove flows rules for the VM removed.
135 *
136 * @param deviceId device to remove rules
137 * @param vmIp IP address of the VM removed
138 */
139 public void removeSwitchingRules(DeviceId deviceId, Ip4Address vmIp) {
140 removeFlowRuleForVMsInSameCnode(deviceId, vmIp);
141 deviceService.getAvailableDevices().forEach(device -> {
142 if (!device.id().equals(deviceId)) {
143 removeVxLanFlowRule(device.id(), vmIp);
144 }
145 });
146 }
147
148 /**
sanghoshinf25d2e02015-11-11 23:07:17 +0900149 * Populates the flow rules for traffic to VMs in the same Cnode as the sender.
sanghoshin94872a12015-10-16 18:04:34 +0900150 *
sanghoshinf25d2e02015-11-11 23:07:17 +0900151 * @param device device to put the rules
152 * @param port port info of the VM
sanghoshin94872a12015-10-16 18:04:34 +0900153 */
sanghoshinf25d2e02015-11-11 23:07:17 +0900154 private void populateFlowRulesForTrafficToSameCnode(Device device, Port port) {
155 Ip4Address vmIp = getFixedIpAddressForPort(port.annotations().value("portName"));
daniel2a2bd7b2015-12-02 13:53:58 +0900156 String portName = port.annotations().value("portName");
157 String vni = getVniForPort(portName);
158 MacAddress vmMacAddress = getVmMacAddressForPort(portName);
159
sanghoshinf25d2e02015-11-11 23:07:17 +0900160 if (vmIp != null) {
daniel2a2bd7b2015-12-02 13:53:58 +0900161 setFlowRuleForVMsInSameCnode(vmIp, device.id(), port, vni, vmMacAddress);
sanghoshinf25d2e02015-11-11 23:07:17 +0900162 }
sanghoshin94872a12015-10-16 18:04:34 +0900163 }
164
165 /**
sanghoshinf25d2e02015-11-11 23:07:17 +0900166 * Populates the flow rules for traffic to VMs in different Cnode using
167 * Nicira extention.
sanghoshin94872a12015-10-16 18:04:34 +0900168 *
sanghoshinf25d2e02015-11-11 23:07:17 +0900169 * @param device device to put rules
170 * @param port port information of the VM
sanghoshin94872a12015-10-16 18:04:34 +0900171 */
sanghoshinf25d2e02015-11-11 23:07:17 +0900172 private void populateFlowRulesForTrafficToDifferentCnode(Device device, Port port) {
173 String portName = port.annotations().value("portName");
174 String channelId = device.annotations().value("channelId");
175 Ip4Address hostIpAddress = Ip4Address.valueOf(channelId.split(":")[0]);
176 Ip4Address fixedIp = getFixedIpAddressForPort(portName);
177 MacAddress vmMac = getVmMacAddressForPort(portName);
178 String vni = getVniForPort(portName);
179 deviceService.getAvailableDevices().forEach(d -> {
180 if (!d.equals(device)) {
181 deviceService.getPorts(d.id()).forEach(p -> {
182 String pName = p.annotations().value("portName");
183 if (!p.equals(port) && vni.equals(getVniForPort(pName))) {
184 String cidx = d.annotations().value("channelId");
185 Ip4Address hostIpx = Ip4Address.valueOf(cidx.split(":")[0]);
186 MacAddress vmMacx = getVmMacAddressForPort(pName);
187 Ip4Address fixedIpx = getFixedIpAddressForPort(pName);
sanghoshin65723ae2015-11-17 22:07:21 +0900188 if (port.isEnabled()) {
189 setVxLanFlowRule(vni, device.id(), hostIpx, fixedIpx, vmMacx);
190 setVxLanFlowRule(vni, d.id(), hostIpAddress, fixedIp, vmMac);
191 }
sanghoshinf25d2e02015-11-11 23:07:17 +0900192 }
193 });
194 }
195 });
sanghoshin94872a12015-10-16 18:04:34 +0900196 }
197
198 /**
sanghoshinf25d2e02015-11-11 23:07:17 +0900199 * Returns the VNI of the VM of the port.
sanghoshin94872a12015-10-16 18:04:34 +0900200 *
sanghoshinf25d2e02015-11-11 23:07:17 +0900201 * @param portName VM port
202 * @return VNI
sanghoshin94872a12015-10-16 18:04:34 +0900203 */
sanghoshinf25d2e02015-11-11 23:07:17 +0900204 private String getVniForPort(String portName) {
205 String uuid = portName.substring(3);
206 OpenstackPort port = openstackPortList.stream()
207 .filter(p -> p.id().startsWith(uuid))
208 .findAny().orElse(null);
209 if (port == null) {
daniel2a2bd7b2015-12-02 13:53:58 +0900210 log.debug("No port information for port {}", portName);
sanghoshinf25d2e02015-11-11 23:07:17 +0900211 return null;
212 }
sanghoshin94872a12015-10-16 18:04:34 +0900213
sanghoshinf25d2e02015-11-11 23:07:17 +0900214 OpenstackNetwork network = openstackNetworkList.stream()
215 .filter(n -> n.id().equals(port.networkId()))
216 .findAny().orElse(null);
217 if (network == null) {
Satish K2e2d6352015-11-27 12:02:15 +0530218 log.warn("No VNI information for network {}", port.networkId());
sanghoshinf25d2e02015-11-11 23:07:17 +0900219 return null;
220 }
sanghoshin94872a12015-10-16 18:04:34 +0900221
sanghoshinf25d2e02015-11-11 23:07:17 +0900222 return network.segmentId();
sanghoshin94872a12015-10-16 18:04:34 +0900223 }
224
225 /**
sanghoshinf25d2e02015-11-11 23:07:17 +0900226 * Returns the Fixed IP address of the VM.
sanghoshin94872a12015-10-16 18:04:34 +0900227 *
sanghoshinf25d2e02015-11-11 23:07:17 +0900228 * @param portName VM port info
229 * @return IP address of the VM
sanghoshin94872a12015-10-16 18:04:34 +0900230 */
sanghoshinf25d2e02015-11-11 23:07:17 +0900231 private Ip4Address getFixedIpAddressForPort(String portName) {
sanghoshin94872a12015-10-16 18:04:34 +0900232
sanghoshinf25d2e02015-11-11 23:07:17 +0900233 String uuid = portName.substring(3);
234 OpenstackPort port = openstackPortList.stream()
235 .filter(p -> p.id().startsWith(uuid))
236 .findFirst().orElse(null);
sanghoshin94872a12015-10-16 18:04:34 +0900237
sanghoshinf25d2e02015-11-11 23:07:17 +0900238 if (port == null) {
239 log.error("There is no port information for port name {}", portName);
240 return null;
241 }
sanghoshin94872a12015-10-16 18:04:34 +0900242
sanghoshinf25d2e02015-11-11 23:07:17 +0900243 if (port.fixedIps().isEmpty()) {
244 log.error("There is no fixed IP info in the port information");
245 return null;
246 }
247
248 return (Ip4Address) port.fixedIps().values().toArray()[0];
249 }
250
251 /**
252 * Returns the MAC address of the VM of the port.
253 *
254 * @param portName VM port
255 * @return MAC address of the VM
256 */
257 private MacAddress getVmMacAddressForPort(String portName) {
258
259 String uuid = portName.substring(3);
260 OpenstackPort port = openstackPortList.stream()
261 .filter(p -> p.id().startsWith(uuid))
262 .findFirst().orElse(null);
263
264 if (port == null) {
sanghoshin65723ae2015-11-17 22:07:21 +0900265 log.error("There is no port information for port name {}", portName);
sanghoshinf25d2e02015-11-11 23:07:17 +0900266 return null;
267 }
268
269 return port.macAddress();
sanghoshin94872a12015-10-16 18:04:34 +0900270 }
271
Hyunsun Moon74ec4062015-12-09 10:58:32 -0800272 private void setFlowRuleForTunnelTag(DeviceId deviceId, Port port, String vni) {
daniel2a2bd7b2015-12-02 13:53:58 +0900273
274 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
275 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
276
277 sBuilder.matchEthType(Ethernet.TYPE_IPV4)
278 .matchInPort(port.number());
279
280 tBuilder.setTunnelId(Long.parseLong(vni));
281
282 ForwardingObjective fo = DefaultForwardingObjective.builder()
283 .withSelector(sBuilder.build())
284 .withTreatment(tBuilder.build())
285 .withPriority(TUNNELTAG_RULE_PRIORITY)
286 .withFlag(ForwardingObjective.Flag.SPECIFIC)
287 .fromApp(appId)
288 .add();
289
290 flowObjectiveService.forward(deviceId, fo);
291 }
292
293
sanghoshin94872a12015-10-16 18:04:34 +0900294 /**
295 * Sets the flow rules for traffic between VMs in the same Cnode.
296 *
297 * @param ip4Address VM IP address
298 * @param id device ID to put rules
299 * @param port VM port
daniel2a2bd7b2015-12-02 13:53:58 +0900300 * @param vni VM VNI
301 * @param vmMacAddress VM MAC address
sanghoshin94872a12015-10-16 18:04:34 +0900302 */
303 private void setFlowRuleForVMsInSameCnode(Ip4Address ip4Address, DeviceId id,
daniel2a2bd7b2015-12-02 13:53:58 +0900304 Port port, String vni, MacAddress vmMacAddress) {
305
306 //For L2 Switching Case
sanghoshin94872a12015-10-16 18:04:34 +0900307 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
308 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
309
310 sBuilder.matchEthType(Ethernet.TYPE_IPV4)
daniel2a2bd7b2015-12-02 13:53:58 +0900311 .matchIPDst(ip4Address.toIpPrefix())
312 .matchTunnelId(Long.parseLong(vni));
313
sanghoshin94872a12015-10-16 18:04:34 +0900314 tBuilder.setOutput(port.number());
315
316 ForwardingObjective fo = DefaultForwardingObjective.builder()
317 .withSelector(sBuilder.build())
318 .withTreatment(tBuilder.build())
sanghoshinf25d2e02015-11-11 23:07:17 +0900319 .withPriority(SWITCHING_RULE_PRIORITY)
daniel2a2bd7b2015-12-02 13:53:58 +0900320 .withFlag(ForwardingObjective.Flag.SPECIFIC)
321 .fromApp(appId)
322 .add();
323
324 flowObjectiveService.forward(id, fo);
sanghoshin94872a12015-10-16 18:04:34 +0900325 }
326
daniel2a2bd7b2015-12-02 13:53:58 +0900327
sanghoshin94872a12015-10-16 18:04:34 +0900328 /**
329 * Sets the flow rules between traffic from VMs in different Cnode.
330 *
331 * @param vni VNI
332 * @param id device ID
333 * @param hostIp host IP of the VM
334 * @param vmIp fixed IP of the VM
335 * @param vmMac MAC address of the VM
sanghoshin94872a12015-10-16 18:04:34 +0900336 */
337 private void setVxLanFlowRule(String vni, DeviceId id, Ip4Address hostIp,
sanghoshinf25d2e02015-11-11 23:07:17 +0900338 Ip4Address vmIp, MacAddress vmMac) {
sanghoshin94872a12015-10-16 18:04:34 +0900339 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
340 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
341
342 sBuilder.matchEthType(Ethernet.TYPE_IPV4)
daniel2a2bd7b2015-12-02 13:53:58 +0900343 .matchTunnelId(Long.parseLong(vni))
sanghoshin94872a12015-10-16 18:04:34 +0900344 .matchIPDst(vmIp.toIpPrefix());
daniel2a2bd7b2015-12-02 13:53:58 +0900345
346 tBuilder.extension(buildNiciraExtenstion(id, hostIp), id)
sanghoshinf25d2e02015-11-11 23:07:17 +0900347 .setOutput(getTunnelPort(id));
sanghoshin94872a12015-10-16 18:04:34 +0900348
349 ForwardingObjective fo = DefaultForwardingObjective.builder()
350 .withSelector(sBuilder.build())
351 .withTreatment(tBuilder.build())
sanghoshinf25d2e02015-11-11 23:07:17 +0900352 .withPriority(SWITCHING_RULE_PRIORITY)
daniel2a2bd7b2015-12-02 13:53:58 +0900353 .withFlag(ForwardingObjective.Flag.SPECIFIC)
sanghoshin94872a12015-10-16 18:04:34 +0900354 .fromApp(appId)
355 .add();
356
357 flowObjectiveService.forward(id, fo);
358 }
sanghoshinf25d2e02015-11-11 23:07:17 +0900359
sanghoshin65723ae2015-11-17 22:07:21 +0900360 private void removeFlowRuleForVMsInSameCnode(DeviceId id, Ip4Address vmIp) {
361 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
362
363 sBuilder.matchEthType(Ethernet.TYPE_IPV4)
364 .matchIPDst(vmIp.toIpPrefix());
365
366 ForwardingObjective fo = DefaultForwardingObjective.builder()
367 .withSelector(sBuilder.build())
368 .withTreatment(DefaultTrafficTreatment.builder().build())
369 .withFlag(ForwardingObjective.Flag.VERSATILE)
370 .withPriority(SWITCHING_RULE_PRIORITY)
371 .fromApp(appId)
372 .remove();
373
374 flowObjectiveService.forward(id, fo);
375 }
376
377 private void removeVxLanFlowRule(DeviceId id, Ip4Address vmIp) {
378 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
379 // XXX: Later, more matches will be added when multiple table is implemented.
380 sBuilder.matchEthType(Ethernet.TYPE_IPV4)
381 .matchIPDst(vmIp.toIpPrefix());
382
383 ForwardingObjective fo = DefaultForwardingObjective.builder()
384 .withSelector(sBuilder.build())
385 .withTreatment(DefaultTrafficTreatment.builder().build())
386 .withFlag(ForwardingObjective.Flag.VERSATILE)
387 .withPriority(SWITCHING_RULE_PRIORITY)
388 .fromApp(appId)
389 .remove();
390
391 flowObjectiveService.forward(id, fo);
392 }
393
sanghoshinf25d2e02015-11-11 23:07:17 +0900394 private ExtensionTreatment buildNiciraExtenstion(DeviceId id, Ip4Address hostIp) {
395 Driver driver = driverService.getDriver(id);
396 DriverHandler driverHandler = new DefaultDriverHandler(new DefaultDriverData(driver, id));
397 ExtensionTreatmentResolver resolver = driverHandler.behaviour(ExtensionTreatmentResolver.class);
398
399 ExtensionTreatment extensionInstruction =
400 resolver.getExtensionInstruction(
401 ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_SET_TUNNEL_DST.type());
402
403 try {
404 extensionInstruction.setPropertyValue("tunnelDst", hostIp);
405 } catch (ExtensionPropertyException e) {
406 log.error("Error setting Nicira extension setting {}", e);
407 }
408
409 return extensionInstruction;
410 }
411
412 private PortNumber getTunnelPort(DeviceId id) {
413 Port port = deviceService.getPorts(id).stream()
414 .filter(p -> p.annotations().value("portName").equals("vxlan"))
415 .findAny().orElse(null);
416
417 if (port == null) {
418 log.error("No TunnelPort was created.");
419 return null;
420 }
421 return port.number();
422 }
sanghoshin94872a12015-10-16 18:04:34 +0900423}