blob: f685224ac631b4b30a0e775c73005777e51d82f0 [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 com.google.common.base.Strings;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070019import org.osgi.service.component.annotations.Activate;
20import org.osgi.service.component.annotations.Component;
21import org.osgi.service.component.annotations.Deactivate;
22import org.osgi.service.component.annotations.Reference;
23import org.osgi.service.component.annotations.ReferenceCardinality;
Hyunsun Moon44aac662017-02-18 02:07:01 +090024import org.onlab.packet.Ethernet;
25import org.onlab.packet.ICMP;
Jian Li2fa3ded2018-08-10 02:22:47 +000026import org.onlab.packet.ICMPEcho;
Hyunsun Moon44aac662017-02-18 02:07:01 +090027import org.onlab.packet.IPv4;
28import org.onlab.packet.IpAddress;
29import org.onlab.packet.MacAddress;
daniel park576969a2018-03-09 07:07:41 +090030import org.onlab.packet.VlanId;
sangho247232c2017-08-24 17:22:08 +090031import org.onlab.util.KryoNamespace;
Daniel Park6a2d95e2018-11-05 18:50:16 +090032import org.onosproject.cluster.ClusterService;
33import org.onosproject.cluster.LeadershipService;
34import org.onosproject.cluster.NodeId;
Hyunsun Moon44aac662017-02-18 02:07:01 +090035import org.onosproject.core.ApplicationId;
36import org.onosproject.core.CoreService;
Hyunsun Moon44aac662017-02-18 02:07:01 +090037import org.onosproject.net.DeviceId;
Daniel Park6a2d95e2018-11-05 18:50:16 +090038import org.onosproject.net.flow.DefaultTrafficSelector;
Hyunsun Moon44aac662017-02-18 02:07:01 +090039import org.onosproject.net.flow.DefaultTrafficTreatment;
Daniel Park6a2d95e2018-11-05 18:50:16 +090040import org.onosproject.net.flow.TrafficSelector;
Hyunsun Moon44aac662017-02-18 02:07:01 +090041import org.onosproject.net.flow.TrafficTreatment;
42import org.onosproject.net.packet.DefaultOutboundPacket;
43import org.onosproject.net.packet.InboundPacket;
44import org.onosproject.net.packet.OutboundPacket;
45import org.onosproject.net.packet.PacketContext;
Hyunsun Moon44aac662017-02-18 02:07:01 +090046import org.onosproject.net.packet.PacketProcessor;
47import org.onosproject.net.packet.PacketService;
48import org.onosproject.openstacknetworking.api.Constants;
daniel park576969a2018-03-09 07:07:41 +090049import org.onosproject.openstacknetworking.api.ExternalPeerRouter;
Hyunsun Moon44aac662017-02-18 02:07:01 +090050import org.onosproject.openstacknetworking.api.InstancePort;
51import org.onosproject.openstacknetworking.api.InstancePortService;
Daniel Park6a2d95e2018-11-05 18:50:16 +090052import org.onosproject.openstacknetworking.api.OpenstackFlowRuleService;
Hyunsun Moon44aac662017-02-18 02:07:01 +090053import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
sangho36721992017-08-03 11:13:17 +090054import org.onosproject.openstacknetworking.api.OpenstackRouterService;
Hyunsun Moon0d457362017-06-27 17:19:41 +090055import org.onosproject.openstacknode.api.OpenstackNode;
Daniel Park6a2d95e2018-11-05 18:50:16 +090056import org.onosproject.openstacknode.api.OpenstackNodeEvent;
57import org.onosproject.openstacknode.api.OpenstackNodeListener;
Hyunsun Moon0d457362017-06-27 17:19:41 +090058import org.onosproject.openstacknode.api.OpenstackNodeService;
sangho247232c2017-08-24 17:22:08 +090059import org.onosproject.store.serializers.KryoNamespaces;
60import org.onosproject.store.service.ConsistentMap;
61import org.onosproject.store.service.Serializer;
62import org.onosproject.store.service.StorageService;
Hyunsun Moon44aac662017-02-18 02:07:01 +090063import org.openstack4j.model.network.ExternalGateway;
Hyunsun Moon44aac662017-02-18 02:07:01 +090064import org.openstack4j.model.network.Port;
65import org.openstack4j.model.network.Router;
66import org.openstack4j.model.network.RouterInterface;
67import org.openstack4j.model.network.Subnet;
sangho36721992017-08-03 11:13:17 +090068import org.openstack4j.openstack.networking.domain.NeutronIP;
Hyunsun Moon44aac662017-02-18 02:07:01 +090069import org.slf4j.Logger;
70
71import java.nio.ByteBuffer;
Hyunsun Moon44aac662017-02-18 02:07:01 +090072import java.util.Objects;
sangho36721992017-08-03 11:13:17 +090073import java.util.Optional;
Hyunsun Moon44aac662017-02-18 02:07:01 +090074import java.util.Set;
75import java.util.concurrent.ExecutorService;
76import java.util.stream.Collectors;
77
sangho247232c2017-08-24 17:22:08 +090078import static com.google.common.base.Preconditions.checkArgument;
Daniel Park4d486842018-07-24 17:06:43 +090079import static com.google.common.base.Preconditions.checkNotNull;
Hyunsun Moon44aac662017-02-18 02:07:01 +090080import static java.util.concurrent.Executors.newSingleThreadExecutor;
Daniel Park4d486842018-07-24 17:06:43 +090081import static org.onlab.packet.ICMP.TYPE_ECHO_REPLY;
82import static org.onlab.packet.ICMP.TYPE_ECHO_REQUEST;
Hyunsun Moon44aac662017-02-18 02:07:01 +090083import static org.onlab.util.Tools.groupedThreads;
sangho36721992017-08-03 11:13:17 +090084import static org.onosproject.openstacknetworking.api.Constants.DEFAULT_GATEWAY_MAC;
Daniel Park6a2d95e2018-11-05 18:50:16 +090085import static org.onosproject.openstacknetworking.api.Constants.GW_COMMON_TABLE;
sangho36721992017-08-03 11:13:17 +090086import static org.onosproject.openstacknetworking.api.Constants.OPENSTACK_NETWORKING_APP_ID;
Daniel Park6a2d95e2018-11-05 18:50:16 +090087import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_INTERNAL_ROUTING_RULE;
Hyunsun Moon0d457362017-06-27 17:19:41 +090088import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.GATEWAY;
Hyunsun Moon44aac662017-02-18 02:07:01 +090089import static org.slf4j.LoggerFactory.getLogger;
90
Hyunsun Moon44aac662017-02-18 02:07:01 +090091/**
92 * Handles ICMP packet received from a gateway node.
93 * For a request for virtual network subnet gateway, it generates fake ICMP reply.
94 * For a request for the external network, it does source NAT with the public IP and
95 * forward the request to the external only if the requested virtual subnet has
96 * external connectivity.
97 */
98@Component(immediate = true)
99public class OpenstackRoutingIcmpHandler {
100
101 protected final Logger log = getLogger(getClass());
102
103 private static final String ERR_REQ = "Failed to handle ICMP request: ";
sangho247232c2017-08-24 17:22:08 +0900104 private static final String ERR_DUPLICATE = " already exists";
Hyunsun Moon44aac662017-02-18 02:07:01 +0900105
Jian Li28ec77f2018-10-31 07:07:25 +0900106 private static final String VXLAN = "VXLAN";
107 private static final String VLAN = "VLAN";
108
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700109 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon44aac662017-02-18 02:07:01 +0900110 protected CoreService coreService;
111
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700112 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon44aac662017-02-18 02:07:01 +0900113 protected PacketService packetService;
114
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700115 @Reference(cardinality = ReferenceCardinality.MANDATORY)
sangho247232c2017-08-24 17:22:08 +0900116 protected StorageService storageService;
117
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700118 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon44aac662017-02-18 02:07:01 +0900119 protected OpenstackNodeService osNodeService;
120
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700121 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon44aac662017-02-18 02:07:01 +0900122 protected InstancePortService instancePortService;
123
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700124 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon44aac662017-02-18 02:07:01 +0900125 protected OpenstackNetworkService osNetworkService;
126
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700127 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Hyunsun Moon44aac662017-02-18 02:07:01 +0900128 protected OpenstackRouterService osRouterService;
129
Ray Milkey5739b2c2018-11-06 14:04:51 -0800130 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Daniel Park6a2d95e2018-11-05 18:50:16 +0900131 protected LeadershipService leadershipService;
132
Ray Milkey5739b2c2018-11-06 14:04:51 -0800133 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Daniel Park6a2d95e2018-11-05 18:50:16 +0900134 protected OpenstackFlowRuleService osFlowRuleService;
135
Ray Milkey5739b2c2018-11-06 14:04:51 -0800136 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Daniel Park6a2d95e2018-11-05 18:50:16 +0900137 protected ClusterService clusterService;
138
Hyunsun Moon44aac662017-02-18 02:07:01 +0900139 private final ExecutorService eventExecutor = newSingleThreadExecutor(
140 groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
sangho072c4dd2017-05-17 10:45:21 +0900141 private final InternalPacketProcessor packetProcessor = new InternalPacketProcessor();
sangho247232c2017-08-24 17:22:08 +0900142 private ConsistentMap<String, InstancePort> icmpInfoMap;
Daniel Park6a2d95e2018-11-05 18:50:16 +0900143 private final OpenstackNodeListener osNodeListener = new InternalNodeEventListener();
sangho247232c2017-08-24 17:22:08 +0900144
145 private static final KryoNamespace SERIALIZER_ICMP_MAP = KryoNamespace.newBuilder()
146 .register(KryoNamespaces.API)
147 .register(InstancePort.class)
Jian Liec5c32b2018-07-13 14:28:58 +0900148 .register(DefaultInstancePort.class)
149 .register(InstancePort.State.class)
sangho247232c2017-08-24 17:22:08 +0900150 .build();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900151
152 private ApplicationId appId;
Daniel Park6a2d95e2018-11-05 18:50:16 +0900153 private NodeId localNodeId;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900154
155 @Activate
156 protected void activate() {
157 appId = coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
158 packetService.addProcessor(packetProcessor, PacketProcessor.director(1));
Daniel Park6a2d95e2018-11-05 18:50:16 +0900159 localNodeId = clusterService.getLocalNode().id();
160 leadershipService.runForLeadership(appId.name());
161 osNodeService.addListener(osNodeListener);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900162
sangho247232c2017-08-24 17:22:08 +0900163 icmpInfoMap = storageService.<String, InstancePort>consistentMapBuilder()
164 .withSerializer(Serializer.using(SERIALIZER_ICMP_MAP))
165 .withName("openstack-icmpmap")
166 .withApplicationId(appId)
167 .build();
168
Hyunsun Moon44aac662017-02-18 02:07:01 +0900169 log.info("Started");
170 }
171
172 @Deactivate
173 protected void deactivate() {
174 packetService.removeProcessor(packetProcessor);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900175 eventExecutor.shutdown();
Daniel Park6a2d95e2018-11-05 18:50:16 +0900176 leadershipService.withdraw(appId.name());
177 osNodeService.removeListener(osNodeListener);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900178
179 log.info("Stopped");
180 }
181
Jian Lid9a24ed2018-08-29 20:53:25 +0900182 private boolean handleEchoRequest(DeviceId srcDevice, MacAddress srcMac, IPv4 ipPacket,
Hyunsun Moon44aac662017-02-18 02:07:01 +0900183 ICMP icmp) {
184 InstancePort instPort = instancePortService.instancePort(srcMac);
185 if (instPort == null) {
Daniel Park4d486842018-07-24 17:06:43 +0900186 log.warn(ERR_REQ + "unknown source host(MAC:{})", srcMac);
Jian Lid9a24ed2018-08-29 20:53:25 +0900187 return false;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900188 }
189
190 IpAddress srcIp = IpAddress.valueOf(ipPacket.getSourceAddress());
Daniel Park4d486842018-07-24 17:06:43 +0900191 IpAddress dstIp = IpAddress.valueOf(ipPacket.getDestinationAddress());
192
Hyunsun Moon44aac662017-02-18 02:07:01 +0900193 Subnet srcSubnet = getSourceSubnet(instPort, srcIp);
194 if (srcSubnet == null) {
Daniel Park4d486842018-07-24 17:06:43 +0900195 log.warn(ERR_REQ + "unknown source subnet(IP:{})", srcIp);
Jian Lid9a24ed2018-08-29 20:53:25 +0900196 return false;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900197 }
Daniel Park4d486842018-07-24 17:06:43 +0900198
Hyunsun Moon44aac662017-02-18 02:07:01 +0900199 if (Strings.isNullOrEmpty(srcSubnet.getGateway())) {
Daniel Park4d486842018-07-24 17:06:43 +0900200 log.warn(ERR_REQ + "source subnet(ID:{}, CIDR:{}) has no gateway",
Hyunsun Moon44aac662017-02-18 02:07:01 +0900201 srcSubnet.getId(), srcSubnet.getCidr());
Jian Lid9a24ed2018-08-29 20:53:25 +0900202 return false;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900203 }
204
205 if (isForSubnetGateway(IpAddress.valueOf(ipPacket.getDestinationAddress()),
206 srcSubnet)) {
207 // this is a request for the subnet gateway
Daniel Park4d486842018-07-24 17:06:43 +0900208 log.trace("Icmp request to gateway {} from {}", dstIp, srcIp);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900209 processRequestForGateway(ipPacket, instPort);
210 } else {
Daniel Park4d486842018-07-24 17:06:43 +0900211 // this is a request for the external network
212 log.trace("Icmp request to external {} from {}", dstIp, srcIp);
213
214 RouterInterface routerInterface = routerInterface(srcSubnet);
215 if (routerInterface == null) {
216 log.warn(ERR_REQ + "failed to get router interface");
Jian Lid9a24ed2018-08-29 20:53:25 +0900217 return false;
daniel park859db252018-04-18 16:00:51 +0900218 }
Daniel Park4d486842018-07-24 17:06:43 +0900219
220 ExternalGateway externalGateway = externalGateway(routerInterface);
221 if (externalGateway == null) {
222 log.warn(ERR_REQ + "failed to get external gateway");
Jian Lid9a24ed2018-08-29 20:53:25 +0900223 return false;
Daniel Park4d486842018-07-24 17:06:43 +0900224 }
225
226 ExternalPeerRouter externalPeerRouter = osNetworkService.externalPeerRouter(externalGateway);
227 if (externalPeerRouter == null) {
228 log.warn(ERR_REQ + "failed to get external peer router");
Jian Lid9a24ed2018-08-29 20:53:25 +0900229 return false;
Daniel Park4d486842018-07-24 17:06:43 +0900230 }
231
232 IpAddress externalIp = getExternalIp(externalGateway, routerInterface);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900233 if (externalIp == null) {
Daniel Park4d486842018-07-24 17:06:43 +0900234 log.warn(ERR_REQ + "failed to get external ip");
Jian Lid9a24ed2018-08-29 20:53:25 +0900235 return false;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900236 }
daniel parkeeb8e042018-02-21 14:06:58 +0900237
daniel park576969a2018-03-09 07:07:41 +0900238 sendRequestForExternal(ipPacket, srcDevice, externalIp, externalPeerRouter);
Daniel Park4d486842018-07-24 17:06:43 +0900239
240 String icmpInfoKey = icmpInfoKey(icmp,
241 externalIp.toString(),
242 IPv4.fromIPv4Address(ipPacket.getDestinationAddress()));
243 log.trace("Created icmpInfo key is {}", icmpInfoKey);
244
daniel parkeeb8e042018-02-21 14:06:58 +0900245 try {
246 icmpInfoMap.compute(icmpInfoKey, (id, existing) -> {
247 checkArgument(existing == null, ERR_DUPLICATE);
248 return instPort;
249 });
250 } catch (IllegalArgumentException e) {
Daniel Park4d486842018-07-24 17:06:43 +0900251 log.warn("IllegalArgumentException occurred because of {}", e.toString());
Jian Lid9a24ed2018-08-29 20:53:25 +0900252 return false;
daniel parkeeb8e042018-02-21 14:06:58 +0900253 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900254 }
Jian Lid9a24ed2018-08-29 20:53:25 +0900255 return true;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900256 }
257
Daniel Park4d486842018-07-24 17:06:43 +0900258 private String icmpInfoKey(ICMP icmp, String srcIp, String dstIp) {
259 return String.valueOf(getIcmpId(icmp))
260 .concat(srcIp)
261 .concat(dstIp);
262 }
263 private RouterInterface routerInterface(Subnet subnet) {
264 checkNotNull(subnet);
265 return osRouterService.routerInterfaces().stream()
daniel parkeeb8e042018-02-21 14:06:58 +0900266 .filter(i -> Objects.equals(i.getSubnetId(), subnet.getId()))
267 .findAny().orElse(null);
Daniel Park4d486842018-07-24 17:06:43 +0900268 }
daniel parkeeb8e042018-02-21 14:06:58 +0900269
Daniel Park4d486842018-07-24 17:06:43 +0900270 private ExternalGateway externalGateway(RouterInterface osRouterIface) {
271 checkNotNull(osRouterIface);
daniel parkeeb8e042018-02-21 14:06:58 +0900272 Router osRouter = osRouterService.router(osRouterIface.getId());
273 if (osRouter == null) {
274 return null;
275 }
276 if (osRouter.getExternalGatewayInfo() == null) {
277 return null;
278 }
Daniel Park4d486842018-07-24 17:06:43 +0900279 return osRouter.getExternalGatewayInfo();
daniel parkeeb8e042018-02-21 14:06:58 +0900280 }
281
Jian Lic38e9032018-08-09 17:08:38 +0900282 private boolean handleEchoReply(IPv4 ipPacket, ICMP icmp) {
Daniel Park4d486842018-07-24 17:06:43 +0900283 String icmpInfoKey = icmpInfoKey(icmp,
284 IPv4.fromIPv4Address(ipPacket.getDestinationAddress()),
285 IPv4.fromIPv4Address(ipPacket.getSourceAddress()));
286 log.trace("Retrieved icmpInfo key is {}", icmpInfoKey);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900287
sangho247232c2017-08-24 17:22:08 +0900288 if (icmpInfoMap.get(icmpInfoKey) != null) {
289 processReplyFromExternal(ipPacket, icmpInfoMap.get(icmpInfoKey).value());
290 icmpInfoMap.remove(icmpInfoKey);
Jian Lic38e9032018-08-09 17:08:38 +0900291 return true;
sangho247232c2017-08-24 17:22:08 +0900292 } else {
Jian Lic38e9032018-08-09 17:08:38 +0900293 log.debug("No ICMP Info for ICMP packet");
294 return false;
sangho247232c2017-08-24 17:22:08 +0900295 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900296 }
297
298 private Subnet getSourceSubnet(InstancePort instance, IpAddress srcIp) {
Daniel Park4d486842018-07-24 17:06:43 +0900299 checkNotNull(instance);
300 checkNotNull(srcIp);
301
Hyunsun Moon44aac662017-02-18 02:07:01 +0900302 Port osPort = osNetworkService.port(instance.portId());
Jian Libcc42282018-09-13 20:59:34 +0900303 return osNetworkService.subnets(osPort.getNetworkId())
304 .stream().findAny().orElse(null);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900305 }
306
307 private boolean isForSubnetGateway(IpAddress dstIp, Subnet srcSubnet) {
308 RouterInterface osRouterIface = osRouterService.routerInterfaces().stream()
309 .filter(i -> Objects.equals(i.getSubnetId(), srcSubnet.getId()))
310 .findAny().orElse(null);
311 if (osRouterIface == null) {
312 log.trace(ERR_REQ + "source subnet(ID:{}, CIDR:{}) has no router",
313 srcSubnet.getId(), srcSubnet.getCidr());
314 return false;
315 }
316
317 Router osRouter = osRouterService.router(osRouterIface.getId());
318 Set<IpAddress> routableGateways = osRouterService.routerInterfaces(osRouter.getId())
319 .stream()
320 .map(iface -> osNetworkService.subnet(iface.getSubnetId()).getGateway())
321 .map(IpAddress::valueOf)
322 .collect(Collectors.toSet());
323
324 return routableGateways.contains(dstIp);
325 }
326
Daniel Park4d486842018-07-24 17:06:43 +0900327 private IpAddress getExternalIp(ExternalGateway externalGateway, RouterInterface osRouterIface) {
328 checkNotNull(externalGateway);
329 checkNotNull(osRouterIface);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900330
331 Router osRouter = osRouterService.router(osRouterIface.getId());
Daniel Park4d486842018-07-24 17:06:43 +0900332 if (osRouter == null) {
333 return null;
Hyunsun Moon44aac662017-02-18 02:07:01 +0900334 }
335
Daniel Park4d486842018-07-24 17:06:43 +0900336 Port exGatewayPort = osNetworkService.ports(externalGateway.getNetworkId())
Hyunsun Moon44aac662017-02-18 02:07:01 +0900337 .stream()
338 .filter(port -> Objects.equals(port.getDeviceId(), osRouter.getId()))
339 .findAny().orElse(null);
340 if (exGatewayPort == null) {
341 final String error = String.format(ERR_REQ +
Daniel Park4d486842018-07-24 17:06:43 +0900342 "no external gateway port for router (ID:%s, name:%s)",
Hyunsun Moon44aac662017-02-18 02:07:01 +0900343 osRouter.getId(), osRouter.getName());
344 throw new IllegalStateException(error);
345 }
sangho36721992017-08-03 11:13:17 +0900346 Optional<NeutronIP> externalIpAddress = (Optional<NeutronIP>) exGatewayPort.getFixedIps().stream().findFirst();
347 if (!externalIpAddress.isPresent() || externalIpAddress.get().getIpAddress() == null) {
348 final String error = String.format(ERR_REQ +
349 "no external gateway IP address for router (ID:%s, name:%s)",
350 osRouter.getId(), osRouter.getName());
Daniel Park4d486842018-07-24 17:06:43 +0900351 log.warn(error);
352 return null;
sangho36721992017-08-03 11:13:17 +0900353 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900354
sangho36721992017-08-03 11:13:17 +0900355 return IpAddress.valueOf(externalIpAddress.get().getIpAddress());
Hyunsun Moon44aac662017-02-18 02:07:01 +0900356 }
357
358 private void processRequestForGateway(IPv4 ipPacket, InstancePort instPort) {
359 ICMP icmpReq = (ICMP) ipPacket.getPayload();
360 icmpReq.setChecksum((short) 0);
Daniel Park4d486842018-07-24 17:06:43 +0900361 icmpReq.setIcmpType(TYPE_ECHO_REPLY);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900362
363 int destinationAddress = ipPacket.getSourceAddress();
364
365 ipPacket.setSourceAddress(ipPacket.getDestinationAddress())
366 .setDestinationAddress(destinationAddress)
367 .resetChecksum();
368
369 ipPacket.setPayload(icmpReq);
370 Ethernet icmpReply = new Ethernet();
371 icmpReply.setEtherType(Ethernet.TYPE_IPV4)
372 .setSourceMACAddress(Constants.DEFAULT_GATEWAY_MAC)
373 .setDestinationMACAddress(instPort.macAddress())
374 .setPayload(ipPacket);
375
376 sendReply(icmpReply, instPort);
377 }
378
daniel parkeeb8e042018-02-21 14:06:58 +0900379 private void sendRequestForExternal(IPv4 ipPacket, DeviceId srcDevice,
daniel park576969a2018-03-09 07:07:41 +0900380 IpAddress srcNatIp, ExternalPeerRouter externalPeerRouter) {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900381 ICMP icmpReq = (ICMP) ipPacket.getPayload();
382 icmpReq.resetChecksum();
383 ipPacket.setSourceAddress(srcNatIp.getIp4Address().toInt()).resetChecksum();
384 ipPacket.setPayload(icmpReq);
385
386 Ethernet icmpRequestEth = new Ethernet();
387 icmpRequestEth.setEtherType(Ethernet.TYPE_IPV4)
388 .setSourceMACAddress(DEFAULT_GATEWAY_MAC)
Jian Li5e2ad4a2018-07-16 13:40:53 +0900389 .setDestinationMACAddress(externalPeerRouter.macAddress());
daniel park576969a2018-03-09 07:07:41 +0900390
Jian Li5e2ad4a2018-07-16 13:40:53 +0900391 if (!externalPeerRouter.vlanId().equals(VlanId.NONE)) {
392 icmpRequestEth.setVlanID(externalPeerRouter.vlanId().toShort());
daniel park576969a2018-03-09 07:07:41 +0900393 }
394
395 icmpRequestEth.setPayload(ipPacket);
Hyunsun Moon44aac662017-02-18 02:07:01 +0900396
Hyunsun Moon0d457362017-06-27 17:19:41 +0900397 OpenstackNode osNode = osNodeService.node(srcDevice);
398 if (osNode == null) {
399 final String error = String.format("Cannot find openstack node for %s",
400 srcDevice);
401 throw new IllegalStateException(error);
402 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900403 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
daniel parkeeb8e042018-02-21 14:06:58 +0900404 .setOutput(osNode.uplinkPortNum())
Hyunsun Moon44aac662017-02-18 02:07:01 +0900405 .build();
406
407 OutboundPacket packet = new DefaultOutboundPacket(
408 srcDevice,
409 treatment,
410 ByteBuffer.wrap(icmpRequestEth.serialize()));
411
412 packetService.emit(packet);
413 }
414
415 private void processReplyFromExternal(IPv4 ipPacket, InstancePort instPort) {
daniel park576969a2018-03-09 07:07:41 +0900416
417 if (instPort.networkId() == null) {
418 return;
419 }
420
Hyunsun Moon44aac662017-02-18 02:07:01 +0900421 ICMP icmpReply = (ICMP) ipPacket.getPayload();
daniel park576969a2018-03-09 07:07:41 +0900422
Hyunsun Moon44aac662017-02-18 02:07:01 +0900423 icmpReply.resetChecksum();
424
425 ipPacket.setDestinationAddress(instPort.ipAddress().getIp4Address().toInt())
426 .resetChecksum();
427 ipPacket.setPayload(icmpReply);
428
429 Ethernet icmpResponseEth = new Ethernet();
430 icmpResponseEth.setEtherType(Ethernet.TYPE_IPV4)
431 .setSourceMACAddress(Constants.DEFAULT_GATEWAY_MAC)
432 .setDestinationMACAddress(instPort.macAddress())
433 .setPayload(ipPacket);
434
435 sendReply(icmpResponseEth, instPort);
436 }
437
438 private void sendReply(Ethernet icmpReply, InstancePort instPort) {
Jian Li28ec77f2018-10-31 07:07:25 +0900439 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder()
440 .setOutput(instPort.portNumber());
441
442 String netId = instPort.networkId();
443 String segId = osNetworkService.segmentId(netId);
444
445 switch (osNetworkService.networkType(netId)) {
446 case VXLAN:
447 tBuilder.setTunnelId(Long.valueOf(segId));
448 break;
449 case VLAN:
450 tBuilder.setVlanId(VlanId.vlanId(segId));
451 break;
452 default:
453 break;
454 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900455
456 OutboundPacket packet = new DefaultOutboundPacket(
457 instPort.deviceId(),
Jian Li28ec77f2018-10-31 07:07:25 +0900458 tBuilder.build(),
Hyunsun Moon44aac662017-02-18 02:07:01 +0900459 ByteBuffer.wrap(icmpReply.serialize()));
460
461 packetService.emit(packet);
462 }
463
464 private short getIcmpId(ICMP icmp) {
Jian Li2fa3ded2018-08-10 02:22:47 +0000465 return ((ICMPEcho) icmp.getPayload()).getIdentifier();
Hyunsun Moon44aac662017-02-18 02:07:01 +0900466 }
467
468 private class InternalPacketProcessor implements PacketProcessor {
469
470 @Override
471 public void process(PacketContext context) {
Hyunsun Moon0d457362017-06-27 17:19:41 +0900472 Set<DeviceId> gateways = osNodeService.completeNodes(GATEWAY)
473 .stream().map(OpenstackNode::intgBridge)
474 .collect(Collectors.toSet());
475
Hyunsun Moon44aac662017-02-18 02:07:01 +0900476 if (context.isHandled()) {
477 return;
Daniel Park4d486842018-07-24 17:06:43 +0900478 }
479
Jian Li28ec77f2018-10-31 07:07:25 +0900480 if (!gateways.isEmpty() &&
481 !gateways.contains(context.inPacket().receivedFrom().deviceId())) {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900482 return;
483 }
484
485 InboundPacket pkt = context.inPacket();
486 Ethernet ethernet = pkt.parsed();
Jian Lid9a24ed2018-08-29 20:53:25 +0900487 if (ethernet == null || ethernet.getEtherType() != Ethernet.TYPE_IPV4) {
Hyunsun Moon44aac662017-02-18 02:07:01 +0900488 return;
489 }
490
491 IPv4 iPacket = (IPv4) ethernet.getPayload();
492 if (iPacket.getProtocol() == IPv4.PROTOCOL_ICMP) {
493 eventExecutor.execute(() -> processIcmpPacket(context, ethernet));
494 }
495 }
Daniel Park4d486842018-07-24 17:06:43 +0900496
497 private void processIcmpPacket(PacketContext context, Ethernet ethernet) {
498 IPv4 ipPacket = (IPv4) ethernet.getPayload();
499 ICMP icmp = (ICMP) ipPacket.getPayload();
500 log.trace("Processing ICMP packet source MAC:{}, source IP:{}," +
501 "dest MAC:{}, dest IP:{}",
502 ethernet.getSourceMAC(),
503 IpAddress.valueOf(ipPacket.getSourceAddress()),
504 ethernet.getDestinationMAC(),
505 IpAddress.valueOf(ipPacket.getDestinationAddress()));
506
507 switch (icmp.getIcmpType()) {
508 case TYPE_ECHO_REQUEST:
Jian Lid9a24ed2018-08-29 20:53:25 +0900509 if (handleEchoRequest(context.inPacket().receivedFrom().deviceId(),
Daniel Park4d486842018-07-24 17:06:43 +0900510 ethernet.getSourceMAC(),
511 ipPacket,
Jian Lid9a24ed2018-08-29 20:53:25 +0900512 icmp)) {
513 context.block();
514 }
Daniel Park4d486842018-07-24 17:06:43 +0900515 break;
516 case TYPE_ECHO_REPLY:
Jian Lic38e9032018-08-09 17:08:38 +0900517 if (handleEchoReply(ipPacket, icmp)) {
518 context.block();
519 }
Daniel Park4d486842018-07-24 17:06:43 +0900520 break;
521 default:
522 break;
523 }
524 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900525 }
Daniel Park6a2d95e2018-11-05 18:50:16 +0900526
527 private class InternalNodeEventListener implements OpenstackNodeListener {
528 @Override
529 public boolean isRelevant(OpenstackNodeEvent event) {
530 // do not allow to proceed without leadership
531 NodeId leader = leadershipService.getLeader(appId.name());
532 return Objects.equals(localNodeId, leader) && event.subject().type() == GATEWAY;
533 }
534
535 @Override
536 public void event(OpenstackNodeEvent event) {
537 OpenstackNode osNode = event.subject();
538 switch (event.type()) {
539 case OPENSTACK_NODE_COMPLETE:
540 eventExecutor.execute(() -> setIcmpReplyRules(osNode.intgBridge(), true));
541 break;
542 case OPENSTACK_NODE_INCOMPLETE:
543 eventExecutor.execute(() -> setIcmpReplyRules(osNode.intgBridge(), false));
544 break;
545 default:
546 break;
547 }
548 }
549
550 private void setIcmpReplyRules(DeviceId deviceId, boolean install) {
551 // Sends ICMP response to controller for SNATing ingress traffic
552 TrafficSelector selector = DefaultTrafficSelector.builder()
553 .matchEthType(Ethernet.TYPE_IPV4)
554 .matchIPProtocol(IPv4.PROTOCOL_ICMP)
555 .matchIcmpType(ICMP.TYPE_ECHO_REPLY)
556 .build();
557
558 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
559 .punt()
560 .build();
561
562 osFlowRuleService.setRule(
563 appId,
564 deviceId,
565 selector,
566 treatment,
567 PRIORITY_INTERNAL_ROUTING_RULE,
568 GW_COMMON_TABLE,
569 install);
570 }
571 }
Hyunsun Moon44aac662017-02-18 02:07:01 +0900572}