blob: 3d1e3fbfd6ae3c132047b00ab0f607db2351f6e2 [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);
sanghoshinf25d2e02015-11-11 23:07:17 +090056 private static final int SWITCHING_RULE_PRIORITY = 50000;
sanghoshin94872a12015-10-16 18:04:34 +090057
58 private FlowObjectiveService flowObjectiveService;
sanghoshinf25d2e02015-11-11 23:07:17 +090059 private DriverService driverService;
60 private DeviceService deviceService;
61 private OpenstackRestHandler restHandler;
sanghoshin94872a12015-10-16 18:04:34 +090062 private ApplicationId appId;
63
sanghoshinf25d2e02015-11-11 23:07:17 +090064 private Collection<OpenstackNetwork> openstackNetworkList;
65 private Collection<OpenstackPort> openstackPortList;
66
sanghoshin94872a12015-10-16 18:04:34 +090067 /**
Ray Milkey87b16b02015-10-30 11:50:00 -070068 * Creates OpenstackSwitchingRulPopulator.
69 *
70 * @param appId application id
sanghoshin94872a12015-10-16 18:04:34 +090071 * @param flowObjectiveService FlowObjectiveService reference
sanghoshinf25d2e02015-11-11 23:07:17 +090072 * @param deviceService DeviceService reference
73 * @param driverService DriverService reference
sanghoshin94872a12015-10-16 18:04:34 +090074 */
75 public OpenstackSwitchingRulePopulator(ApplicationId appId,
sanghoshinf25d2e02015-11-11 23:07:17 +090076 FlowObjectiveService flowObjectiveService,
77 DeviceService deviceService,
78 OpenstackRestHandler restHandler,
79 DriverService driverService) {
sanghoshin94872a12015-10-16 18:04:34 +090080 this.flowObjectiveService = flowObjectiveService;
sanghoshinf25d2e02015-11-11 23:07:17 +090081 this.deviceService = deviceService;
82 this.driverService = driverService;
83 this.restHandler = restHandler;
sanghoshin94872a12015-10-16 18:04:34 +090084 this.appId = appId;
sanghoshinf25d2e02015-11-11 23:07:17 +090085
86 openstackNetworkList = restHandler.getNetworks();
87 openstackPortList = restHandler.getPorts();
sanghoshin94872a12015-10-16 18:04:34 +090088 }
89
90 /**
sanghoshinf25d2e02015-11-11 23:07:17 +090091 * Populates flow rules for the VM created.
sanghoshin94872a12015-10-16 18:04:34 +090092 *
sanghoshinf25d2e02015-11-11 23:07:17 +090093 * @param device device to populate rules to
94 * @param port port for the VM created
sanghoshin94872a12015-10-16 18:04:34 +090095 */
sanghoshinf25d2e02015-11-11 23:07:17 +090096 public void populateSwitchingRules(Device device, Port port) {
97 populateFlowRulesForTrafficToSameCnode(device, port);
98 populateFlowRulesForTrafficToDifferentCnode(device, port);
sanghoshin94872a12015-10-16 18:04:34 +090099 }
100
101 /**
sanghoshin075e3e72015-11-25 16:34:29 +0900102 * Returns OpenstackPort object for the Port reference given.
103 *
104 * @param port Port object
105 * @return OpenstackPort reference, or null when not found
106 */
107 public OpenstackPort openstackPort(Port port) {
108 String uuid = port.annotations().value("portName").substring(3);
109 return openstackPortList.stream().filter(p -> p.id().startsWith(uuid))
110 .findAny().orElse(null);
111 }
112
113 /**
sanghoshinf25d2e02015-11-11 23:07:17 +0900114 * Populates the flow rules for traffic to VMs in the same Cnode as the sender.
sanghoshin94872a12015-10-16 18:04:34 +0900115 *
sanghoshinf25d2e02015-11-11 23:07:17 +0900116 * @param device device to put the rules
117 * @param port port info of the VM
sanghoshin94872a12015-10-16 18:04:34 +0900118 */
sanghoshinf25d2e02015-11-11 23:07:17 +0900119 private void populateFlowRulesForTrafficToSameCnode(Device device, Port port) {
120 Ip4Address vmIp = getFixedIpAddressForPort(port.annotations().value("portName"));
121 if (vmIp != null) {
122 setFlowRuleForVMsInSameCnode(vmIp, device.id(), port);
123 }
sanghoshin94872a12015-10-16 18:04:34 +0900124 }
125
126 /**
sanghoshinf25d2e02015-11-11 23:07:17 +0900127 * Populates the flow rules for traffic to VMs in different Cnode using
128 * Nicira extention.
sanghoshin94872a12015-10-16 18:04:34 +0900129 *
sanghoshinf25d2e02015-11-11 23:07:17 +0900130 * @param device device to put rules
131 * @param port port information of the VM
sanghoshin94872a12015-10-16 18:04:34 +0900132 */
sanghoshinf25d2e02015-11-11 23:07:17 +0900133 private void populateFlowRulesForTrafficToDifferentCnode(Device device, Port port) {
134 String portName = port.annotations().value("portName");
135 String channelId = device.annotations().value("channelId");
136 Ip4Address hostIpAddress = Ip4Address.valueOf(channelId.split(":")[0]);
137 Ip4Address fixedIp = getFixedIpAddressForPort(portName);
138 MacAddress vmMac = getVmMacAddressForPort(portName);
139 String vni = getVniForPort(portName);
140 deviceService.getAvailableDevices().forEach(d -> {
141 if (!d.equals(device)) {
142 deviceService.getPorts(d.id()).forEach(p -> {
143 String pName = p.annotations().value("portName");
144 if (!p.equals(port) && vni.equals(getVniForPort(pName))) {
145 String cidx = d.annotations().value("channelId");
146 Ip4Address hostIpx = Ip4Address.valueOf(cidx.split(":")[0]);
147 MacAddress vmMacx = getVmMacAddressForPort(pName);
148 Ip4Address fixedIpx = getFixedIpAddressForPort(pName);
149
150 setVxLanFlowRule(vni, device.id(), hostIpx, fixedIpx, vmMacx);
151 setVxLanFlowRule(vni, d.id(), hostIpAddress, fixedIp, vmMac);
152 }
153 });
154 }
155 });
sanghoshin94872a12015-10-16 18:04:34 +0900156 }
157
158 /**
sanghoshinf25d2e02015-11-11 23:07:17 +0900159 * Returns the VNI of the VM of the port.
sanghoshin94872a12015-10-16 18:04:34 +0900160 *
sanghoshinf25d2e02015-11-11 23:07:17 +0900161 * @param portName VM port
162 * @return VNI
sanghoshin94872a12015-10-16 18:04:34 +0900163 */
sanghoshinf25d2e02015-11-11 23:07:17 +0900164 private String getVniForPort(String portName) {
165 String uuid = portName.substring(3);
166 OpenstackPort port = openstackPortList.stream()
167 .filter(p -> p.id().startsWith(uuid))
168 .findAny().orElse(null);
169 if (port == null) {
170 log.warn("No port information for port {}", portName);
171 return null;
172 }
sanghoshin94872a12015-10-16 18:04:34 +0900173
sanghoshinf25d2e02015-11-11 23:07:17 +0900174 OpenstackNetwork network = openstackNetworkList.stream()
175 .filter(n -> n.id().equals(port.networkId()))
176 .findAny().orElse(null);
177 if (network == null) {
Satish K2e2d6352015-11-27 12:02:15 +0530178 log.warn("No VNI information for network {}", port.networkId());
sanghoshinf25d2e02015-11-11 23:07:17 +0900179 return null;
180 }
sanghoshin94872a12015-10-16 18:04:34 +0900181
sanghoshinf25d2e02015-11-11 23:07:17 +0900182 return network.segmentId();
sanghoshin94872a12015-10-16 18:04:34 +0900183 }
184
185 /**
sanghoshinf25d2e02015-11-11 23:07:17 +0900186 * Returns the Fixed IP address of the VM.
sanghoshin94872a12015-10-16 18:04:34 +0900187 *
sanghoshinf25d2e02015-11-11 23:07:17 +0900188 * @param portName VM port info
189 * @return IP address of the VM
sanghoshin94872a12015-10-16 18:04:34 +0900190 */
sanghoshinf25d2e02015-11-11 23:07:17 +0900191 private Ip4Address getFixedIpAddressForPort(String portName) {
sanghoshin94872a12015-10-16 18:04:34 +0900192
sanghoshinf25d2e02015-11-11 23:07:17 +0900193 String uuid = portName.substring(3);
194 OpenstackPort port = openstackPortList.stream()
195 .filter(p -> p.id().startsWith(uuid))
196 .findFirst().orElse(null);
sanghoshin94872a12015-10-16 18:04:34 +0900197
sanghoshinf25d2e02015-11-11 23:07:17 +0900198 if (port == null) {
199 log.error("There is no port information for port name {}", portName);
200 return null;
201 }
sanghoshin94872a12015-10-16 18:04:34 +0900202
sanghoshinf25d2e02015-11-11 23:07:17 +0900203 if (port.fixedIps().isEmpty()) {
204 log.error("There is no fixed IP info in the port information");
205 return null;
206 }
207
208 return (Ip4Address) port.fixedIps().values().toArray()[0];
209 }
210
211 /**
212 * Returns the MAC address of the VM of the port.
213 *
214 * @param portName VM port
215 * @return MAC address of the VM
216 */
217 private MacAddress getVmMacAddressForPort(String portName) {
218
219 String uuid = portName.substring(3);
220 OpenstackPort port = openstackPortList.stream()
221 .filter(p -> p.id().startsWith(uuid))
222 .findFirst().orElse(null);
223
224 if (port == null) {
225 log.error("There is port information for port name {}", portName);
226 return null;
227 }
228
229 return port.macAddress();
sanghoshin94872a12015-10-16 18:04:34 +0900230 }
231
232 /**
233 * Sets the flow rules for traffic between VMs in the same Cnode.
234 *
235 * @param ip4Address VM IP address
236 * @param id device ID to put rules
237 * @param port VM port
sanghoshin94872a12015-10-16 18:04:34 +0900238 */
239 private void setFlowRuleForVMsInSameCnode(Ip4Address ip4Address, DeviceId id,
sanghoshinf25d2e02015-11-11 23:07:17 +0900240 Port port) {
sanghoshin94872a12015-10-16 18:04:34 +0900241 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
242 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
243
244 sBuilder.matchEthType(Ethernet.TYPE_IPV4)
sanghoshinf25d2e02015-11-11 23:07:17 +0900245 .matchIPDst(ip4Address.toIpPrefix());
sanghoshin94872a12015-10-16 18:04:34 +0900246 tBuilder.setOutput(port.number());
247
248 ForwardingObjective fo = DefaultForwardingObjective.builder()
249 .withSelector(sBuilder.build())
250 .withTreatment(tBuilder.build())
sanghoshinf25d2e02015-11-11 23:07:17 +0900251 .withPriority(SWITCHING_RULE_PRIORITY)
sanghoshin94872a12015-10-16 18:04:34 +0900252 .withFlag(ForwardingObjective.Flag.VERSATILE)
253 .fromApp(appId)
254 .add();
255
256 flowObjectiveService.forward(id, fo);
257 }
258
259 /**
260 * Sets the flow rules between traffic from VMs in different Cnode.
261 *
262 * @param vni VNI
263 * @param id device ID
264 * @param hostIp host IP of the VM
265 * @param vmIp fixed IP of the VM
266 * @param vmMac MAC address of the VM
sanghoshin94872a12015-10-16 18:04:34 +0900267 */
268 private void setVxLanFlowRule(String vni, DeviceId id, Ip4Address hostIp,
sanghoshinf25d2e02015-11-11 23:07:17 +0900269 Ip4Address vmIp, MacAddress vmMac) {
sanghoshin94872a12015-10-16 18:04:34 +0900270 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
271 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
272
273 sBuilder.matchEthType(Ethernet.TYPE_IPV4)
274 .matchIPDst(vmIp.toIpPrefix());
275 tBuilder.setTunnelId(Long.parseLong(vni))
sanghoshinf25d2e02015-11-11 23:07:17 +0900276 .extension(buildNiciraExtenstion(id, hostIp), id)
277 .setOutput(getTunnelPort(id));
sanghoshin94872a12015-10-16 18:04:34 +0900278
279 ForwardingObjective fo = DefaultForwardingObjective.builder()
280 .withSelector(sBuilder.build())
281 .withTreatment(tBuilder.build())
sanghoshinf25d2e02015-11-11 23:07:17 +0900282 .withPriority(SWITCHING_RULE_PRIORITY)
sanghoshin94872a12015-10-16 18:04:34 +0900283 .withFlag(ForwardingObjective.Flag.VERSATILE)
284 .fromApp(appId)
285 .add();
286
287 flowObjectiveService.forward(id, fo);
288 }
sanghoshinf25d2e02015-11-11 23:07:17 +0900289
290 private ExtensionTreatment buildNiciraExtenstion(DeviceId id, Ip4Address hostIp) {
291 Driver driver = driverService.getDriver(id);
292 DriverHandler driverHandler = new DefaultDriverHandler(new DefaultDriverData(driver, id));
293 ExtensionTreatmentResolver resolver = driverHandler.behaviour(ExtensionTreatmentResolver.class);
294
295 ExtensionTreatment extensionInstruction =
296 resolver.getExtensionInstruction(
297 ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_SET_TUNNEL_DST.type());
298
299 try {
300 extensionInstruction.setPropertyValue("tunnelDst", hostIp);
301 } catch (ExtensionPropertyException e) {
302 log.error("Error setting Nicira extension setting {}", e);
303 }
304
305 return extensionInstruction;
306 }
307
308 private PortNumber getTunnelPort(DeviceId id) {
309 Port port = deviceService.getPorts(id).stream()
310 .filter(p -> p.annotations().value("portName").equals("vxlan"))
311 .findAny().orElse(null);
312
313 if (port == null) {
314 log.error("No TunnelPort was created.");
315 return null;
316 }
317 return port.number();
318 }
sanghoshin94872a12015-10-16 18:04:34 +0900319}