blob: 5b6868bf1c378879f149d0088cd556a9cbf389a1 [file] [log] [blame]
Hyunsun Moon44aac662017-02-18 02:07:01 +09001/*
2 * Copyright 2016-present 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.impl;
17
18import 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;
23import org.onlab.packet.Ethernet;
24import org.onlab.packet.IPv4;
25import org.onlab.packet.IpAddress;
26import org.onlab.packet.IpPrefix;
27import org.onlab.packet.MacAddress;
28import org.onlab.packet.TCP;
29import org.onlab.packet.TpPort;
30import org.onlab.packet.UDP;
31import org.onlab.util.KryoNamespace;
32import org.onosproject.core.ApplicationId;
33import org.onosproject.core.CoreService;
34import org.onosproject.net.DeviceId;
35import org.onosproject.net.device.DeviceService;
36import org.onosproject.net.flow.DefaultTrafficSelector;
37import org.onosproject.net.flow.DefaultTrafficTreatment;
38import org.onosproject.net.flow.TrafficSelector;
39import org.onosproject.net.flow.TrafficTreatment;
40import org.onosproject.net.flowobjective.DefaultForwardingObjective;
41import org.onosproject.net.flowobjective.FlowObjectiveService;
42import org.onosproject.net.flowobjective.ForwardingObjective;
43import org.onosproject.net.packet.DefaultOutboundPacket;
44import org.onosproject.net.packet.InboundPacket;
45import org.onosproject.net.packet.PacketContext;
46import org.onosproject.net.packet.PacketProcessor;
47import org.onosproject.net.packet.PacketService;
48import org.onosproject.openstacknetworking.api.InstancePort;
49import org.onosproject.openstacknetworking.api.InstancePortService;
50import org.onosproject.openstacknetworking.api.OpenstackRouterService;
51import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
52import org.onosproject.openstacknode.OpenstackNodeService;
53import org.onosproject.scalablegateway.api.ScalableGatewayService;
54import org.onosproject.store.serializers.KryoNamespaces;
55import org.onosproject.store.service.ConsistentMap;
56import org.onosproject.store.service.Serializer;
57import org.onosproject.store.service.StorageService;
58import org.openstack4j.model.network.ExternalGateway;
59import org.openstack4j.model.network.IP;
60import org.openstack4j.model.network.Network;
61import org.openstack4j.model.network.Port;
62import org.openstack4j.model.network.Router;
63import org.openstack4j.model.network.RouterInterface;
64import org.openstack4j.model.network.Subnet;
65import org.slf4j.Logger;
66
67import java.nio.ByteBuffer;
68import java.util.Objects;
69import java.util.concurrent.ExecutorService;
70
71import static java.util.concurrent.Executors.newSingleThreadExecutor;
72import static org.onlab.util.Tools.groupedThreads;
73import static org.onosproject.openstacknetworking.api.Constants.*;
74import static org.slf4j.LoggerFactory.getLogger;
75
76/**
77 * Handle packets needs SNAT.
78 */
79@Component(immediate = true)
80public class OpenstackRoutingSnatHandler {
81
82 private final Logger log = getLogger(getClass());
83
84 private static final String ERR_PACKETIN = "Failed to handle packet in: ";
85 private static final int TIME_OUT_SNAT_RULE = 120;
86 private static final int TIME_OUT_SNAT_PORT = 1200 * 1000;
87 private static final int TP_PORT_MINIMUM_NUM = 1024;
88 private static final int TP_PORT_MAXIMUM_NUM = 65535;
89
90 private static final KryoNamespace.Builder NUMBER_SERIALIZER = KryoNamespace.newBuilder()
91 .register(KryoNamespaces.API);
92
93 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
94 protected CoreService coreService;
95
96 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
97 protected PacketService packetService;
98
99 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
100 protected StorageService storageService;
101
102 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
103 protected FlowObjectiveService flowObjectiveService;
104
105 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
106 protected DeviceService deviceService;
107
108 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
109 protected InstancePortService instancePortService;
110
111 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
112 protected OpenstackNodeService osNodeService;
113
114 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
115 protected OpenstackNetworkService osNetworkService;
116
117 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
118 protected OpenstackRouterService osRouterService;
119
120 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
121 protected ScalableGatewayService gatewayService;
122
123 private final ExecutorService eventExecutor = newSingleThreadExecutor(
124 groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
125 private final InternalPacketProcessor packetProcessor = new InternalPacketProcessor();
126
127 private ConsistentMap<Integer, String> tpPortNumMap;
128 private ApplicationId appId;
129
130 @Activate
131 protected void activate() {
132 appId = coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
133 tpPortNumMap = storageService.<Integer, String>consistentMapBuilder()
134 .withSerializer(Serializer.using(NUMBER_SERIALIZER.build()))
135 .withName("openstackrouting-tpportnum")
136 .withApplicationId(appId)
137 .build();
138
139 packetService.addProcessor(packetProcessor, PacketProcessor.director(1));
140 log.info("Started");
141 }
142
143 @Deactivate
144 protected void deactivate() {
145 packetService.removeProcessor(packetProcessor);
146 eventExecutor.shutdown();
147 log.info("Stopped");
148 }
149
150 private void processSnatPacket(PacketContext context, Ethernet eth) {
151 IPv4 iPacket = (IPv4) eth.getPayload();
152 InboundPacket packetIn = context.inPacket();
153
154 int patPort = getPortNum(eth.getSourceMAC(),
155 iPacket.getDestinationAddress());
156
157 InstancePort srcInstPort = instancePortService.instancePort(eth.getSourceMAC());
158 if (srcInstPort == null) {
159 log.trace(ERR_PACKETIN + "source host(MAC:{}) does not exist",
160 eth.getSourceMAC());
161 return;
162 }
163 IpAddress srcIp = IpAddress.valueOf(iPacket.getSourceAddress());
164 Subnet srcSubnet = getSourceSubnet(srcInstPort, srcIp);
165 IpAddress externalGatewayIp = getExternalIp(srcSubnet);
166 if (externalGatewayIp == null) {
167 return;
168 }
169
170 populateSnatFlowRules(context.inPacket(),
171 srcInstPort,
172 TpPort.tpPort(patPort),
173 externalGatewayIp);
174
175 packetOut((Ethernet) eth.clone(),
176 packetIn.receivedFrom().deviceId(),
177 patPort,
178 externalGatewayIp);
179 }
180
181 private Subnet getSourceSubnet(InstancePort instance, IpAddress srcIp) {
182 Port osPort = osNetworkService.port(instance.portId());
183 IP fixedIp = osPort.getFixedIps().stream()
184 .filter(ip -> IpAddress.valueOf(ip.getIpAddress()).equals(srcIp))
185 .findAny().orElse(null);
186 if (fixedIp == null) {
187 return null;
188 }
189 return osNetworkService.subnet(fixedIp.getSubnetId());
190 }
191
192 private IpAddress getExternalIp(Subnet srcSubnet) {
193 RouterInterface osRouterIface = osRouterService.routerInterfaces().stream()
194 .filter(i -> Objects.equals(i.getSubnetId(), srcSubnet.getId()))
195 .findAny().orElse(null);
196 if (osRouterIface == null) {
197 // this subnet is not connected to the router
198 log.trace(ERR_PACKETIN + "source subnet(ID:{}, CIDR:{}) has no router",
199 srcSubnet.getId(), srcSubnet.getCidr());
200 return null;
201 }
202
203 Router osRouter = osRouterService.router(osRouterIface.getId());
204 if (osRouter.getExternalGatewayInfo() == null) {
205 // this router does not have external connectivity
206 log.trace(ERR_PACKETIN + "router({}) has no external gateway",
207 osRouter.getName());
208 return null;
209 }
210
211 ExternalGateway exGatewayInfo = osRouter.getExternalGatewayInfo();
212 if (!exGatewayInfo.isEnableSnat()) {
213 // SNAT is disabled in this router
214 log.trace(ERR_PACKETIN + "router({}) SNAT is disabled", osRouter.getName());
215 return null;
216 }
217
218 // TODO fix openstack4j for ExternalGateway provides external fixed IP list
219 Port exGatewayPort = osNetworkService.ports(exGatewayInfo.getNetworkId())
220 .stream()
221 .filter(port -> Objects.equals(port.getDeviceId(), osRouter.getId()))
222 .findAny().orElse(null);
223 if (exGatewayPort == null) {
224 log.trace(ERR_PACKETIN + "no external gateway port for router({})",
225 osRouter.getName());
226 return null;
227 }
228
229 return IpAddress.valueOf(exGatewayPort.getFixedIps().stream()
230 .findFirst().get().getIpAddress());
231 }
232
233 private void populateSnatFlowRules(InboundPacket packetIn, InstancePort srcInstPort,
234 TpPort patPort, IpAddress externalIp) {
235 Network osNet = osNetworkService.network(srcInstPort.networkId());
236 if (osNet == null) {
237 final String error = String.format(ERR_PACKETIN + "network %s not found",
238 srcInstPort.networkId());
239 throw new IllegalStateException(error);
240 }
241
242 setDownstreamRules(srcInstPort,
243 Long.parseLong(osNet.getProviderSegID()),
244 externalIp,
245 patPort,
246 packetIn);
247
248 setUpstreamRules(Long.parseLong(osNet.getProviderSegID()),
249 externalIp,
250 patPort,
251 packetIn);
252 }
253
254 private void setDownstreamRules(InstancePort srcInstPort, Long srcVni,
255 IpAddress externalIp, TpPort patPort,
256 InboundPacket packetIn) {
257 IPv4 iPacket = (IPv4) packetIn.parsed().getPayload();
258 IpAddress internalIp = IpAddress.valueOf(iPacket.getSourceAddress());
259
260 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
261 .matchEthType(Ethernet.TYPE_IPV4)
262 .matchIPProtocol(iPacket.getProtocol())
263 .matchIPDst(IpPrefix.valueOf(externalIp, 32))
264 .matchIPSrc(IpPrefix.valueOf(iPacket.getDestinationAddress(), 32));
265
266 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder()
267 .setTunnelId(srcVni)
268 .setEthDst(packetIn.parsed().getSourceMAC())
269 .setIpDst(internalIp);
270
271 switch (iPacket.getProtocol()) {
272 case IPv4.PROTOCOL_TCP:
273 TCP tcpPacket = (TCP) iPacket.getPayload();
274 sBuilder.matchTcpSrc(TpPort.tpPort(tcpPacket.getDestinationPort()))
275 .matchTcpDst(patPort);
276 tBuilder.setTcpDst(TpPort.tpPort(tcpPacket.getSourcePort()));
277 break;
278 case IPv4.PROTOCOL_UDP:
279 UDP udpPacket = (UDP) iPacket.getPayload();
280 sBuilder.matchUdpSrc(TpPort.tpPort(udpPacket.getDestinationPort()))
281 .matchUdpDst(patPort);
282 tBuilder.setUdpDst(TpPort.tpPort(udpPacket.getSourcePort()));
283 break;
284 default:
285 break;
286 }
287
288 gatewayService.getGatewayDeviceIds().forEach(deviceId -> {
289 DeviceId srcDeviceId = srcInstPort.deviceId();
290 TrafficTreatment.Builder tmpBuilder =
291 DefaultTrafficTreatment.builder(tBuilder.build());
292 tmpBuilder.extension(RulePopulatorUtil.buildExtension(
293 deviceService,
294 deviceId,
295 osNodeService.dataIp(srcDeviceId).get().getIp4Address()), deviceId)
296 .setOutput(osNodeService.tunnelPort(deviceId).get());
297
298 ForwardingObjective fo = DefaultForwardingObjective.builder()
299 .withSelector(sBuilder.build())
300 .withTreatment(tmpBuilder.build())
301 .withFlag(ForwardingObjective.Flag.VERSATILE)
302 .withPriority(PRIORITY_SNAT_RULE)
303 .makeTemporary(TIME_OUT_SNAT_RULE)
304 .fromApp(appId)
305 .add();
306
307 flowObjectiveService.forward(deviceId, fo);
308 });
309 }
310
311 private void setUpstreamRules(Long srcVni, IpAddress externalIp, TpPort patPort,
312 InboundPacket packetIn) {
313 IPv4 iPacket = (IPv4) packetIn.parsed().getPayload();
314 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
315 .matchEthType(Ethernet.TYPE_IPV4)
316 .matchIPProtocol(iPacket.getProtocol())
317 .matchTunnelId(srcVni)
318 .matchIPSrc(IpPrefix.valueOf(iPacket.getSourceAddress(), 32))
319 .matchIPDst(IpPrefix.valueOf(iPacket.getDestinationAddress(), 32));
320
321 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
322 switch (iPacket.getProtocol()) {
323 case IPv4.PROTOCOL_TCP:
324 TCP tcpPacket = (TCP) iPacket.getPayload();
325 sBuilder.matchTcpSrc(TpPort.tpPort(tcpPacket.getSourcePort()))
326 .matchTcpDst(TpPort.tpPort(tcpPacket.getDestinationPort()));
327 tBuilder.setTcpSrc(patPort)
328 .setEthDst(DEFAULT_EXTERNAL_ROUTER_MAC);
329 break;
330 case IPv4.PROTOCOL_UDP:
331 UDP udpPacket = (UDP) iPacket.getPayload();
332 sBuilder.matchUdpSrc(TpPort.tpPort(udpPacket.getSourcePort()))
333 .matchUdpDst(TpPort.tpPort(udpPacket.getDestinationPort()));
334 tBuilder.setUdpSrc(patPort)
335 .setEthDst(DEFAULT_EXTERNAL_ROUTER_MAC);
336
337 break;
338 default:
339 log.debug("Unsupported IPv4 protocol {}");
340 break;
341 }
342
343 tBuilder.setIpSrc(externalIp);
344 gatewayService.getGatewayDeviceIds().forEach(deviceId -> {
345 TrafficTreatment.Builder tmpBuilder =
346 DefaultTrafficTreatment.builder(tBuilder.build());
347 tmpBuilder.setOutput(gatewayService.getUplinkPort(deviceId));
348 ForwardingObjective fo = DefaultForwardingObjective.builder()
349 .withSelector(sBuilder.build())
350 .withTreatment(tmpBuilder.build())
351 .withFlag(ForwardingObjective.Flag.VERSATILE)
352 .withPriority(PRIORITY_SNAT_RULE)
353 .makeTemporary(TIME_OUT_SNAT_RULE)
354 .fromApp(appId)
355 .add();
356
357 flowObjectiveService.forward(deviceId, fo);
358 });
359 }
360
361 private void packetOut(Ethernet ethPacketIn, DeviceId srcDevice, int patPort,
362 IpAddress externalIp) {
363 IPv4 iPacket = (IPv4) ethPacketIn.getPayload();
364
365 switch (iPacket.getProtocol()) {
366 case IPv4.PROTOCOL_TCP:
367 TCP tcpPacket = (TCP) iPacket.getPayload();
368 tcpPacket.setSourcePort(patPort);
369 tcpPacket.resetChecksum();
370 tcpPacket.setParent(iPacket);
371 iPacket.setPayload(tcpPacket);
372 break;
373 case IPv4.PROTOCOL_UDP:
374 UDP udpPacket = (UDP) iPacket.getPayload();
375 udpPacket.setSourcePort(patPort);
376 udpPacket.resetChecksum();
377 udpPacket.setParent(iPacket);
378 iPacket.setPayload(udpPacket);
379 break;
380 default:
381 log.trace("Temporally, this method can process UDP and TCP protocol.");
382 return;
383 }
384
385 iPacket.setSourceAddress(externalIp.toString());
386 iPacket.resetChecksum();
387 iPacket.setParent(ethPacketIn);
388 ethPacketIn.setDestinationMACAddress(DEFAULT_EXTERNAL_ROUTER_MAC);
389 ethPacketIn.setPayload(iPacket);
390
391 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
392 .setOutput(gatewayService.getUplinkPort(srcDevice))
393 .build();
394 ethPacketIn.resetChecksum();
395 packetService.emit(new DefaultOutboundPacket(
396 srcDevice,
397 treatment,
398 ByteBuffer.wrap(ethPacketIn.serialize())));
399 }
400
401 private int getPortNum(MacAddress sourceMac, int destinationAddress) {
402 int portNum = findUnusedPortNum();
403 if (portNum == 0) {
404 clearPortNumMap();
405 portNum = findUnusedPortNum();
406 }
407 tpPortNumMap.put(portNum, sourceMac.toString().concat(":").concat(String.valueOf(destinationAddress)));
408 return portNum;
409 }
410
411 private int findUnusedPortNum() {
412 for (int i = TP_PORT_MINIMUM_NUM; i < TP_PORT_MAXIMUM_NUM; i++) {
413 if (!tpPortNumMap.containsKey(i)) {
414 return i;
415 }
416 }
417 return 0;
418 }
419
420 private void clearPortNumMap() {
421 tpPortNumMap.entrySet().forEach(e -> {
422 if (System.currentTimeMillis() - e.getValue().creationTime() > TIME_OUT_SNAT_PORT) {
423 tpPortNumMap.remove(e.getKey());
424 }
425 });
426 }
427
428 private class InternalPacketProcessor implements PacketProcessor {
429
430 @Override
431 public void process(PacketContext context) {
432 if (context.isHandled()) {
433 return;
434 } else if (!gatewayService.getGatewayDeviceIds().contains(
435 context.inPacket().receivedFrom().deviceId())) {
436 // return if the packet is not from gateway nodes
437 return;
438 }
439
440 InboundPacket pkt = context.inPacket();
441 Ethernet eth = pkt.parsed();
442 if (eth == null || eth.getEtherType() == Ethernet.TYPE_ARP) {
443 return;
444 }
445
446 IPv4 iPacket = (IPv4) eth.getPayload();
447 switch (iPacket.getProtocol()) {
448 case IPv4.PROTOCOL_ICMP:
449 break;
450 case IPv4.PROTOCOL_UDP:
451 UDP udpPacket = (UDP) iPacket.getPayload();
452 if (udpPacket.getDestinationPort() == UDP.DHCP_SERVER_PORT &&
453 udpPacket.getSourcePort() == UDP.DHCP_CLIENT_PORT) {
454 // don't process DHCP
455 break;
456 }
457 default:
458 eventExecutor.execute(() -> processSnatPacket(context, eth));
459 break;
460 }
461 }
462 }
463}