blob: bba8f7778a730864500ec1f9665692aa5a097d53 [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) {
daniel2a2bd7b2015-12-02 13:53:58 +0900100 populateFlowRulesForVMSetTunnelTag(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 */
111 private void populateFlowRulesForVMSetTunnelTag(Device device, Port port) {
112 Ip4Address vmIp = getFixedIpAddressForPort(port.annotations().value("portName"));
113 String portName = port.annotations().value("portName");
114 String vni = getVniForPort(portName);
115
116 if (vmIp != null) {
117 setFlowRuleForVMSetTunnelTag(device.id(), port, vni);
118 }
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 /**
sanghoshinf25d2e02015-11-11 23:07:17 +0900134 * Populates the flow rules for traffic to VMs in the same Cnode as the sender.
sanghoshin94872a12015-10-16 18:04:34 +0900135 *
sanghoshinf25d2e02015-11-11 23:07:17 +0900136 * @param device device to put the rules
137 * @param port port info of the VM
sanghoshin94872a12015-10-16 18:04:34 +0900138 */
sanghoshinf25d2e02015-11-11 23:07:17 +0900139 private void populateFlowRulesForTrafficToSameCnode(Device device, Port port) {
140 Ip4Address vmIp = getFixedIpAddressForPort(port.annotations().value("portName"));
daniel2a2bd7b2015-12-02 13:53:58 +0900141 String portName = port.annotations().value("portName");
142 String vni = getVniForPort(portName);
143 MacAddress vmMacAddress = getVmMacAddressForPort(portName);
144
sanghoshinf25d2e02015-11-11 23:07:17 +0900145 if (vmIp != null) {
daniel2a2bd7b2015-12-02 13:53:58 +0900146 setFlowRuleForVMsInSameCnode(vmIp, device.id(), port, vni, vmMacAddress);
sanghoshinf25d2e02015-11-11 23:07:17 +0900147 }
sanghoshin94872a12015-10-16 18:04:34 +0900148 }
149
150 /**
sanghoshinf25d2e02015-11-11 23:07:17 +0900151 * Populates the flow rules for traffic to VMs in different Cnode using
152 * Nicira extention.
sanghoshin94872a12015-10-16 18:04:34 +0900153 *
sanghoshinf25d2e02015-11-11 23:07:17 +0900154 * @param device device to put rules
155 * @param port port information of the VM
sanghoshin94872a12015-10-16 18:04:34 +0900156 */
sanghoshinf25d2e02015-11-11 23:07:17 +0900157 private void populateFlowRulesForTrafficToDifferentCnode(Device device, Port port) {
158 String portName = port.annotations().value("portName");
159 String channelId = device.annotations().value("channelId");
160 Ip4Address hostIpAddress = Ip4Address.valueOf(channelId.split(":")[0]);
161 Ip4Address fixedIp = getFixedIpAddressForPort(portName);
162 MacAddress vmMac = getVmMacAddressForPort(portName);
163 String vni = getVniForPort(portName);
164 deviceService.getAvailableDevices().forEach(d -> {
165 if (!d.equals(device)) {
166 deviceService.getPorts(d.id()).forEach(p -> {
167 String pName = p.annotations().value("portName");
168 if (!p.equals(port) && vni.equals(getVniForPort(pName))) {
169 String cidx = d.annotations().value("channelId");
170 Ip4Address hostIpx = Ip4Address.valueOf(cidx.split(":")[0]);
171 MacAddress vmMacx = getVmMacAddressForPort(pName);
172 Ip4Address fixedIpx = getFixedIpAddressForPort(pName);
173
174 setVxLanFlowRule(vni, device.id(), hostIpx, fixedIpx, vmMacx);
175 setVxLanFlowRule(vni, d.id(), hostIpAddress, fixedIp, vmMac);
176 }
177 });
178 }
179 });
sanghoshin94872a12015-10-16 18:04:34 +0900180 }
181
182 /**
sanghoshinf25d2e02015-11-11 23:07:17 +0900183 * Returns the VNI of the VM of the port.
sanghoshin94872a12015-10-16 18:04:34 +0900184 *
sanghoshinf25d2e02015-11-11 23:07:17 +0900185 * @param portName VM port
186 * @return VNI
sanghoshin94872a12015-10-16 18:04:34 +0900187 */
sanghoshinf25d2e02015-11-11 23:07:17 +0900188 private String getVniForPort(String portName) {
189 String uuid = portName.substring(3);
190 OpenstackPort port = openstackPortList.stream()
191 .filter(p -> p.id().startsWith(uuid))
192 .findAny().orElse(null);
193 if (port == null) {
daniel2a2bd7b2015-12-02 13:53:58 +0900194 log.debug("No port information for port {}", portName);
sanghoshinf25d2e02015-11-11 23:07:17 +0900195 return null;
196 }
sanghoshin94872a12015-10-16 18:04:34 +0900197
sanghoshinf25d2e02015-11-11 23:07:17 +0900198 OpenstackNetwork network = openstackNetworkList.stream()
199 .filter(n -> n.id().equals(port.networkId()))
200 .findAny().orElse(null);
201 if (network == null) {
Satish K2e2d6352015-11-27 12:02:15 +0530202 log.warn("No VNI information for network {}", port.networkId());
sanghoshinf25d2e02015-11-11 23:07:17 +0900203 return null;
204 }
sanghoshin94872a12015-10-16 18:04:34 +0900205
sanghoshinf25d2e02015-11-11 23:07:17 +0900206 return network.segmentId();
sanghoshin94872a12015-10-16 18:04:34 +0900207 }
208
209 /**
sanghoshinf25d2e02015-11-11 23:07:17 +0900210 * Returns the Fixed IP address of the VM.
sanghoshin94872a12015-10-16 18:04:34 +0900211 *
sanghoshinf25d2e02015-11-11 23:07:17 +0900212 * @param portName VM port info
213 * @return IP address of the VM
sanghoshin94872a12015-10-16 18:04:34 +0900214 */
sanghoshinf25d2e02015-11-11 23:07:17 +0900215 private Ip4Address getFixedIpAddressForPort(String portName) {
sanghoshin94872a12015-10-16 18:04:34 +0900216
sanghoshinf25d2e02015-11-11 23:07:17 +0900217 String uuid = portName.substring(3);
218 OpenstackPort port = openstackPortList.stream()
219 .filter(p -> p.id().startsWith(uuid))
220 .findFirst().orElse(null);
sanghoshin94872a12015-10-16 18:04:34 +0900221
sanghoshinf25d2e02015-11-11 23:07:17 +0900222 if (port == null) {
223 log.error("There is no port information for port name {}", portName);
224 return null;
225 }
sanghoshin94872a12015-10-16 18:04:34 +0900226
sanghoshinf25d2e02015-11-11 23:07:17 +0900227 if (port.fixedIps().isEmpty()) {
228 log.error("There is no fixed IP info in the port information");
229 return null;
230 }
231
232 return (Ip4Address) port.fixedIps().values().toArray()[0];
233 }
234
235 /**
236 * Returns the MAC address of the VM of the port.
237 *
238 * @param portName VM port
239 * @return MAC address of the VM
240 */
241 private MacAddress getVmMacAddressForPort(String portName) {
242
243 String uuid = portName.substring(3);
244 OpenstackPort port = openstackPortList.stream()
245 .filter(p -> p.id().startsWith(uuid))
246 .findFirst().orElse(null);
247
248 if (port == null) {
249 log.error("There is port information for port name {}", portName);
250 return null;
251 }
252
253 return port.macAddress();
sanghoshin94872a12015-10-16 18:04:34 +0900254 }
255
daniel2a2bd7b2015-12-02 13:53:58 +0900256 private void setFlowRuleForVMSetTunnelTag(DeviceId deviceId, Port port, String vni) {
257
258 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
259 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
260
261 sBuilder.matchEthType(Ethernet.TYPE_IPV4)
262 .matchInPort(port.number());
263
264 tBuilder.setTunnelId(Long.parseLong(vni));
265
266 ForwardingObjective fo = DefaultForwardingObjective.builder()
267 .withSelector(sBuilder.build())
268 .withTreatment(tBuilder.build())
269 .withPriority(TUNNELTAG_RULE_PRIORITY)
270 .withFlag(ForwardingObjective.Flag.SPECIFIC)
271 .fromApp(appId)
272 .add();
273
274 flowObjectiveService.forward(deviceId, fo);
275 }
276
277
sanghoshin94872a12015-10-16 18:04:34 +0900278 /**
279 * Sets the flow rules for traffic between VMs in the same Cnode.
280 *
281 * @param ip4Address VM IP address
282 * @param id device ID to put rules
283 * @param port VM port
daniel2a2bd7b2015-12-02 13:53:58 +0900284 * @param vni VM VNI
285 * @param vmMacAddress VM MAC address
sanghoshin94872a12015-10-16 18:04:34 +0900286 */
287 private void setFlowRuleForVMsInSameCnode(Ip4Address ip4Address, DeviceId id,
daniel2a2bd7b2015-12-02 13:53:58 +0900288 Port port, String vni, MacAddress vmMacAddress) {
289
290 //For L2 Switching Case
sanghoshin94872a12015-10-16 18:04:34 +0900291 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
292 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
293
294 sBuilder.matchEthType(Ethernet.TYPE_IPV4)
daniel2a2bd7b2015-12-02 13:53:58 +0900295 .matchIPDst(ip4Address.toIpPrefix())
296 .matchTunnelId(Long.parseLong(vni));
297
sanghoshin94872a12015-10-16 18:04:34 +0900298 tBuilder.setOutput(port.number());
299
300 ForwardingObjective fo = DefaultForwardingObjective.builder()
301 .withSelector(sBuilder.build())
302 .withTreatment(tBuilder.build())
sanghoshinf25d2e02015-11-11 23:07:17 +0900303 .withPriority(SWITCHING_RULE_PRIORITY)
daniel2a2bd7b2015-12-02 13:53:58 +0900304 .withFlag(ForwardingObjective.Flag.SPECIFIC)
305 .fromApp(appId)
306 .add();
307
308 flowObjectiveService.forward(id, fo);
309
310 //For L3 Ease-West Routing Case
311 sBuilder = DefaultTrafficSelector.builder();
312 tBuilder = DefaultTrafficTreatment.builder();
313
314 sBuilder.matchEthType(Ethernet.TYPE_IPV4)
315 .matchIPDst(ip4Address.toIpPrefix());
316
317 tBuilder.setEthDst(vmMacAddress)
318 .setOutput(port.number());
319
320 fo = DefaultForwardingObjective.builder()
321 .withSelector(sBuilder.build())
322 .withTreatment(tBuilder.build())
323 .withPriority(EAST_WEST_ROUTING_RULE_PRIORITY)
324 .withFlag(ForwardingObjective.Flag.SPECIFIC)
sanghoshin94872a12015-10-16 18:04:34 +0900325 .fromApp(appId)
326 .add();
327
328 flowObjectiveService.forward(id, fo);
329 }
330
daniel2a2bd7b2015-12-02 13:53:58 +0900331
sanghoshin94872a12015-10-16 18:04:34 +0900332 /**
333 * Sets the flow rules between traffic from VMs in different Cnode.
334 *
335 * @param vni VNI
336 * @param id device ID
337 * @param hostIp host IP of the VM
338 * @param vmIp fixed IP of the VM
339 * @param vmMac MAC address of the VM
sanghoshin94872a12015-10-16 18:04:34 +0900340 */
341 private void setVxLanFlowRule(String vni, DeviceId id, Ip4Address hostIp,
sanghoshinf25d2e02015-11-11 23:07:17 +0900342 Ip4Address vmIp, MacAddress vmMac) {
sanghoshin94872a12015-10-16 18:04:34 +0900343 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
344 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
345
346 sBuilder.matchEthType(Ethernet.TYPE_IPV4)
daniel2a2bd7b2015-12-02 13:53:58 +0900347 .matchTunnelId(Long.parseLong(vni))
sanghoshin94872a12015-10-16 18:04:34 +0900348 .matchIPDst(vmIp.toIpPrefix());
daniel2a2bd7b2015-12-02 13:53:58 +0900349
350 tBuilder.extension(buildNiciraExtenstion(id, hostIp), id)
sanghoshinf25d2e02015-11-11 23:07:17 +0900351 .setOutput(getTunnelPort(id));
sanghoshin94872a12015-10-16 18:04:34 +0900352
353 ForwardingObjective fo = DefaultForwardingObjective.builder()
354 .withSelector(sBuilder.build())
355 .withTreatment(tBuilder.build())
sanghoshinf25d2e02015-11-11 23:07:17 +0900356 .withPriority(SWITCHING_RULE_PRIORITY)
daniel2a2bd7b2015-12-02 13:53:58 +0900357 .withFlag(ForwardingObjective.Flag.SPECIFIC)
sanghoshin94872a12015-10-16 18:04:34 +0900358 .fromApp(appId)
359 .add();
360
361 flowObjectiveService.forward(id, fo);
362 }
sanghoshinf25d2e02015-11-11 23:07:17 +0900363
364 private ExtensionTreatment buildNiciraExtenstion(DeviceId id, Ip4Address hostIp) {
365 Driver driver = driverService.getDriver(id);
366 DriverHandler driverHandler = new DefaultDriverHandler(new DefaultDriverData(driver, id));
367 ExtensionTreatmentResolver resolver = driverHandler.behaviour(ExtensionTreatmentResolver.class);
368
369 ExtensionTreatment extensionInstruction =
370 resolver.getExtensionInstruction(
371 ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_SET_TUNNEL_DST.type());
372
373 try {
374 extensionInstruction.setPropertyValue("tunnelDst", hostIp);
375 } catch (ExtensionPropertyException e) {
376 log.error("Error setting Nicira extension setting {}", e);
377 }
378
379 return extensionInstruction;
380 }
381
382 private PortNumber getTunnelPort(DeviceId id) {
383 Port port = deviceService.getPorts(id).stream()
384 .filter(p -> p.annotations().value("portName").equals("vxlan"))
385 .findAny().orElse(null);
386
387 if (port == null) {
388 log.error("No TunnelPort was created.");
389 return null;
390 }
391 return port.number();
392 }
sanghoshin94872a12015-10-16 18:04:34 +0900393}