blob: 96785985ec0f57dbe29d460046edc1469ec32adf [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;
23import org.onlab.packet.MacAddress;
24import org.onosproject.core.ApplicationId;
25import org.onosproject.net.DeviceId;
26import org.onosproject.net.Port;
27import org.onosproject.net.PortNumber;
28import org.onosproject.net.device.DeviceService;
29import org.onosproject.net.flow.DefaultTrafficSelector;
30import org.onosproject.net.flow.DefaultTrafficTreatment;
31import org.onosproject.net.flow.TrafficSelector;
32import org.onosproject.net.flow.TrafficTreatment;
33import org.onosproject.net.packet.DefaultOutboundPacket;
34import org.onosproject.net.packet.OutboundPacket;
sangho0c2a3da2016-02-16 13:39:07 +090035import org.onosproject.net.packet.PacketContext;
Daniel Park81a61a12016-02-26 08:24:44 +090036import org.onosproject.net.packet.PacketPriority;
37import org.onosproject.net.packet.PacketService;
38import org.onosproject.openstackinterface.OpenstackInterfaceService;
39import org.onosproject.openstackinterface.OpenstackPort;
sangho48907542016-03-28 16:07:07 +090040import org.onosproject.openstacknetworking.OpenstackNetworkingConfig;
Daniel Park81a61a12016-02-26 08:24:44 +090041import org.onosproject.openstacknetworking.OpenstackPortInfo;
42import org.onosproject.openstacknetworking.OpenstackSwitchingService;
Kyuhwi Choi92d9ea42016-06-13 17:28:00 +090043import org.onosproject.scalablegateway.api.ScalableGatewayService;
Daniel Park81a61a12016-02-26 08:24:44 +090044import org.slf4j.Logger;
45
46import java.nio.ByteBuffer;
47import java.util.Map;
48import java.util.Optional;
49
50import static com.google.common.base.Preconditions.checkNotNull;
51
52import static org.slf4j.LoggerFactory.getLogger;
53
sangho0c2a3da2016-02-16 13:39:07 +090054
55/**
Daniel Park81a61a12016-02-26 08:24:44 +090056 * Handle ICMP packet sent from Openstack Gateway nodes.
sangho0c2a3da2016-02-16 13:39:07 +090057 */
Daniel Park81a61a12016-02-26 08:24:44 +090058public class OpenstackIcmpHandler {
59 protected final Logger log = getLogger(getClass());
sangho0c2a3da2016-02-16 13:39:07 +090060
Daniel Park81a61a12016-02-26 08:24:44 +090061 private final PacketService packetService;
62 private final DeviceService deviceService;
63 private final Map<String, OpenstackPortInfo> icmpInfoMap = Maps.newHashMap();
64 private final OpenstackSwitchingService openstackSwitchingService;
65 private final OpenstackInterfaceService openstackService;
Kyuhwi Choi92d9ea42016-06-13 17:28:00 +090066 private final ScalableGatewayService gatewayService;
sangho48907542016-03-28 16:07:07 +090067 private final OpenstackNetworkingConfig config;
Daniel Park81a61a12016-02-26 08:24:44 +090068 private static final MacAddress GATEWAY_MAC = MacAddress.valueOf("1f:1f:1f:1f:1f:1f");
69 private static final String NETWORK_ROUTER_INTERFACE = "network:router_interface";
70 private static final String PORTNAME = "portName";
hyungseo Ryu38b5f182016-06-14 16:42:27 +090071 private static final String NETWORK_ROUTER_GATEWAY = "network:router_gateway";
72 private static final String NETWORK_FLOATING_IP = "network:floatingip";
Kyuhwi Choi92d9ea42016-06-13 17:28:00 +090073 private static final String EXTERNAL_NODE_NULL = "There is no external node about this deviceId []";
Daniel Park81a61a12016-02-26 08:24:44 +090074 /**
75 * Default constructor.
76 *
hyungseo Ryu38b5f182016-06-14 16:42:27 +090077 * @param packetService packet service
78 * @param deviceService device service
79 * @param openstackService openstackInterface service
80 * @param config openstackRoutingConfig
Daniel Park81a61a12016-02-26 08:24:44 +090081 * @param openstackSwitchingService openstackSwitching service
Kyuhwi Choi92d9ea42016-06-13 17:28:00 +090082 * @param gatewayService scalable gateway service
Daniel Park81a61a12016-02-26 08:24:44 +090083 */
84 OpenstackIcmpHandler(PacketService packetService, DeviceService deviceService,
sangho48907542016-03-28 16:07:07 +090085 OpenstackInterfaceService openstackService, OpenstackNetworkingConfig config,
Kyuhwi Choi92d9ea42016-06-13 17:28:00 +090086 OpenstackSwitchingService openstackSwitchingService, ScalableGatewayService gatewayService) {
Daniel Park81a61a12016-02-26 08:24:44 +090087 this.packetService = packetService;
88 this.deviceService = deviceService;
89 this.openstackService = checkNotNull(openstackService);
90 this.config = checkNotNull(config);
91 this.openstackSwitchingService = checkNotNull(openstackSwitchingService);
Kyuhwi Choi92d9ea42016-06-13 17:28:00 +090092 this.gatewayService = gatewayService;
sangho0c2a3da2016-02-16 13:39:07 +090093 }
94
Daniel Park81a61a12016-02-26 08:24:44 +090095 /**
96 * Requests ICMP packet.
97 *
98 * @param appId Application Id
99 */
100 public void requestPacket(ApplicationId appId) {
101 TrafficSelector icmpSelector = DefaultTrafficSelector.builder()
102 .matchEthType(Ethernet.TYPE_IPV4)
103 .matchIPProtocol(IPv4.PROTOCOL_ICMP)
104 .build();
105
Kyuhwi Choi92d9ea42016-06-13 17:28:00 +0900106 Map<DeviceId, PortNumber> externalInfoMap = getExternalInfo();
107
108 externalInfoMap.keySet().forEach(deviceId ->
109 packetService.requestPackets(icmpSelector,
110 PacketPriority.CONTROL,
111 appId,
112 Optional.of(deviceId)));
sangho0c2a3da2016-02-16 13:39:07 +0900113 }
Daniel Park81a61a12016-02-26 08:24:44 +0900114
115 /**
116 * Handles ICMP packet.
117 *
hyungseo Ryu38b5f182016-06-14 16:42:27 +0900118 * @param context packet context
Daniel Park81a61a12016-02-26 08:24:44 +0900119 * @param ethernet ethernet
120 */
121 public void processIcmpPacket(PacketContext context, Ethernet ethernet) {
122 checkNotNull(context, "context can not be null");
123 checkNotNull(ethernet, "ethernet can not be null");
124
125 IPv4 ipPacket = (IPv4) ethernet.getPayload();
126
127 log.debug("icmpEvent called from ip {}, mac {}", Ip4Address.valueOf(ipPacket.getSourceAddress()).toString(),
128 ethernet.getSourceMAC().toString());
129
130 ICMP icmp = (ICMP) ipPacket.getPayload();
131 short icmpId = getIcmpId(icmp);
132
133 DeviceId deviceId = context.inPacket().receivedFrom().deviceId();
hyungseo Ryu38b5f182016-06-14 16:42:27 +0900134 PortNumber portNumber = context.inPacket().receivedFrom().port();
Daniel Park81a61a12016-02-26 08:24:44 +0900135 if (icmp.getIcmpType() == ICMP.TYPE_ECHO_REQUEST) {
136 //TODO: Considers icmp between internal subnets which are belonged to the same router.
137
138 OpenstackPortInfo openstackPortInfo =
139 getOpenstackPortInfo(Ip4Address.valueOf(ipPacket.getSourceAddress()), ethernet.getSourceMAC());
140
hyungseo Ryu38b5f182016-06-14 16:42:27 +0900141 //checkNotNull(openstackPortInfo, "openstackPortInfo can not be null");
142 if (requestToOpenstackRoutingNetwork(ipPacket.getDestinationAddress())) {
143 if (openstackPortInfo == null) {
144 if (config.gatewayBridgeId().equals(context.inPacket().receivedFrom().deviceId().toString())) {
145 if (portNumber.equals(getPortForAnnotationPortName(deviceId,
146 config.gatewayExternalInterfaceName()))) {
147 processIcmpPacketSentToExtenal(ipPacket, icmp, ipPacket.getSourceAddress(),
148 ethernet.getSourceMAC(), deviceId, portNumber);
149 return;
150 }
151 }
152 return;
153 } else {
154 processIcmpPacketSentToGateway(ipPacket, icmp, openstackPortInfo);
155 return;
156 }
157 }
Daniel Park81a61a12016-02-26 08:24:44 +0900158
159 if (ipPacket.getDestinationAddress() == openstackPortInfo.gatewayIP().toInt()) {
160 processIcmpPacketSentToGateway(ipPacket, icmp, openstackPortInfo);
161 } else {
162 Ip4Address pNatIpAddress = pNatIpForPort(openstackPortInfo);
163 checkNotNull(pNatIpAddress, "pNatIpAddress can not be null");
164
165 sendRequestPacketToExt(ipPacket, icmp, deviceId, pNatIpAddress);
166
167 String icmpInfoKey = String.valueOf(icmpId)
168 .concat(String.valueOf(pNatIpAddress.toInt()))
169 .concat(String.valueOf(ipPacket.getDestinationAddress()));
170 icmpInfoMap.putIfAbsent(icmpInfoKey, openstackPortInfo);
171 }
172 } else if (icmp.getIcmpType() == ICMP.TYPE_ECHO_REPLY) {
173 String icmpInfoKey = String.valueOf(icmpId)
174 .concat(String.valueOf(ipPacket.getDestinationAddress()))
175 .concat(String.valueOf(ipPacket.getSourceAddress()));
176
177 processResponsePacketFromExternalToHost(ipPacket, icmp, icmpInfoMap.get(icmpInfoKey));
178
179 icmpInfoMap.remove(icmpInfoKey);
180 }
181 }
182
hyungseo Ryu38b5f182016-06-14 16:42:27 +0900183 private void processIcmpPacketSentToExtenal(IPv4 icmpRequestIpv4, ICMP icmpRequest,
184 int destAddr, MacAddress destMac,
185 DeviceId deviceId, PortNumber portNumber) {
186 icmpRequest.setChecksum((short) 0);
187 icmpRequest.setIcmpType(ICMP.TYPE_ECHO_REPLY).resetChecksum();
188 icmpRequestIpv4.setSourceAddress(icmpRequestIpv4.getDestinationAddress())
189 .setDestinationAddress(destAddr).resetChecksum();
190 icmpRequestIpv4.setPayload(icmpRequest);
191 Ethernet icmpResponseEth = new Ethernet();
192 icmpResponseEth.setEtherType(Ethernet.TYPE_IPV4)
193 .setSourceMACAddress(config.gatewayExternalInterfaceMac())
194 .setDestinationMACAddress(destMac).setPayload(icmpRequestIpv4);
195 TrafficTreatment treatment = DefaultTrafficTreatment.builder().setOutput(portNumber).build();
196 OutboundPacket packet = new DefaultOutboundPacket(deviceId,
197 treatment, ByteBuffer.wrap(icmpResponseEth.serialize()));
198 packetService.emit(packet);
199 }
200
Daniel Park81a61a12016-02-26 08:24:44 +0900201 private void processIcmpPacketSentToGateway(IPv4 icmpRequestIpv4, ICMP icmpRequest,
202 OpenstackPortInfo openstackPortInfo) {
hyungseo Ryu38b5f182016-06-14 16:42:27 +0900203 icmpRequest.setChecksum((short) 0);
Daniel Park81a61a12016-02-26 08:24:44 +0900204 icmpRequest.setIcmpType(ICMP.TYPE_ECHO_REPLY)
205 .resetChecksum();
206
207 icmpRequestIpv4.setSourceAddress(icmpRequestIpv4.getDestinationAddress())
208 .setDestinationAddress(openstackPortInfo.ip().toInt())
209 .resetChecksum();
210
211 icmpRequestIpv4.setPayload(icmpRequest);
212
213 Ethernet icmpResponseEth = new Ethernet();
214
215 icmpResponseEth.setEtherType(Ethernet.TYPE_IPV4)
216 .setSourceMACAddress(GATEWAY_MAC)
217 .setDestinationMACAddress(openstackPortInfo.mac())
218 .setPayload(icmpRequestIpv4);
219
220 sendResponsePacketToHost(icmpResponseEth, openstackPortInfo);
221 }
222
223 private void sendRequestPacketToExt(IPv4 icmpRequestIpv4, ICMP icmpRequest, DeviceId deviceId,
224 Ip4Address pNatIpAddress) {
225 icmpRequest.resetChecksum();
226 icmpRequestIpv4.setSourceAddress(pNatIpAddress.toInt())
227 .resetChecksum();
228 icmpRequestIpv4.setPayload(icmpRequest);
229
230 Ethernet icmpRequestEth = new Ethernet();
231
232 icmpRequestEth.setEtherType(Ethernet.TYPE_IPV4)
Daniel Park81a61a12016-02-26 08:24:44 +0900233 .setPayload(icmpRequestIpv4);
234
Kyuhwi Choi92d9ea42016-06-13 17:28:00 +0900235 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
236
237 Map<DeviceId, PortNumber> externalInforMap = getExternalInfo();
238
239 if (externalInforMap.size() == 0 || !externalInforMap.containsKey(deviceId)) {
240 log.error(EXTERNAL_NODE_NULL, deviceId.toString());
241 return;
242 }
243 tBuilder.setOutput(externalInforMap.get(deviceId));
244
245 TrafficTreatment treatment = tBuilder.build();
Daniel Park81a61a12016-02-26 08:24:44 +0900246
247 OutboundPacket packet = new DefaultOutboundPacket(deviceId,
248 treatment, ByteBuffer.wrap(icmpRequestEth.serialize()));
249
250 packetService.emit(packet);
251 }
252
253 private void processResponsePacketFromExternalToHost(IPv4 icmpResponseIpv4, ICMP icmpResponse,
254 OpenstackPortInfo openstackPortInfo) {
255 icmpResponse.resetChecksum();
256
257 icmpResponseIpv4.setDestinationAddress(openstackPortInfo.ip().toInt())
258 .resetChecksum();
259 icmpResponseIpv4.setPayload(icmpResponse);
260
261 Ethernet icmpResponseEth = new Ethernet();
262
263 icmpResponseEth.setEtherType(Ethernet.TYPE_IPV4)
264 .setSourceMACAddress(GATEWAY_MAC)
265 .setDestinationMACAddress(openstackPortInfo.mac())
266 .setPayload(icmpResponseIpv4);
267
268 sendResponsePacketToHost(icmpResponseEth, openstackPortInfo);
269 }
270
271 private void sendResponsePacketToHost(Ethernet icmpResponseEth, OpenstackPortInfo openstackPortInfo) {
272 Map.Entry<String, OpenstackPortInfo> entry = openstackSwitchingService.openstackPortInfo().entrySet().stream()
273 .filter(e -> e.getValue().mac().equals(openstackPortInfo.mac()))
274 .findAny().orElse(null);
275
276 if (entry == null) {
277 return;
278 }
279
280 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
281 .setOutput(getPortForAnnotationPortName(openstackPortInfo.deviceId(), entry.getKey()))
282 .build();
283
284 OutboundPacket packet = new DefaultOutboundPacket(openstackPortInfo.deviceId(),
285 treatment, ByteBuffer.wrap(icmpResponseEth.serialize()));
286
287 packetService.emit(packet);
288 }
289
290 private OpenstackPortInfo getOpenstackPortInfo(Ip4Address sourceIp, MacAddress sourceMac) {
291 checkNotNull(openstackSwitchingService.openstackPortInfo(), "openstackportinfo collection can not be null");
292
293 return openstackSwitchingService.openstackPortInfo().values()
294 .stream().filter(p -> p.ip().equals(sourceIp) && p.mac().equals(sourceMac))
295 .findAny().orElse(null);
296 }
297
298 private short getIcmpId(ICMP icmp) {
299 return ByteBuffer.wrap(icmp.serialize(), 4, 2).getShort();
300 }
301
302 private Ip4Address pNatIpForPort(OpenstackPortInfo openstackPortInfo) {
303
304 OpenstackPort openstackPort = openstackService.ports().stream()
305 .filter(p -> p.deviceOwner().equals(NETWORK_ROUTER_INTERFACE) &&
306 p.networkId().equals(openstackPortInfo.networkId()))
307 .findAny().orElse(null);
308
309 checkNotNull(openstackPort, "openstackPort can not be null");
310
311 return openstackService.router(openstackPort.deviceId())
312 .gatewayExternalInfo().externalFixedIps().values()
313 .stream().findAny().orElse(null);
314 }
315
316 private PortNumber getPortForAnnotationPortName(DeviceId deviceId, String match) {
317 Port port = deviceService.getPorts(deviceId).stream()
318 .filter(p -> p.annotations().value(PORTNAME).equals(match))
319 .findAny().orElse(null);
320
321 checkNotNull(port, "port cannot be null");
322
323 return port.number();
324 }
hyungseo Ryu38b5f182016-06-14 16:42:27 +0900325
326 private boolean requestToOpenstackRoutingNetwork(int destAddr) {
327 OpenstackPort port = openstackService.ports().stream()
328 .filter(p -> p.deviceOwner().equals(NETWORK_ROUTER_GATEWAY) ||
329 p.deviceOwner().equals(NETWORK_FLOATING_IP))
330 .filter(p -> p.fixedIps().containsValue(
331 Ip4Address.valueOf(destAddr)))
332 .findAny().orElse(null);
333 if (port == null) {
334 return false;
335 }
336 return true;
337 }
Kyuhwi Choi92d9ea42016-06-13 17:28:00 +0900338 private Map<DeviceId, PortNumber> getExternalInfo() {
339 Map<DeviceId, PortNumber> externalInfoMap = Maps.newHashMap();
340 gatewayService.getGatewayDeviceIds().forEach(deviceId ->
341 externalInfoMap.putIfAbsent(deviceId, gatewayService.getGatewayExternalPorts(deviceId).get(0)));
342 return externalInfoMap;
343 }
344}