blob: d1072cb7ca8d482a6e7d09158f0267b0fd6c20fa [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;
Jian Li2d68c192018-12-13 15:52:59 +090030import org.onosproject.net.PortNumber;
Hyunsun Moon44aac662017-02-18 02:07:01 +090031import org.onosproject.net.device.DeviceService;
32import org.onosproject.net.flow.DefaultTrafficSelector;
33import org.onosproject.net.flow.DefaultTrafficTreatment;
34import org.onosproject.net.flow.TrafficSelector;
35import org.onosproject.net.flow.TrafficTreatment;
Hyunsun Moon44aac662017-02-18 02:07:01 +090036import org.onosproject.net.packet.DefaultOutboundPacket;
37import org.onosproject.net.packet.InboundPacket;
38import org.onosproject.net.packet.PacketContext;
39import org.onosproject.net.packet.PacketProcessor;
40import org.onosproject.net.packet.PacketService;
daniel park576969a2018-03-09 07:07:41 +090041import org.onosproject.openstacknetworking.api.ExternalPeerRouter;
Hyunsun Moon44aac662017-02-18 02:07:01 +090042import org.onosproject.openstacknetworking.api.InstancePort;
43import org.onosproject.openstacknetworking.api.InstancePortService;
sanghodc375372017-06-08 10:41:30 +090044import org.onosproject.openstacknetworking.api.OpenstackFlowRuleService;
SONA Project6bc5c4a2018-12-14 23:49:52 +090045import org.onosproject.openstacknetworking.api.OpenstackNetwork.Type;
Hyunsun Moon44aac662017-02-18 02:07:01 +090046import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
sanghodc375372017-06-08 10:41:30 +090047import org.onosproject.openstacknetworking.api.OpenstackRouterService;
Jian Li26949762018-03-30 15:46:37 +090048import org.onosproject.openstacknetworking.util.RulePopulatorUtil;
Hyunsun Moon0d457362017-06-27 17:19:41 +090049import org.onosproject.openstacknode.api.OpenstackNode;
50import org.onosproject.openstacknode.api.OpenstackNodeService;
Hyunsun Moon44aac662017-02-18 02:07:01 +090051import org.onosproject.store.serializers.KryoNamespaces;
52import org.onosproject.store.service.ConsistentMap;
daniel park0bc7fdb2017-03-13 14:20:08 +090053import org.onosproject.store.service.DistributedSet;
Hyunsun Moon44aac662017-02-18 02:07:01 +090054import org.onosproject.store.service.Serializer;
55import org.onosproject.store.service.StorageService;
Hyunsun Moon44aac662017-02-18 02:07:01 +090056import org.openstack4j.model.network.IP;
57import org.openstack4j.model.network.Network;
58import org.openstack4j.model.network.Port;
Hyunsun Moon44aac662017-02-18 02:07:01 +090059import org.openstack4j.model.network.Subnet;
Jian Liebde74d2018-11-14 00:18:57 +090060import org.osgi.service.component.annotations.Activate;
61import org.osgi.service.component.annotations.Component;
62import org.osgi.service.component.annotations.Deactivate;
63import org.osgi.service.component.annotations.Reference;
64import org.osgi.service.component.annotations.ReferenceCardinality;
Hyunsun Moon44aac662017-02-18 02:07:01 +090065import org.slf4j.Logger;
66
67import java.nio.ByteBuffer;
Hyunsun Moon0d457362017-06-27 17:19:41 +090068import java.util.Set;
Hyunsun Moon44aac662017-02-18 02:07:01 +090069import java.util.concurrent.ExecutorService;
Hyunsun Moon0d457362017-06-27 17:19:41 +090070import java.util.stream.Collectors;
Hyunsun Moon44aac662017-02-18 02:07:01 +090071
72import static java.util.concurrent.Executors.newSingleThreadExecutor;
73import static org.onlab.util.Tools.groupedThreads;
daniel parkeeb8e042018-02-21 14:06:58 +090074import static org.onosproject.openstacknetworking.api.Constants.DEFAULT_GATEWAY_MAC;
75import static org.onosproject.openstacknetworking.api.Constants.GW_COMMON_TABLE;
76import static org.onosproject.openstacknetworking.api.Constants.OPENSTACK_NETWORKING_APP_ID;
77import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_SNAT_RULE;
Jian Liebde74d2018-11-14 00:18:57 +090078import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.externalIpFromSubnet;
79import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.externalPeerRouterFromSubnet;
Jian Li2d68c192018-12-13 15:52:59 +090080import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.tunnelPortNumByNetType;
Hyunsun Moon0d457362017-06-27 17:19:41 +090081import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.GATEWAY;
Hyunsun Moon44aac662017-02-18 02:07:01 +090082import static org.slf4j.LoggerFactory.getLogger;
83
84/**
85 * Handle packets needs SNAT.
86 */
87@Component(immediate = true)
88public class OpenstackRoutingSnatHandler {
89
90 private final Logger log = getLogger(getClass());
91
92 private static final String ERR_PACKETIN = "Failed to handle packet in: ";
daniel parkee8700b2017-05-11 15:50:03 +090093 private static final String ERR_UNSUPPORTED_NET_TYPE = "Unsupported network type";
Ray Milkey3717e602018-02-01 13:49:47 -080094 private static final long TIME_OUT_SNAT_PORT_MS = 120L * 1000L;
sangho79d6a832017-05-02 14:53:46 +090095 private static final int TP_PORT_MINIMUM_NUM = 65000;
Hyunsun Moon44aac662017-02-18 02:07:01 +090096 private static final int TP_PORT_MAXIMUM_NUM = 65535;
Jian Li6a47fd02018-11-27 21:51:03 +090097 private static final int VM_PREFIX = 32;
Hyunsun Moon44aac662017-02-18 02:07:01 +090098
99 private static final KryoNamespace.Builder NUMBER_SERIALIZER = KryoNamespace.newBuilder()
100 .register(KryoNamespaces.API);
101
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700102 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon44aac662017-02-18 02:07:01 +0900103 protected CoreService coreService;
104
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700105 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon44aac662017-02-18 02:07:01 +0900106 protected PacketService packetService;
107
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700108 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon44aac662017-02-18 02:07:01 +0900109 protected StorageService storageService;
110
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700111 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon44aac662017-02-18 02:07:01 +0900112 protected DeviceService deviceService;
113
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700114 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon44aac662017-02-18 02:07:01 +0900115 protected InstancePortService instancePortService;
116
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700117 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon44aac662017-02-18 02:07:01 +0900118 protected OpenstackNodeService osNodeService;
119
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700120 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon44aac662017-02-18 02:07:01 +0900121 protected OpenstackNetworkService osNetworkService;
122
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700123 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon44aac662017-02-18 02:07:01 +0900124 protected OpenstackRouterService osRouterService;
125
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700126 @Reference(cardinality = ReferenceCardinality.MANDATORY)
sanghodc375372017-06-08 10:41:30 +0900127 protected OpenstackFlowRuleService osFlowRuleService;
128
Hyunsun Moon44aac662017-02-18 02:07:01 +0900129 private final ExecutorService eventExecutor = newSingleThreadExecutor(
130 groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
Hyunsun Moon0d457362017-06-27 17:19:41 +0900131 private final PacketProcessor packetProcessor = new InternalPacketProcessor();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900132
daniel park0bc7fdb2017-03-13 14:20:08 +0900133 private ConsistentMap<Integer, Long> allocatedPortNumMap;
134 private DistributedSet<Integer> unUsedPortNumSet;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900135 private ApplicationId appId;
136
137 @Activate
138 protected void activate() {
139 appId = coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
daniel park0bc7fdb2017-03-13 14:20:08 +0900140
141 allocatedPortNumMap = storageService.<Integer, Long>consistentMapBuilder()
Hyunsun Moon44aac662017-02-18 02:07:01 +0900142 .withSerializer(Serializer.using(NUMBER_SERIALIZER.build()))
daniel park0bc7fdb2017-03-13 14:20:08 +0900143 .withName("openstackrouting-allocatedportnummap")
Hyunsun Moon44aac662017-02-18 02:07:01 +0900144 .withApplicationId(appId)
145 .build();
146
daniel park0bc7fdb2017-03-13 14:20:08 +0900147 unUsedPortNumSet = storageService.<Integer>setBuilder()
148 .withName("openstackrouting-unusedportnumset")
149 .withSerializer(Serializer.using(KryoNamespaces.API))
150 .build()
151 .asDistributedSet();
152
153 initializeUnusedPortNumSet();
154
Hyunsun Moon44aac662017-02-18 02:07:01 +0900155 packetService.addProcessor(packetProcessor, PacketProcessor.director(1));
156 log.info("Started");
157 }
158
daniel park0bc7fdb2017-03-13 14:20:08 +0900159 private void initializeUnusedPortNumSet() {
160 for (int i = TP_PORT_MINIMUM_NUM; i < TP_PORT_MAXIMUM_NUM; i++) {
Hyunsun Moon0e058f22017-04-19 17:00:52 +0900161 if (!allocatedPortNumMap.containsKey(i)) {
162 unUsedPortNumSet.add(i);
daniel park0bc7fdb2017-03-13 14:20:08 +0900163 }
164 }
165
166 clearPortNumMap();
167 }
168
Hyunsun Moon44aac662017-02-18 02:07:01 +0900169 @Deactivate
170 protected void deactivate() {
171 packetService.removeProcessor(packetProcessor);
172 eventExecutor.shutdown();
173 log.info("Stopped");
174 }
175
176 private void processSnatPacket(PacketContext context, Ethernet eth) {
177 IPv4 iPacket = (IPv4) eth.getPayload();
178 InboundPacket packetIn = context.inPacket();
179
Hyunsun Moon0e058f22017-04-19 17:00:52 +0900180 int patPort = getPortNum();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900181
182 InstancePort srcInstPort = instancePortService.instancePort(eth.getSourceMAC());
183 if (srcInstPort == null) {
Hyunsun Moon0e058f22017-04-19 17:00:52 +0900184 log.error(ERR_PACKETIN + "source host(MAC:{}) does not exist",
Hyunsun Moon44aac662017-02-18 02:07:01 +0900185 eth.getSourceMAC());
186 return;
187 }
Hyunsun Moon0e058f22017-04-19 17:00:52 +0900188
Hyunsun Moon44aac662017-02-18 02:07:01 +0900189 IpAddress srcIp = IpAddress.valueOf(iPacket.getSourceAddress());
190 Subnet srcSubnet = getSourceSubnet(srcInstPort, srcIp);
Jian Liebde74d2018-11-14 00:18:57 +0900191 IpAddress externalGatewayIp =
192 externalIpFromSubnet(srcSubnet, osRouterService, osNetworkService);
daniel parkb5817102018-02-15 00:18:51 +0900193
Hyunsun Moon44aac662017-02-18 02:07:01 +0900194 if (externalGatewayIp == null) {
195 return;
196 }
197
Jian Liebde74d2018-11-14 00:18:57 +0900198 ExternalPeerRouter externalPeerRouter =
199 externalPeerRouterFromSubnet(srcSubnet, osRouterService, osNetworkService);
daniel park576969a2018-03-09 07:07:41 +0900200 if (externalPeerRouter == null) {
daniel parkb5817102018-02-15 00:18:51 +0900201 return;
202 }
203
Hyunsun Moon44aac662017-02-18 02:07:01 +0900204 populateSnatFlowRules(context.inPacket(),
205 srcInstPort,
206 TpPort.tpPort(patPort),
daniel park576969a2018-03-09 07:07:41 +0900207 externalGatewayIp, externalPeerRouter);
208
Hyunsun Moon44aac662017-02-18 02:07:01 +0900209
Ray Milkeyf0c47612017-09-28 11:29:38 -0700210 packetOut(eth.duplicate(),
Hyunsun Moon44aac662017-02-18 02:07:01 +0900211 packetIn.receivedFrom().deviceId(),
212 patPort,
daniel park576969a2018-03-09 07:07:41 +0900213 externalGatewayIp, externalPeerRouter);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900214 }
215
216 private Subnet getSourceSubnet(InstancePort instance, IpAddress srcIp) {
217 Port osPort = osNetworkService.port(instance.portId());
218 IP fixedIp = osPort.getFixedIps().stream()
219 .filter(ip -> IpAddress.valueOf(ip.getIpAddress()).equals(srcIp))
220 .findAny().orElse(null);
221 if (fixedIp == null) {
222 return null;
223 }
224 return osNetworkService.subnet(fixedIp.getSubnetId());
225 }
226
Jian Li5ecfd1a2018-12-10 11:41:03 +0900227 private void populateSnatFlowRules(InboundPacket packetIn,
228 InstancePort srcInstPort,
229 TpPort patPort, IpAddress externalIp,
230 ExternalPeerRouter externalPeerRouter) {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900231 Network osNet = osNetworkService.network(srcInstPort.networkId());
SONA Project6bc5c4a2018-12-14 23:49:52 +0900232 Type netType = osNetworkService.networkType(srcInstPort.networkId());
233
Hyunsun Moon44aac662017-02-18 02:07:01 +0900234 if (osNet == null) {
Jian Li71670d12018-03-02 21:31:07 +0900235 final String error = String.format("%s network %s not found",
236 ERR_PACKETIN, srcInstPort.networkId());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900237 throw new IllegalStateException(error);
238 }
239
240 setDownstreamRules(srcInstPort,
daniel parkee8700b2017-05-11 15:50:03 +0900241 osNet.getProviderSegID(),
SONA Project6bc5c4a2018-12-14 23:49:52 +0900242 netType,
Hyunsun Moon44aac662017-02-18 02:07:01 +0900243 externalIp,
daniel park576969a2018-03-09 07:07:41 +0900244 externalPeerRouter,
Hyunsun Moon44aac662017-02-18 02:07:01 +0900245 patPort,
246 packetIn);
247
daniel parkee8700b2017-05-11 15:50:03 +0900248 setUpstreamRules(osNet.getProviderSegID(),
SONA Project6bc5c4a2018-12-14 23:49:52 +0900249 netType,
Hyunsun Moon44aac662017-02-18 02:07:01 +0900250 externalIp,
daniel park576969a2018-03-09 07:07:41 +0900251 externalPeerRouter,
Hyunsun Moon44aac662017-02-18 02:07:01 +0900252 patPort,
253 packetIn);
254 }
255
Hyunsun Moon0d457362017-06-27 17:19:41 +0900256 private void setDownstreamRules(InstancePort srcInstPort, String segmentId,
SONA Project6bc5c4a2018-12-14 23:49:52 +0900257 Type networkType,
daniel parkb5817102018-02-15 00:18:51 +0900258 IpAddress externalIp,
daniel park576969a2018-03-09 07:07:41 +0900259 ExternalPeerRouter externalPeerRouter,
daniel parkb5817102018-02-15 00:18:51 +0900260 TpPort patPort,
Hyunsun Moon44aac662017-02-18 02:07:01 +0900261 InboundPacket packetIn) {
262 IPv4 iPacket = (IPv4) packetIn.parsed().getPayload();
263 IpAddress internalIp = IpAddress.valueOf(iPacket.getSourceAddress());
264
265 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
266 .matchEthType(Ethernet.TYPE_IPV4)
267 .matchIPProtocol(iPacket.getProtocol())
Jian Li6a47fd02018-11-27 21:51:03 +0900268 .matchIPDst(IpPrefix.valueOf(externalIp.getIp4Address(), VM_PREFIX))
269 .matchIPSrc(IpPrefix.valueOf(iPacket.getDestinationAddress(), VM_PREFIX));
Hyunsun Moon44aac662017-02-18 02:07:01 +0900270
271 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder()
Hyunsun Moon44aac662017-02-18 02:07:01 +0900272 .setEthDst(packetIn.parsed().getSourceMAC())
273 .setIpDst(internalIp);
274
Jian Li5e2ad4a2018-07-16 13:40:53 +0900275 if (!externalPeerRouter.vlanId().equals(VlanId.NONE)) {
276 sBuilder.matchVlanId(externalPeerRouter.vlanId());
daniel park576969a2018-03-09 07:07:41 +0900277 tBuilder.popVlan();
278 }
279
daniel parkee8700b2017-05-11 15:50:03 +0900280 switch (networkType) {
281 case VXLAN:
Jian Li2d68c192018-12-13 15:52:59 +0900282 case GRE:
daniel parkee8700b2017-05-11 15:50:03 +0900283 tBuilder.setTunnelId(Long.parseLong(segmentId));
284 break;
285 case VLAN:
286 tBuilder.pushVlan()
Daniel Parkc64b4c62018-05-09 18:13:39 +0900287 .setVlanId(VlanId.vlanId(segmentId));
daniel parkee8700b2017-05-11 15:50:03 +0900288 break;
289 default:
Jian Li71670d12018-03-02 21:31:07 +0900290 final String error = String.format("%s %s",
291 ERR_UNSUPPORTED_NET_TYPE, networkType.toString());
daniel parkee8700b2017-05-11 15:50:03 +0900292 throw new IllegalStateException(error);
293 }
294
295
Hyunsun Moon44aac662017-02-18 02:07:01 +0900296 switch (iPacket.getProtocol()) {
297 case IPv4.PROTOCOL_TCP:
298 TCP tcpPacket = (TCP) iPacket.getPayload();
299 sBuilder.matchTcpSrc(TpPort.tpPort(tcpPacket.getDestinationPort()))
300 .matchTcpDst(patPort);
301 tBuilder.setTcpDst(TpPort.tpPort(tcpPacket.getSourcePort()));
302 break;
303 case IPv4.PROTOCOL_UDP:
304 UDP udpPacket = (UDP) iPacket.getPayload();
305 sBuilder.matchUdpSrc(TpPort.tpPort(udpPacket.getDestinationPort()))
306 .matchUdpDst(patPort);
307 tBuilder.setUdpDst(TpPort.tpPort(udpPacket.getSourcePort()));
308 break;
309 default:
310 break;
311 }
312
Hyunsun Moon0d457362017-06-27 17:19:41 +0900313 OpenstackNode srcNode = osNodeService.node(srcInstPort.deviceId());
314 osNodeService.completeNodes(GATEWAY).forEach(gNode -> {
Jian Li6a47fd02018-11-27 21:51:03 +0900315 TrafficTreatment treatment =
316 getDownStreamTreatment(networkType, tBuilder, gNode, srcNode);
sanghodc375372017-06-08 10:41:30 +0900317 osFlowRuleService.setRule(
318 appId,
Hyunsun Moon0d457362017-06-27 17:19:41 +0900319 gNode.intgBridge(),
sanghodc375372017-06-08 10:41:30 +0900320 sBuilder.build(),
Jian Li6a47fd02018-11-27 21:51:03 +0900321 treatment,
sanghodc375372017-06-08 10:41:30 +0900322 PRIORITY_SNAT_RULE,
323 GW_COMMON_TABLE,
324 true);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900325 });
326 }
327
SONA Project6bc5c4a2018-12-14 23:49:52 +0900328 private TrafficTreatment getDownStreamTreatment(Type networkType,
Jian Li6a47fd02018-11-27 21:51:03 +0900329 TrafficTreatment.Builder tBuilder,
330 OpenstackNode gNode,
331 OpenstackNode srcNode) {
332 TrafficTreatment.Builder tmpBuilder =
333 DefaultTrafficTreatment.builder(tBuilder.build());
334 switch (networkType) {
335 case VXLAN:
Jian Li2d68c192018-12-13 15:52:59 +0900336 case GRE:
SONA Project6bc5c4a2018-12-14 23:49:52 +0900337 PortNumber portNum = tunnelPortNumByNetType(networkType, gNode);
Jian Li6a47fd02018-11-27 21:51:03 +0900338 tmpBuilder.extension(RulePopulatorUtil.buildExtension(
339 deviceService,
340 gNode.intgBridge(),
341 srcNode.dataIp().getIp4Address()), gNode.intgBridge())
Jian Li2d68c192018-12-13 15:52:59 +0900342 .setOutput(portNum);
Jian Li6a47fd02018-11-27 21:51:03 +0900343 break;
344 case VLAN:
345 tmpBuilder.setOutput(gNode.vlanPortNum());
346 break;
347 default:
348 final String error = String.format("%s %s",
349 ERR_UNSUPPORTED_NET_TYPE, networkType.toString());
350 throw new IllegalStateException(error);
351 }
352
353 return tmpBuilder.build();
354 }
355
Jian Li5ecfd1a2018-12-10 11:41:03 +0900356 private void setUpstreamRules(String segmentId,
SONA Project6bc5c4a2018-12-14 23:49:52 +0900357 Type networkType,
Jian Li5ecfd1a2018-12-10 11:41:03 +0900358 IpAddress externalIp,
359 ExternalPeerRouter externalPeerRouter,
daniel parkb5817102018-02-15 00:18:51 +0900360 TpPort patPort,
Hyunsun Moon44aac662017-02-18 02:07:01 +0900361 InboundPacket packetIn) {
362 IPv4 iPacket = (IPv4) packetIn.parsed().getPayload();
daniel parkee8700b2017-05-11 15:50:03 +0900363
364 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
Hyunsun Moon44aac662017-02-18 02:07:01 +0900365 .matchEthType(Ethernet.TYPE_IPV4)
366 .matchIPProtocol(iPacket.getProtocol())
Jian Li6a47fd02018-11-27 21:51:03 +0900367 .matchIPSrc(IpPrefix.valueOf(iPacket.getSourceAddress(), VM_PREFIX))
368 .matchIPDst(IpPrefix.valueOf(iPacket.getDestinationAddress(), VM_PREFIX));
Hyunsun Moon44aac662017-02-18 02:07:01 +0900369
370 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
daniel parkee8700b2017-05-11 15:50:03 +0900371
372 switch (networkType) {
373 case VXLAN:
Jian Li2d68c192018-12-13 15:52:59 +0900374 case GRE:
daniel parkee8700b2017-05-11 15:50:03 +0900375 sBuilder.matchTunnelId(Long.parseLong(segmentId));
376 break;
377 case VLAN:
378 sBuilder.matchVlanId(VlanId.vlanId(segmentId));
379 tBuilder.popVlan();
380 break;
381 default:
Jian Li71670d12018-03-02 21:31:07 +0900382 final String error = String.format("%s %s",
383 ERR_UNSUPPORTED_NET_TYPE, networkType.toString());
daniel parkee8700b2017-05-11 15:50:03 +0900384 throw new IllegalStateException(error);
385 }
386
Hyunsun Moon44aac662017-02-18 02:07:01 +0900387 switch (iPacket.getProtocol()) {
388 case IPv4.PROTOCOL_TCP:
389 TCP tcpPacket = (TCP) iPacket.getPayload();
390 sBuilder.matchTcpSrc(TpPort.tpPort(tcpPacket.getSourcePort()))
391 .matchTcpDst(TpPort.tpPort(tcpPacket.getDestinationPort()));
Jian Li6a47fd02018-11-27 21:51:03 +0900392 tBuilder.setTcpSrc(patPort).setEthDst(externalPeerRouter.macAddress());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900393 break;
394 case IPv4.PROTOCOL_UDP:
395 UDP udpPacket = (UDP) iPacket.getPayload();
396 sBuilder.matchUdpSrc(TpPort.tpPort(udpPacket.getSourcePort()))
397 .matchUdpDst(TpPort.tpPort(udpPacket.getDestinationPort()));
Jian Li6a47fd02018-11-27 21:51:03 +0900398 tBuilder.setUdpSrc(patPort).setEthDst(externalPeerRouter.macAddress());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900399 break;
400 default:
401 log.debug("Unsupported IPv4 protocol {}");
402 break;
403 }
404
Jian Li5e2ad4a2018-07-16 13:40:53 +0900405 if (!externalPeerRouter.vlanId().equals(VlanId.NONE)) {
406 tBuilder.pushVlan().setVlanId(externalPeerRouter.vlanId());
daniel park576969a2018-03-09 07:07:41 +0900407 }
408
Hyunsun Moon44aac662017-02-18 02:07:01 +0900409 tBuilder.setIpSrc(externalIp);
Hyunsun Moon0d457362017-06-27 17:19:41 +0900410 osNodeService.completeNodes(GATEWAY).forEach(gNode -> {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900411 TrafficTreatment.Builder tmpBuilder =
412 DefaultTrafficTreatment.builder(tBuilder.build());
daniel parkb5817102018-02-15 00:18:51 +0900413 tmpBuilder.setOutput(gNode.uplinkPortNum());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900414
sanghodc375372017-06-08 10:41:30 +0900415 osFlowRuleService.setRule(
416 appId,
Hyunsun Moon0d457362017-06-27 17:19:41 +0900417 gNode.intgBridge(),
sanghodc375372017-06-08 10:41:30 +0900418 sBuilder.build(),
419 tmpBuilder.build(),
420 PRIORITY_SNAT_RULE,
421 GW_COMMON_TABLE,
422 true);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900423 });
424 }
425
426 private void packetOut(Ethernet ethPacketIn, DeviceId srcDevice, int patPort,
daniel park576969a2018-03-09 07:07:41 +0900427 IpAddress externalIp, ExternalPeerRouter externalPeerRouter) {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900428 IPv4 iPacket = (IPv4) ethPacketIn.getPayload();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900429 switch (iPacket.getProtocol()) {
430 case IPv4.PROTOCOL_TCP:
Jian Li6a47fd02018-11-27 21:51:03 +0900431 iPacket.setPayload(buildPacketOutTcp(iPacket, patPort));
Hyunsun Moon44aac662017-02-18 02:07:01 +0900432 break;
433 case IPv4.PROTOCOL_UDP:
Jian Li6a47fd02018-11-27 21:51:03 +0900434 iPacket.setPayload(buildPacketOutUdp(iPacket, patPort));
Hyunsun Moon44aac662017-02-18 02:07:01 +0900435 break;
436 default:
437 log.trace("Temporally, this method can process UDP and TCP protocol.");
438 return;
439 }
440
441 iPacket.setSourceAddress(externalIp.toString());
442 iPacket.resetChecksum();
443 iPacket.setParent(ethPacketIn);
daniel park576969a2018-03-09 07:07:41 +0900444 ethPacketIn.setSourceMACAddress(DEFAULT_GATEWAY_MAC);
Jian Li5e2ad4a2018-07-16 13:40:53 +0900445 ethPacketIn.setDestinationMACAddress(externalPeerRouter.macAddress());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900446 ethPacketIn.setPayload(iPacket);
daniel park576969a2018-03-09 07:07:41 +0900447
Jian Li5e2ad4a2018-07-16 13:40:53 +0900448 if (!externalPeerRouter.vlanId().equals(VlanId.NONE)) {
449 ethPacketIn.setVlanID(externalPeerRouter.vlanId().toShort());
daniel park576969a2018-03-09 07:07:41 +0900450 }
451
daniel parkee8700b2017-05-11 15:50:03 +0900452 ethPacketIn.resetChecksum();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900453
Hyunsun Moon0d457362017-06-27 17:19:41 +0900454 OpenstackNode srcNode = osNodeService.node(srcDevice);
455 if (srcNode == null) {
456 final String error = String.format("Cannot find openstack node for %s",
457 srcDevice);
458 throw new IllegalStateException(error);
459 }
daniel parkee8700b2017-05-11 15:50:03 +0900460
daniel park576969a2018-03-09 07:07:41 +0900461 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
462
Hyunsun Moon44aac662017-02-18 02:07:01 +0900463 packetService.emit(new DefaultOutboundPacket(
464 srcDevice,
daniel park576969a2018-03-09 07:07:41 +0900465 tBuilder.setOutput(srcNode.uplinkPortNum()).build(),
Hyunsun Moon44aac662017-02-18 02:07:01 +0900466 ByteBuffer.wrap(ethPacketIn.serialize())));
467 }
468
Jian Li6a47fd02018-11-27 21:51:03 +0900469 private TCP buildPacketOutTcp(IPv4 iPacket, int patPort) {
470 TCP tcpPacket = (TCP) iPacket.getPayload();
471 tcpPacket.setSourcePort(patPort);
472 tcpPacket.resetChecksum();
473 tcpPacket.setParent(iPacket);
474
475 return tcpPacket;
476 }
477
478 private UDP buildPacketOutUdp(IPv4 iPacket, int patPort) {
479 UDP udpPacket = (UDP) iPacket.getPayload();
480 udpPacket.setSourcePort(patPort);
481 udpPacket.resetChecksum();
482 udpPacket.setParent(iPacket);
483
484 return udpPacket;
485 }
486
Hyunsun Moon0e058f22017-04-19 17:00:52 +0900487 private int getPortNum() {
daniel park0bc7fdb2017-03-13 14:20:08 +0900488 if (unUsedPortNumSet.isEmpty()) {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900489 clearPortNumMap();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900490 }
daniel park0bc7fdb2017-03-13 14:20:08 +0900491 int portNum = findUnusedPortNum();
daniel park0bc7fdb2017-03-13 14:20:08 +0900492 if (portNum != 0) {
Hyunsun Moon0e058f22017-04-19 17:00:52 +0900493 unUsedPortNumSet.remove(portNum);
494 allocatedPortNumMap.put(portNum, System.currentTimeMillis());
daniel park0bc7fdb2017-03-13 14:20:08 +0900495 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900496 return portNum;
497 }
498
499 private int findUnusedPortNum() {
Hyunsun Moon0e058f22017-04-19 17:00:52 +0900500 return unUsedPortNumSet.stream().findAny().orElse(0);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900501 }
502
503 private void clearPortNumMap() {
daniel park0bc7fdb2017-03-13 14:20:08 +0900504 allocatedPortNumMap.entrySet().forEach(e -> {
Hyunsun Moon0e058f22017-04-19 17:00:52 +0900505 if (System.currentTimeMillis() - e.getValue().value() > TIME_OUT_SNAT_PORT_MS) {
daniel park0bc7fdb2017-03-13 14:20:08 +0900506 allocatedPortNumMap.remove(e.getKey());
507 unUsedPortNumSet.add(e.getKey());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900508 }
509 });
510 }
511
512 private class InternalPacketProcessor implements PacketProcessor {
513
514 @Override
515 public void process(PacketContext context) {
Jian Li34220ea2018-11-14 01:30:24 +0900516
Hyunsun Moon44aac662017-02-18 02:07:01 +0900517 if (context.isHandled()) {
518 return;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900519 }
520
521 InboundPacket pkt = context.inPacket();
522 Ethernet eth = pkt.parsed();
523 if (eth == null || eth.getEtherType() == Ethernet.TYPE_ARP) {
524 return;
525 }
526
527 IPv4 iPacket = (IPv4) eth.getPayload();
528 switch (iPacket.getProtocol()) {
529 case IPv4.PROTOCOL_ICMP:
530 break;
531 case IPv4.PROTOCOL_UDP:
532 UDP udpPacket = (UDP) iPacket.getPayload();
533 if (udpPacket.getDestinationPort() == UDP.DHCP_SERVER_PORT &&
534 udpPacket.getSourcePort() == UDP.DHCP_CLIENT_PORT) {
Jian Li6a47fd02018-11-27 21:51:03 +0900535 break; // don't process DHCP
Hyunsun Moon44aac662017-02-18 02:07:01 +0900536 }
537 default:
Jian Li34220ea2018-11-14 01:30:24 +0900538 eventExecutor.execute(() -> {
Jian Li34220ea2018-11-14 01:30:24 +0900539 if (!isRelevantHelper(context)) {
540 return;
541 }
Jian Li34220ea2018-11-14 01:30:24 +0900542 processSnatPacket(context, eth);
543 });
Hyunsun Moon44aac662017-02-18 02:07:01 +0900544 break;
545 }
546 }
Jian Li34220ea2018-11-14 01:30:24 +0900547
548 private boolean isRelevantHelper(PacketContext context) {
549 Set<DeviceId> gateways = osNodeService.completeNodes(GATEWAY)
550 .stream().map(OpenstackNode::intgBridge)
551 .collect(Collectors.toSet());
552
553 return gateways.contains(context.inPacket().receivedFrom().deviceId());
554 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900555 }
556}