blob: 7beb11db374fc9ff935662e4e9edc61065e42ea0 [file] [log] [blame]
Hyunsun Moon44aac662017-02-18 02:07:01 +09001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2016-present Open Networking Foundation
Hyunsun Moon44aac662017-02-18 02:07:01 +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.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;
daniel parkb5817102018-02-15 00:18:51 +090027import org.onlab.packet.MacAddress;
Hyunsun Moon44aac662017-02-18 02:07:01 +090028import org.onlab.packet.TCP;
29import org.onlab.packet.TpPort;
30import org.onlab.packet.UDP;
daniel parkee8700b2017-05-11 15:50:03 +090031import org.onlab.packet.VlanId;
Hyunsun Moon44aac662017-02-18 02:07:01 +090032import org.onlab.util.KryoNamespace;
33import org.onosproject.core.ApplicationId;
34import org.onosproject.core.CoreService;
35import org.onosproject.net.DeviceId;
36import org.onosproject.net.device.DeviceService;
37import org.onosproject.net.flow.DefaultTrafficSelector;
38import org.onosproject.net.flow.DefaultTrafficTreatment;
39import org.onosproject.net.flow.TrafficSelector;
40import org.onosproject.net.flow.TrafficTreatment;
Hyunsun Moon44aac662017-02-18 02:07:01 +090041import org.onosproject.net.packet.DefaultOutboundPacket;
42import org.onosproject.net.packet.InboundPacket;
43import org.onosproject.net.packet.PacketContext;
44import org.onosproject.net.packet.PacketProcessor;
45import org.onosproject.net.packet.PacketService;
46import org.onosproject.openstacknetworking.api.InstancePort;
47import org.onosproject.openstacknetworking.api.InstancePortService;
sanghodc375372017-06-08 10:41:30 +090048import org.onosproject.openstacknetworking.api.OpenstackFlowRuleService;
Hyunsun Moon44aac662017-02-18 02:07:01 +090049import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
sanghodc375372017-06-08 10:41:30 +090050import org.onosproject.openstacknetworking.api.OpenstackRouterService;
Hyunsun Moon0d457362017-06-27 17:19:41 +090051import org.onosproject.openstacknode.api.OpenstackNode;
52import org.onosproject.openstacknode.api.OpenstackNodeService;
Hyunsun Moon44aac662017-02-18 02:07:01 +090053import org.onosproject.store.serializers.KryoNamespaces;
54import org.onosproject.store.service.ConsistentMap;
daniel park0bc7fdb2017-03-13 14:20:08 +090055import org.onosproject.store.service.DistributedSet;
Hyunsun Moon44aac662017-02-18 02:07:01 +090056import 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;
daniel parkee8700b2017-05-11 15:50:03 +090061import org.openstack4j.model.network.NetworkType;
Hyunsun Moon44aac662017-02-18 02:07:01 +090062import org.openstack4j.model.network.Port;
63import org.openstack4j.model.network.Router;
64import org.openstack4j.model.network.RouterInterface;
65import org.openstack4j.model.network.Subnet;
66import org.slf4j.Logger;
67
68import java.nio.ByteBuffer;
69import java.util.Objects;
Hyunsun Moon0d457362017-06-27 17:19:41 +090070import java.util.Set;
Hyunsun Moon44aac662017-02-18 02:07:01 +090071import java.util.concurrent.ExecutorService;
Hyunsun Moon0d457362017-06-27 17:19:41 +090072import java.util.stream.Collectors;
Hyunsun Moon44aac662017-02-18 02:07:01 +090073
74import static java.util.concurrent.Executors.newSingleThreadExecutor;
75import static org.onlab.util.Tools.groupedThreads;
Hyunsun Moon0d457362017-06-27 17:19:41 +090076import static org.onosproject.openstacknetworking.api.Constants.*;
77import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.GATEWAY;
Hyunsun Moon44aac662017-02-18 02:07:01 +090078import static org.slf4j.LoggerFactory.getLogger;
79
80/**
81 * Handle packets needs SNAT.
82 */
83@Component(immediate = true)
84public class OpenstackRoutingSnatHandler {
85
86 private final Logger log = getLogger(getClass());
87
88 private static final String ERR_PACKETIN = "Failed to handle packet in: ";
daniel parkee8700b2017-05-11 15:50:03 +090089 private static final String ERR_UNSUPPORTED_NET_TYPE = "Unsupported network type";
Ray Milkey3717e602018-02-01 13:49:47 -080090 private static final long TIME_OUT_SNAT_PORT_MS = 120L * 1000L;
sangho79d6a832017-05-02 14:53:46 +090091 private static final int TP_PORT_MINIMUM_NUM = 65000;
Hyunsun Moon44aac662017-02-18 02:07:01 +090092 private static final int TP_PORT_MAXIMUM_NUM = 65535;
93
94 private static final KryoNamespace.Builder NUMBER_SERIALIZER = KryoNamespace.newBuilder()
95 .register(KryoNamespaces.API);
96
97 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
98 protected CoreService coreService;
99
100 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
101 protected PacketService packetService;
102
103 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
104 protected StorageService storageService;
105
106 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Hyunsun Moon44aac662017-02-18 02:07:01 +0900107 protected DeviceService deviceService;
108
109 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
110 protected InstancePortService instancePortService;
111
112 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
113 protected OpenstackNodeService osNodeService;
114
115 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
116 protected OpenstackNetworkService osNetworkService;
117
118 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
119 protected OpenstackRouterService osRouterService;
120
sanghodc375372017-06-08 10:41:30 +0900121 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
122 protected OpenstackFlowRuleService osFlowRuleService;
123
Hyunsun Moon44aac662017-02-18 02:07:01 +0900124 private final ExecutorService eventExecutor = newSingleThreadExecutor(
125 groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
Hyunsun Moon0d457362017-06-27 17:19:41 +0900126 private final PacketProcessor packetProcessor = new InternalPacketProcessor();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900127
daniel park0bc7fdb2017-03-13 14:20:08 +0900128 private ConsistentMap<Integer, Long> allocatedPortNumMap;
129 private DistributedSet<Integer> unUsedPortNumSet;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900130 private ApplicationId appId;
131
132 @Activate
133 protected void activate() {
134 appId = coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
daniel park0bc7fdb2017-03-13 14:20:08 +0900135
136 allocatedPortNumMap = storageService.<Integer, Long>consistentMapBuilder()
Hyunsun Moon44aac662017-02-18 02:07:01 +0900137 .withSerializer(Serializer.using(NUMBER_SERIALIZER.build()))
daniel park0bc7fdb2017-03-13 14:20:08 +0900138 .withName("openstackrouting-allocatedportnummap")
Hyunsun Moon44aac662017-02-18 02:07:01 +0900139 .withApplicationId(appId)
140 .build();
141
daniel park0bc7fdb2017-03-13 14:20:08 +0900142 unUsedPortNumSet = storageService.<Integer>setBuilder()
143 .withName("openstackrouting-unusedportnumset")
144 .withSerializer(Serializer.using(KryoNamespaces.API))
145 .build()
146 .asDistributedSet();
147
148 initializeUnusedPortNumSet();
149
Hyunsun Moon44aac662017-02-18 02:07:01 +0900150 packetService.addProcessor(packetProcessor, PacketProcessor.director(1));
151 log.info("Started");
152 }
153
daniel park0bc7fdb2017-03-13 14:20:08 +0900154 private void initializeUnusedPortNumSet() {
155 for (int i = TP_PORT_MINIMUM_NUM; i < TP_PORT_MAXIMUM_NUM; i++) {
Hyunsun Moon0e058f22017-04-19 17:00:52 +0900156 if (!allocatedPortNumMap.containsKey(i)) {
157 unUsedPortNumSet.add(i);
daniel park0bc7fdb2017-03-13 14:20:08 +0900158 }
159 }
160
161 clearPortNumMap();
162 }
163
Hyunsun Moon44aac662017-02-18 02:07:01 +0900164 @Deactivate
165 protected void deactivate() {
166 packetService.removeProcessor(packetProcessor);
167 eventExecutor.shutdown();
168 log.info("Stopped");
169 }
170
171 private void processSnatPacket(PacketContext context, Ethernet eth) {
172 IPv4 iPacket = (IPv4) eth.getPayload();
173 InboundPacket packetIn = context.inPacket();
174
Hyunsun Moon0e058f22017-04-19 17:00:52 +0900175 int patPort = getPortNum();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900176
177 InstancePort srcInstPort = instancePortService.instancePort(eth.getSourceMAC());
178 if (srcInstPort == null) {
Hyunsun Moon0e058f22017-04-19 17:00:52 +0900179 log.error(ERR_PACKETIN + "source host(MAC:{}) does not exist",
Hyunsun Moon44aac662017-02-18 02:07:01 +0900180 eth.getSourceMAC());
181 return;
182 }
Hyunsun Moon0e058f22017-04-19 17:00:52 +0900183
Hyunsun Moon44aac662017-02-18 02:07:01 +0900184 IpAddress srcIp = IpAddress.valueOf(iPacket.getSourceAddress());
185 Subnet srcSubnet = getSourceSubnet(srcInstPort, srcIp);
186 IpAddress externalGatewayIp = getExternalIp(srcSubnet);
daniel parkb5817102018-02-15 00:18:51 +0900187
Hyunsun Moon44aac662017-02-18 02:07:01 +0900188 if (externalGatewayIp == null) {
189 return;
190 }
191
daniel parkb5817102018-02-15 00:18:51 +0900192 MacAddress externalPeerRouterMac = getExternalPeerRouterMac(srcSubnet);
193 if (externalPeerRouterMac == null) {
194 return;
195 }
196
Hyunsun Moon44aac662017-02-18 02:07:01 +0900197 populateSnatFlowRules(context.inPacket(),
198 srcInstPort,
199 TpPort.tpPort(patPort),
daniel parkb5817102018-02-15 00:18:51 +0900200 externalGatewayIp, externalPeerRouterMac);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900201
Ray Milkeyf0c47612017-09-28 11:29:38 -0700202 packetOut(eth.duplicate(),
Hyunsun Moon44aac662017-02-18 02:07:01 +0900203 packetIn.receivedFrom().deviceId(),
204 patPort,
daniel parkb5817102018-02-15 00:18:51 +0900205 externalGatewayIp, externalPeerRouterMac);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900206 }
207
208 private Subnet getSourceSubnet(InstancePort instance, IpAddress srcIp) {
209 Port osPort = osNetworkService.port(instance.portId());
210 IP fixedIp = osPort.getFixedIps().stream()
211 .filter(ip -> IpAddress.valueOf(ip.getIpAddress()).equals(srcIp))
212 .findAny().orElse(null);
213 if (fixedIp == null) {
214 return null;
215 }
216 return osNetworkService.subnet(fixedIp.getSubnetId());
217 }
218
daniel parkb5817102018-02-15 00:18:51 +0900219 private MacAddress getExternalPeerRouterMac(Subnet srcSubnet) {
220 RouterInterface osRouterIface = osRouterService.routerInterfaces().stream()
221 .filter(i -> Objects.equals(i.getSubnetId(), srcSubnet.getId()))
222 .findAny().orElse(null);
223 if (osRouterIface == null) {
224 // this subnet is not connected to the router
225 log.trace(ERR_PACKETIN + "source subnet(ID:{}, CIDR:{}) has no router",
226 srcSubnet.getId(), srcSubnet.getCidr());
227 return null;
228 }
229
230 Router osRouter = osRouterService.router(osRouterIface.getId());
231 if (osRouter == null) {
232 return null;
233 }
234 if (osRouter.getExternalGatewayInfo() == null) {
235 // this router does not have external connectivity
236 log.trace(ERR_PACKETIN + "router({}) has no external gateway",
237 osRouter.getName());
238 return null;
239 }
240
241 ExternalGateway exGatewayInfo = osRouter.getExternalGatewayInfo();
242
243 return osNetworkService.externalPeerRouterMac(exGatewayInfo);
244 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900245 private IpAddress getExternalIp(Subnet srcSubnet) {
246 RouterInterface osRouterIface = osRouterService.routerInterfaces().stream()
247 .filter(i -> Objects.equals(i.getSubnetId(), srcSubnet.getId()))
248 .findAny().orElse(null);
249 if (osRouterIface == null) {
250 // this subnet is not connected to the router
251 log.trace(ERR_PACKETIN + "source subnet(ID:{}, CIDR:{}) has no router",
252 srcSubnet.getId(), srcSubnet.getCidr());
253 return null;
254 }
255
256 Router osRouter = osRouterService.router(osRouterIface.getId());
257 if (osRouter.getExternalGatewayInfo() == null) {
258 // this router does not have external connectivity
259 log.trace(ERR_PACKETIN + "router({}) has no external gateway",
260 osRouter.getName());
261 return null;
262 }
263
264 ExternalGateway exGatewayInfo = osRouter.getExternalGatewayInfo();
265 if (!exGatewayInfo.isEnableSnat()) {
266 // SNAT is disabled in this router
267 log.trace(ERR_PACKETIN + "router({}) SNAT is disabled", osRouter.getName());
268 return null;
269 }
270
271 // TODO fix openstack4j for ExternalGateway provides external fixed IP list
272 Port exGatewayPort = osNetworkService.ports(exGatewayInfo.getNetworkId())
273 .stream()
274 .filter(port -> Objects.equals(port.getDeviceId(), osRouter.getId()))
275 .findAny().orElse(null);
276 if (exGatewayPort == null) {
277 log.trace(ERR_PACKETIN + "no external gateway port for router({})",
278 osRouter.getName());
279 return null;
280 }
281
282 return IpAddress.valueOf(exGatewayPort.getFixedIps().stream()
283 .findFirst().get().getIpAddress());
284 }
285
286 private void populateSnatFlowRules(InboundPacket packetIn, InstancePort srcInstPort,
daniel parkb5817102018-02-15 00:18:51 +0900287 TpPort patPort, IpAddress externalIp, MacAddress externalPeerRouterMac) {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900288 Network osNet = osNetworkService.network(srcInstPort.networkId());
289 if (osNet == null) {
290 final String error = String.format(ERR_PACKETIN + "network %s not found",
291 srcInstPort.networkId());
292 throw new IllegalStateException(error);
293 }
294
295 setDownstreamRules(srcInstPort,
daniel parkee8700b2017-05-11 15:50:03 +0900296 osNet.getProviderSegID(),
297 osNet.getNetworkType(),
Hyunsun Moon44aac662017-02-18 02:07:01 +0900298 externalIp,
299 patPort,
300 packetIn);
301
daniel parkee8700b2017-05-11 15:50:03 +0900302 setUpstreamRules(osNet.getProviderSegID(),
303 osNet.getNetworkType(),
Hyunsun Moon44aac662017-02-18 02:07:01 +0900304 externalIp,
daniel parkb5817102018-02-15 00:18:51 +0900305 externalPeerRouterMac,
Hyunsun Moon44aac662017-02-18 02:07:01 +0900306 patPort,
307 packetIn);
308 }
309
Hyunsun Moon0d457362017-06-27 17:19:41 +0900310 private void setDownstreamRules(InstancePort srcInstPort, String segmentId,
311 NetworkType networkType,
daniel parkb5817102018-02-15 00:18:51 +0900312 IpAddress externalIp,
313 TpPort patPort,
Hyunsun Moon44aac662017-02-18 02:07:01 +0900314 InboundPacket packetIn) {
315 IPv4 iPacket = (IPv4) packetIn.parsed().getPayload();
316 IpAddress internalIp = IpAddress.valueOf(iPacket.getSourceAddress());
317
318 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
319 .matchEthType(Ethernet.TYPE_IPV4)
320 .matchIPProtocol(iPacket.getProtocol())
Hyunsun Moon0d457362017-06-27 17:19:41 +0900321 .matchIPDst(IpPrefix.valueOf(externalIp.getIp4Address(), 32))
Hyunsun Moon44aac662017-02-18 02:07:01 +0900322 .matchIPSrc(IpPrefix.valueOf(iPacket.getDestinationAddress(), 32));
323
324 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder()
Hyunsun Moon44aac662017-02-18 02:07:01 +0900325 .setEthDst(packetIn.parsed().getSourceMAC())
326 .setIpDst(internalIp);
327
daniel parkee8700b2017-05-11 15:50:03 +0900328 switch (networkType) {
329 case VXLAN:
330 tBuilder.setTunnelId(Long.parseLong(segmentId));
331 break;
332 case VLAN:
333 tBuilder.pushVlan()
334 .setVlanId(VlanId.vlanId(segmentId))
335 .setEthSrc(DEFAULT_GATEWAY_MAC);
336 break;
337 default:
338 final String error = String.format(
339 ERR_UNSUPPORTED_NET_TYPE + "%s",
340 networkType.toString());
341 throw new IllegalStateException(error);
342 }
343
344
Hyunsun Moon44aac662017-02-18 02:07:01 +0900345 switch (iPacket.getProtocol()) {
346 case IPv4.PROTOCOL_TCP:
347 TCP tcpPacket = (TCP) iPacket.getPayload();
348 sBuilder.matchTcpSrc(TpPort.tpPort(tcpPacket.getDestinationPort()))
349 .matchTcpDst(patPort);
350 tBuilder.setTcpDst(TpPort.tpPort(tcpPacket.getSourcePort()));
351 break;
352 case IPv4.PROTOCOL_UDP:
353 UDP udpPacket = (UDP) iPacket.getPayload();
354 sBuilder.matchUdpSrc(TpPort.tpPort(udpPacket.getDestinationPort()))
355 .matchUdpDst(patPort);
356 tBuilder.setUdpDst(TpPort.tpPort(udpPacket.getSourcePort()));
357 break;
358 default:
359 break;
360 }
361
Hyunsun Moon0d457362017-06-27 17:19:41 +0900362 OpenstackNode srcNode = osNodeService.node(srcInstPort.deviceId());
363 osNodeService.completeNodes(GATEWAY).forEach(gNode -> {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900364 TrafficTreatment.Builder tmpBuilder =
365 DefaultTrafficTreatment.builder(tBuilder.build());
daniel parkee8700b2017-05-11 15:50:03 +0900366 switch (networkType) {
367 case VXLAN:
368 tmpBuilder.extension(RulePopulatorUtil.buildExtension(
369 deviceService,
Hyunsun Moon0d457362017-06-27 17:19:41 +0900370 gNode.intgBridge(),
371 srcNode.dataIp().getIp4Address()), gNode.intgBridge())
372 .setOutput(gNode.tunnelPortNum());
daniel parkee8700b2017-05-11 15:50:03 +0900373 break;
374 case VLAN:
Hyunsun Moon0d457362017-06-27 17:19:41 +0900375 tmpBuilder.setOutput(gNode.vlanPortNum());
daniel parkee8700b2017-05-11 15:50:03 +0900376 break;
377 default:
Hyunsun Moon0d457362017-06-27 17:19:41 +0900378 final String error = String.format(ERR_UNSUPPORTED_NET_TYPE + "%s",
daniel parkee8700b2017-05-11 15:50:03 +0900379 networkType.toString());
380 throw new IllegalStateException(error);
381 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900382
sanghodc375372017-06-08 10:41:30 +0900383 osFlowRuleService.setRule(
384 appId,
Hyunsun Moon0d457362017-06-27 17:19:41 +0900385 gNode.intgBridge(),
sanghodc375372017-06-08 10:41:30 +0900386 sBuilder.build(),
387 tmpBuilder.build(),
388 PRIORITY_SNAT_RULE,
389 GW_COMMON_TABLE,
390 true);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900391 });
392 }
393
Hyunsun Moon0d457362017-06-27 17:19:41 +0900394 private void setUpstreamRules(String segmentId, NetworkType networkType,
daniel parkb5817102018-02-15 00:18:51 +0900395 IpAddress externalIp, MacAddress externalPeerRouterMac,
396 TpPort patPort,
Hyunsun Moon44aac662017-02-18 02:07:01 +0900397 InboundPacket packetIn) {
398 IPv4 iPacket = (IPv4) packetIn.parsed().getPayload();
daniel parkee8700b2017-05-11 15:50:03 +0900399
400 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
Hyunsun Moon44aac662017-02-18 02:07:01 +0900401 .matchEthType(Ethernet.TYPE_IPV4)
402 .matchIPProtocol(iPacket.getProtocol())
Hyunsun Moon44aac662017-02-18 02:07:01 +0900403 .matchIPSrc(IpPrefix.valueOf(iPacket.getSourceAddress(), 32))
404 .matchIPDst(IpPrefix.valueOf(iPacket.getDestinationAddress(), 32));
405
406 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
daniel parkee8700b2017-05-11 15:50:03 +0900407
408 switch (networkType) {
409 case VXLAN:
410 sBuilder.matchTunnelId(Long.parseLong(segmentId));
411 break;
412 case VLAN:
413 sBuilder.matchVlanId(VlanId.vlanId(segmentId));
414 tBuilder.popVlan();
415 break;
416 default:
Hyunsun Moon0d457362017-06-27 17:19:41 +0900417 final String error = String.format(ERR_UNSUPPORTED_NET_TYPE + "%s",
daniel parkee8700b2017-05-11 15:50:03 +0900418 networkType.toString());
419 throw new IllegalStateException(error);
420 }
421
Hyunsun Moon44aac662017-02-18 02:07:01 +0900422 switch (iPacket.getProtocol()) {
423 case IPv4.PROTOCOL_TCP:
424 TCP tcpPacket = (TCP) iPacket.getPayload();
425 sBuilder.matchTcpSrc(TpPort.tpPort(tcpPacket.getSourcePort()))
426 .matchTcpDst(TpPort.tpPort(tcpPacket.getDestinationPort()));
427 tBuilder.setTcpSrc(patPort)
daniel parkb5817102018-02-15 00:18:51 +0900428 .setEthDst(externalPeerRouterMac);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900429 break;
430 case IPv4.PROTOCOL_UDP:
431 UDP udpPacket = (UDP) iPacket.getPayload();
432 sBuilder.matchUdpSrc(TpPort.tpPort(udpPacket.getSourcePort()))
433 .matchUdpDst(TpPort.tpPort(udpPacket.getDestinationPort()));
434 tBuilder.setUdpSrc(patPort)
daniel parkb5817102018-02-15 00:18:51 +0900435 .setEthDst(externalPeerRouterMac);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900436 break;
437 default:
438 log.debug("Unsupported IPv4 protocol {}");
439 break;
440 }
441
442 tBuilder.setIpSrc(externalIp);
Hyunsun Moon0d457362017-06-27 17:19:41 +0900443 osNodeService.completeNodes(GATEWAY).forEach(gNode -> {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900444 TrafficTreatment.Builder tmpBuilder =
445 DefaultTrafficTreatment.builder(tBuilder.build());
daniel parkb5817102018-02-15 00:18:51 +0900446 tmpBuilder.setOutput(gNode.uplinkPortNum());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900447
sanghodc375372017-06-08 10:41:30 +0900448 osFlowRuleService.setRule(
449 appId,
Hyunsun Moon0d457362017-06-27 17:19:41 +0900450 gNode.intgBridge(),
sanghodc375372017-06-08 10:41:30 +0900451 sBuilder.build(),
452 tmpBuilder.build(),
453 PRIORITY_SNAT_RULE,
454 GW_COMMON_TABLE,
455 true);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900456 });
457 }
458
459 private void packetOut(Ethernet ethPacketIn, DeviceId srcDevice, int patPort,
daniel parkb5817102018-02-15 00:18:51 +0900460 IpAddress externalIp, MacAddress externalPeerRouterMac) {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900461 IPv4 iPacket = (IPv4) ethPacketIn.getPayload();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900462 switch (iPacket.getProtocol()) {
463 case IPv4.PROTOCOL_TCP:
464 TCP tcpPacket = (TCP) iPacket.getPayload();
465 tcpPacket.setSourcePort(patPort);
466 tcpPacket.resetChecksum();
467 tcpPacket.setParent(iPacket);
468 iPacket.setPayload(tcpPacket);
469 break;
470 case IPv4.PROTOCOL_UDP:
471 UDP udpPacket = (UDP) iPacket.getPayload();
472 udpPacket.setSourcePort(patPort);
473 udpPacket.resetChecksum();
474 udpPacket.setParent(iPacket);
475 iPacket.setPayload(udpPacket);
476 break;
477 default:
478 log.trace("Temporally, this method can process UDP and TCP protocol.");
479 return;
480 }
481
482 iPacket.setSourceAddress(externalIp.toString());
483 iPacket.resetChecksum();
484 iPacket.setParent(ethPacketIn);
daniel parkb5817102018-02-15 00:18:51 +0900485 ethPacketIn.setDestinationMACAddress(externalPeerRouterMac);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900486 ethPacketIn.setPayload(iPacket);
daniel parkee8700b2017-05-11 15:50:03 +0900487 ethPacketIn.resetChecksum();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900488
Hyunsun Moon0d457362017-06-27 17:19:41 +0900489 OpenstackNode srcNode = osNodeService.node(srcDevice);
490 if (srcNode == null) {
491 final String error = String.format("Cannot find openstack node for %s",
492 srcDevice);
493 throw new IllegalStateException(error);
494 }
daniel parkee8700b2017-05-11 15:50:03 +0900495
Hyunsun Moon0d457362017-06-27 17:19:41 +0900496 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
daniel parkb5817102018-02-15 00:18:51 +0900497 .setOutput(srcNode.uplinkPortNum()).build();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900498 packetService.emit(new DefaultOutboundPacket(
499 srcDevice,
500 treatment,
501 ByteBuffer.wrap(ethPacketIn.serialize())));
502 }
503
Hyunsun Moon0e058f22017-04-19 17:00:52 +0900504 private int getPortNum() {
daniel park0bc7fdb2017-03-13 14:20:08 +0900505 if (unUsedPortNumSet.isEmpty()) {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900506 clearPortNumMap();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900507 }
daniel park0bc7fdb2017-03-13 14:20:08 +0900508 int portNum = findUnusedPortNum();
daniel park0bc7fdb2017-03-13 14:20:08 +0900509 if (portNum != 0) {
Hyunsun Moon0e058f22017-04-19 17:00:52 +0900510 unUsedPortNumSet.remove(portNum);
511 allocatedPortNumMap.put(portNum, System.currentTimeMillis());
daniel park0bc7fdb2017-03-13 14:20:08 +0900512 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900513 return portNum;
514 }
515
516 private int findUnusedPortNum() {
Hyunsun Moon0e058f22017-04-19 17:00:52 +0900517 return unUsedPortNumSet.stream().findAny().orElse(0);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900518 }
519
520 private void clearPortNumMap() {
daniel park0bc7fdb2017-03-13 14:20:08 +0900521 allocatedPortNumMap.entrySet().forEach(e -> {
Hyunsun Moon0e058f22017-04-19 17:00:52 +0900522 if (System.currentTimeMillis() - e.getValue().value() > TIME_OUT_SNAT_PORT_MS) {
daniel park0bc7fdb2017-03-13 14:20:08 +0900523 allocatedPortNumMap.remove(e.getKey());
524 unUsedPortNumSet.add(e.getKey());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900525 }
526 });
527 }
528
529 private class InternalPacketProcessor implements PacketProcessor {
530
531 @Override
532 public void process(PacketContext context) {
Hyunsun Moon0d457362017-06-27 17:19:41 +0900533 Set<DeviceId> gateways = osNodeService.completeNodes(OpenstackNode.NodeType.GATEWAY)
534 .stream().map(OpenstackNode::intgBridge)
535 .collect(Collectors.toSet());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900536 if (context.isHandled()) {
537 return;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900538 } else if (!gateways.contains(context.inPacket().receivedFrom().deviceId())) {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900539 // return if the packet is not from gateway nodes
540 return;
541 }
542
543 InboundPacket pkt = context.inPacket();
544 Ethernet eth = pkt.parsed();
545 if (eth == null || eth.getEtherType() == Ethernet.TYPE_ARP) {
546 return;
547 }
548
549 IPv4 iPacket = (IPv4) eth.getPayload();
550 switch (iPacket.getProtocol()) {
551 case IPv4.PROTOCOL_ICMP:
552 break;
553 case IPv4.PROTOCOL_UDP:
554 UDP udpPacket = (UDP) iPacket.getPayload();
555 if (udpPacket.getDestinationPort() == UDP.DHCP_SERVER_PORT &&
556 udpPacket.getSourcePort() == UDP.DHCP_CLIENT_PORT) {
557 // don't process DHCP
558 break;
559 }
560 default:
561 eventExecutor.execute(() -> processSnatPacket(context, eth));
562 break;
563 }
564 }
565 }
566}