blob: 234e9cec421df0b82054922d60b32b5f20ad3abc [file] [log] [blame]
sangho0c2a3da2016-02-16 13:39:07 +09001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2016-present Open Networking Laboratory
sangho0c2a3da2016-02-16 13:39:07 +09003 *
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
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -070018import org.apache.felix.scr.annotations.Activate;
19import org.apache.felix.scr.annotations.Component;
20import org.apache.felix.scr.annotations.Deactivate;
21import org.apache.felix.scr.annotations.Reference;
22import org.apache.felix.scr.annotations.ReferenceCardinality;
sangho0c2a3da2016-02-16 13:39:07 +090023import org.onlab.packet.Ethernet;
24import org.onlab.packet.IPv4;
Kyuhwi Choie2b37e32016-02-05 14:04:14 +090025import org.onlab.packet.Ip4Address;
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -070026import org.onlab.packet.IpAddress;
27import org.onlab.packet.IpPrefix;
sangho0c2a3da2016-02-16 13:39:07 +090028import org.onlab.packet.MacAddress;
29import org.onlab.packet.TCP;
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -070030import org.onlab.packet.TpPort;
sangho0c2a3da2016-02-16 13:39:07 +090031import org.onlab.packet.UDP;
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -070032import org.onlab.util.KryoNamespace;
33import org.onlab.util.Tools;
34import org.onosproject.core.ApplicationId;
35import org.onosproject.core.CoreService;
sangho0c2a3da2016-02-16 13:39:07 +090036import org.onosproject.net.DeviceId;
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -070037import org.onosproject.net.Host;
38import org.onosproject.net.device.DeviceService;
39import org.onosproject.net.flow.DefaultTrafficSelector;
sangho0c2a3da2016-02-16 13:39:07 +090040import org.onosproject.net.flow.DefaultTrafficTreatment;
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -070041import org.onosproject.net.flow.TrafficSelector;
sangho0c2a3da2016-02-16 13:39:07 +090042import org.onosproject.net.flow.TrafficTreatment;
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -070043import org.onosproject.net.flowobjective.DefaultForwardingObjective;
44import org.onosproject.net.flowobjective.FlowObjectiveService;
45import org.onosproject.net.flowobjective.ForwardingObjective;
46import org.onosproject.net.host.HostService;
sangho0c2a3da2016-02-16 13:39:07 +090047import org.onosproject.net.packet.DefaultOutboundPacket;
48import org.onosproject.net.packet.InboundPacket;
49import org.onosproject.net.packet.PacketContext;
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -070050import org.onosproject.net.packet.PacketProcessor;
sangho0c2a3da2016-02-16 13:39:07 +090051import org.onosproject.net.packet.PacketService;
sangho93447f12016-02-24 00:33:22 +090052import org.onosproject.openstackinterface.OpenstackInterfaceService;
sangho93447f12016-02-24 00:33:22 +090053import org.onosproject.openstackinterface.OpenstackPort;
54import org.onosproject.openstackinterface.OpenstackRouter;
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -070055import org.onosproject.openstacknetworking.RulePopulatorUtil;
56import org.onosproject.openstacknode.OpenstackNodeService;
Kyuhwi Choi92d9ea42016-06-13 17:28:00 +090057import org.onosproject.scalablegateway.api.ScalableGatewayService;
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -070058import org.onosproject.store.serializers.KryoNamespaces;
59import org.onosproject.store.service.ConsistentMap;
60import org.onosproject.store.service.Serializer;
61import org.onosproject.store.service.StorageService;
sangho0c2a3da2016-02-16 13:39:07 +090062import org.slf4j.Logger;
sangho0c2a3da2016-02-16 13:39:07 +090063
64import java.nio.ByteBuffer;
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -070065import java.util.Objects;
66import java.util.Optional;
67import java.util.concurrent.ExecutorService;
sangho0c2a3da2016-02-16 13:39:07 +090068
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -070069import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
70import static org.onlab.util.Tools.groupedThreads;
71import static org.onosproject.openstacknetworking.Constants.*;
72import static org.slf4j.LoggerFactory.getLogger;
sangho0c2a3da2016-02-16 13:39:07 +090073
74/**
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -070075 * Handle NAT packet processing for managing flow rules in openstack nodes.
sangho0c2a3da2016-02-16 13:39:07 +090076 */
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -070077@Component(immediate = true)
78public class OpenstackPnatHandler {
79 private final Logger log = getLogger(getClass());
sangho0c2a3da2016-02-16 13:39:07 +090080
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -070081 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
82 protected CoreService coreService;
sangho0c2a3da2016-02-16 13:39:07 +090083
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -070084 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
85 protected PacketService packetService;
Kyuhwi Choie2b37e32016-02-05 14:04:14 +090086
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -070087 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
88 protected HostService hostService;
sangho0c2a3da2016-02-16 13:39:07 +090089
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -070090 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
91 protected StorageService storageService;
92
93 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
94 protected FlowObjectiveService flowObjectiveService;
95
96 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
97 protected DeviceService deviceService;
98
99 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
100 protected OpenstackInterfaceService openstackService;
101
102 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
103 protected OpenstackNodeService nodeService;
104
105 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
106 protected ScalableGatewayService gatewayService;
107
108 private static final KryoNamespace.Builder NUMBER_SERIALIZER = KryoNamespace.newBuilder()
109 .register(KryoNamespaces.API);
110
111 private static final int PNAT_PORT_EXPIRE_TIME = 1200 * 1000;
112 private static final int TP_PORT_MINIMUM_NUM = 1024;
113 private static final int TP_PORT_MAXIMUM_NUM = 65535;
114
115 private final ExecutorService eventExecutor = newSingleThreadScheduledExecutor(
116 groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
117 private final InternalPacketProcessor packetProcessor = new InternalPacketProcessor();
118
119 private ConsistentMap<Integer, String> tpPortNumMap;
120 private ApplicationId appId;
121
122 @Activate
123 protected void activate() {
124 appId = coreService.registerApplication(ROUTING_APP_ID);
125 tpPortNumMap = storageService.<Integer, String>consistentMapBuilder()
126 .withSerializer(Serializer.using(NUMBER_SERIALIZER.build()))
127 .withName("openstackrouting-tpportnum")
128 .withApplicationId(appId)
129 .build();
130
131 packetService.addProcessor(packetProcessor, PacketProcessor.director(1));
132 log.info("Started");
sangho0c2a3da2016-02-16 13:39:07 +0900133 }
134
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700135 @Deactivate
136 protected void deactivate() {
137 packetService.removeProcessor(packetProcessor);
138 log.info("Stopped");
139 }
sangho0c2a3da2016-02-16 13:39:07 +0900140
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700141 private void processPnatPacket(PacketContext context, Ethernet ethernet) {
142 IPv4 iPacket = (IPv4) ethernet.getPayload();
143 InboundPacket inboundPacket = context.inPacket();
144
145 int srcPort = getPortNum(ethernet.getSourceMAC(), iPacket.getDestinationAddress());
146 OpenstackPort osPort = getOpenstackPort(ethernet.getSourceMAC());
147 if (osPort == null) {
148 return;
149 }
150 Ip4Address externalGatewayIp = getExternalGatewayIp(osPort);
151 if (externalGatewayIp == null) {
sangho0c2a3da2016-02-16 13:39:07 +0900152 return;
153 }
154
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700155 populatePnatFlowRules(context.inPacket(),
156 osPort,
157 TpPort.tpPort(srcPort),
158 externalGatewayIp);
sangho0c2a3da2016-02-16 13:39:07 +0900159
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700160 packetOut((Ethernet) ethernet.clone(),
161 inboundPacket.receivedFrom().deviceId(),
162 srcPort,
163 externalGatewayIp);
sangho0c2a3da2016-02-16 13:39:07 +0900164 }
165
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700166 private void packetOut(Ethernet ethernet, DeviceId deviceId, int portNum, Ip4Address externalIp) {
sangho0c2a3da2016-02-16 13:39:07 +0900167 IPv4 iPacket = (IPv4) ethernet.getPayload();
sangho0c2a3da2016-02-16 13:39:07 +0900168 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
169
170 switch (iPacket.getProtocol()) {
171 case IPv4.PROTOCOL_TCP:
172 TCP tcpPacket = (TCP) iPacket.getPayload();
173 tcpPacket.setSourcePort(portNum);
174 tcpPacket.resetChecksum();
175 tcpPacket.setParent(iPacket);
176 iPacket.setPayload(tcpPacket);
177 break;
178 case IPv4.PROTOCOL_UDP:
179 UDP udpPacket = (UDP) iPacket.getPayload();
180 udpPacket.setSourcePort(portNum);
181 udpPacket.resetChecksum();
182 udpPacket.setParent(iPacket);
183 iPacket.setPayload(udpPacket);
184 break;
185 default:
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700186 log.trace("Temporally, this method can process UDP and TCP protocol.");
Kyuhwi Choie2b37e32016-02-05 14:04:14 +0900187 return;
sangho0c2a3da2016-02-16 13:39:07 +0900188 }
189
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700190 iPacket.setSourceAddress(externalIp.toString());
sangho0c2a3da2016-02-16 13:39:07 +0900191 iPacket.resetChecksum();
Kyuhwi Choie2b37e32016-02-05 14:04:14 +0900192 iPacket.setParent(ethernet);
sangho5c8f0482016-08-04 23:50:52 +0900193 ethernet.setDestinationMACAddress(DEFAULT_EXTERNAL_ROUTER_MAC);
Kyuhwi Choiee9e3712016-02-22 22:49:36 +0900194 ethernet.setPayload(iPacket);
sangho6032f342016-07-07 14:32:03 +0900195
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700196 treatment.setOutput(gatewayService.getUplinkPort(deviceId));
197 ethernet.resetChecksum();
198 packetService.emit(new DefaultOutboundPacket(
199 deviceId,
200 treatment.build(),
201 ByteBuffer.wrap(ethernet.serialize())));
202 }
203
204 private int getPortNum(MacAddress sourceMac, int destinationAddress) {
205 int portNum = findUnusedPortNum();
206 if (portNum == 0) {
207 clearPortNumMap();
208 portNum = findUnusedPortNum();
209 }
210 tpPortNumMap.put(portNum, sourceMac.toString().concat(":").concat(String.valueOf(destinationAddress)));
211 return portNum;
212 }
213
214 private int findUnusedPortNum() {
215 for (int i = TP_PORT_MINIMUM_NUM; i < TP_PORT_MAXIMUM_NUM; i++) {
216 if (!tpPortNumMap.containsKey(i)) {
217 return i;
218 }
219 }
220 return 0;
221 }
222
223 private void clearPortNumMap() {
224 tpPortNumMap.entrySet().forEach(e -> {
225 if (System.currentTimeMillis() - e.getValue().creationTime() > PNAT_PORT_EXPIRE_TIME) {
226 tpPortNumMap.remove(e.getKey());
227 }
228 });
229 }
230
231 // TODO there can be multiple routers connected to a particular openstack port
232 // TODO cache router information
233 private Ip4Address getExternalGatewayIp(OpenstackPort osPort) {
234 Optional<OpenstackPort> routerPort = openstackService.ports().stream()
235 .filter(p -> p.deviceOwner().equals(DEVICE_OWNER_ROUTER_INTERFACE))
236 .filter(p -> checkSameSubnet(p, osPort))
237 .findAny();
238 if (!routerPort.isPresent()) {
239 log.warn("No router is connected to network {}", osPort.networkId());
240 return null;
241 }
242
243 OpenstackRouter osRouter = openstackService.router(routerPort.get().deviceId());
244 if (osRouter == null) {
245 log.warn("Failed to get OpenStack router {}",
246 routerPort.get().deviceId());
247 return null;
248 }
249
250 return osRouter.gatewayExternalInfo().externalFixedIps().values()
251 .stream().findAny().orElse(null);
252 }
253
254 private OpenstackPort getOpenstackPort(MacAddress srcMac) {
255 Optional<Host> host = hostService.getHostsByMac(srcMac).stream()
256 .filter(h -> h.annotations().value(PORT_ID) != null)
257 .findAny();
258 if (!host.isPresent()) {
259 log.warn("Failed to find a host with MAC:{}", srcMac);
260 return null;
261 }
262 return openstackService.port(host.get().annotations().value(PORT_ID));
263 }
264
265 private boolean checkSameSubnet(OpenstackPort osPortA, OpenstackPort osPortB) {
266 return osPortA.fixedIps().keySet().stream()
267 .anyMatch(subnetId -> osPortB.fixedIps().keySet().contains(subnetId));
268 }
269
270 private void populatePnatFlowRules(InboundPacket inboundPacket,
271 OpenstackPort osPort,
272 TpPort patPort,
273 Ip4Address externalIp) {
274 long vni = getVni(osPort.networkId());
275 populatePnatIncomingFlowRules(vni, externalIp, patPort, inboundPacket);
276 populatePnatOutgoingFlowRules(vni, externalIp, patPort, inboundPacket);
277 }
278
279 private long getVni(String netId) {
280 // TODO remove this and use host vxlan annotation if applicable
281 return Long.parseLong(openstackService.network(netId).segmentId());
282 }
283
284 private void populatePnatOutgoingFlowRules(long vni, Ip4Address externalIp, TpPort patPort,
285 InboundPacket inboundPacket) {
286 IPv4 iPacket = (IPv4) inboundPacket.parsed().getPayload();
287
288 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
289 sBuilder.matchEthType(Ethernet.TYPE_IPV4)
290 .matchIPProtocol(iPacket.getProtocol())
291 .matchTunnelId(vni)
292 .matchIPSrc(IpPrefix.valueOf(iPacket.getSourceAddress(), 32))
293 .matchIPDst(IpPrefix.valueOf(iPacket.getDestinationAddress(), 32));
294
295 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
296 switch (iPacket.getProtocol()) {
297 case IPv4.PROTOCOL_TCP:
298 TCP tcpPacket = (TCP) iPacket.getPayload();
299 sBuilder.matchTcpSrc(TpPort.tpPort(tcpPacket.getSourcePort()))
300 .matchTcpDst(TpPort.tpPort(tcpPacket.getDestinationPort()));
301 tBuilder.setTcpSrc(patPort)
302 .setEthDst(DEFAULT_EXTERNAL_ROUTER_MAC);
303 break;
304 case IPv4.PROTOCOL_UDP:
305 UDP udpPacket = (UDP) iPacket.getPayload();
306 sBuilder.matchUdpSrc(TpPort.tpPort(udpPacket.getSourcePort()))
307 .matchUdpDst(TpPort.tpPort(udpPacket.getDestinationPort()));
308 tBuilder.setUdpSrc(patPort)
309 .setEthDst(DEFAULT_EXTERNAL_ROUTER_MAC);
310
311 break;
312 default:
313 log.debug("Unsupported IPv4 protocol {}");
314 break;
315 }
316
317 tBuilder.setIpSrc(externalIp);
Sho SHIMIZU8ebb04a2016-10-06 15:58:29 -0700318 gatewayService.getGatewayNodes().forEach(gateway -> {
sangho4d287732016-08-04 23:24:13 +0900319 TrafficTreatment.Builder tmpBuilder = DefaultTrafficTreatment.builder(tBuilder.build());
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700320 tmpBuilder.setOutput(gatewayService.getUplinkPort(gateway.getGatewayDeviceId()));
321 ForwardingObjective fo = DefaultForwardingObjective.builder()
322 .withSelector(sBuilder.build())
323 .withTreatment(tmpBuilder.build())
324 .withFlag(ForwardingObjective.Flag.VERSATILE)
325 .withPriority(PNAT_RULE_PRIORITY)
326 .makeTemporary(PNAT_TIMEOUT)
327 .fromApp(appId)
328 .add();
329
330 flowObjectiveService.forward(gateway.getGatewayDeviceId(), fo);
331 });
332 }
333
334 private void populatePnatIncomingFlowRules(long vni, Ip4Address externalIp, TpPort patPort,
335 InboundPacket inboundPacket) {
336 IPv4 iPacket = (IPv4) inboundPacket.parsed().getPayload();
337 IpAddress internalIp = IpAddress.valueOf(iPacket.getSourceAddress());
338
339 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
340 sBuilder.matchEthType(Ethernet.TYPE_IPV4)
341 .matchIPProtocol(iPacket.getProtocol())
342 .matchIPDst(IpPrefix.valueOf(externalIp, 32))
343 .matchIPSrc(IpPrefix.valueOf(iPacket.getDestinationAddress(), 32));
344
345 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
346 tBuilder.setTunnelId(vni)
347 .setEthDst(inboundPacket.parsed().getSourceMAC())
348 .setIpDst(internalIp);
349
350 switch (iPacket.getProtocol()) {
351 case IPv4.PROTOCOL_TCP:
352 TCP tcpPacket = (TCP) iPacket.getPayload();
353 sBuilder.matchTcpSrc(TpPort.tpPort(tcpPacket.getDestinationPort()))
354 .matchTcpDst(patPort);
355 tBuilder.setTcpDst(TpPort.tpPort(tcpPacket.getSourcePort()));
356 break;
357 case IPv4.PROTOCOL_UDP:
358 UDP udpPacket = (UDP) iPacket.getPayload();
359 sBuilder.matchUdpSrc(TpPort.tpPort(udpPacket.getDestinationPort()))
360 .matchUdpDst(patPort);
361 tBuilder.setUdpDst(TpPort.tpPort(udpPacket.getSourcePort()));
362 break;
363 default:
364 break;
365 }
366
367 Optional<Host> srcVm = Tools.stream(hostService.getHostsByIp(internalIp))
368 .filter(host -> Objects.equals(
369 host.annotations().value(VXLAN_ID),
370 String.valueOf(vni)))
371 .findFirst();
372 if (!srcVm.isPresent()) {
373 log.warn("Failed to find source VM with IP {}", internalIp);
Kyuhwi Choi92d9ea42016-06-13 17:28:00 +0900374 return;
375 }
Kyuhwi Choi92d9ea42016-06-13 17:28:00 +0900376
Sho SHIMIZU8ebb04a2016-10-06 15:58:29 -0700377 gatewayService.getGatewayDeviceIds().forEach(deviceId -> {
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700378 DeviceId srcDeviceId = srcVm.get().location().deviceId();
sangho4d287732016-08-04 23:24:13 +0900379 TrafficTreatment.Builder tmpBuilder = DefaultTrafficTreatment.builder(tBuilder.build());
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700380 tmpBuilder.extension(RulePopulatorUtil.buildExtension(
381 deviceService,
382 deviceId,
383 nodeService.dataIp(srcDeviceId).get().getIp4Address()), deviceId)
384 .setOutput(nodeService.tunnelPort(deviceId).get());
sangho0c2a3da2016-02-16 13:39:07 +0900385
Hyunsun Moonb3eb84d2016-07-27 19:10:52 -0700386 ForwardingObjective fo = DefaultForwardingObjective.builder()
387 .withSelector(sBuilder.build())
388 .withTreatment(tmpBuilder.build())
389 .withFlag(ForwardingObjective.Flag.VERSATILE)
390 .withPriority(PNAT_RULE_PRIORITY)
391 .makeTemporary(PNAT_TIMEOUT)
392 .fromApp(appId)
393 .add();
394
395 flowObjectiveService.forward(deviceId, fo);
396 });
397 }
398
399 private class InternalPacketProcessor implements PacketProcessor {
400
401 @Override
402 public void process(PacketContext context) {
403 if (context.isHandled()) {
404 return;
405 } else if (!gatewayService.getGatewayDeviceIds().contains(
406 context.inPacket().receivedFrom().deviceId())) {
407 // return if the packet is not from gateway nodes
408 return;
409 }
410
411 InboundPacket pkt = context.inPacket();
412 Ethernet ethernet = pkt.parsed();
413 if (ethernet == null || ethernet.getEtherType() == Ethernet.TYPE_ARP) {
414 return;
415 }
416
417 IPv4 iPacket = (IPv4) ethernet.getPayload();
418 switch (iPacket.getProtocol()) {
419 case IPv4.PROTOCOL_ICMP:
420 break;
421 case IPv4.PROTOCOL_UDP:
422 UDP udpPacket = (UDP) iPacket.getPayload();
423 if (udpPacket.getDestinationPort() == UDP.DHCP_SERVER_PORT &&
424 udpPacket.getSourcePort() == UDP.DHCP_CLIENT_PORT) {
425 // don't process DHCP
426 break;
427 }
428 default:
429 eventExecutor.execute(() -> processPnatPacket(context, ethernet));
430 break;
431 }
432 }
sangho0c2a3da2016-02-16 13:39:07 +0900433 }
Sho SHIMIZU8ebb04a2016-10-06 15:58:29 -0700434}