blob: cc0fecd4f994718c918b6b4ecbba677fd9469527 [file] [log] [blame]
sangho0c2a3da2016-02-16 13:39:07 +09001/*
2 * Copyright 2016 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 */
16package org.onosproject.openstacknetworking.routing;
17
18import org.onlab.packet.Ethernet;
19import org.onlab.packet.IPv4;
20import org.onlab.packet.Ip4Address;
21import org.onlab.packet.IpAddress;
22import org.onlab.packet.IpPrefix;
23import org.onlab.packet.MacAddress;
24import org.onlab.packet.TCP;
25import org.onlab.packet.TpPort;
26import org.onlab.packet.UDP;
27import org.onosproject.core.ApplicationId;
28import org.onosproject.net.Device;
29import org.onosproject.net.DeviceId;
30import org.onosproject.net.Port;
31import org.onosproject.net.PortNumber;
32import org.onosproject.net.behaviour.ExtensionTreatmentResolver;
33import org.onosproject.net.device.DeviceService;
34import org.onosproject.net.driver.DefaultDriverData;
35import org.onosproject.net.driver.DefaultDriverHandler;
36import org.onosproject.net.driver.Driver;
37import org.onosproject.net.driver.DriverHandler;
38import org.onosproject.net.driver.DriverService;
39import org.onosproject.net.flow.DefaultTrafficSelector;
40import org.onosproject.net.flow.DefaultTrafficTreatment;
41import org.onosproject.net.flow.TrafficSelector;
42import org.onosproject.net.flow.TrafficTreatment;
43import org.onosproject.net.flow.instructions.ExtensionPropertyException;
44import org.onosproject.net.flow.instructions.ExtensionTreatment;
45import org.onosproject.net.flow.instructions.ExtensionTreatmentType;
46import org.onosproject.net.flowobjective.DefaultForwardingObjective;
47import org.onosproject.net.flowobjective.FlowObjectiveService;
48import org.onosproject.net.flowobjective.ForwardingObjective;
49import org.onosproject.net.packet.InboundPacket;
50import org.onosproject.openstacknetworking.OpenstackNetworkingService;
51import org.onosproject.openstacknetworking.OpenstackPort;
52import org.onosproject.openstacknetworking.OpenstackRouter;
53import org.onosproject.openstacknetworking.OpenstackRouterInterface;
Kyuhwi Choie2b37e32016-02-05 14:04:14 +090054import org.onosproject.openstacknetworking.OpenstackSubnet;
sangho0c2a3da2016-02-16 13:39:07 +090055import org.slf4j.Logger;
56import org.slf4j.LoggerFactory;
57
58import java.util.stream.StreamSupport;
59
60import static com.google.common.base.Preconditions.checkNotNull;
61
62/**
63 * Populates Routing Flow Rules.
64 */
65public class OpenstackRoutingRulePopulator {
66
Kyuhwi Choie2b37e32016-02-05 14:04:14 +090067 private final Logger log = LoggerFactory.getLogger(getClass());
sangho0c2a3da2016-02-16 13:39:07 +090068
Kyuhwi Choie2b37e32016-02-05 14:04:14 +090069 private final ApplicationId appId;
70 private final FlowObjectiveService flowObjectiveService;
71 private final OpenstackNetworkingService openstackService;
72 private final DeviceService deviceService;
73 private final DriverService driverService;
sangho0c2a3da2016-02-16 13:39:07 +090074
Kyuhwi Choie2b37e32016-02-05 14:04:14 +090075 private static final String PORTNAME_PREFIX_VM = "tap";
76 private static final String PORTNAME_PREFIX_ROUTER = "qr";
77 private static final String PORTNAME_PREFIX_TUNNEL = "vxlan";
78 private static final String PORTNAME = "portName";
79
80 private static final String PORTNOTNULL = "Port can not be null";
81 private static final String TUNNEL_DESTINATION = "tunnelDst";
82 private static final String DEVICE_ANNOTATION_CHANNELID = "channelId";
sangho0c2a3da2016-02-16 13:39:07 +090083 private static final int ROUTING_RULE_PRIORITY = 25000;
84 private static final int PNAT_RULE_PRIORITY = 24000;
85 private static final int PNAT_TIMEOUT = 120;
86 private static final MacAddress GATEWAYMAC = MacAddress.valueOf("1f:1f:1f:1f:1f:1f");
87
88 private InboundPacket inboundPacket;
89 private OpenstackPort openstackPort;
90 private int portNum;
91 private MacAddress externalInterface;
92 private MacAddress externalRouter;
93 private OpenstackRouter router;
94 private OpenstackRouterInterface routerInterface;
95
96 // TODO: This will be replaced to get the information from openstackswitchingservice.
Kyuhwi Choie2b37e32016-02-05 14:04:14 +090097 private static final String EXTERNAL_INTERFACE_NAME = "veth0";
sangho0c2a3da2016-02-16 13:39:07 +090098
Kyuhwi Choie2b37e32016-02-05 14:04:14 +090099 /**
100 * The constructor of openstackRoutingRulePopulator.
101 *
102 * @param appId Caller`s appId
103 * @param openstackService OpenstackNetworkingService
104 * @param flowObjectiveService FlowObjectiveService
105 * @param deviceService DeviceService
106 * @param driverService DriverService
107 */
sangho0c2a3da2016-02-16 13:39:07 +0900108 public OpenstackRoutingRulePopulator(ApplicationId appId, OpenstackNetworkingService openstackService,
109 FlowObjectiveService flowObjectiveService,
110 DeviceService deviceService, DriverService driverService) {
111 this.appId = appId;
112 this.flowObjectiveService = flowObjectiveService;
113 this.openstackService = openstackService;
114 this.deviceService = deviceService;
115 this.driverService = driverService;
116 }
117
Kyuhwi Choie2b37e32016-02-05 14:04:14 +0900118 /**
119 * Populates flow rules for Pnat configurations.
120 * @param inboundPacket Packet-in event packet
121 * @param openstackPort Target VM information
122 * @param portNum Pnat port number
123 * @param externalIp
124 * @param externalInterfaceMacAddress Gateway external interface macaddress
125 * @param externalRouterMacAddress Outer(physical) router`s macaddress
126 */
sangho0c2a3da2016-02-16 13:39:07 +0900127 public void populatePnatFlowRules(InboundPacket inboundPacket, OpenstackPort openstackPort, int portNum,
Kyuhwi Choie2b37e32016-02-05 14:04:14 +0900128 Ip4Address externalIp, MacAddress externalInterfaceMacAddress,
129 MacAddress externalRouterMacAddress) {
sangho0c2a3da2016-02-16 13:39:07 +0900130 this.inboundPacket = inboundPacket;
131 this.openstackPort = openstackPort;
132 this.portNum = portNum;
133 this.externalInterface = externalInterfaceMacAddress;
134 this.externalRouter = externalRouterMacAddress;
135
136 long vni = getVni(openstackPort);
137
Kyuhwi Choie2b37e32016-02-05 14:04:14 +0900138 populatePnatIncomingFlowRules(vni, externalIp);
139 populatePnatOutgoingFlowRules(vni, externalIp);
sangho0c2a3da2016-02-16 13:39:07 +0900140 }
141
Kyuhwi Choie2b37e32016-02-05 14:04:14 +0900142 private void populatePnatOutgoingFlowRules(long vni, Ip4Address externalIp) {
sangho0c2a3da2016-02-16 13:39:07 +0900143 IPv4 iPacket = (IPv4) inboundPacket.parsed().getPayload();
144
145 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
146 sBuilder.matchEthType(Ethernet.TYPE_IPV4)
147 .matchIPProtocol(iPacket.getProtocol())
148 .matchTunnelId(vni)
149 .matchIPSrc(IpPrefix.valueOf(iPacket.getSourceAddress(), 32))
150 .matchIPDst(IpPrefix.valueOf(iPacket.getDestinationAddress(), 32));
151
152 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
153 tBuilder.setEthSrc(externalInterface)
Kyuhwi Choie2b37e32016-02-05 14:04:14 +0900154 .setEthDst(externalRouter)
155 .setIpSrc(externalIp);
sangho0c2a3da2016-02-16 13:39:07 +0900156
157 switch (iPacket.getProtocol()) {
158 case IPv4.PROTOCOL_TCP:
159 TCP tcpPacket = (TCP) iPacket.getPayload();
160 sBuilder.matchTcpSrc(TpPort.tpPort(tcpPacket.getSourcePort()))
161 .matchTcpDst(TpPort.tpPort(tcpPacket.getDestinationPort()));
Kyuhwi Choie2b37e32016-02-05 14:04:14 +0900162 tBuilder.setTcpSrc(TpPort.tpPort(portNum));
sangho0c2a3da2016-02-16 13:39:07 +0900163 break;
164 case IPv4.PROTOCOL_UDP:
165 UDP udpPacket = (UDP) iPacket.getPayload();
Kyuhwi Choie2b37e32016-02-05 14:04:14 +0900166 sBuilder.matchUdpSrc(TpPort.tpPort(udpPacket.getSourcePort()))
sangho0c2a3da2016-02-16 13:39:07 +0900167 .matchUdpDst(TpPort.tpPort(udpPacket.getDestinationPort()));
Kyuhwi Choie2b37e32016-02-05 14:04:14 +0900168 tBuilder.setUdpSrc(TpPort.tpPort(portNum));
sangho0c2a3da2016-02-16 13:39:07 +0900169 break;
170 default:
171 break;
172 }
173
Kyuhwi Choie2b37e32016-02-05 14:04:14 +0900174 Port port = checkNotNull(getPortNumOfExternalInterface(), PORTNOTNULL);
sangho0c2a3da2016-02-16 13:39:07 +0900175 tBuilder.setOutput(port.number());
176
177 ForwardingObjective fo = DefaultForwardingObjective.builder()
178 .withSelector(sBuilder.build())
179 .withTreatment(tBuilder.build())
180 .withFlag(ForwardingObjective.Flag.VERSATILE)
181 .withPriority(PNAT_RULE_PRIORITY)
182 .makeTemporary(PNAT_TIMEOUT)
183 .fromApp(appId)
184 .add();
185
186 flowObjectiveService.forward(inboundPacket.receivedFrom().deviceId(), fo);
187 }
188
189 private Port getPortNumOfExternalInterface() {
190 return deviceService.getPorts(inboundPacket.receivedFrom().deviceId()).stream()
Kyuhwi Choie2b37e32016-02-05 14:04:14 +0900191 .filter(p -> p.annotations().value(PORTNAME).equals(EXTERNAL_INTERFACE_NAME))
sangho0c2a3da2016-02-16 13:39:07 +0900192 .findAny().orElse(null);
193 }
194
195
Kyuhwi Choie2b37e32016-02-05 14:04:14 +0900196 private void populatePnatIncomingFlowRules(long vni, Ip4Address externalIp) {
sangho0c2a3da2016-02-16 13:39:07 +0900197 IPv4 iPacket = (IPv4) inboundPacket.parsed().getPayload();
198 DeviceId deviceId = inboundPacket.receivedFrom().deviceId();
199
200 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
201 sBuilder.matchEthType(Ethernet.TYPE_IPV4)
202 .matchIPProtocol(iPacket.getProtocol())
Kyuhwi Choie2b37e32016-02-05 14:04:14 +0900203 .matchIPDst(IpPrefix.valueOf(externalIp, 32))
sangho0c2a3da2016-02-16 13:39:07 +0900204 .matchIPSrc(IpPrefix.valueOf(iPacket.getDestinationAddress(), 32));
205
206 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
207 tBuilder.setTunnelId(vni)
208 .setIpDst(IpAddress.valueOf(iPacket.getSourceAddress()));
209
210 switch (iPacket.getProtocol()) {
211 case IPv4.PROTOCOL_TCP:
212 TCP tcpPacket = (TCP) iPacket.getPayload();
213 sBuilder.matchTcpSrc(TpPort.tpPort(tcpPacket.getDestinationPort()))
214 .matchTcpDst(TpPort.tpPort(portNum));
215 tBuilder.setTcpDst(TpPort.tpPort(tcpPacket.getSourcePort()));
216 break;
217 case IPv4.PROTOCOL_UDP:
218 UDP udpPacket = (UDP) iPacket.getPayload();
219 sBuilder.matchUdpSrc(TpPort.tpPort(udpPacket.getDestinationPort()))
220 .matchUdpDst(TpPort.tpPort(portNum));
221 tBuilder.setUdpDst(TpPort.tpPort(udpPacket.getSourcePort()));
222 break;
223 default:
224 break;
225 }
226
227 tBuilder.extension(buildNiciraExtenstion(deviceId, Ip4Address.valueOf(iPacket.getSourceAddress())), deviceId)
228 .setOutput(getTunnelPort(deviceId));
229
230 ForwardingObjective fo = DefaultForwardingObjective.builder()
231 .withSelector(sBuilder.build())
232 .withTreatment(tBuilder.build())
233 .withFlag(ForwardingObjective.Flag.VERSATILE)
234 .withPriority(PNAT_RULE_PRIORITY)
235 .makeTemporary(PNAT_TIMEOUT)
236 .fromApp(appId)
237 .add();
238
239 flowObjectiveService.forward(inboundPacket.receivedFrom().deviceId(), fo);
240 }
241
242 private ExtensionTreatment buildNiciraExtenstion(DeviceId id, Ip4Address hostIp) {
243 Driver driver = driverService.getDriver(id);
244 DriverHandler driverHandler = new DefaultDriverHandler(new DefaultDriverData(driver, id));
245 ExtensionTreatmentResolver resolver = driverHandler.behaviour(ExtensionTreatmentResolver.class);
246
247 ExtensionTreatment extensionInstruction =
248 resolver.getExtensionInstruction(
249 ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_SET_TUNNEL_DST.type());
250
251 try {
Kyuhwi Choie2b37e32016-02-05 14:04:14 +0900252 extensionInstruction.setPropertyValue(TUNNEL_DESTINATION, hostIp);
sangho0c2a3da2016-02-16 13:39:07 +0900253 } catch (ExtensionPropertyException e) {
254 log.error("Error setting Nicira extension setting {}", e);
255 }
256
257 return extensionInstruction;
258 }
259
260 private PortNumber getTunnelPort(DeviceId deviceId) {
261 Port port = deviceService.getPorts(deviceId).stream()
Kyuhwi Choie2b37e32016-02-05 14:04:14 +0900262 .filter(p -> p.annotations().value(PORTNAME).equals(PORTNAME_PREFIX_TUNNEL))
sangho0c2a3da2016-02-16 13:39:07 +0900263 .findAny().orElse(null);
264
265 if (port == null) {
266 log.error("No TunnelPort was created.");
267 return null;
268 }
269 return port.number();
270
271 }
272
Kyuhwi Choie2b37e32016-02-05 14:04:14 +0900273 /**
274 * Populates flow rules from openstackComputeNode to GatewayNode.
275 *
276 * @param vni Target network
277 * @param router corresponding router
278 * @param routerInterface corresponding routerInterface
279 */
sangho0c2a3da2016-02-16 13:39:07 +0900280 public void populateExternalRules(long vni, OpenstackRouter router,
281 OpenstackRouterInterface routerInterface) {
282 this.router = router;
283 this.routerInterface = routerInterface;
284
285 // 1. computeNode to gateway
286 populateComputeNodeRules(vni);
287 // 2. gatewayNode to controller
288 populateRuleGatewaytoController(vni);
289 }
290
291 private void populateRuleGatewaytoController(long vni) {
292 Device gatewayDevice = getGatewayNode();
293 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
294 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
295
296 sBuilder.matchEthType(Ethernet.TYPE_IPV4)
297 .matchTunnelId(vni)
298 .matchEthDst(GATEWAYMAC);
299 tBuilder.setOutput(PortNumber.CONTROLLER);
300
301 ForwardingObjective fo = DefaultForwardingObjective.builder()
302 .withSelector(sBuilder.build())
303 .withTreatment(tBuilder.build())
304 .withFlag(ForwardingObjective.Flag.VERSATILE)
305 .withPriority(ROUTING_RULE_PRIORITY)
306 .fromApp(appId)
307 .add();
308
309 flowObjectiveService.forward(gatewayDevice.id(), fo);
310 }
311
312 private void populateComputeNodeRules(long vni) {
313 Device gatewayDevice = getGatewayNode();
314
315 StreamSupport.stream(deviceService.getAvailableDevices().spliterator(), false)
316 .filter(d -> !checkGatewayNode(d.id()))
317 .forEach(d -> populateRuleToGateway(d, gatewayDevice, vni));
sangho0c2a3da2016-02-16 13:39:07 +0900318 }
319
320 private void populateRuleToGateway(Device d, Device gatewayDevice, long vni) {
321 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
322 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
323
324 sBuilder.matchEthType(Ethernet.TYPE_IPV4)
325 .matchTunnelId(vni)
326 .matchEthDst(GATEWAYMAC);
327 tBuilder.extension(buildNiciraExtenstion(d.id(), getIPAddressforDevice(gatewayDevice)), d.id())
328 .setOutput(getTunnelPort(d.id()));
329
330 ForwardingObjective fo = DefaultForwardingObjective.builder()
331 .withSelector(sBuilder.build())
332 .withTreatment(tBuilder.build())
333 .withFlag(ForwardingObjective.Flag.SPECIFIC)
334 .withPriority(ROUTING_RULE_PRIORITY)
335 .fromApp(appId)
336 .add();
337
338 flowObjectiveService.forward(d.id(), fo);
339 }
340
341 private Ip4Address getIPAddressforDevice(Device device) {
Kyuhwi Choie2b37e32016-02-05 14:04:14 +0900342 return Ip4Address.valueOf(device.annotations().value(DEVICE_ANNOTATION_CHANNELID).split(":")[0]);
sangho0c2a3da2016-02-16 13:39:07 +0900343 }
344
345 private Device getGatewayNode() {
Kyuhwi Choie2b37e32016-02-05 14:04:14 +0900346 return checkNotNull(StreamSupport.stream(deviceService.getAvailableDevices().spliterator(), false)
347 .filter(d -> checkGatewayNode(d.id()))
348 .findAny()
349 .orElse(null));
sangho0c2a3da2016-02-16 13:39:07 +0900350 }
351
352 private boolean checkGatewayNode(DeviceId deviceId) {
353 return !deviceService.getPorts(deviceId).stream().anyMatch(port ->
Kyuhwi Choie2b37e32016-02-05 14:04:14 +0900354 port.annotations().value(PORTNAME).startsWith(PORTNAME_PREFIX_ROUTER) ||
355 port.annotations().value(PORTNAME).startsWith(PORTNAME_PREFIX_VM));
sangho0c2a3da2016-02-16 13:39:07 +0900356 }
357
358 private long getVni(OpenstackPort openstackPort) {
359 return Long.parseLong(openstackService.network(openstackPort.networkId()).segmentId());
360 }
361
Kyuhwi Choie2b37e32016-02-05 14:04:14 +0900362 private long getVni(OpenstackSubnet openstackSubnet) {
363 return Long.parseLong(openstackService.network(openstackSubnet.networkId()).segmentId());
364 }
365
366 /**
367 * Remove flow rules for external connection.
368 *
369 * @param routerInterface Corresponding routerInterface
370 */
sangho0c2a3da2016-02-16 13:39:07 +0900371 public void removeExternalRules(OpenstackRouterInterface routerInterface) {
Kyuhwi Choie2b37e32016-02-05 14:04:14 +0900372 OpenstackSubnet openstackSubnet = openstackService.subnet(routerInterface.subnetId());
sangho0c2a3da2016-02-16 13:39:07 +0900373 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
374 sBuilder.matchEthType(Ethernet.TYPE_IPV4)
Kyuhwi Choie2b37e32016-02-05 14:04:14 +0900375 .matchTunnelId(getVni(openstackSubnet))
sangho0c2a3da2016-02-16 13:39:07 +0900376 .matchEthDst(GATEWAYMAC);
377
378 StreamSupport.stream(deviceService.getAvailableDevices().spliterator(), false)
379 .forEach(d -> {
380 if (checkGatewayNode(d.id())) {
381 removeExternalRule(d.id(), sBuilder, ForwardingObjective.Flag.VERSATILE);
382 } else {
383 removeExternalRule(d.id(), sBuilder, ForwardingObjective.Flag.SPECIFIC);
384 }
385 });
386
387 }
388
389 private void removeExternalRule(DeviceId id, TrafficSelector.Builder sBuilder, ForwardingObjective.Flag flag) {
390 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
391
392 ForwardingObjective fo = DefaultForwardingObjective.builder()
393 .withSelector(sBuilder.build())
394 .withTreatment(tBuilder.build())
395 .withFlag(flag)
396 .withPriority(ROUTING_RULE_PRIORITY)
397 .fromApp(appId)
398 .remove();
399
400 flowObjectiveService.forward(id, fo);
401 }
402
403}