blob: 9f792bd6f6ce5164f693d1113de95ee30e939165 [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;
daniel parkeeb8e042018-02-21 14:06:58 +090076import static org.onosproject.openstacknetworking.api.Constants.DEFAULT_GATEWAY_MAC;
77import static org.onosproject.openstacknetworking.api.Constants.GW_COMMON_TABLE;
78import static org.onosproject.openstacknetworking.api.Constants.OPENSTACK_NETWORKING_APP_ID;
79import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_SNAT_RULE;
Hyunsun Moon0d457362017-06-27 17:19:41 +090080import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.GATEWAY;
Hyunsun Moon44aac662017-02-18 02:07:01 +090081import static org.slf4j.LoggerFactory.getLogger;
82
83/**
84 * Handle packets needs SNAT.
85 */
86@Component(immediate = true)
87public class OpenstackRoutingSnatHandler {
88
89 private final Logger log = getLogger(getClass());
90
91 private static final String ERR_PACKETIN = "Failed to handle packet in: ";
daniel parkee8700b2017-05-11 15:50:03 +090092 private static final String ERR_UNSUPPORTED_NET_TYPE = "Unsupported network type";
Ray Milkey3717e602018-02-01 13:49:47 -080093 private static final long TIME_OUT_SNAT_PORT_MS = 120L * 1000L;
sangho79d6a832017-05-02 14:53:46 +090094 private static final int TP_PORT_MINIMUM_NUM = 65000;
Hyunsun Moon44aac662017-02-18 02:07:01 +090095 private static final int TP_PORT_MAXIMUM_NUM = 65535;
96
97 private static final KryoNamespace.Builder NUMBER_SERIALIZER = KryoNamespace.newBuilder()
98 .register(KryoNamespaces.API);
99
100 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
101 protected CoreService coreService;
102
103 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
104 protected PacketService packetService;
105
106 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
107 protected StorageService storageService;
108
109 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Hyunsun Moon44aac662017-02-18 02:07:01 +0900110 protected DeviceService deviceService;
111
112 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
113 protected InstancePortService instancePortService;
114
115 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
116 protected OpenstackNodeService osNodeService;
117
118 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
119 protected OpenstackNetworkService osNetworkService;
120
121 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
122 protected OpenstackRouterService osRouterService;
123
sanghodc375372017-06-08 10:41:30 +0900124 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
125 protected OpenstackFlowRuleService osFlowRuleService;
126
Hyunsun Moon44aac662017-02-18 02:07:01 +0900127 private final ExecutorService eventExecutor = newSingleThreadExecutor(
128 groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
Hyunsun Moon0d457362017-06-27 17:19:41 +0900129 private final PacketProcessor packetProcessor = new InternalPacketProcessor();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900130
daniel park0bc7fdb2017-03-13 14:20:08 +0900131 private ConsistentMap<Integer, Long> allocatedPortNumMap;
132 private DistributedSet<Integer> unUsedPortNumSet;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900133 private ApplicationId appId;
134
135 @Activate
136 protected void activate() {
137 appId = coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
daniel park0bc7fdb2017-03-13 14:20:08 +0900138
139 allocatedPortNumMap = storageService.<Integer, Long>consistentMapBuilder()
Hyunsun Moon44aac662017-02-18 02:07:01 +0900140 .withSerializer(Serializer.using(NUMBER_SERIALIZER.build()))
daniel park0bc7fdb2017-03-13 14:20:08 +0900141 .withName("openstackrouting-allocatedportnummap")
Hyunsun Moon44aac662017-02-18 02:07:01 +0900142 .withApplicationId(appId)
143 .build();
144
daniel park0bc7fdb2017-03-13 14:20:08 +0900145 unUsedPortNumSet = storageService.<Integer>setBuilder()
146 .withName("openstackrouting-unusedportnumset")
147 .withSerializer(Serializer.using(KryoNamespaces.API))
148 .build()
149 .asDistributedSet();
150
151 initializeUnusedPortNumSet();
152
Hyunsun Moon44aac662017-02-18 02:07:01 +0900153 packetService.addProcessor(packetProcessor, PacketProcessor.director(1));
154 log.info("Started");
155 }
156
daniel park0bc7fdb2017-03-13 14:20:08 +0900157 private void initializeUnusedPortNumSet() {
158 for (int i = TP_PORT_MINIMUM_NUM; i < TP_PORT_MAXIMUM_NUM; i++) {
Hyunsun Moon0e058f22017-04-19 17:00:52 +0900159 if (!allocatedPortNumMap.containsKey(i)) {
160 unUsedPortNumSet.add(i);
daniel park0bc7fdb2017-03-13 14:20:08 +0900161 }
162 }
163
164 clearPortNumMap();
165 }
166
Hyunsun Moon44aac662017-02-18 02:07:01 +0900167 @Deactivate
168 protected void deactivate() {
169 packetService.removeProcessor(packetProcessor);
170 eventExecutor.shutdown();
171 log.info("Stopped");
172 }
173
174 private void processSnatPacket(PacketContext context, Ethernet eth) {
175 IPv4 iPacket = (IPv4) eth.getPayload();
176 InboundPacket packetIn = context.inPacket();
177
Hyunsun Moon0e058f22017-04-19 17:00:52 +0900178 int patPort = getPortNum();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900179
180 InstancePort srcInstPort = instancePortService.instancePort(eth.getSourceMAC());
181 if (srcInstPort == null) {
Hyunsun Moon0e058f22017-04-19 17:00:52 +0900182 log.error(ERR_PACKETIN + "source host(MAC:{}) does not exist",
Hyunsun Moon44aac662017-02-18 02:07:01 +0900183 eth.getSourceMAC());
184 return;
185 }
Hyunsun Moon0e058f22017-04-19 17:00:52 +0900186
Hyunsun Moon44aac662017-02-18 02:07:01 +0900187 IpAddress srcIp = IpAddress.valueOf(iPacket.getSourceAddress());
188 Subnet srcSubnet = getSourceSubnet(srcInstPort, srcIp);
189 IpAddress externalGatewayIp = getExternalIp(srcSubnet);
daniel parkb5817102018-02-15 00:18:51 +0900190
Hyunsun Moon44aac662017-02-18 02:07:01 +0900191 if (externalGatewayIp == null) {
192 return;
193 }
194
daniel parkeeb8e042018-02-21 14:06:58 +0900195 MacAddress externalPeerRouterMac = externalPeerRouterMac(srcSubnet);
daniel parkb5817102018-02-15 00:18:51 +0900196 if (externalPeerRouterMac == null) {
197 return;
198 }
199
Hyunsun Moon44aac662017-02-18 02:07:01 +0900200 populateSnatFlowRules(context.inPacket(),
201 srcInstPort,
202 TpPort.tpPort(patPort),
daniel parkb5817102018-02-15 00:18:51 +0900203 externalGatewayIp, externalPeerRouterMac);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900204
Ray Milkeyf0c47612017-09-28 11:29:38 -0700205 packetOut(eth.duplicate(),
Hyunsun Moon44aac662017-02-18 02:07:01 +0900206 packetIn.receivedFrom().deviceId(),
207 patPort,
daniel parkb5817102018-02-15 00:18:51 +0900208 externalGatewayIp, externalPeerRouterMac);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900209 }
210
daniel parkeeb8e042018-02-21 14:06:58 +0900211 private MacAddress externalPeerRouterMac(Subnet subnet) {
212 RouterInterface osRouterIface = osRouterService.routerInterfaces().stream()
213 .filter(i -> Objects.equals(i.getSubnetId(), subnet.getId()))
214 .findAny().orElse(null);
215 if (osRouterIface == null) {
216 return null;
217 }
218
219 Router osRouter = osRouterService.router(osRouterIface.getId());
220 if (osRouter == null) {
221 return null;
222 }
223 if (osRouter.getExternalGatewayInfo() == null) {
224 return null;
225 }
226
227 ExternalGateway exGatewayInfo = osRouter.getExternalGatewayInfo();
228
229 return osNetworkService.externalPeerRouterMac(exGatewayInfo);
230 }
231
Hyunsun Moon44aac662017-02-18 02:07:01 +0900232 private Subnet getSourceSubnet(InstancePort instance, IpAddress srcIp) {
233 Port osPort = osNetworkService.port(instance.portId());
234 IP fixedIp = osPort.getFixedIps().stream()
235 .filter(ip -> IpAddress.valueOf(ip.getIpAddress()).equals(srcIp))
236 .findAny().orElse(null);
237 if (fixedIp == null) {
238 return null;
239 }
240 return osNetworkService.subnet(fixedIp.getSubnetId());
241 }
242
243 private IpAddress getExternalIp(Subnet srcSubnet) {
244 RouterInterface osRouterIface = osRouterService.routerInterfaces().stream()
245 .filter(i -> Objects.equals(i.getSubnetId(), srcSubnet.getId()))
246 .findAny().orElse(null);
247 if (osRouterIface == null) {
248 // this subnet is not connected to the router
249 log.trace(ERR_PACKETIN + "source subnet(ID:{}, CIDR:{}) has no router",
250 srcSubnet.getId(), srcSubnet.getCidr());
251 return null;
252 }
253
254 Router osRouter = osRouterService.router(osRouterIface.getId());
255 if (osRouter.getExternalGatewayInfo() == null) {
256 // this router does not have external connectivity
257 log.trace(ERR_PACKETIN + "router({}) has no external gateway",
258 osRouter.getName());
259 return null;
260 }
261
262 ExternalGateway exGatewayInfo = osRouter.getExternalGatewayInfo();
263 if (!exGatewayInfo.isEnableSnat()) {
264 // SNAT is disabled in this router
265 log.trace(ERR_PACKETIN + "router({}) SNAT is disabled", osRouter.getName());
266 return null;
267 }
268
269 // TODO fix openstack4j for ExternalGateway provides external fixed IP list
270 Port exGatewayPort = osNetworkService.ports(exGatewayInfo.getNetworkId())
271 .stream()
272 .filter(port -> Objects.equals(port.getDeviceId(), osRouter.getId()))
273 .findAny().orElse(null);
274 if (exGatewayPort == null) {
275 log.trace(ERR_PACKETIN + "no external gateway port for router({})",
276 osRouter.getName());
277 return null;
278 }
279
280 return IpAddress.valueOf(exGatewayPort.getFixedIps().stream()
281 .findFirst().get().getIpAddress());
282 }
283
284 private void populateSnatFlowRules(InboundPacket packetIn, InstancePort srcInstPort,
daniel parkb5817102018-02-15 00:18:51 +0900285 TpPort patPort, IpAddress externalIp, MacAddress externalPeerRouterMac) {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900286 Network osNet = osNetworkService.network(srcInstPort.networkId());
287 if (osNet == null) {
Jian Li71670d12018-03-02 21:31:07 +0900288 final String error = String.format("%s network %s not found",
289 ERR_PACKETIN, srcInstPort.networkId());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900290 throw new IllegalStateException(error);
291 }
292
293 setDownstreamRules(srcInstPort,
daniel parkee8700b2017-05-11 15:50:03 +0900294 osNet.getProviderSegID(),
295 osNet.getNetworkType(),
Hyunsun Moon44aac662017-02-18 02:07:01 +0900296 externalIp,
297 patPort,
298 packetIn);
299
daniel parkee8700b2017-05-11 15:50:03 +0900300 setUpstreamRules(osNet.getProviderSegID(),
301 osNet.getNetworkType(),
Hyunsun Moon44aac662017-02-18 02:07:01 +0900302 externalIp,
daniel parkb5817102018-02-15 00:18:51 +0900303 externalPeerRouterMac,
Hyunsun Moon44aac662017-02-18 02:07:01 +0900304 patPort,
305 packetIn);
306 }
307
Hyunsun Moon0d457362017-06-27 17:19:41 +0900308 private void setDownstreamRules(InstancePort srcInstPort, String segmentId,
309 NetworkType networkType,
daniel parkb5817102018-02-15 00:18:51 +0900310 IpAddress externalIp,
311 TpPort patPort,
Hyunsun Moon44aac662017-02-18 02:07:01 +0900312 InboundPacket packetIn) {
313 IPv4 iPacket = (IPv4) packetIn.parsed().getPayload();
314 IpAddress internalIp = IpAddress.valueOf(iPacket.getSourceAddress());
315
316 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
317 .matchEthType(Ethernet.TYPE_IPV4)
318 .matchIPProtocol(iPacket.getProtocol())
Hyunsun Moon0d457362017-06-27 17:19:41 +0900319 .matchIPDst(IpPrefix.valueOf(externalIp.getIp4Address(), 32))
Hyunsun Moon44aac662017-02-18 02:07:01 +0900320 .matchIPSrc(IpPrefix.valueOf(iPacket.getDestinationAddress(), 32));
321
322 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder()
Hyunsun Moon44aac662017-02-18 02:07:01 +0900323 .setEthDst(packetIn.parsed().getSourceMAC())
324 .setIpDst(internalIp);
325
daniel parkee8700b2017-05-11 15:50:03 +0900326 switch (networkType) {
327 case VXLAN:
328 tBuilder.setTunnelId(Long.parseLong(segmentId));
329 break;
330 case VLAN:
331 tBuilder.pushVlan()
332 .setVlanId(VlanId.vlanId(segmentId))
333 .setEthSrc(DEFAULT_GATEWAY_MAC);
334 break;
335 default:
Jian Li71670d12018-03-02 21:31:07 +0900336 final String error = String.format("%s %s",
337 ERR_UNSUPPORTED_NET_TYPE, networkType.toString());
daniel parkee8700b2017-05-11 15:50:03 +0900338 throw new IllegalStateException(error);
339 }
340
341
Hyunsun Moon44aac662017-02-18 02:07:01 +0900342 switch (iPacket.getProtocol()) {
343 case IPv4.PROTOCOL_TCP:
344 TCP tcpPacket = (TCP) iPacket.getPayload();
345 sBuilder.matchTcpSrc(TpPort.tpPort(tcpPacket.getDestinationPort()))
346 .matchTcpDst(patPort);
347 tBuilder.setTcpDst(TpPort.tpPort(tcpPacket.getSourcePort()));
348 break;
349 case IPv4.PROTOCOL_UDP:
350 UDP udpPacket = (UDP) iPacket.getPayload();
351 sBuilder.matchUdpSrc(TpPort.tpPort(udpPacket.getDestinationPort()))
352 .matchUdpDst(patPort);
353 tBuilder.setUdpDst(TpPort.tpPort(udpPacket.getSourcePort()));
354 break;
355 default:
356 break;
357 }
358
Hyunsun Moon0d457362017-06-27 17:19:41 +0900359 OpenstackNode srcNode = osNodeService.node(srcInstPort.deviceId());
360 osNodeService.completeNodes(GATEWAY).forEach(gNode -> {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900361 TrafficTreatment.Builder tmpBuilder =
362 DefaultTrafficTreatment.builder(tBuilder.build());
daniel parkee8700b2017-05-11 15:50:03 +0900363 switch (networkType) {
364 case VXLAN:
365 tmpBuilder.extension(RulePopulatorUtil.buildExtension(
366 deviceService,
Hyunsun Moon0d457362017-06-27 17:19:41 +0900367 gNode.intgBridge(),
368 srcNode.dataIp().getIp4Address()), gNode.intgBridge())
369 .setOutput(gNode.tunnelPortNum());
daniel parkee8700b2017-05-11 15:50:03 +0900370 break;
371 case VLAN:
Hyunsun Moon0d457362017-06-27 17:19:41 +0900372 tmpBuilder.setOutput(gNode.vlanPortNum());
daniel parkee8700b2017-05-11 15:50:03 +0900373 break;
374 default:
Jian Li71670d12018-03-02 21:31:07 +0900375 final String error = String.format("%s %s",
376 ERR_UNSUPPORTED_NET_TYPE, networkType.toString());
daniel parkee8700b2017-05-11 15:50:03 +0900377 throw new IllegalStateException(error);
378 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900379
sanghodc375372017-06-08 10:41:30 +0900380 osFlowRuleService.setRule(
381 appId,
Hyunsun Moon0d457362017-06-27 17:19:41 +0900382 gNode.intgBridge(),
sanghodc375372017-06-08 10:41:30 +0900383 sBuilder.build(),
384 tmpBuilder.build(),
385 PRIORITY_SNAT_RULE,
386 GW_COMMON_TABLE,
387 true);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900388 });
389 }
390
Hyunsun Moon0d457362017-06-27 17:19:41 +0900391 private void setUpstreamRules(String segmentId, NetworkType networkType,
daniel parkb5817102018-02-15 00:18:51 +0900392 IpAddress externalIp, MacAddress externalPeerRouterMac,
393 TpPort patPort,
Hyunsun Moon44aac662017-02-18 02:07:01 +0900394 InboundPacket packetIn) {
395 IPv4 iPacket = (IPv4) packetIn.parsed().getPayload();
daniel parkee8700b2017-05-11 15:50:03 +0900396
397 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
Hyunsun Moon44aac662017-02-18 02:07:01 +0900398 .matchEthType(Ethernet.TYPE_IPV4)
399 .matchIPProtocol(iPacket.getProtocol())
Hyunsun Moon44aac662017-02-18 02:07:01 +0900400 .matchIPSrc(IpPrefix.valueOf(iPacket.getSourceAddress(), 32))
401 .matchIPDst(IpPrefix.valueOf(iPacket.getDestinationAddress(), 32));
402
403 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
daniel parkee8700b2017-05-11 15:50:03 +0900404
405 switch (networkType) {
406 case VXLAN:
407 sBuilder.matchTunnelId(Long.parseLong(segmentId));
408 break;
409 case VLAN:
410 sBuilder.matchVlanId(VlanId.vlanId(segmentId));
411 tBuilder.popVlan();
412 break;
413 default:
Jian Li71670d12018-03-02 21:31:07 +0900414 final String error = String.format("%s %s",
415 ERR_UNSUPPORTED_NET_TYPE, networkType.toString());
daniel parkee8700b2017-05-11 15:50:03 +0900416 throw new IllegalStateException(error);
417 }
418
Hyunsun Moon44aac662017-02-18 02:07:01 +0900419 switch (iPacket.getProtocol()) {
420 case IPv4.PROTOCOL_TCP:
421 TCP tcpPacket = (TCP) iPacket.getPayload();
422 sBuilder.matchTcpSrc(TpPort.tpPort(tcpPacket.getSourcePort()))
423 .matchTcpDst(TpPort.tpPort(tcpPacket.getDestinationPort()));
424 tBuilder.setTcpSrc(patPort)
daniel parkb5817102018-02-15 00:18:51 +0900425 .setEthDst(externalPeerRouterMac);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900426 break;
427 case IPv4.PROTOCOL_UDP:
428 UDP udpPacket = (UDP) iPacket.getPayload();
429 sBuilder.matchUdpSrc(TpPort.tpPort(udpPacket.getSourcePort()))
430 .matchUdpDst(TpPort.tpPort(udpPacket.getDestinationPort()));
431 tBuilder.setUdpSrc(patPort)
daniel parkb5817102018-02-15 00:18:51 +0900432 .setEthDst(externalPeerRouterMac);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900433 break;
434 default:
435 log.debug("Unsupported IPv4 protocol {}");
436 break;
437 }
438
439 tBuilder.setIpSrc(externalIp);
Hyunsun Moon0d457362017-06-27 17:19:41 +0900440 osNodeService.completeNodes(GATEWAY).forEach(gNode -> {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900441 TrafficTreatment.Builder tmpBuilder =
442 DefaultTrafficTreatment.builder(tBuilder.build());
daniel parkb5817102018-02-15 00:18:51 +0900443 tmpBuilder.setOutput(gNode.uplinkPortNum());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900444
sanghodc375372017-06-08 10:41:30 +0900445 osFlowRuleService.setRule(
446 appId,
Hyunsun Moon0d457362017-06-27 17:19:41 +0900447 gNode.intgBridge(),
sanghodc375372017-06-08 10:41:30 +0900448 sBuilder.build(),
449 tmpBuilder.build(),
450 PRIORITY_SNAT_RULE,
451 GW_COMMON_TABLE,
452 true);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900453 });
454 }
455
456 private void packetOut(Ethernet ethPacketIn, DeviceId srcDevice, int patPort,
daniel parkb5817102018-02-15 00:18:51 +0900457 IpAddress externalIp, MacAddress externalPeerRouterMac) {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900458 IPv4 iPacket = (IPv4) ethPacketIn.getPayload();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900459 switch (iPacket.getProtocol()) {
460 case IPv4.PROTOCOL_TCP:
461 TCP tcpPacket = (TCP) iPacket.getPayload();
462 tcpPacket.setSourcePort(patPort);
463 tcpPacket.resetChecksum();
464 tcpPacket.setParent(iPacket);
465 iPacket.setPayload(tcpPacket);
466 break;
467 case IPv4.PROTOCOL_UDP:
468 UDP udpPacket = (UDP) iPacket.getPayload();
469 udpPacket.setSourcePort(patPort);
470 udpPacket.resetChecksum();
471 udpPacket.setParent(iPacket);
472 iPacket.setPayload(udpPacket);
473 break;
474 default:
475 log.trace("Temporally, this method can process UDP and TCP protocol.");
476 return;
477 }
478
479 iPacket.setSourceAddress(externalIp.toString());
480 iPacket.resetChecksum();
481 iPacket.setParent(ethPacketIn);
daniel parkb5817102018-02-15 00:18:51 +0900482 ethPacketIn.setDestinationMACAddress(externalPeerRouterMac);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900483 ethPacketIn.setPayload(iPacket);
daniel parkee8700b2017-05-11 15:50:03 +0900484 ethPacketIn.resetChecksum();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900485
Hyunsun Moon0d457362017-06-27 17:19:41 +0900486 OpenstackNode srcNode = osNodeService.node(srcDevice);
487 if (srcNode == null) {
488 final String error = String.format("Cannot find openstack node for %s",
489 srcDevice);
490 throw new IllegalStateException(error);
491 }
daniel parkee8700b2017-05-11 15:50:03 +0900492
Hyunsun Moon0d457362017-06-27 17:19:41 +0900493 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
daniel parkb5817102018-02-15 00:18:51 +0900494 .setOutput(srcNode.uplinkPortNum()).build();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900495 packetService.emit(new DefaultOutboundPacket(
496 srcDevice,
497 treatment,
498 ByteBuffer.wrap(ethPacketIn.serialize())));
499 }
500
Hyunsun Moon0e058f22017-04-19 17:00:52 +0900501 private int getPortNum() {
daniel park0bc7fdb2017-03-13 14:20:08 +0900502 if (unUsedPortNumSet.isEmpty()) {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900503 clearPortNumMap();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900504 }
daniel park0bc7fdb2017-03-13 14:20:08 +0900505 int portNum = findUnusedPortNum();
daniel park0bc7fdb2017-03-13 14:20:08 +0900506 if (portNum != 0) {
Hyunsun Moon0e058f22017-04-19 17:00:52 +0900507 unUsedPortNumSet.remove(portNum);
508 allocatedPortNumMap.put(portNum, System.currentTimeMillis());
daniel park0bc7fdb2017-03-13 14:20:08 +0900509 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900510 return portNum;
511 }
512
513 private int findUnusedPortNum() {
Hyunsun Moon0e058f22017-04-19 17:00:52 +0900514 return unUsedPortNumSet.stream().findAny().orElse(0);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900515 }
516
517 private void clearPortNumMap() {
daniel park0bc7fdb2017-03-13 14:20:08 +0900518 allocatedPortNumMap.entrySet().forEach(e -> {
Hyunsun Moon0e058f22017-04-19 17:00:52 +0900519 if (System.currentTimeMillis() - e.getValue().value() > TIME_OUT_SNAT_PORT_MS) {
daniel park0bc7fdb2017-03-13 14:20:08 +0900520 allocatedPortNumMap.remove(e.getKey());
521 unUsedPortNumSet.add(e.getKey());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900522 }
523 });
524 }
525
526 private class InternalPacketProcessor implements PacketProcessor {
527
528 @Override
529 public void process(PacketContext context) {
Hyunsun Moon0d457362017-06-27 17:19:41 +0900530 Set<DeviceId> gateways = osNodeService.completeNodes(OpenstackNode.NodeType.GATEWAY)
531 .stream().map(OpenstackNode::intgBridge)
532 .collect(Collectors.toSet());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900533 if (context.isHandled()) {
534 return;
Hyunsun Moon0d457362017-06-27 17:19:41 +0900535 } else if (!gateways.contains(context.inPacket().receivedFrom().deviceId())) {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900536 // return if the packet is not from gateway nodes
537 return;
538 }
539
540 InboundPacket pkt = context.inPacket();
541 Ethernet eth = pkt.parsed();
542 if (eth == null || eth.getEtherType() == Ethernet.TYPE_ARP) {
543 return;
544 }
545
546 IPv4 iPacket = (IPv4) eth.getPayload();
547 switch (iPacket.getProtocol()) {
548 case IPv4.PROTOCOL_ICMP:
549 break;
550 case IPv4.PROTOCOL_UDP:
551 UDP udpPacket = (UDP) iPacket.getPayload();
552 if (udpPacket.getDestinationPort() == UDP.DHCP_SERVER_PORT &&
553 udpPacket.getSourcePort() == UDP.DHCP_CLIENT_PORT) {
554 // don't process DHCP
555 break;
556 }
557 default:
558 eventExecutor.execute(() -> processSnatPacket(context, eth));
559 break;
560 }
561 }
562 }
563}