blob: 4c5dc38002ce0250f122e4a8de758caca0d523c1 [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;
43import org.slf4j.Logger;
44
45import java.nio.ByteBuffer;
46import java.util.Map;
47import java.util.Optional;
48
49import static com.google.common.base.Preconditions.checkNotNull;
50
51import static org.slf4j.LoggerFactory.getLogger;
52
sangho0c2a3da2016-02-16 13:39:07 +090053
54/**
Daniel Park81a61a12016-02-26 08:24:44 +090055 * Handle ICMP packet sent from Openstack Gateway nodes.
sangho0c2a3da2016-02-16 13:39:07 +090056 */
Daniel Park81a61a12016-02-26 08:24:44 +090057public class OpenstackIcmpHandler {
58 protected final Logger log = getLogger(getClass());
sangho0c2a3da2016-02-16 13:39:07 +090059
Daniel Park81a61a12016-02-26 08:24:44 +090060 private final PacketService packetService;
61 private final DeviceService deviceService;
62 private final Map<String, OpenstackPortInfo> icmpInfoMap = Maps.newHashMap();
63 private final OpenstackSwitchingService openstackSwitchingService;
64 private final OpenstackInterfaceService openstackService;
sangho48907542016-03-28 16:07:07 +090065 private final OpenstackNetworkingConfig config;
Daniel Park81a61a12016-02-26 08:24:44 +090066 private static final MacAddress GATEWAY_MAC = MacAddress.valueOf("1f:1f:1f:1f:1f:1f");
67 private static final String NETWORK_ROUTER_INTERFACE = "network:router_interface";
68 private static final String PORTNAME = "portName";
69
70 /**
71 * Default constructor.
72 *
73 * @param packetService packet service
74 * @param deviceService device service
75 * @param openstackService openstackInterface service
76 * @param config openstackRoutingConfig
77 * @param openstackSwitchingService openstackSwitching service
78 */
79 OpenstackIcmpHandler(PacketService packetService, DeviceService deviceService,
sangho48907542016-03-28 16:07:07 +090080 OpenstackInterfaceService openstackService, OpenstackNetworkingConfig config,
Daniel Park81a61a12016-02-26 08:24:44 +090081 OpenstackSwitchingService openstackSwitchingService) {
82 this.packetService = packetService;
83 this.deviceService = deviceService;
84 this.openstackService = checkNotNull(openstackService);
85 this.config = checkNotNull(config);
86 this.openstackSwitchingService = checkNotNull(openstackSwitchingService);
sangho0c2a3da2016-02-16 13:39:07 +090087 }
88
Daniel Park81a61a12016-02-26 08:24:44 +090089 /**
90 * Requests ICMP packet.
91 *
92 * @param appId Application Id
93 */
94 public void requestPacket(ApplicationId appId) {
95 TrafficSelector icmpSelector = DefaultTrafficSelector.builder()
96 .matchEthType(Ethernet.TYPE_IPV4)
97 .matchIPProtocol(IPv4.PROTOCOL_ICMP)
98 .build();
99
100 packetService.requestPackets(icmpSelector,
101 PacketPriority.CONTROL,
102 appId,
103 Optional.of(DeviceId.deviceId(config.gatewayBridgeId())));
sangho0c2a3da2016-02-16 13:39:07 +0900104 }
Daniel Park81a61a12016-02-26 08:24:44 +0900105
106 /**
107 * Handles ICMP packet.
108 *
109 * @param context packet context
110 * @param ethernet ethernet
111 */
112 public void processIcmpPacket(PacketContext context, Ethernet ethernet) {
113 checkNotNull(context, "context can not be null");
114 checkNotNull(ethernet, "ethernet can not be null");
115
116 IPv4 ipPacket = (IPv4) ethernet.getPayload();
117
118 log.debug("icmpEvent called from ip {}, mac {}", Ip4Address.valueOf(ipPacket.getSourceAddress()).toString(),
119 ethernet.getSourceMAC().toString());
120
121 ICMP icmp = (ICMP) ipPacket.getPayload();
122 short icmpId = getIcmpId(icmp);
123
124 DeviceId deviceId = context.inPacket().receivedFrom().deviceId();
125 if (icmp.getIcmpType() == ICMP.TYPE_ECHO_REQUEST) {
126 //TODO: Considers icmp between internal subnets which are belonged to the same router.
127
128 OpenstackPortInfo openstackPortInfo =
129 getOpenstackPortInfo(Ip4Address.valueOf(ipPacket.getSourceAddress()), ethernet.getSourceMAC());
130
131 checkNotNull(openstackPortInfo, "openstackPortInfo can not be null");
132
133 if (ipPacket.getDestinationAddress() == openstackPortInfo.gatewayIP().toInt()) {
134 processIcmpPacketSentToGateway(ipPacket, icmp, openstackPortInfo);
135 } else {
136 Ip4Address pNatIpAddress = pNatIpForPort(openstackPortInfo);
137 checkNotNull(pNatIpAddress, "pNatIpAddress can not be null");
138
139 sendRequestPacketToExt(ipPacket, icmp, deviceId, pNatIpAddress);
140
141 String icmpInfoKey = String.valueOf(icmpId)
142 .concat(String.valueOf(pNatIpAddress.toInt()))
143 .concat(String.valueOf(ipPacket.getDestinationAddress()));
144 icmpInfoMap.putIfAbsent(icmpInfoKey, openstackPortInfo);
145 }
146 } else if (icmp.getIcmpType() == ICMP.TYPE_ECHO_REPLY) {
147 String icmpInfoKey = String.valueOf(icmpId)
148 .concat(String.valueOf(ipPacket.getDestinationAddress()))
149 .concat(String.valueOf(ipPacket.getSourceAddress()));
150
151 processResponsePacketFromExternalToHost(ipPacket, icmp, icmpInfoMap.get(icmpInfoKey));
152
153 icmpInfoMap.remove(icmpInfoKey);
154 }
155 }
156
157 private void processIcmpPacketSentToGateway(IPv4 icmpRequestIpv4, ICMP icmpRequest,
158 OpenstackPortInfo openstackPortInfo) {
159 icmpRequest.setIcmpType(ICMP.TYPE_ECHO_REPLY)
160 .resetChecksum();
161
162 icmpRequestIpv4.setSourceAddress(icmpRequestIpv4.getDestinationAddress())
163 .setDestinationAddress(openstackPortInfo.ip().toInt())
164 .resetChecksum();
165
166 icmpRequestIpv4.setPayload(icmpRequest);
167
168 Ethernet icmpResponseEth = new Ethernet();
169
170 icmpResponseEth.setEtherType(Ethernet.TYPE_IPV4)
171 .setSourceMACAddress(GATEWAY_MAC)
172 .setDestinationMACAddress(openstackPortInfo.mac())
173 .setPayload(icmpRequestIpv4);
174
175 sendResponsePacketToHost(icmpResponseEth, openstackPortInfo);
176 }
177
178 private void sendRequestPacketToExt(IPv4 icmpRequestIpv4, ICMP icmpRequest, DeviceId deviceId,
179 Ip4Address pNatIpAddress) {
180 icmpRequest.resetChecksum();
181 icmpRequestIpv4.setSourceAddress(pNatIpAddress.toInt())
182 .resetChecksum();
183 icmpRequestIpv4.setPayload(icmpRequest);
184
185 Ethernet icmpRequestEth = new Ethernet();
186
187 icmpRequestEth.setEtherType(Ethernet.TYPE_IPV4)
188 .setSourceMACAddress(MacAddress.valueOf(config.gatewayExternalInterfaceMac()))
189 .setDestinationMACAddress(MacAddress.valueOf(config.physicalRouterMac()))
190 .setPayload(icmpRequestIpv4);
191
192 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
193 .setOutput(getPortForAnnotationPortName(DeviceId.deviceId(config.gatewayBridgeId()),
194 config.gatewayExternalInterfaceName()))
195 .build();
196
197 OutboundPacket packet = new DefaultOutboundPacket(deviceId,
198 treatment, ByteBuffer.wrap(icmpRequestEth.serialize()));
199
200 packetService.emit(packet);
201 }
202
203 private void processResponsePacketFromExternalToHost(IPv4 icmpResponseIpv4, ICMP icmpResponse,
204 OpenstackPortInfo openstackPortInfo) {
205 icmpResponse.resetChecksum();
206
207 icmpResponseIpv4.setDestinationAddress(openstackPortInfo.ip().toInt())
208 .resetChecksum();
209 icmpResponseIpv4.setPayload(icmpResponse);
210
211 Ethernet icmpResponseEth = new Ethernet();
212
213 icmpResponseEth.setEtherType(Ethernet.TYPE_IPV4)
214 .setSourceMACAddress(GATEWAY_MAC)
215 .setDestinationMACAddress(openstackPortInfo.mac())
216 .setPayload(icmpResponseIpv4);
217
218 sendResponsePacketToHost(icmpResponseEth, openstackPortInfo);
219 }
220
221 private void sendResponsePacketToHost(Ethernet icmpResponseEth, OpenstackPortInfo openstackPortInfo) {
222 Map.Entry<String, OpenstackPortInfo> entry = openstackSwitchingService.openstackPortInfo().entrySet().stream()
223 .filter(e -> e.getValue().mac().equals(openstackPortInfo.mac()))
224 .findAny().orElse(null);
225
226 if (entry == null) {
227 return;
228 }
229
230 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
231 .setOutput(getPortForAnnotationPortName(openstackPortInfo.deviceId(), entry.getKey()))
232 .build();
233
234 OutboundPacket packet = new DefaultOutboundPacket(openstackPortInfo.deviceId(),
235 treatment, ByteBuffer.wrap(icmpResponseEth.serialize()));
236
237 packetService.emit(packet);
238 }
239
240 private OpenstackPortInfo getOpenstackPortInfo(Ip4Address sourceIp, MacAddress sourceMac) {
241 checkNotNull(openstackSwitchingService.openstackPortInfo(), "openstackportinfo collection can not be null");
242
243 return openstackSwitchingService.openstackPortInfo().values()
244 .stream().filter(p -> p.ip().equals(sourceIp) && p.mac().equals(sourceMac))
245 .findAny().orElse(null);
246 }
247
248 private short getIcmpId(ICMP icmp) {
249 return ByteBuffer.wrap(icmp.serialize(), 4, 2).getShort();
250 }
251
252 private Ip4Address pNatIpForPort(OpenstackPortInfo openstackPortInfo) {
253
254 OpenstackPort openstackPort = openstackService.ports().stream()
255 .filter(p -> p.deviceOwner().equals(NETWORK_ROUTER_INTERFACE) &&
256 p.networkId().equals(openstackPortInfo.networkId()))
257 .findAny().orElse(null);
258
259 checkNotNull(openstackPort, "openstackPort can not be null");
260
261 return openstackService.router(openstackPort.deviceId())
262 .gatewayExternalInfo().externalFixedIps().values()
263 .stream().findAny().orElse(null);
264 }
265
266 private PortNumber getPortForAnnotationPortName(DeviceId deviceId, String match) {
267 Port port = deviceService.getPorts(deviceId).stream()
268 .filter(p -> p.annotations().value(PORTNAME).equals(match))
269 .findAny().orElse(null);
270
271 checkNotNull(port, "port cannot be null");
272
273 return port.number();
274 }
275}