blob: 8b3339be21ea021ef6e95cf9414835fefa2db26d [file] [log] [blame]
sangho0c2a3da2016-02-16 13:39:07 +09001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2016-present Open Networking Laboratory
sangho0c2a3da2016-02-16 13:39:07 +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 *
Daniel Park81a61a12016-02-26 08:24:44 +09008 * http://www.apache.org/licenses/LICENSE-2.0
sangho0c2a3da2016-02-16 13:39:07 +09009 *
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.routing;
17
Daniel Park81a61a12016-02-26 08:24:44 +090018import com.google.common.collect.Maps;
19import org.onlab.packet.Ethernet;
20import org.onlab.packet.ICMP;
21import org.onlab.packet.IPv4;
22import org.onlab.packet.Ip4Address;
sangho6032f342016-07-07 14:32:03 +090023import org.onlab.packet.IpAddress;
Daniel Park81a61a12016-02-26 08:24:44 +090024import org.onlab.packet.MacAddress;
25import org.onosproject.core.ApplicationId;
26import org.onosproject.net.DeviceId;
sangho6032f342016-07-07 14:32:03 +090027import org.onosproject.net.Host;
Daniel Park81a61a12016-02-26 08:24:44 +090028import org.onosproject.net.Port;
29import org.onosproject.net.PortNumber;
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;
sangho6032f342016-07-07 14:32:03 +090035import org.onosproject.net.host.HostService;
Daniel Park81a61a12016-02-26 08:24:44 +090036import org.onosproject.net.packet.DefaultOutboundPacket;
37import org.onosproject.net.packet.OutboundPacket;
sangho0c2a3da2016-02-16 13:39:07 +090038import org.onosproject.net.packet.PacketContext;
Daniel Park81a61a12016-02-26 08:24:44 +090039import org.onosproject.net.packet.PacketPriority;
40import org.onosproject.net.packet.PacketService;
sangho6032f342016-07-07 14:32:03 +090041import org.onosproject.openstacknetworking.Constants;
Daniel Park81a61a12016-02-26 08:24:44 +090042import org.onosproject.openstackinterface.OpenstackInterfaceService;
43import org.onosproject.openstackinterface.OpenstackPort;
sangho6032f342016-07-07 14:32:03 +090044import org.onosproject.openstacknode.OpenstackNode;
45import org.onosproject.openstacknode.OpenstackNodeService;
Kyuhwi Choi92d9ea42016-06-13 17:28:00 +090046import org.onosproject.scalablegateway.api.ScalableGatewayService;
Daniel Park81a61a12016-02-26 08:24:44 +090047import org.slf4j.Logger;
48
49import java.nio.ByteBuffer;
50import java.util.Map;
51import java.util.Optional;
52
53import static com.google.common.base.Preconditions.checkNotNull;
54
55import static org.slf4j.LoggerFactory.getLogger;
56
sangho0c2a3da2016-02-16 13:39:07 +090057
58/**
Daniel Park81a61a12016-02-26 08:24:44 +090059 * Handle ICMP packet sent from Openstack Gateway nodes.
sangho0c2a3da2016-02-16 13:39:07 +090060 */
Daniel Park81a61a12016-02-26 08:24:44 +090061public class OpenstackIcmpHandler {
62 protected final Logger log = getLogger(getClass());
sangho0c2a3da2016-02-16 13:39:07 +090063
sangho6032f342016-07-07 14:32:03 +090064
Daniel Park81a61a12016-02-26 08:24:44 +090065 private static final String NETWORK_ROUTER_INTERFACE = "network:router_interface";
66 private static final String PORTNAME = "portName";
hyungseo Ryu38b5f182016-06-14 16:42:27 +090067 private static final String NETWORK_ROUTER_GATEWAY = "network:router_gateway";
68 private static final String NETWORK_FLOATING_IP = "network:floatingip";
sangho6032f342016-07-07 14:32:03 +090069
70 private final PacketService packetService;
71 private final DeviceService deviceService;
72 private final ScalableGatewayService gatewayService;
73 private final HostService hostService;
74 private final Map<String, Host> icmpInfoMap = Maps.newHashMap();
75 private final OpenstackInterfaceService openstackService;
76 private final OpenstackNodeService nodeService;
77
Daniel Park81a61a12016-02-26 08:24:44 +090078 /**
79 * Default constructor.
80 *
hyungseo Ryu38b5f182016-06-14 16:42:27 +090081 * @param packetService packet service
82 * @param deviceService device service
83 * @param openstackService openstackInterface service
Daniel Park81a61a12016-02-26 08:24:44 +090084 */
sangho6032f342016-07-07 14:32:03 +090085 OpenstackIcmpHandler(PacketService packetService,
86 DeviceService deviceService,
87 HostService hostService,
88 OpenstackInterfaceService openstackService,
89 OpenstackNodeService nodeService,
90 ScalableGatewayService gatewayService
91 ) {
Daniel Park81a61a12016-02-26 08:24:44 +090092 this.packetService = packetService;
93 this.deviceService = deviceService;
sangho6032f342016-07-07 14:32:03 +090094 this.hostService = hostService;
Daniel Park81a61a12016-02-26 08:24:44 +090095 this.openstackService = checkNotNull(openstackService);
sangho6032f342016-07-07 14:32:03 +090096 this.nodeService = nodeService;
Kyuhwi Choi92d9ea42016-06-13 17:28:00 +090097 this.gatewayService = gatewayService;
sangho0c2a3da2016-02-16 13:39:07 +090098 }
99
Daniel Park81a61a12016-02-26 08:24:44 +0900100 /**
101 * Requests ICMP packet.
102 *
103 * @param appId Application Id
104 */
105 public void requestPacket(ApplicationId appId) {
106 TrafficSelector icmpSelector = DefaultTrafficSelector.builder()
107 .matchEthType(Ethernet.TYPE_IPV4)
108 .matchIPProtocol(IPv4.PROTOCOL_ICMP)
109 .build();
110
sangho6032f342016-07-07 14:32:03 +0900111 // TODO: Return the correct gateway node
112 Optional<OpenstackNode> gwNode = nodeService.nodes().stream()
113 .filter(n -> n.type().equals(OpenstackNodeService.NodeType.GATEWAY))
114 .findFirst();
Kyuhwi Choi92d9ea42016-06-13 17:28:00 +0900115
sangho6032f342016-07-07 14:32:03 +0900116 if (!gwNode.isPresent()) {
117 log.warn("No Gateway is defined.");
118 return;
119 }
120
121 packetService.requestPackets(icmpSelector,
122 PacketPriority.CONTROL,
123 appId,
124 Optional.of(gwNode.get().intBridge()));
sangho0c2a3da2016-02-16 13:39:07 +0900125 }
Daniel Park81a61a12016-02-26 08:24:44 +0900126
127 /**
128 * Handles ICMP packet.
129 *
hyungseo Ryu38b5f182016-06-14 16:42:27 +0900130 * @param context packet context
Daniel Park81a61a12016-02-26 08:24:44 +0900131 * @param ethernet ethernet
132 */
133 public void processIcmpPacket(PacketContext context, Ethernet ethernet) {
134 checkNotNull(context, "context can not be null");
135 checkNotNull(ethernet, "ethernet can not be null");
136
137 IPv4 ipPacket = (IPv4) ethernet.getPayload();
138
139 log.debug("icmpEvent called from ip {}, mac {}", Ip4Address.valueOf(ipPacket.getSourceAddress()).toString(),
140 ethernet.getSourceMAC().toString());
141
142 ICMP icmp = (ICMP) ipPacket.getPayload();
143 short icmpId = getIcmpId(icmp);
144
145 DeviceId deviceId = context.inPacket().receivedFrom().deviceId();
hyungseo Ryu38b5f182016-06-14 16:42:27 +0900146 PortNumber portNumber = context.inPacket().receivedFrom().port();
Daniel Park81a61a12016-02-26 08:24:44 +0900147 if (icmp.getIcmpType() == ICMP.TYPE_ECHO_REQUEST) {
148 //TODO: Considers icmp between internal subnets which are belonged to the same router.
149
sangho6032f342016-07-07 14:32:03 +0900150 //OpenstackPortInfo openstackPortInfo =
151 // getOpenstackPortInfo(Ip4Address.valueOf(ipPacket.getSourceAddress()), ethernet.getSourceMAC());
Daniel Park81a61a12016-02-26 08:24:44 +0900152
hyungseo Ryu38b5f182016-06-14 16:42:27 +0900153 //checkNotNull(openstackPortInfo, "openstackPortInfo can not be null");
sangho6032f342016-07-07 14:32:03 +0900154 /* XXX Is this handling ICMP to gateway ?????
hyungseo Ryu38b5f182016-06-14 16:42:27 +0900155 if (requestToOpenstackRoutingNetwork(ipPacket.getDestinationAddress())) {
sangho6032f342016-07-07 14:32:03 +0900156 Host host =
hyungseo Ryu38b5f182016-06-14 16:42:27 +0900157 if (openstackPortInfo == null) {
158 if (config.gatewayBridgeId().equals(context.inPacket().receivedFrom().deviceId().toString())) {
159 if (portNumber.equals(getPortForAnnotationPortName(deviceId,
160 config.gatewayExternalInterfaceName()))) {
161 processIcmpPacketSentToExtenal(ipPacket, icmp, ipPacket.getSourceAddress(),
162 ethernet.getSourceMAC(), deviceId, portNumber);
163 return;
164 }
165 }
166 return;
167 } else {
sangho6032f342016-07-07 14:32:03 +0900168 processIcmpPacketSentToGateway(ipPacket, icmp, host);
hyungseo Ryu38b5f182016-06-14 16:42:27 +0900169 return;
170 }
171 }
sangho6032f342016-07-07 14:32:03 +0900172 */
Daniel Park81a61a12016-02-26 08:24:44 +0900173
sangho6032f342016-07-07 14:32:03 +0900174 Optional<Host> host = hostService.getHostsByMac(ethernet.getSourceMAC()).stream().findFirst();
175 if (!host.isPresent()) {
176 log.warn("No host found for MAC {}", ethernet.getSourceMAC());
177 return;
178 }
179
180 IpAddress gatewayIp = IpAddress.valueOf(host.get().annotations().value(Constants.GATEWAY_IP));
181 if (ipPacket.getDestinationAddress() == gatewayIp.getIp4Address().toInt()) {
182 processIcmpPacketSentToGateway(ipPacket, icmp, host.get());
Daniel Park81a61a12016-02-26 08:24:44 +0900183 } else {
sangho6032f342016-07-07 14:32:03 +0900184 Ip4Address pNatIpAddress = pNatIpForPort(host.get());
Daniel Park81a61a12016-02-26 08:24:44 +0900185 checkNotNull(pNatIpAddress, "pNatIpAddress can not be null");
186
187 sendRequestPacketToExt(ipPacket, icmp, deviceId, pNatIpAddress);
188
189 String icmpInfoKey = String.valueOf(icmpId)
190 .concat(String.valueOf(pNatIpAddress.toInt()))
191 .concat(String.valueOf(ipPacket.getDestinationAddress()));
sangho6032f342016-07-07 14:32:03 +0900192 icmpInfoMap.putIfAbsent(icmpInfoKey, host.get());
Daniel Park81a61a12016-02-26 08:24:44 +0900193 }
194 } else if (icmp.getIcmpType() == ICMP.TYPE_ECHO_REPLY) {
195 String icmpInfoKey = String.valueOf(icmpId)
196 .concat(String.valueOf(ipPacket.getDestinationAddress()))
197 .concat(String.valueOf(ipPacket.getSourceAddress()));
198
199 processResponsePacketFromExternalToHost(ipPacket, icmp, icmpInfoMap.get(icmpInfoKey));
200
201 icmpInfoMap.remove(icmpInfoKey);
202 }
203 }
204
hyungseo Ryu38b5f182016-06-14 16:42:27 +0900205 private void processIcmpPacketSentToExtenal(IPv4 icmpRequestIpv4, ICMP icmpRequest,
206 int destAddr, MacAddress destMac,
207 DeviceId deviceId, PortNumber portNumber) {
208 icmpRequest.setChecksum((short) 0);
209 icmpRequest.setIcmpType(ICMP.TYPE_ECHO_REPLY).resetChecksum();
210 icmpRequestIpv4.setSourceAddress(icmpRequestIpv4.getDestinationAddress())
211 .setDestinationAddress(destAddr).resetChecksum();
212 icmpRequestIpv4.setPayload(icmpRequest);
213 Ethernet icmpResponseEth = new Ethernet();
214 icmpResponseEth.setEtherType(Ethernet.TYPE_IPV4)
sangho6032f342016-07-07 14:32:03 +0900215 // TODO: Get the correct GW MAC
216 .setSourceMACAddress(Constants.GW_EXT_INT_MAC)
hyungseo Ryu38b5f182016-06-14 16:42:27 +0900217 .setDestinationMACAddress(destMac).setPayload(icmpRequestIpv4);
218 TrafficTreatment treatment = DefaultTrafficTreatment.builder().setOutput(portNumber).build();
219 OutboundPacket packet = new DefaultOutboundPacket(deviceId,
220 treatment, ByteBuffer.wrap(icmpResponseEth.serialize()));
221 packetService.emit(packet);
222 }
223
Daniel Park81a61a12016-02-26 08:24:44 +0900224 private void processIcmpPacketSentToGateway(IPv4 icmpRequestIpv4, ICMP icmpRequest,
sangho6032f342016-07-07 14:32:03 +0900225 Host host) {
hyungseo Ryu38b5f182016-06-14 16:42:27 +0900226 icmpRequest.setChecksum((short) 0);
Daniel Park81a61a12016-02-26 08:24:44 +0900227 icmpRequest.setIcmpType(ICMP.TYPE_ECHO_REPLY)
228 .resetChecksum();
229
sangho6032f342016-07-07 14:32:03 +0900230 Ip4Address ipAddress = host.ipAddresses().stream().findAny().get().getIp4Address();
Daniel Park81a61a12016-02-26 08:24:44 +0900231 icmpRequestIpv4.setSourceAddress(icmpRequestIpv4.getDestinationAddress())
sangho6032f342016-07-07 14:32:03 +0900232 .setDestinationAddress(ipAddress.toInt())
Daniel Park81a61a12016-02-26 08:24:44 +0900233 .resetChecksum();
234
235 icmpRequestIpv4.setPayload(icmpRequest);
236
237 Ethernet icmpResponseEth = new Ethernet();
238
239 icmpResponseEth.setEtherType(Ethernet.TYPE_IPV4)
sangho6032f342016-07-07 14:32:03 +0900240 .setSourceMACAddress(Constants.GATEWAY_MAC)
241 .setDestinationMACAddress(host.mac())
Daniel Park81a61a12016-02-26 08:24:44 +0900242 .setPayload(icmpRequestIpv4);
243
sangho6032f342016-07-07 14:32:03 +0900244 sendResponsePacketToHost(icmpResponseEth, host);
Daniel Park81a61a12016-02-26 08:24:44 +0900245 }
246
247 private void sendRequestPacketToExt(IPv4 icmpRequestIpv4, ICMP icmpRequest, DeviceId deviceId,
248 Ip4Address pNatIpAddress) {
249 icmpRequest.resetChecksum();
250 icmpRequestIpv4.setSourceAddress(pNatIpAddress.toInt())
251 .resetChecksum();
252 icmpRequestIpv4.setPayload(icmpRequest);
253
254 Ethernet icmpRequestEth = new Ethernet();
255
256 icmpRequestEth.setEtherType(Ethernet.TYPE_IPV4)
sangho6032f342016-07-07 14:32:03 +0900257 // TODO: Get the correct one - Scalable Gateway ...
258 .setSourceMACAddress(Constants.GW_EXT_INT_MAC)
259 .setDestinationMACAddress(Constants.PHY_ROUTER_MAC)
Daniel Park81a61a12016-02-26 08:24:44 +0900260 .setPayload(icmpRequestIpv4);
261
sangho6032f342016-07-07 14:32:03 +0900262 // TODO: Return the correct gateway node
263 Optional<OpenstackNode> gwNode = nodeService.nodes().stream()
264 .filter(n -> n.type().equals(OpenstackNodeService.NodeType.GATEWAY))
265 .findFirst();
Kyuhwi Choi92d9ea42016-06-13 17:28:00 +0900266
sangho6032f342016-07-07 14:32:03 +0900267 if (!gwNode.isPresent()) {
268 log.warn("No Gateway is defined.");
Kyuhwi Choi92d9ea42016-06-13 17:28:00 +0900269 return;
270 }
Kyuhwi Choi92d9ea42016-06-13 17:28:00 +0900271
sangho6032f342016-07-07 14:32:03 +0900272 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
273 // FIXME: please double check this.
274 .setOutput(getPortForAnnotationPortName(gwNode.get().intBridge(),
275 // FIXME: please double check this.
276 org.onosproject.openstacknode.Constants.PATCH_INTG_BRIDGE))
277 .build();
Daniel Park81a61a12016-02-26 08:24:44 +0900278
279 OutboundPacket packet = new DefaultOutboundPacket(deviceId,
280 treatment, ByteBuffer.wrap(icmpRequestEth.serialize()));
281
282 packetService.emit(packet);
283 }
284
285 private void processResponsePacketFromExternalToHost(IPv4 icmpResponseIpv4, ICMP icmpResponse,
sangho6032f342016-07-07 14:32:03 +0900286 Host host) {
Daniel Park81a61a12016-02-26 08:24:44 +0900287 icmpResponse.resetChecksum();
288
sangho6032f342016-07-07 14:32:03 +0900289 Ip4Address ipAddress = host.ipAddresses().stream().findFirst().get().getIp4Address();
290 icmpResponseIpv4.setDestinationAddress(ipAddress.toInt())
Daniel Park81a61a12016-02-26 08:24:44 +0900291 .resetChecksum();
292 icmpResponseIpv4.setPayload(icmpResponse);
293
294 Ethernet icmpResponseEth = new Ethernet();
295
296 icmpResponseEth.setEtherType(Ethernet.TYPE_IPV4)
sangho6032f342016-07-07 14:32:03 +0900297 .setSourceMACAddress(Constants.GATEWAY_MAC)
298 .setDestinationMACAddress(host.mac())
Daniel Park81a61a12016-02-26 08:24:44 +0900299 .setPayload(icmpResponseIpv4);
300
sangho6032f342016-07-07 14:32:03 +0900301 sendResponsePacketToHost(icmpResponseEth, host);
Daniel Park81a61a12016-02-26 08:24:44 +0900302 }
303
sangho6032f342016-07-07 14:32:03 +0900304 private void sendResponsePacketToHost(Ethernet icmpResponseEth, Host host) {
Daniel Park81a61a12016-02-26 08:24:44 +0900305
306 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
sangho6032f342016-07-07 14:32:03 +0900307 .setOutput(host.location().port())
Daniel Park81a61a12016-02-26 08:24:44 +0900308 .build();
309
sangho6032f342016-07-07 14:32:03 +0900310 OutboundPacket packet = new DefaultOutboundPacket(host.location().deviceId(),
Daniel Park81a61a12016-02-26 08:24:44 +0900311 treatment, ByteBuffer.wrap(icmpResponseEth.serialize()));
312
313 packetService.emit(packet);
314 }
315
Daniel Park81a61a12016-02-26 08:24:44 +0900316 private short getIcmpId(ICMP icmp) {
317 return ByteBuffer.wrap(icmp.serialize(), 4, 2).getShort();
318 }
319
sangho6032f342016-07-07 14:32:03 +0900320 private Ip4Address pNatIpForPort(Host host) {
Daniel Park81a61a12016-02-26 08:24:44 +0900321
322 OpenstackPort openstackPort = openstackService.ports().stream()
323 .filter(p -> p.deviceOwner().equals(NETWORK_ROUTER_INTERFACE) &&
sangho6032f342016-07-07 14:32:03 +0900324 p.networkId().equals(host.annotations().value(Constants.NETWORK_ID)))
Daniel Park81a61a12016-02-26 08:24:44 +0900325 .findAny().orElse(null);
326
327 checkNotNull(openstackPort, "openstackPort can not be null");
328
329 return openstackService.router(openstackPort.deviceId())
330 .gatewayExternalInfo().externalFixedIps().values()
331 .stream().findAny().orElse(null);
332 }
333
334 private PortNumber getPortForAnnotationPortName(DeviceId deviceId, String match) {
335 Port port = deviceService.getPorts(deviceId).stream()
336 .filter(p -> p.annotations().value(PORTNAME).equals(match))
337 .findAny().orElse(null);
338
339 checkNotNull(port, "port cannot be null");
340
341 return port.number();
342 }
hyungseo Ryu38b5f182016-06-14 16:42:27 +0900343
344 private boolean requestToOpenstackRoutingNetwork(int destAddr) {
345 OpenstackPort port = openstackService.ports().stream()
346 .filter(p -> p.deviceOwner().equals(NETWORK_ROUTER_GATEWAY) ||
347 p.deviceOwner().equals(NETWORK_FLOATING_IP))
348 .filter(p -> p.fixedIps().containsValue(
349 Ip4Address.valueOf(destAddr)))
350 .findAny().orElse(null);
351 if (port == null) {
352 return false;
353 }
354 return true;
355 }
Kyuhwi Choi92d9ea42016-06-13 17:28:00 +0900356 private Map<DeviceId, PortNumber> getExternalInfo() {
357 Map<DeviceId, PortNumber> externalInfoMap = Maps.newHashMap();
358 gatewayService.getGatewayDeviceIds().forEach(deviceId ->
359 externalInfoMap.putIfAbsent(deviceId, gatewayService.getGatewayExternalPorts(deviceId).get(0)));
360 return externalInfoMap;
361 }
362}