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