blob: e493bd2267bcba8027881cd10ec5b9abd9544ee1 [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
Hyunsun Moon44aac662017-02-18 02:07:01 +090018import org.onlab.packet.Ethernet;
19import org.onlab.packet.IPv4;
20import org.onlab.packet.IpAddress;
21import org.onlab.packet.IpPrefix;
Hyunsun Moon44aac662017-02-18 02:07:01 +090022import org.onlab.packet.TCP;
23import org.onlab.packet.TpPort;
24import org.onlab.packet.UDP;
daniel parkee8700b2017-05-11 15:50:03 +090025import org.onlab.packet.VlanId;
Hyunsun Moon44aac662017-02-18 02:07:01 +090026import org.onlab.util.KryoNamespace;
27import org.onosproject.core.ApplicationId;
28import org.onosproject.core.CoreService;
29import org.onosproject.net.DeviceId;
30import org.onosproject.net.device.DeviceService;
31import org.onosproject.net.flow.DefaultTrafficSelector;
32import org.onosproject.net.flow.DefaultTrafficTreatment;
33import org.onosproject.net.flow.TrafficSelector;
34import org.onosproject.net.flow.TrafficTreatment;
Hyunsun Moon44aac662017-02-18 02:07:01 +090035import org.onosproject.net.packet.DefaultOutboundPacket;
36import org.onosproject.net.packet.InboundPacket;
37import org.onosproject.net.packet.PacketContext;
38import org.onosproject.net.packet.PacketProcessor;
39import org.onosproject.net.packet.PacketService;
daniel park576969a2018-03-09 07:07:41 +090040import org.onosproject.openstacknetworking.api.ExternalPeerRouter;
Hyunsun Moon44aac662017-02-18 02:07:01 +090041import org.onosproject.openstacknetworking.api.InstancePort;
42import org.onosproject.openstacknetworking.api.InstancePortService;
sanghodc375372017-06-08 10:41:30 +090043import org.onosproject.openstacknetworking.api.OpenstackFlowRuleService;
Hyunsun Moon44aac662017-02-18 02:07:01 +090044import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
sanghodc375372017-06-08 10:41:30 +090045import org.onosproject.openstacknetworking.api.OpenstackRouterService;
Jian Li26949762018-03-30 15:46:37 +090046import org.onosproject.openstacknetworking.util.RulePopulatorUtil;
Hyunsun Moon0d457362017-06-27 17:19:41 +090047import org.onosproject.openstacknode.api.OpenstackNode;
48import org.onosproject.openstacknode.api.OpenstackNodeService;
Hyunsun Moon44aac662017-02-18 02:07:01 +090049import org.onosproject.store.serializers.KryoNamespaces;
50import org.onosproject.store.service.ConsistentMap;
daniel park0bc7fdb2017-03-13 14:20:08 +090051import org.onosproject.store.service.DistributedSet;
Hyunsun Moon44aac662017-02-18 02:07:01 +090052import org.onosproject.store.service.Serializer;
53import org.onosproject.store.service.StorageService;
Hyunsun Moon44aac662017-02-18 02:07:01 +090054import org.openstack4j.model.network.IP;
55import org.openstack4j.model.network.Network;
daniel parkee8700b2017-05-11 15:50:03 +090056import org.openstack4j.model.network.NetworkType;
Hyunsun Moon44aac662017-02-18 02:07:01 +090057import org.openstack4j.model.network.Port;
Hyunsun Moon44aac662017-02-18 02:07:01 +090058import org.openstack4j.model.network.Subnet;
Jian Liebde74d2018-11-14 00:18:57 +090059import org.osgi.service.component.annotations.Activate;
60import org.osgi.service.component.annotations.Component;
61import org.osgi.service.component.annotations.Deactivate;
62import org.osgi.service.component.annotations.Reference;
63import org.osgi.service.component.annotations.ReferenceCardinality;
Hyunsun Moon44aac662017-02-18 02:07:01 +090064import org.slf4j.Logger;
65
66import java.nio.ByteBuffer;
Hyunsun Moon0d457362017-06-27 17:19:41 +090067import java.util.Set;
Hyunsun Moon44aac662017-02-18 02:07:01 +090068import java.util.concurrent.ExecutorService;
Hyunsun Moon0d457362017-06-27 17:19:41 +090069import java.util.stream.Collectors;
Hyunsun Moon44aac662017-02-18 02:07:01 +090070
71import static java.util.concurrent.Executors.newSingleThreadExecutor;
72import static org.onlab.util.Tools.groupedThreads;
daniel parkeeb8e042018-02-21 14:06:58 +090073import static org.onosproject.openstacknetworking.api.Constants.DEFAULT_GATEWAY_MAC;
74import static org.onosproject.openstacknetworking.api.Constants.GW_COMMON_TABLE;
75import static org.onosproject.openstacknetworking.api.Constants.OPENSTACK_NETWORKING_APP_ID;
76import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_SNAT_RULE;
Jian Liebde74d2018-11-14 00:18:57 +090077import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.externalIpFromSubnet;
78import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.externalPeerRouterFromSubnet;
Hyunsun Moon0d457362017-06-27 17:19:41 +090079import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.GATEWAY;
Hyunsun Moon44aac662017-02-18 02:07:01 +090080import static org.slf4j.LoggerFactory.getLogger;
81
82/**
83 * Handle packets needs SNAT.
84 */
85@Component(immediate = true)
86public class OpenstackRoutingSnatHandler {
87
88 private final Logger log = getLogger(getClass());
89
90 private static final String ERR_PACKETIN = "Failed to handle packet in: ";
daniel parkee8700b2017-05-11 15:50:03 +090091 private static final String ERR_UNSUPPORTED_NET_TYPE = "Unsupported network type";
Ray Milkey3717e602018-02-01 13:49:47 -080092 private static final long TIME_OUT_SNAT_PORT_MS = 120L * 1000L;
sangho79d6a832017-05-02 14:53:46 +090093 private static final int TP_PORT_MINIMUM_NUM = 65000;
Hyunsun Moon44aac662017-02-18 02:07:01 +090094 private static final int TP_PORT_MAXIMUM_NUM = 65535;
Jian Li6a47fd02018-11-27 21:51:03 +090095 private static final int VM_PREFIX = 32;
Hyunsun Moon44aac662017-02-18 02:07:01 +090096
97 private static final KryoNamespace.Builder NUMBER_SERIALIZER = KryoNamespace.newBuilder()
98 .register(KryoNamespaces.API);
99
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700100 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon44aac662017-02-18 02:07:01 +0900101 protected CoreService coreService;
102
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700103 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon44aac662017-02-18 02:07:01 +0900104 protected PacketService packetService;
105
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700106 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon44aac662017-02-18 02:07:01 +0900107 protected StorageService storageService;
108
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700109 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon44aac662017-02-18 02:07:01 +0900110 protected DeviceService deviceService;
111
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700112 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon44aac662017-02-18 02:07:01 +0900113 protected InstancePortService instancePortService;
114
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700115 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon44aac662017-02-18 02:07:01 +0900116 protected OpenstackNodeService osNodeService;
117
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700118 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon44aac662017-02-18 02:07:01 +0900119 protected OpenstackNetworkService osNetworkService;
120
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700121 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon44aac662017-02-18 02:07:01 +0900122 protected OpenstackRouterService osRouterService;
123
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700124 @Reference(cardinality = ReferenceCardinality.MANDATORY)
sanghodc375372017-06-08 10:41:30 +0900125 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);
Jian Liebde74d2018-11-14 00:18:57 +0900189 IpAddress externalGatewayIp =
190 externalIpFromSubnet(srcSubnet, osRouterService, osNetworkService);
daniel parkb5817102018-02-15 00:18:51 +0900191
Hyunsun Moon44aac662017-02-18 02:07:01 +0900192 if (externalGatewayIp == null) {
193 return;
194 }
195
Jian Liebde74d2018-11-14 00:18:57 +0900196 ExternalPeerRouter externalPeerRouter =
197 externalPeerRouterFromSubnet(srcSubnet, osRouterService, osNetworkService);
daniel park576969a2018-03-09 07:07:41 +0900198 if (externalPeerRouter == null) {
daniel parkb5817102018-02-15 00:18:51 +0900199 return;
200 }
201
Hyunsun Moon44aac662017-02-18 02:07:01 +0900202 populateSnatFlowRules(context.inPacket(),
203 srcInstPort,
204 TpPort.tpPort(patPort),
daniel park576969a2018-03-09 07:07:41 +0900205 externalGatewayIp, externalPeerRouter);
206
Hyunsun Moon44aac662017-02-18 02:07:01 +0900207
Ray Milkeyf0c47612017-09-28 11:29:38 -0700208 packetOut(eth.duplicate(),
Hyunsun Moon44aac662017-02-18 02:07:01 +0900209 packetIn.receivedFrom().deviceId(),
210 patPort,
daniel park576969a2018-03-09 07:07:41 +0900211 externalGatewayIp, externalPeerRouter);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900212 }
213
214 private Subnet getSourceSubnet(InstancePort instance, IpAddress srcIp) {
215 Port osPort = osNetworkService.port(instance.portId());
216 IP fixedIp = osPort.getFixedIps().stream()
217 .filter(ip -> IpAddress.valueOf(ip.getIpAddress()).equals(srcIp))
218 .findAny().orElse(null);
219 if (fixedIp == null) {
220 return null;
221 }
222 return osNetworkService.subnet(fixedIp.getSubnetId());
223 }
224
Jian Li5ecfd1a2018-12-10 11:41:03 +0900225 private void populateSnatFlowRules(InboundPacket packetIn,
226 InstancePort srcInstPort,
227 TpPort patPort, IpAddress externalIp,
228 ExternalPeerRouter externalPeerRouter) {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900229 Network osNet = osNetworkService.network(srcInstPort.networkId());
230 if (osNet == null) {
Jian Li71670d12018-03-02 21:31:07 +0900231 final String error = String.format("%s network %s not found",
232 ERR_PACKETIN, srcInstPort.networkId());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900233 throw new IllegalStateException(error);
234 }
235
236 setDownstreamRules(srcInstPort,
daniel parkee8700b2017-05-11 15:50:03 +0900237 osNet.getProviderSegID(),
238 osNet.getNetworkType(),
Hyunsun Moon44aac662017-02-18 02:07:01 +0900239 externalIp,
daniel park576969a2018-03-09 07:07:41 +0900240 externalPeerRouter,
Hyunsun Moon44aac662017-02-18 02:07:01 +0900241 patPort,
242 packetIn);
243
daniel parkee8700b2017-05-11 15:50:03 +0900244 setUpstreamRules(osNet.getProviderSegID(),
245 osNet.getNetworkType(),
Hyunsun Moon44aac662017-02-18 02:07:01 +0900246 externalIp,
daniel park576969a2018-03-09 07:07:41 +0900247 externalPeerRouter,
Hyunsun Moon44aac662017-02-18 02:07:01 +0900248 patPort,
249 packetIn);
250 }
251
Hyunsun Moon0d457362017-06-27 17:19:41 +0900252 private void setDownstreamRules(InstancePort srcInstPort, String segmentId,
253 NetworkType networkType,
daniel parkb5817102018-02-15 00:18:51 +0900254 IpAddress externalIp,
daniel park576969a2018-03-09 07:07:41 +0900255 ExternalPeerRouter externalPeerRouter,
daniel parkb5817102018-02-15 00:18:51 +0900256 TpPort patPort,
Hyunsun Moon44aac662017-02-18 02:07:01 +0900257 InboundPacket packetIn) {
258 IPv4 iPacket = (IPv4) packetIn.parsed().getPayload();
259 IpAddress internalIp = IpAddress.valueOf(iPacket.getSourceAddress());
260
261 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
262 .matchEthType(Ethernet.TYPE_IPV4)
263 .matchIPProtocol(iPacket.getProtocol())
Jian Li6a47fd02018-11-27 21:51:03 +0900264 .matchIPDst(IpPrefix.valueOf(externalIp.getIp4Address(), VM_PREFIX))
265 .matchIPSrc(IpPrefix.valueOf(iPacket.getDestinationAddress(), VM_PREFIX));
Hyunsun Moon44aac662017-02-18 02:07:01 +0900266
267 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder()
Hyunsun Moon44aac662017-02-18 02:07:01 +0900268 .setEthDst(packetIn.parsed().getSourceMAC())
269 .setIpDst(internalIp);
270
Jian Li5e2ad4a2018-07-16 13:40:53 +0900271 if (!externalPeerRouter.vlanId().equals(VlanId.NONE)) {
272 sBuilder.matchVlanId(externalPeerRouter.vlanId());
daniel park576969a2018-03-09 07:07:41 +0900273 tBuilder.popVlan();
274 }
275
daniel parkee8700b2017-05-11 15:50:03 +0900276 switch (networkType) {
277 case VXLAN:
278 tBuilder.setTunnelId(Long.parseLong(segmentId));
279 break;
280 case VLAN:
281 tBuilder.pushVlan()
Daniel Parkc64b4c62018-05-09 18:13:39 +0900282 .setVlanId(VlanId.vlanId(segmentId));
daniel parkee8700b2017-05-11 15:50:03 +0900283 break;
284 default:
Jian Li71670d12018-03-02 21:31:07 +0900285 final String error = String.format("%s %s",
286 ERR_UNSUPPORTED_NET_TYPE, networkType.toString());
daniel parkee8700b2017-05-11 15:50:03 +0900287 throw new IllegalStateException(error);
288 }
289
290
Hyunsun Moon44aac662017-02-18 02:07:01 +0900291 switch (iPacket.getProtocol()) {
292 case IPv4.PROTOCOL_TCP:
293 TCP tcpPacket = (TCP) iPacket.getPayload();
294 sBuilder.matchTcpSrc(TpPort.tpPort(tcpPacket.getDestinationPort()))
295 .matchTcpDst(patPort);
296 tBuilder.setTcpDst(TpPort.tpPort(tcpPacket.getSourcePort()));
297 break;
298 case IPv4.PROTOCOL_UDP:
299 UDP udpPacket = (UDP) iPacket.getPayload();
300 sBuilder.matchUdpSrc(TpPort.tpPort(udpPacket.getDestinationPort()))
301 .matchUdpDst(patPort);
302 tBuilder.setUdpDst(TpPort.tpPort(udpPacket.getSourcePort()));
303 break;
304 default:
305 break;
306 }
307
Hyunsun Moon0d457362017-06-27 17:19:41 +0900308 OpenstackNode srcNode = osNodeService.node(srcInstPort.deviceId());
309 osNodeService.completeNodes(GATEWAY).forEach(gNode -> {
Jian Li6a47fd02018-11-27 21:51:03 +0900310 TrafficTreatment treatment =
311 getDownStreamTreatment(networkType, tBuilder, gNode, srcNode);
sanghodc375372017-06-08 10:41:30 +0900312 osFlowRuleService.setRule(
313 appId,
Hyunsun Moon0d457362017-06-27 17:19:41 +0900314 gNode.intgBridge(),
sanghodc375372017-06-08 10:41:30 +0900315 sBuilder.build(),
Jian Li6a47fd02018-11-27 21:51:03 +0900316 treatment,
sanghodc375372017-06-08 10:41:30 +0900317 PRIORITY_SNAT_RULE,
318 GW_COMMON_TABLE,
319 true);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900320 });
321 }
322
Jian Li6a47fd02018-11-27 21:51:03 +0900323 private TrafficTreatment getDownStreamTreatment(NetworkType networkType,
324 TrafficTreatment.Builder tBuilder,
325 OpenstackNode gNode,
326 OpenstackNode srcNode) {
327 TrafficTreatment.Builder tmpBuilder =
328 DefaultTrafficTreatment.builder(tBuilder.build());
329 switch (networkType) {
330 case VXLAN:
331 tmpBuilder.extension(RulePopulatorUtil.buildExtension(
332 deviceService,
333 gNode.intgBridge(),
334 srcNode.dataIp().getIp4Address()), gNode.intgBridge())
335 .setOutput(gNode.tunnelPortNum());
336 break;
337 case VLAN:
338 tmpBuilder.setOutput(gNode.vlanPortNum());
339 break;
340 default:
341 final String error = String.format("%s %s",
342 ERR_UNSUPPORTED_NET_TYPE, networkType.toString());
343 throw new IllegalStateException(error);
344 }
345
346 return tmpBuilder.build();
347 }
348
Jian Li5ecfd1a2018-12-10 11:41:03 +0900349 private void setUpstreamRules(String segmentId,
350 NetworkType networkType,
351 IpAddress externalIp,
352 ExternalPeerRouter externalPeerRouter,
daniel parkb5817102018-02-15 00:18:51 +0900353 TpPort patPort,
Hyunsun Moon44aac662017-02-18 02:07:01 +0900354 InboundPacket packetIn) {
355 IPv4 iPacket = (IPv4) packetIn.parsed().getPayload();
daniel parkee8700b2017-05-11 15:50:03 +0900356
357 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
Hyunsun Moon44aac662017-02-18 02:07:01 +0900358 .matchEthType(Ethernet.TYPE_IPV4)
359 .matchIPProtocol(iPacket.getProtocol())
Jian Li6a47fd02018-11-27 21:51:03 +0900360 .matchIPSrc(IpPrefix.valueOf(iPacket.getSourceAddress(), VM_PREFIX))
361 .matchIPDst(IpPrefix.valueOf(iPacket.getDestinationAddress(), VM_PREFIX));
Hyunsun Moon44aac662017-02-18 02:07:01 +0900362
363 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
daniel parkee8700b2017-05-11 15:50:03 +0900364
365 switch (networkType) {
366 case VXLAN:
367 sBuilder.matchTunnelId(Long.parseLong(segmentId));
368 break;
369 case VLAN:
370 sBuilder.matchVlanId(VlanId.vlanId(segmentId));
371 tBuilder.popVlan();
372 break;
373 default:
Jian Li71670d12018-03-02 21:31:07 +0900374 final String error = String.format("%s %s",
375 ERR_UNSUPPORTED_NET_TYPE, networkType.toString());
daniel parkee8700b2017-05-11 15:50:03 +0900376 throw new IllegalStateException(error);
377 }
378
Hyunsun Moon44aac662017-02-18 02:07:01 +0900379 switch (iPacket.getProtocol()) {
380 case IPv4.PROTOCOL_TCP:
381 TCP tcpPacket = (TCP) iPacket.getPayload();
382 sBuilder.matchTcpSrc(TpPort.tpPort(tcpPacket.getSourcePort()))
383 .matchTcpDst(TpPort.tpPort(tcpPacket.getDestinationPort()));
Jian Li6a47fd02018-11-27 21:51:03 +0900384 tBuilder.setTcpSrc(patPort).setEthDst(externalPeerRouter.macAddress());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900385 break;
386 case IPv4.PROTOCOL_UDP:
387 UDP udpPacket = (UDP) iPacket.getPayload();
388 sBuilder.matchUdpSrc(TpPort.tpPort(udpPacket.getSourcePort()))
389 .matchUdpDst(TpPort.tpPort(udpPacket.getDestinationPort()));
Jian Li6a47fd02018-11-27 21:51:03 +0900390 tBuilder.setUdpSrc(patPort).setEthDst(externalPeerRouter.macAddress());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900391 break;
392 default:
393 log.debug("Unsupported IPv4 protocol {}");
394 break;
395 }
396
Jian Li5e2ad4a2018-07-16 13:40:53 +0900397 if (!externalPeerRouter.vlanId().equals(VlanId.NONE)) {
398 tBuilder.pushVlan().setVlanId(externalPeerRouter.vlanId());
daniel park576969a2018-03-09 07:07:41 +0900399 }
400
Hyunsun Moon44aac662017-02-18 02:07:01 +0900401 tBuilder.setIpSrc(externalIp);
Hyunsun Moon0d457362017-06-27 17:19:41 +0900402 osNodeService.completeNodes(GATEWAY).forEach(gNode -> {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900403 TrafficTreatment.Builder tmpBuilder =
404 DefaultTrafficTreatment.builder(tBuilder.build());
daniel parkb5817102018-02-15 00:18:51 +0900405 tmpBuilder.setOutput(gNode.uplinkPortNum());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900406
sanghodc375372017-06-08 10:41:30 +0900407 osFlowRuleService.setRule(
408 appId,
Hyunsun Moon0d457362017-06-27 17:19:41 +0900409 gNode.intgBridge(),
sanghodc375372017-06-08 10:41:30 +0900410 sBuilder.build(),
411 tmpBuilder.build(),
412 PRIORITY_SNAT_RULE,
413 GW_COMMON_TABLE,
414 true);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900415 });
416 }
417
418 private void packetOut(Ethernet ethPacketIn, DeviceId srcDevice, int patPort,
daniel park576969a2018-03-09 07:07:41 +0900419 IpAddress externalIp, ExternalPeerRouter externalPeerRouter) {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900420 IPv4 iPacket = (IPv4) ethPacketIn.getPayload();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900421 switch (iPacket.getProtocol()) {
422 case IPv4.PROTOCOL_TCP:
Jian Li6a47fd02018-11-27 21:51:03 +0900423 iPacket.setPayload(buildPacketOutTcp(iPacket, patPort));
Hyunsun Moon44aac662017-02-18 02:07:01 +0900424 break;
425 case IPv4.PROTOCOL_UDP:
Jian Li6a47fd02018-11-27 21:51:03 +0900426 iPacket.setPayload(buildPacketOutUdp(iPacket, patPort));
Hyunsun Moon44aac662017-02-18 02:07:01 +0900427 break;
428 default:
429 log.trace("Temporally, this method can process UDP and TCP protocol.");
430 return;
431 }
432
433 iPacket.setSourceAddress(externalIp.toString());
434 iPacket.resetChecksum();
435 iPacket.setParent(ethPacketIn);
daniel park576969a2018-03-09 07:07:41 +0900436 ethPacketIn.setSourceMACAddress(DEFAULT_GATEWAY_MAC);
Jian Li5e2ad4a2018-07-16 13:40:53 +0900437 ethPacketIn.setDestinationMACAddress(externalPeerRouter.macAddress());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900438 ethPacketIn.setPayload(iPacket);
daniel park576969a2018-03-09 07:07:41 +0900439
Jian Li5e2ad4a2018-07-16 13:40:53 +0900440 if (!externalPeerRouter.vlanId().equals(VlanId.NONE)) {
441 ethPacketIn.setVlanID(externalPeerRouter.vlanId().toShort());
daniel park576969a2018-03-09 07:07:41 +0900442 }
443
daniel parkee8700b2017-05-11 15:50:03 +0900444 ethPacketIn.resetChecksum();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900445
Hyunsun Moon0d457362017-06-27 17:19:41 +0900446 OpenstackNode srcNode = osNodeService.node(srcDevice);
447 if (srcNode == null) {
448 final String error = String.format("Cannot find openstack node for %s",
449 srcDevice);
450 throw new IllegalStateException(error);
451 }
daniel parkee8700b2017-05-11 15:50:03 +0900452
daniel park576969a2018-03-09 07:07:41 +0900453 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
454
Hyunsun Moon44aac662017-02-18 02:07:01 +0900455 packetService.emit(new DefaultOutboundPacket(
456 srcDevice,
daniel park576969a2018-03-09 07:07:41 +0900457 tBuilder.setOutput(srcNode.uplinkPortNum()).build(),
Hyunsun Moon44aac662017-02-18 02:07:01 +0900458 ByteBuffer.wrap(ethPacketIn.serialize())));
459 }
460
Jian Li6a47fd02018-11-27 21:51:03 +0900461 private TCP buildPacketOutTcp(IPv4 iPacket, int patPort) {
462 TCP tcpPacket = (TCP) iPacket.getPayload();
463 tcpPacket.setSourcePort(patPort);
464 tcpPacket.resetChecksum();
465 tcpPacket.setParent(iPacket);
466
467 return tcpPacket;
468 }
469
470 private UDP buildPacketOutUdp(IPv4 iPacket, int patPort) {
471 UDP udpPacket = (UDP) iPacket.getPayload();
472 udpPacket.setSourcePort(patPort);
473 udpPacket.resetChecksum();
474 udpPacket.setParent(iPacket);
475
476 return udpPacket;
477 }
478
Hyunsun Moon0e058f22017-04-19 17:00:52 +0900479 private int getPortNum() {
daniel park0bc7fdb2017-03-13 14:20:08 +0900480 if (unUsedPortNumSet.isEmpty()) {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900481 clearPortNumMap();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900482 }
daniel park0bc7fdb2017-03-13 14:20:08 +0900483 int portNum = findUnusedPortNum();
daniel park0bc7fdb2017-03-13 14:20:08 +0900484 if (portNum != 0) {
Hyunsun Moon0e058f22017-04-19 17:00:52 +0900485 unUsedPortNumSet.remove(portNum);
486 allocatedPortNumMap.put(portNum, System.currentTimeMillis());
daniel park0bc7fdb2017-03-13 14:20:08 +0900487 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900488 return portNum;
489 }
490
491 private int findUnusedPortNum() {
Hyunsun Moon0e058f22017-04-19 17:00:52 +0900492 return unUsedPortNumSet.stream().findAny().orElse(0);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900493 }
494
495 private void clearPortNumMap() {
daniel park0bc7fdb2017-03-13 14:20:08 +0900496 allocatedPortNumMap.entrySet().forEach(e -> {
Hyunsun Moon0e058f22017-04-19 17:00:52 +0900497 if (System.currentTimeMillis() - e.getValue().value() > TIME_OUT_SNAT_PORT_MS) {
daniel park0bc7fdb2017-03-13 14:20:08 +0900498 allocatedPortNumMap.remove(e.getKey());
499 unUsedPortNumSet.add(e.getKey());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900500 }
501 });
502 }
503
504 private class InternalPacketProcessor implements PacketProcessor {
505
506 @Override
507 public void process(PacketContext context) {
Jian Li34220ea2018-11-14 01:30:24 +0900508
Hyunsun Moon44aac662017-02-18 02:07:01 +0900509 if (context.isHandled()) {
510 return;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900511 }
512
513 InboundPacket pkt = context.inPacket();
514 Ethernet eth = pkt.parsed();
515 if (eth == null || eth.getEtherType() == Ethernet.TYPE_ARP) {
516 return;
517 }
518
519 IPv4 iPacket = (IPv4) eth.getPayload();
520 switch (iPacket.getProtocol()) {
521 case IPv4.PROTOCOL_ICMP:
522 break;
523 case IPv4.PROTOCOL_UDP:
524 UDP udpPacket = (UDP) iPacket.getPayload();
525 if (udpPacket.getDestinationPort() == UDP.DHCP_SERVER_PORT &&
526 udpPacket.getSourcePort() == UDP.DHCP_CLIENT_PORT) {
Jian Li6a47fd02018-11-27 21:51:03 +0900527 break; // don't process DHCP
Hyunsun Moon44aac662017-02-18 02:07:01 +0900528 }
529 default:
Jian Li34220ea2018-11-14 01:30:24 +0900530 eventExecutor.execute(() -> {
Jian Li34220ea2018-11-14 01:30:24 +0900531 if (!isRelevantHelper(context)) {
532 return;
533 }
Jian Li34220ea2018-11-14 01:30:24 +0900534 processSnatPacket(context, eth);
535 });
Hyunsun Moon44aac662017-02-18 02:07:01 +0900536 break;
537 }
538 }
Jian Li34220ea2018-11-14 01:30:24 +0900539
540 private boolean isRelevantHelper(PacketContext context) {
541 Set<DeviceId> gateways = osNodeService.completeNodes(GATEWAY)
542 .stream().map(OpenstackNode::intgBridge)
543 .collect(Collectors.toSet());
544
545 return gateways.contains(context.inPacket().receivedFrom().deviceId());
546 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900547 }
548}