blob: 7db84bb703d634a1fc67a74c11cc135d4f544b46 [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.
sangho6032f342016-07-07 14:32:03 +0900149 Optional<Host> host = hostService.getHostsByMac(ethernet.getSourceMAC()).stream().findFirst();
150 if (!host.isPresent()) {
151 log.warn("No host found for MAC {}", ethernet.getSourceMAC());
152 return;
153 }
154
155 IpAddress gatewayIp = IpAddress.valueOf(host.get().annotations().value(Constants.GATEWAY_IP));
156 if (ipPacket.getDestinationAddress() == gatewayIp.getIp4Address().toInt()) {
157 processIcmpPacketSentToGateway(ipPacket, icmp, host.get());
Daniel Park81a61a12016-02-26 08:24:44 +0900158 } else {
sangho6032f342016-07-07 14:32:03 +0900159 Ip4Address pNatIpAddress = pNatIpForPort(host.get());
Daniel Park81a61a12016-02-26 08:24:44 +0900160 checkNotNull(pNatIpAddress, "pNatIpAddress can not be null");
161
162 sendRequestPacketToExt(ipPacket, icmp, deviceId, pNatIpAddress);
163
164 String icmpInfoKey = String.valueOf(icmpId)
165 .concat(String.valueOf(pNatIpAddress.toInt()))
166 .concat(String.valueOf(ipPacket.getDestinationAddress()));
sangho6032f342016-07-07 14:32:03 +0900167 icmpInfoMap.putIfAbsent(icmpInfoKey, host.get());
Daniel Park81a61a12016-02-26 08:24:44 +0900168 }
169 } else if (icmp.getIcmpType() == ICMP.TYPE_ECHO_REPLY) {
170 String icmpInfoKey = String.valueOf(icmpId)
171 .concat(String.valueOf(ipPacket.getDestinationAddress()))
172 .concat(String.valueOf(ipPacket.getSourceAddress()));
173
174 processResponsePacketFromExternalToHost(ipPacket, icmp, icmpInfoMap.get(icmpInfoKey));
175
176 icmpInfoMap.remove(icmpInfoKey);
177 }
178 }
179
hyungseo Ryu38b5f182016-06-14 16:42:27 +0900180 private void processIcmpPacketSentToExtenal(IPv4 icmpRequestIpv4, ICMP icmpRequest,
181 int destAddr, MacAddress destMac,
182 DeviceId deviceId, PortNumber portNumber) {
183 icmpRequest.setChecksum((short) 0);
184 icmpRequest.setIcmpType(ICMP.TYPE_ECHO_REPLY).resetChecksum();
185 icmpRequestIpv4.setSourceAddress(icmpRequestIpv4.getDestinationAddress())
186 .setDestinationAddress(destAddr).resetChecksum();
187 icmpRequestIpv4.setPayload(icmpRequest);
188 Ethernet icmpResponseEth = new Ethernet();
189 icmpResponseEth.setEtherType(Ethernet.TYPE_IPV4)
sangho6032f342016-07-07 14:32:03 +0900190 // TODO: Get the correct GW MAC
191 .setSourceMACAddress(Constants.GW_EXT_INT_MAC)
hyungseo Ryu38b5f182016-06-14 16:42:27 +0900192 .setDestinationMACAddress(destMac).setPayload(icmpRequestIpv4);
193 TrafficTreatment treatment = DefaultTrafficTreatment.builder().setOutput(portNumber).build();
194 OutboundPacket packet = new DefaultOutboundPacket(deviceId,
195 treatment, ByteBuffer.wrap(icmpResponseEth.serialize()));
196 packetService.emit(packet);
197 }
198
Daniel Park81a61a12016-02-26 08:24:44 +0900199 private void processIcmpPacketSentToGateway(IPv4 icmpRequestIpv4, ICMP icmpRequest,
sangho6032f342016-07-07 14:32:03 +0900200 Host host) {
hyungseo Ryu38b5f182016-06-14 16:42:27 +0900201 icmpRequest.setChecksum((short) 0);
Daniel Park81a61a12016-02-26 08:24:44 +0900202 icmpRequest.setIcmpType(ICMP.TYPE_ECHO_REPLY)
203 .resetChecksum();
204
sangho6032f342016-07-07 14:32:03 +0900205 Ip4Address ipAddress = host.ipAddresses().stream().findAny().get().getIp4Address();
Daniel Park81a61a12016-02-26 08:24:44 +0900206 icmpRequestIpv4.setSourceAddress(icmpRequestIpv4.getDestinationAddress())
sangho6032f342016-07-07 14:32:03 +0900207 .setDestinationAddress(ipAddress.toInt())
Daniel Park81a61a12016-02-26 08:24:44 +0900208 .resetChecksum();
209
210 icmpRequestIpv4.setPayload(icmpRequest);
211
212 Ethernet icmpResponseEth = new Ethernet();
213
214 icmpResponseEth.setEtherType(Ethernet.TYPE_IPV4)
sangho6032f342016-07-07 14:32:03 +0900215 .setSourceMACAddress(Constants.GATEWAY_MAC)
216 .setDestinationMACAddress(host.mac())
Daniel Park81a61a12016-02-26 08:24:44 +0900217 .setPayload(icmpRequestIpv4);
218
sangho6032f342016-07-07 14:32:03 +0900219 sendResponsePacketToHost(icmpResponseEth, host);
Daniel Park81a61a12016-02-26 08:24:44 +0900220 }
221
222 private void sendRequestPacketToExt(IPv4 icmpRequestIpv4, ICMP icmpRequest, DeviceId deviceId,
223 Ip4Address pNatIpAddress) {
224 icmpRequest.resetChecksum();
225 icmpRequestIpv4.setSourceAddress(pNatIpAddress.toInt())
226 .resetChecksum();
227 icmpRequestIpv4.setPayload(icmpRequest);
228
229 Ethernet icmpRequestEth = new Ethernet();
230
231 icmpRequestEth.setEtherType(Ethernet.TYPE_IPV4)
sangho6032f342016-07-07 14:32:03 +0900232 // TODO: Get the correct one - Scalable Gateway ...
233 .setSourceMACAddress(Constants.GW_EXT_INT_MAC)
234 .setDestinationMACAddress(Constants.PHY_ROUTER_MAC)
Daniel Park81a61a12016-02-26 08:24:44 +0900235 .setPayload(icmpRequestIpv4);
236
sangho6032f342016-07-07 14:32:03 +0900237 // TODO: Return the correct gateway node
238 Optional<OpenstackNode> gwNode = nodeService.nodes().stream()
239 .filter(n -> n.type().equals(OpenstackNodeService.NodeType.GATEWAY))
240 .findFirst();
Kyuhwi Choi92d9ea42016-06-13 17:28:00 +0900241
sangho6032f342016-07-07 14:32:03 +0900242 if (!gwNode.isPresent()) {
243 log.warn("No Gateway is defined.");
Kyuhwi Choi92d9ea42016-06-13 17:28:00 +0900244 return;
245 }
Kyuhwi Choi92d9ea42016-06-13 17:28:00 +0900246
sangho6032f342016-07-07 14:32:03 +0900247 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
248 // FIXME: please double check this.
249 .setOutput(getPortForAnnotationPortName(gwNode.get().intBridge(),
250 // FIXME: please double check this.
251 org.onosproject.openstacknode.Constants.PATCH_INTG_BRIDGE))
252 .build();
Daniel Park81a61a12016-02-26 08:24:44 +0900253
254 OutboundPacket packet = new DefaultOutboundPacket(deviceId,
255 treatment, ByteBuffer.wrap(icmpRequestEth.serialize()));
256
257 packetService.emit(packet);
258 }
259
260 private void processResponsePacketFromExternalToHost(IPv4 icmpResponseIpv4, ICMP icmpResponse,
sangho6032f342016-07-07 14:32:03 +0900261 Host host) {
Daniel Park81a61a12016-02-26 08:24:44 +0900262 icmpResponse.resetChecksum();
263
sangho6032f342016-07-07 14:32:03 +0900264 Ip4Address ipAddress = host.ipAddresses().stream().findFirst().get().getIp4Address();
265 icmpResponseIpv4.setDestinationAddress(ipAddress.toInt())
Daniel Park81a61a12016-02-26 08:24:44 +0900266 .resetChecksum();
267 icmpResponseIpv4.setPayload(icmpResponse);
268
269 Ethernet icmpResponseEth = new Ethernet();
270
271 icmpResponseEth.setEtherType(Ethernet.TYPE_IPV4)
sangho6032f342016-07-07 14:32:03 +0900272 .setSourceMACAddress(Constants.GATEWAY_MAC)
273 .setDestinationMACAddress(host.mac())
Daniel Park81a61a12016-02-26 08:24:44 +0900274 .setPayload(icmpResponseIpv4);
275
sangho6032f342016-07-07 14:32:03 +0900276 sendResponsePacketToHost(icmpResponseEth, host);
Daniel Park81a61a12016-02-26 08:24:44 +0900277 }
278
sangho6032f342016-07-07 14:32:03 +0900279 private void sendResponsePacketToHost(Ethernet icmpResponseEth, Host host) {
Daniel Park81a61a12016-02-26 08:24:44 +0900280
281 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
sangho6032f342016-07-07 14:32:03 +0900282 .setOutput(host.location().port())
Daniel Park81a61a12016-02-26 08:24:44 +0900283 .build();
284
sangho6032f342016-07-07 14:32:03 +0900285 OutboundPacket packet = new DefaultOutboundPacket(host.location().deviceId(),
Daniel Park81a61a12016-02-26 08:24:44 +0900286 treatment, ByteBuffer.wrap(icmpResponseEth.serialize()));
287
288 packetService.emit(packet);
289 }
290
Daniel Park81a61a12016-02-26 08:24:44 +0900291 private short getIcmpId(ICMP icmp) {
292 return ByteBuffer.wrap(icmp.serialize(), 4, 2).getShort();
293 }
294
sangho6032f342016-07-07 14:32:03 +0900295 private Ip4Address pNatIpForPort(Host host) {
Daniel Park81a61a12016-02-26 08:24:44 +0900296
297 OpenstackPort openstackPort = openstackService.ports().stream()
298 .filter(p -> p.deviceOwner().equals(NETWORK_ROUTER_INTERFACE) &&
sangho6032f342016-07-07 14:32:03 +0900299 p.networkId().equals(host.annotations().value(Constants.NETWORK_ID)))
Daniel Park81a61a12016-02-26 08:24:44 +0900300 .findAny().orElse(null);
301
302 checkNotNull(openstackPort, "openstackPort can not be null");
303
304 return openstackService.router(openstackPort.deviceId())
305 .gatewayExternalInfo().externalFixedIps().values()
306 .stream().findAny().orElse(null);
307 }
308
309 private PortNumber getPortForAnnotationPortName(DeviceId deviceId, String match) {
310 Port port = deviceService.getPorts(deviceId).stream()
311 .filter(p -> p.annotations().value(PORTNAME).equals(match))
312 .findAny().orElse(null);
313
314 checkNotNull(port, "port cannot be null");
315
316 return port.number();
317 }
hyungseo Ryu38b5f182016-06-14 16:42:27 +0900318
319 private boolean requestToOpenstackRoutingNetwork(int destAddr) {
320 OpenstackPort port = openstackService.ports().stream()
321 .filter(p -> p.deviceOwner().equals(NETWORK_ROUTER_GATEWAY) ||
322 p.deviceOwner().equals(NETWORK_FLOATING_IP))
323 .filter(p -> p.fixedIps().containsValue(
324 Ip4Address.valueOf(destAddr)))
325 .findAny().orElse(null);
326 if (port == null) {
327 return false;
328 }
329 return true;
330 }
Kyuhwi Choi92d9ea42016-06-13 17:28:00 +0900331 private Map<DeviceId, PortNumber> getExternalInfo() {
332 Map<DeviceId, PortNumber> externalInfoMap = Maps.newHashMap();
333 gatewayService.getGatewayDeviceIds().forEach(deviceId ->
Hyunsun Moonf9a16ed2016-07-20 21:59:48 -0700334 externalInfoMap.putIfAbsent(deviceId, gatewayService.getUplinkPort(deviceId)));
Kyuhwi Choi92d9ea42016-06-13 17:28:00 +0900335 return externalInfoMap;
336 }
337}