blob: fc3dd17bfc02285318d37b1e80e59ac509de15ec [file] [log] [blame]
sangho80f11cb2015-04-01 13:05:26 -07001/*
Brian O'Connor0947d7e2017-08-03 21:12:30 -07002 * Copyright 2015-present Open Networking Foundation
sangho80f11cb2015-04-01 13:05:26 -07003 *
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 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
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.segmentrouting;
17
sangho80f11cb2015-04-01 13:05:26 -070018import org.onlab.packet.Ethernet;
19import org.onlab.packet.ICMP;
Pier Ventreb6b81d52016-12-02 08:16:05 -080020import org.onlab.packet.ICMP6;
sangho80f11cb2015-04-01 13:05:26 -070021import org.onlab.packet.IPv4;
Pier Ventreb6b81d52016-12-02 08:16:05 -080022import org.onlab.packet.IPv6;
sangho80f11cb2015-04-01 13:05:26 -070023import org.onlab.packet.Ip4Address;
Pier Ventre1a655962016-11-28 16:48:06 -080024import org.onlab.packet.Ip6Address;
Pier Ventreadb4ae62016-11-23 09:57:42 -080025import org.onlab.packet.IpAddress;
sangho80f11cb2015-04-01 13:05:26 -070026import org.onlab.packet.MPLS;
Pier Ventre1a655962016-11-28 16:48:06 -080027import org.onlab.packet.MacAddress;
28import org.onlab.packet.VlanId;
Pier Ventreb6b81d52016-12-02 08:16:05 -080029import org.onlab.packet.ndp.NeighborSolicitation;
Ray Milkey2a31aeb2017-08-03 16:28:24 -070030import org.onosproject.net.neighbour.NeighbourMessageContext;
31import org.onosproject.net.neighbour.NeighbourMessageType;
sangho80f11cb2015-04-01 13:05:26 -070032import org.onosproject.net.ConnectPoint;
33import org.onosproject.net.DeviceId;
Jayakumar Thazhath0cca97a2017-09-11 02:00:20 -040034import org.onosproject.net.intf.Interface;
sangho80f11cb2015-04-01 13:05:26 -070035import org.onosproject.net.flow.DefaultTrafficTreatment;
36import org.onosproject.net.flow.TrafficTreatment;
Pier Ventre1a655962016-11-28 16:48:06 -080037import org.onosproject.net.host.HostService;
sangho80f11cb2015-04-01 13:05:26 -070038import org.onosproject.net.packet.DefaultOutboundPacket;
sangho80f11cb2015-04-01 13:05:26 -070039import org.onosproject.net.packet.OutboundPacket;
Charles Chan319d1a22015-11-03 10:42:14 -080040import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
Pier Ventre1a655962016-11-28 16:48:06 -080041import org.onosproject.segmentrouting.config.SegmentRoutingAppConfig;
sangho80f11cb2015-04-01 13:05:26 -070042import org.slf4j.Logger;
43import org.slf4j.LoggerFactory;
44
Jonathan Hartd53ebc42015-04-07 16:46:33 -070045import java.nio.ByteBuffer;
Charles Chand3727b72017-03-13 13:10:30 -070046import java.util.Arrays;
pier6a2052b2019-06-28 22:17:31 +020047import java.util.Objects;
Charles Chanb8caeab2017-06-02 19:23:51 -070048import java.util.Optional;
Saurav Dasc28b3432015-10-30 17:45:38 -070049import java.util.Set;
pier6a2052b2019-06-28 22:17:31 +020050import java.util.stream.Collectors;
Jonathan Hartd53ebc42015-04-07 16:46:33 -070051
Charles Chanb7f75ac2016-01-11 18:28:54 -080052/**
53 * Handler of ICMP packets that responses or forwards ICMP packets that
54 * are sent to the controller.
55 */
Pier Ventreb6b81d52016-12-02 08:16:05 -080056public class IcmpHandler extends SegmentRoutingNeighbourHandler {
sangho80f11cb2015-04-01 13:05:26 -070057
58 private static Logger log = LoggerFactory.getLogger(IcmpHandler.class);
sangho80f11cb2015-04-01 13:05:26 -070059
60 /**
61 * Creates an IcmpHandler object.
62 *
63 * @param srManager SegmentRoutingManager object
64 */
65 public IcmpHandler(SegmentRoutingManager srManager) {
Pier Ventreb6b81d52016-12-02 08:16:05 -080066 super(srManager);
67 }
68
69 /**
70 * Utility function to send packet out.
71 *
72 * @param outport the output port
73 * @param payload the packet to send
pier6a2052b2019-06-28 22:17:31 +020074 * @param destSid the segment id of the dest device
Pier Ventreb6b81d52016-12-02 08:16:05 -080075 * @param destIpAddress the destination ip address
76 * @param allowedHops the hop limit/ttl
77 */
78 private void sendPacketOut(ConnectPoint outport,
79 Ethernet payload,
pier6a2052b2019-06-28 22:17:31 +020080 int destSid,
Pier Ventreb6b81d52016-12-02 08:16:05 -080081 IpAddress destIpAddress,
82 byte allowedHops) {
pier6a2052b2019-06-28 22:17:31 +020083 int origSid;
84 try {
85 if (destIpAddress.isIp4()) {
86 origSid = config.getIPv4SegmentId(outport.deviceId());
87 } else {
88 origSid = config.getIPv6SegmentId(outport.deviceId());
89 }
90 } catch (DeviceConfigNotFoundException e) {
91 log.warn(e.getMessage() + " Aborting sendPacketOut");
92 return;
Pier Ventreb6b81d52016-12-02 08:16:05 -080093 }
94
pier6a2052b2019-06-28 22:17:31 +020095 if (destSid == -1 || origSid == destSid ||
96 srManager.interfaceService.isConfigured(outport)) {
Pier Ventreb6b81d52016-12-02 08:16:05 -080097 TrafficTreatment treatment = DefaultTrafficTreatment.builder().
98 setOutput(outport.port()).build();
99 OutboundPacket packet = new DefaultOutboundPacket(outport.deviceId(),
100 treatment, ByteBuffer.wrap(payload.serialize()));
pier6a2052b2019-06-28 22:17:31 +0200101 log.trace("Sending packet {} to {}", payload, outport);
Pier Ventreb6b81d52016-12-02 08:16:05 -0800102 srManager.packetService.emit(packet);
103 } else {
Pier Ventreb6b81d52016-12-02 08:16:05 -0800104 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
105 .setOutput(outport.port())
106 .build();
107
108 payload.setEtherType(Ethernet.MPLS_UNICAST);
109 MPLS mplsPkt = new MPLS();
pier6a2052b2019-06-28 22:17:31 +0200110 mplsPkt.setLabel(destSid);
Pier Ventreb6b81d52016-12-02 08:16:05 -0800111 mplsPkt.setTtl(allowedHops);
112 mplsPkt.setPayload(payload.getPayload());
113 payload.setPayload(mplsPkt);
Pier Ventreb6b81d52016-12-02 08:16:05 -0800114 OutboundPacket packet = new DefaultOutboundPacket(outport.deviceId(),
115 treatment, ByteBuffer.wrap(payload.serialize()));
pier6a2052b2019-06-28 22:17:31 +0200116 log.trace("Sending packet {} to {}", payload, outport);
Pier Ventreb6b81d52016-12-02 08:16:05 -0800117 srManager.packetService.emit(packet);
118 }
sangho80f11cb2015-04-01 13:05:26 -0700119 }
120
pier6a2052b2019-06-28 22:17:31 +0200121 private IpAddress selectRouterIpAddress(IpAddress destIpAddress, ConnectPoint outPort,
122 Set<ConnectPoint> connectPoints) {
123 IpAddress routerIpAddress;
124 // Let's get first the online connect points
125 Set<ConnectPoint> onlineCps = connectPoints.stream()
126 .filter(connectPoint -> srManager.deviceService.isAvailable(connectPoint.deviceId()))
127 .collect(Collectors.toSet());
128 // Check if ping is local
129 if (onlineCps.contains(outPort)) {
130 routerIpAddress = config.getRouterIpAddress(destIpAddress, outPort.deviceId());
131 log.trace("Local ping received from {} - send to {}", destIpAddress, routerIpAddress);
132 return routerIpAddress;
133 }
134 // Check if it comes from a remote device. Loopbacks are sorted comparing byte by byte
135 // FIXME if we lose both links from the chosen leaf to spine - ping will fail
136 routerIpAddress = onlineCps.stream()
137 .filter(onlineCp -> !onlineCp.deviceId().equals(outPort.deviceId()))
138 .map(selectedCp -> config.getRouterIpAddress(destIpAddress, selectedCp.deviceId()))
139 .filter(Objects::nonNull)
140 .sorted()
141 .findFirst().orElse(null);
142 if (routerIpAddress != null) {
143 log.trace("Remote ping received from {} - send to {}", destIpAddress, routerIpAddress);
144 } else {
145 log.warn("Not found a valid loopback for ping coming from {} - {}", destIpAddress, outPort);
146 }
147 return routerIpAddress;
148 }
149
150 private Ip4Address selectRouterIp4Address(IpAddress destIpAddress, ConnectPoint outPort,
151 Set<ConnectPoint> connectPoints) {
152 IpAddress routerIpAddress = selectRouterIpAddress(destIpAddress, outPort, connectPoints);
153 return routerIpAddress != null ? routerIpAddress.getIp4Address() : null;
154 }
155
156 private Ip6Address selectRouterIp6Address(IpAddress destIpAddress, ConnectPoint outPort,
157 Set<ConnectPoint> connectPoints) {
158 IpAddress routerIpAddress = selectRouterIpAddress(destIpAddress, outPort, connectPoints);
159 return routerIpAddress != null ? routerIpAddress.getIp6Address() : null;
160 }
161
Pier Ventre1a655962016-11-28 16:48:06 -0800162 //////////////////////////////////////
163 // ICMP Echo/Reply Protocol //
164 //////////////////////////////////////
165
sangho80f11cb2015-04-01 13:05:26 -0700166 /**
167 * Process incoming ICMP packet.
Charles Chanb78b1132017-03-29 17:24:39 -0700168 * If it is an ICMP request to router, then sends an ICMP response.
169 * Otherwise ignore the packet.
sangho80f11cb2015-04-01 13:05:26 -0700170 *
Pier Ventreb6b81d52016-12-02 08:16:05 -0800171 * @param eth inbound ICMP packet
172 * @param inPort the input port
sangho80f11cb2015-04-01 13:05:26 -0700173 */
Pier Ventreb6b81d52016-12-02 08:16:05 -0800174 public void processIcmp(Ethernet eth, ConnectPoint inPort) {
175 DeviceId deviceId = inPort.deviceId();
176 IPv4 ipv4Packet = (IPv4) eth.getPayload();
pier6a2052b2019-06-28 22:17:31 +0200177 ICMP icmp = (ICMP) ipv4Packet.getPayload();
Pier Ventreb6b81d52016-12-02 08:16:05 -0800178 Ip4Address destinationAddress = Ip4Address.valueOf(ipv4Packet.getDestinationAddress());
Pier Ventreb6a7f342016-11-26 21:05:22 -0800179 Set<IpAddress> gatewayIpAddresses = config.getPortIPs(deviceId);
Pier Ventreadb4ae62016-11-23 09:57:42 -0800180 IpAddress routerIp;
pier6a2052b2019-06-28 22:17:31 +0200181
182 // Only proceed with echo request
183 if (icmp.getIcmpType() != ICMP.TYPE_ECHO_REQUEST) {
184 return;
185 }
186
Charles Chan319d1a22015-11-03 10:42:14 -0800187 try {
Pier Ventreadb4ae62016-11-23 09:57:42 -0800188 routerIp = config.getRouterIpv4(deviceId);
Charles Chan319d1a22015-11-03 10:42:14 -0800189 } catch (DeviceConfigNotFoundException e) {
190 log.warn(e.getMessage() + " Aborting processPacketIn.");
191 return;
192 }
pier6a2052b2019-06-28 22:17:31 +0200193
194 // Get pair ip - if it exists
195 IpAddress pairRouterIp;
196 try {
197 DeviceId pairDeviceId = config.getPairDeviceId(deviceId);
198 pairRouterIp = pairDeviceId != null ? config.getRouterIpv4(pairDeviceId) : null;
199 } catch (DeviceConfigNotFoundException e) {
200 pairRouterIp = null;
201 }
202
sangho80f11cb2015-04-01 13:05:26 -0700203 // ICMP to the router IP or gateway IP
pier6a2052b2019-06-28 22:17:31 +0200204 if (destinationAddress.equals(routerIp.getIp4Address()) ||
205 (pairRouterIp != null && destinationAddress.equals(pairRouterIp.getIp4Address())) ||
206 gatewayIpAddresses.contains(destinationAddress)) {
Pier Ventreb6b81d52016-12-02 08:16:05 -0800207 sendIcmpResponse(eth, inPort);
sangho80f11cb2015-04-01 13:05:26 -0700208 } else {
Charles Chanb78b1132017-03-29 17:24:39 -0700209 log.trace("Ignore ICMP that targets for {}", destinationAddress);
sangho80f11cb2015-04-01 13:05:26 -0700210 }
211 }
212
Charles Chanf4586112015-11-09 16:37:23 -0800213 /**
214 * Sends an ICMP reply message.
215 *
Charles Chanf4586112015-11-09 16:37:23 -0800216 * @param icmpRequest the original ICMP request
217 * @param outport the output port where the ICMP reply should be sent to
218 */
Pier Ventreadb4ae62016-11-23 09:57:42 -0800219 private void sendIcmpResponse(Ethernet icmpRequest, ConnectPoint outport) {
Pier Ventreb6b81d52016-12-02 08:16:05 -0800220 Ethernet icmpReplyEth = ICMP.buildIcmpReply(icmpRequest);
sangho80f11cb2015-04-01 13:05:26 -0700221 IPv4 icmpRequestIpv4 = (IPv4) icmpRequest.getPayload();
Pier Ventreb6b81d52016-12-02 08:16:05 -0800222 IPv4 icmpReplyIpv4 = (IPv4) icmpReplyEth.getPayload();
223 Ip4Address destIpAddress = Ip4Address.valueOf(icmpRequestIpv4.getSourceAddress());
pier6a2052b2019-06-28 22:17:31 +0200224
225 // Get the available connect points
226 Set<ConnectPoint> destConnectPoints = config.getConnectPointsForASubnetHost(destIpAddress);
227 // Select a router
228 Ip4Address destRouterAddress = selectRouterIp4Address(destIpAddress, outport, destConnectPoints);
Charles Chanb8caeab2017-06-02 19:23:51 -0700229
230 // Note: Source IP of the ICMP request doesn't belong to any configured subnet.
231 // The source might be an indirectly attached host (e.g. behind a router)
232 // Lookup the route store for the nexthop instead.
233 if (destRouterAddress == null) {
Charles Chanf0ae41e2017-08-23 13:55:39 -0700234 Optional<DeviceId> deviceId = srManager.routeService
235 .longestPrefixLookup(destIpAddress).map(srManager::nextHopLocations)
236 .flatMap(locations -> locations.stream().findFirst())
237 .map(ConnectPoint::deviceId);
238 if (deviceId.isPresent()) {
Charles Chanb8caeab2017-06-02 19:23:51 -0700239 try {
Charles Chanf0ae41e2017-08-23 13:55:39 -0700240 destRouterAddress = config.getRouterIpv4(deviceId.get());
Charles Chanb8caeab2017-06-02 19:23:51 -0700241 } catch (DeviceConfigNotFoundException e) {
Charles Chanf0ae41e2017-08-23 13:55:39 -0700242 log.warn("Device config for {} not found. Abort ICMP processing", deviceId);
Charles Chanb8caeab2017-06-02 19:23:51 -0700243 return;
244 }
245 }
246 }
247
Pier Ventreadb4ae62016-11-23 09:57:42 -0800248 int destSid = config.getIPv4SegmentId(destRouterAddress);
Charles Chan70661362016-12-09 12:54:49 -0800249 if (destSid < 0) {
Charles Chanb8caeab2017-06-02 19:23:51 -0700250 log.warn("Failed to lookup SID of the switch that {} attaches to. " +
251 "Unable to process ICMP request.", destIpAddress);
sangho80f11cb2015-04-01 13:05:26 -0700252 return;
253 }
Pier Ventreb6b81d52016-12-02 08:16:05 -0800254 sendPacketOut(outport, icmpReplyEth, destSid, destIpAddress, icmpReplyIpv4.getTtl());
sangho80f11cb2015-04-01 13:05:26 -0700255 }
256
Pier Ventreb6b81d52016-12-02 08:16:05 -0800257 ///////////////////////////////////////////
258 // ICMPv6 Echo/Reply Protocol //
259 ///////////////////////////////////////////
sangho80f11cb2015-04-01 13:05:26 -0700260
Pier Ventreb6b81d52016-12-02 08:16:05 -0800261 /**
262 * Process incoming ICMPv6 packet.
Charles Chanb78b1132017-03-29 17:24:39 -0700263 * If it is an ICMPv6 request to router, then sends an ICMPv6 response.
264 * Otherwise ignore the packet.
Pier Ventreb6b81d52016-12-02 08:16:05 -0800265 *
266 * @param eth the incoming ICMPv6 packet
267 * @param inPort the input port
268 */
269 public void processIcmpv6(Ethernet eth, ConnectPoint inPort) {
270 DeviceId deviceId = inPort.deviceId();
271 IPv6 ipv6Packet = (IPv6) eth.getPayload();
Charles Chanfa4b2732017-10-23 12:43:06 -0700272 ICMP6 icmp6 = (ICMP6) ipv6Packet.getPayload();
Pier Ventreb6b81d52016-12-02 08:16:05 -0800273 Ip6Address destinationAddress = Ip6Address.valueOf(ipv6Packet.getDestinationAddress());
274 Set<IpAddress> gatewayIpAddresses = config.getPortIPs(deviceId);
275 IpAddress routerIp;
Jayakumar Thazhath0cca97a2017-09-11 02:00:20 -0400276
Charles Chanfa4b2732017-10-23 12:43:06 -0700277 // Only proceed with echo request
278 if (icmp6.getIcmpType() != ICMP6.ECHO_REQUEST) {
279 return;
280 }
281
Pier Ventreb6b81d52016-12-02 08:16:05 -0800282 try {
283 routerIp = config.getRouterIpv6(deviceId);
284 } catch (DeviceConfigNotFoundException e) {
pier6a2052b2019-06-28 22:17:31 +0200285 log.warn(e.getMessage() + " Aborting processPacketIn.");
286 return;
287 }
288
289 // Get pair ip - if it exists
290 IpAddress pairRouterIp;
291 try {
292 DeviceId pairDeviceId = config.getPairDeviceId(deviceId);
293 pairRouterIp = pairDeviceId != null ? config.getRouterIpv6(pairDeviceId) : null;
294 } catch (DeviceConfigNotFoundException e) {
295 pairRouterIp = null;
296 }
297
298 Optional<Ip6Address> linkLocalIp = getLinkLocalIp(inPort);
299 // Ensure ICMP to the router IP, EUI-64 link-local IP, or gateway IP
300 if (destinationAddress.equals(routerIp.getIp6Address()) ||
301 (linkLocalIp.isPresent() && destinationAddress.equals(linkLocalIp.get())) ||
302 (pairRouterIp != null && destinationAddress.equals(pairRouterIp.getIp6Address())) ||
303 gatewayIpAddresses.contains(destinationAddress)) {
304 sendIcmpv6Response(eth, inPort);
305 } else {
306 log.trace("Ignore ICMPv6 that targets for {}", destinationAddress);
Pier Ventreb6b81d52016-12-02 08:16:05 -0800307 }
308 }
309
310 /**
311 * Sends an ICMPv6 reply message.
312 *
Pier Ventreb6b81d52016-12-02 08:16:05 -0800313 * @param ethRequest the original ICMP request
314 * @param outport the output port where the ICMP reply should be sent to
315 */
316 private void sendIcmpv6Response(Ethernet ethRequest, ConnectPoint outport) {
pier6a2052b2019-06-28 22:17:31 +0200317 int destSid = -1;
Pier Ventreb6b81d52016-12-02 08:16:05 -0800318 Ethernet ethReply = ICMP6.buildIcmp6Reply(ethRequest);
319 IPv6 icmpRequestIpv6 = (IPv6) ethRequest.getPayload();
320 IPv6 icmpReplyIpv6 = (IPv6) ethRequest.getPayload();
Charles Chanf8da1c72018-03-06 15:45:03 -0800321 // Source IP of the echo "reply"
322 Ip6Address srcIpAddress = Ip6Address.valueOf(icmpRequestIpv6.getDestinationAddress());
323 // Destination IP of the echo "reply"
Pier Ventreb6b81d52016-12-02 08:16:05 -0800324 Ip6Address destIpAddress = Ip6Address.valueOf(icmpRequestIpv6.getSourceAddress());
Charles Chanf8da1c72018-03-06 15:45:03 -0800325 Optional<Ip6Address> linkLocalIp = getLinkLocalIp(outport);
Charles Chanb8caeab2017-06-02 19:23:51 -0700326
Charles Chanf8da1c72018-03-06 15:45:03 -0800327 // Fast path if the echo request targets the link-local address of switch interface
328 if (linkLocalIp.isPresent() && srcIpAddress.equals(linkLocalIp.get())) {
pier6a2052b2019-06-28 22:17:31 +0200329 sendPacketOut(outport, ethReply, destSid, destIpAddress, icmpReplyIpv6.getHopLimit());
Charles Chanf8da1c72018-03-06 15:45:03 -0800330 return;
331 }
332
pier6a2052b2019-06-28 22:17:31 +0200333 // Get the available connect points
334 Set<ConnectPoint> destConnectPoints = config.getConnectPointsForASubnetHost(destIpAddress);
335 // Select a router
336 Ip6Address destRouterAddress = selectRouterIp6Address(destIpAddress, outport, destConnectPoints);
337
Charles Chanb8caeab2017-06-02 19:23:51 -0700338 // Note: Source IP of the ICMP request doesn't belong to any configured subnet.
339 // The source might be an indirect host behind a router.
340 // Lookup the route store for the nexthop instead.
341 if (destRouterAddress == null) {
Charles Chanf0ae41e2017-08-23 13:55:39 -0700342 Optional<DeviceId> deviceId = srManager.routeService
343 .longestPrefixLookup(destIpAddress).map(srManager::nextHopLocations)
344 .flatMap(locations -> locations.stream().findFirst())
345 .map(ConnectPoint::deviceId);
346 if (deviceId.isPresent()) {
Charles Chanb8caeab2017-06-02 19:23:51 -0700347 try {
Charles Chanf0ae41e2017-08-23 13:55:39 -0700348 destRouterAddress = config.getRouterIpv6(deviceId.get());
Charles Chanb8caeab2017-06-02 19:23:51 -0700349 } catch (DeviceConfigNotFoundException e) {
Charles Chanf0ae41e2017-08-23 13:55:39 -0700350 log.warn("Device config for {} not found. Abort ICMPv6 processing", deviceId);
Charles Chanb8caeab2017-06-02 19:23:51 -0700351 return;
352 }
353 }
354 }
355
pier6a2052b2019-06-28 22:17:31 +0200356 destSid = config.getIPv6SegmentId(destRouterAddress);
357 if (destSid < 0) {
358 log.warn("Failed to lookup SID of the switch that {} attaches to. " +
359 "Unable to process ICMPv6 request.", destIpAddress);
360 return;
Pier Ventreb6b81d52016-12-02 08:16:05 -0800361 }
pier6a2052b2019-06-28 22:17:31 +0200362 sendPacketOut(outport, ethReply, destSid, destIpAddress, icmpReplyIpv6.getHopLimit());
sangho80f11cb2015-04-01 13:05:26 -0700363 }
sangho9b169e32015-04-14 16:27:13 -0700364
Pier Ventre1a655962016-11-28 16:48:06 -0800365 ///////////////////////////////////////////
366 // ICMPv6 Neighbour Discovery Protocol //
367 ///////////////////////////////////////////
sangho9b169e32015-04-14 16:27:13 -0700368
Pier Ventre1a655962016-11-28 16:48:06 -0800369 /**
370 * Process incoming NDP packet.
371 *
372 * If it is an NDP request for the router or for the gateway, then sends a NDP reply.
373 * If it is an NDP request to unknown host flood in the subnet.
374 * If it is an NDP packet to known host forward the packet to the host.
375 *
376 * FIXME If the NDP packets use link local addresses we fail.
377 *
378 * @param pkt inbound packet
379 * @param hostService the host service
380 */
381 public void processPacketIn(NeighbourMessageContext pkt, HostService hostService) {
Charles Chanb78b1132017-03-29 17:24:39 -0700382 // First we validate the ndp packet
Pier Ventre1a655962016-11-28 16:48:06 -0800383 SegmentRoutingAppConfig appConfig = srManager.cfgService
384 .getConfig(srManager.appId, SegmentRoutingAppConfig.class);
385 if (appConfig != null && appConfig.suppressSubnet().contains(pkt.inPort())) {
386 // Ignore NDP packets come from suppressed ports
387 pkt.drop();
388 return;
389 }
Pier Ventre1a655962016-11-28 16:48:06 -0800390
391 if (pkt.type() == NeighbourMessageType.REQUEST) {
392 handleNdpRequest(pkt, hostService);
393 } else {
394 handleNdpReply(pkt, hostService);
395 }
396
397 }
398
399 /**
Pier Ventre1a655962016-11-28 16:48:06 -0800400 * Helper method to handle the ndp requests.
Pier Ventre1a655962016-11-28 16:48:06 -0800401 * @param pkt the ndp packet request and context information
402 * @param hostService the host service
403 */
404 private void handleNdpRequest(NeighbourMessageContext pkt, HostService hostService) {
Charles Chanb3016ed2017-02-27 15:50:43 -0800405 // ND request for the gateway. We have to reply on behalf of the gateway.
Pier Ventre1a655962016-11-28 16:48:06 -0800406 if (isNdpForGateway(pkt)) {
Jayakumar Thazhath0cca97a2017-09-11 02:00:20 -0400407 log.trace("Sending NDP reply on behalf of gateway IP for pkt: {}", pkt.target());
408 MacAddress routerMac = config.getRouterMacForAGatewayIp(pkt.target());
Charles Chan76adba22018-03-30 12:11:34 -0700409 if (routerMac == null) {
410 log.warn("Router MAC of {} is not configured. Cannot handle NDP request from {}",
411 pkt.inPort().deviceId(), pkt.sender());
412 return;
413 }
Charles Chan1e544ca2018-06-29 14:28:39 -0700414 sendResponse(pkt, routerMac, hostService, true);
Pier Ventre1a655962016-11-28 16:48:06 -0800415 } else {
Jayakumar Thazhath0cca97a2017-09-11 02:00:20 -0400416
417 // Process NDP targets towards EUI-64 address.
418 try {
419 DeviceId deviceId = pkt.inPort().deviceId();
Charles Chanfa4b2732017-10-23 12:43:06 -0700420
Charles Chanf8da1c72018-03-06 15:45:03 -0800421 Optional<Ip6Address> linkLocalIp = getLinkLocalIp(pkt.inPort());
Charles Chanfa4b2732017-10-23 12:43:06 -0700422 if (linkLocalIp.isPresent() && pkt.target().equals(linkLocalIp.get())) {
Jayakumar Thazhath0cca97a2017-09-11 02:00:20 -0400423 MacAddress routerMac = config.getDeviceMac(deviceId);
Charles Chan1e544ca2018-06-29 14:28:39 -0700424 sendResponse(pkt, routerMac, hostService, true);
Jayakumar Thazhath0cca97a2017-09-11 02:00:20 -0400425 }
426 } catch (DeviceConfigNotFoundException e) {
427 log.warn(e.getMessage() + " Unable to handle NDP packet to {}. Aborting.", pkt.target());
428 return;
429 }
430
Charles Chanb3016ed2017-02-27 15:50:43 -0800431 // NOTE: Ignore NDP packets except those target for the router
432 // We will reconsider enabling this when we have host learning support
Pier Ventre1a655962016-11-28 16:48:06 -0800433 /*
Charles Chanb3016ed2017-02-27 15:50:43 -0800434 // ND request for an host. We do a search by Ip.
Pier Ventre1a655962016-11-28 16:48:06 -0800435 Set<Host> hosts = hostService.getHostsByIp(pkt.target());
Charles Chanb3016ed2017-02-27 15:50:43 -0800436 // Possible misconfiguration ? In future this case
437 // should be handled we can have same hosts in different VLANs.
Pier Ventre1a655962016-11-28 16:48:06 -0800438 if (hosts.size() > 1) {
439 log.warn("More than one host with IP {}", pkt.target());
440 }
441 Host targetHost = hosts.stream().findFirst().orElse(null);
Charles Chanb3016ed2017-02-27 15:50:43 -0800442 // If we know the host forward to its attachment point.
Pier Ventre1a655962016-11-28 16:48:06 -0800443 if (targetHost != null) {
444 log.debug("Forward NDP request to the target host");
445 pkt.forward(targetHost.location());
446 } else {
Charles Chanb3016ed2017-02-27 15:50:43 -0800447 // Flood otherwise.
Pier Ventre1a655962016-11-28 16:48:06 -0800448 log.debug("Flood NDP request to the target subnet");
449 flood(pkt);
450 }
Charles Chanb3016ed2017-02-27 15:50:43 -0800451 */
Pier Ventre1a655962016-11-28 16:48:06 -0800452 }
453 }
454
455 /**
456 * Helper method to handle the ndp replies.
457 *
458 * @param pkt the ndp packet reply and context information
459 * @param hostService the host service
460 */
461 private void handleNdpReply(NeighbourMessageContext pkt, HostService hostService) {
462 if (isNdpForGateway(pkt)) {
463 log.debug("Forwarding all the ip packets we stored");
464 Ip6Address hostIpAddress = pkt.sender().getIp6Address();
465 srManager.ipHandler.forwardPackets(pkt.inPort().deviceId(), hostIpAddress);
466 } else {
Charles Chanb3016ed2017-02-27 15:50:43 -0800467 // NOTE: Ignore NDP packets except those target for the router
468 // We will reconsider enabling this when we have host learning support
469 /*
Pier Ventre1a655962016-11-28 16:48:06 -0800470 HostId hostId = HostId.hostId(pkt.dstMac(), pkt.vlan());
471 Host targetHost = hostService.getHost(hostId);
472 if (targetHost != null) {
473 log.debug("Forwarding the reply to the host");
474 pkt.forward(targetHost.location());
475 } else {
Charles Chanb3016ed2017-02-27 15:50:43 -0800476 // We don't have to flood towards spine facing ports.
Charles Chan10b0fb72017-02-02 16:20:42 -0800477 if (pkt.vlan().equals(SegmentRoutingManager.INTERNAL_VLAN)) {
Pier Ventre1a655962016-11-28 16:48:06 -0800478 return;
479 }
480 log.debug("Flooding the reply to the subnet");
481 flood(pkt);
482 }
Charles Chanb3016ed2017-02-27 15:50:43 -0800483 */
Pier Ventre1a655962016-11-28 16:48:06 -0800484 }
485 }
486
487 /**
488 * Utility to verify if the ND are for the gateway.
489 *
490 * @param pkt the ndp packet
491 * @return true if the ndp is for the gateway. False otherwise
492 */
493 private boolean isNdpForGateway(NeighbourMessageContext pkt) {
494 DeviceId deviceId = pkt.inPort().deviceId();
495 Set<IpAddress> gatewayIpAddresses = null;
Jayakumar Thazhath0cca97a2017-09-11 02:00:20 -0400496
Pier Ventre1a655962016-11-28 16:48:06 -0800497 try {
498 if (pkt.target().equals(config.getRouterIpv6(deviceId))) {
499 return true;
500 }
501 gatewayIpAddresses = config.getPortIPs(deviceId);
502 } catch (DeviceConfigNotFoundException e) {
503 log.warn(e.getMessage() + " Aborting check for router IP in processing ndp");
Jayakumar Thazhath0cca97a2017-09-11 02:00:20 -0400504 return false;
Pier Ventre1a655962016-11-28 16:48:06 -0800505 }
Charles Chand3727b72017-03-13 13:10:30 -0700506 return gatewayIpAddresses != null && gatewayIpAddresses.stream()
507 .filter(IpAddress::isIp6)
508 .anyMatch(gatewayIp -> gatewayIp.equals(pkt.target()) ||
509 Arrays.equals(IPv6.getSolicitNodeAddress(gatewayIp.toOctets()),
Jayakumar Thazhath0cca97a2017-09-11 02:00:20 -0400510 pkt.target().toOctets()));
Pier Ventre1a655962016-11-28 16:48:06 -0800511 }
512
513 /**
Pier Ventreb6b81d52016-12-02 08:16:05 -0800514 * Sends a NDP request for the target IP address to all ports except in-port.
Pier Ventre1a655962016-11-28 16:48:06 -0800515 *
Pier Ventreb6b81d52016-12-02 08:16:05 -0800516 * @param deviceId Switch device ID
517 * @param targetAddress target IP address for ARP
518 * @param inPort in-port
Pier Ventre1a655962016-11-28 16:48:06 -0800519 */
Pier Ventreb6b81d52016-12-02 08:16:05 -0800520 public void sendNdpRequest(DeviceId deviceId, IpAddress targetAddress, ConnectPoint inPort) {
521 byte[] senderMacAddress = new byte[MacAddress.MAC_ADDRESS_LENGTH];
522 byte[] senderIpAddress = new byte[Ip6Address.BYTE_LENGTH];
Charles Chanb78b1132017-03-29 17:24:39 -0700523 // Retrieves device info.
Pier Luigi6a83c4a2017-01-29 12:38:48 -0800524 if (!getSenderInfo(senderMacAddress, senderIpAddress, deviceId, targetAddress)) {
525 log.warn("Aborting sendNdpRequest, we cannot get all the information needed");
526 return;
527 }
Charles Chanb78b1132017-03-29 17:24:39 -0700528 // We have to compute the dst mac address and dst ip address.
Pier Ventreb6b81d52016-12-02 08:16:05 -0800529 byte[] dstIp = IPv6.getSolicitNodeAddress(targetAddress.toOctets());
530 byte[] dstMac = IPv6.getMCastMacAddress(dstIp);
Charles Chanb78b1132017-03-29 17:24:39 -0700531 // Creates the request.
Pier Ventreb6b81d52016-12-02 08:16:05 -0800532 Ethernet ndpRequest = NeighborSolicitation.buildNdpSolicit(
Ray Milkey95f4ab62019-01-25 15:59:36 -0800533 targetAddress.getIp6Address(),
534 Ip6Address.valueOf(senderIpAddress),
535 Ip6Address.valueOf(dstIp),
536 MacAddress.valueOf(senderMacAddress),
537 MacAddress.valueOf(dstMac),
Pier Ventreb6b81d52016-12-02 08:16:05 -0800538 VlanId.NONE
539 );
540 flood(ndpRequest, inPort, targetAddress);
Pier Ventre1a655962016-11-28 16:48:06 -0800541 }
Charles Chanf8da1c72018-03-06 15:45:03 -0800542
543 /**
544 * Returns link-local IP of given connect point.
545 *
546 * @param cp connect point
547 * @return optional link-local IP
548 */
549 private Optional<Ip6Address> getLinkLocalIp(ConnectPoint cp) {
550 return srManager.interfaceService.getInterfacesByPort(cp)
551 .stream()
552 .map(Interface::mac)
553 .map(MacAddress::toBytes)
554 .map(IPv6::getLinkLocalAddress)
555 .map(Ip6Address::valueOf)
556 .findFirst();
557 }
sangho80f11cb2015-04-01 13:05:26 -0700558}