blob: e7c92030ee58fa6ae3ada63c5be5f0d11d285373 [file] [log] [blame]
sanghob35a6192015-04-01 13:05:26 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2015-present Open Networking Foundation
sanghob35a6192015-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
sanghob35a6192015-04-01 13:05:26 -070018import org.onlab.packet.Ethernet;
19import org.onlab.packet.ICMP;
Pier Ventre735b8c82016-12-02 08:16:05 -080020import org.onlab.packet.ICMP6;
sanghob35a6192015-04-01 13:05:26 -070021import org.onlab.packet.IPv4;
Pier Ventre735b8c82016-12-02 08:16:05 -080022import org.onlab.packet.IPv6;
sanghob35a6192015-04-01 13:05:26 -070023import org.onlab.packet.Ip4Address;
Pier Ventref4b5fce2016-11-28 16:48:06 -080024import org.onlab.packet.Ip6Address;
Pier Ventree0ae7a32016-11-23 09:57:42 -080025import org.onlab.packet.IpAddress;
sanghob35a6192015-04-01 13:05:26 -070026import org.onlab.packet.MPLS;
Pier Ventref4b5fce2016-11-28 16:48:06 -080027import org.onlab.packet.MacAddress;
28import org.onlab.packet.VlanId;
Pier Ventre735b8c82016-12-02 08:16:05 -080029import org.onlab.packet.ndp.NeighborSolicitation;
Ray Milkeyb65d7842017-08-03 16:28:24 -070030import org.onosproject.net.neighbour.NeighbourMessageContext;
31import org.onosproject.net.neighbour.NeighbourMessageType;
sanghob35a6192015-04-01 13:05:26 -070032import org.onosproject.net.ConnectPoint;
33import org.onosproject.net.DeviceId;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -040034import org.onosproject.net.intf.Interface;
sanghob35a6192015-04-01 13:05:26 -070035import org.onosproject.net.flow.DefaultTrafficTreatment;
36import org.onosproject.net.flow.TrafficTreatment;
Pier Ventref4b5fce2016-11-28 16:48:06 -080037import org.onosproject.net.host.HostService;
sanghob35a6192015-04-01 13:05:26 -070038import org.onosproject.net.packet.DefaultOutboundPacket;
sanghob35a6192015-04-01 13:05:26 -070039import org.onosproject.net.packet.OutboundPacket;
Charles Chan0b4e6182015-11-03 10:42:14 -080040import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
Pier Ventref4b5fce2016-11-28 16:48:06 -080041import org.onosproject.segmentrouting.config.SegmentRoutingAppConfig;
sanghob35a6192015-04-01 13:05:26 -070042import org.slf4j.Logger;
43import org.slf4j.LoggerFactory;
44
Jonathan Hart2a655752015-04-07 16:46:33 -070045import java.nio.ByteBuffer;
Charles Chan0ed44fb2017-03-13 13:10:30 -070046import java.util.Arrays;
Charles Chan40abef42017-06-02 19:23:51 -070047import java.util.Optional;
Saurav Das837e0bb2015-10-30 17:45:38 -070048import java.util.Set;
Jonathan Hart2a655752015-04-07 16:46:33 -070049
Charles Chane849c192016-01-11 18:28:54 -080050/**
51 * Handler of ICMP packets that responses or forwards ICMP packets that
52 * are sent to the controller.
53 */
Pier Ventre735b8c82016-12-02 08:16:05 -080054public class IcmpHandler extends SegmentRoutingNeighbourHandler {
sanghob35a6192015-04-01 13:05:26 -070055
56 private static Logger log = LoggerFactory.getLogger(IcmpHandler.class);
sanghob35a6192015-04-01 13:05:26 -070057
58 /**
59 * Creates an IcmpHandler object.
60 *
61 * @param srManager SegmentRoutingManager object
62 */
63 public IcmpHandler(SegmentRoutingManager srManager) {
Pier Ventre735b8c82016-12-02 08:16:05 -080064 super(srManager);
65 }
66
67 /**
68 * Utility function to send packet out.
69 *
70 * @param outport the output port
71 * @param payload the packet to send
72 * @param sid the segment id
73 * @param destIpAddress the destination ip address
74 * @param allowedHops the hop limit/ttl
75 */
76 private void sendPacketOut(ConnectPoint outport,
77 Ethernet payload,
78 int sid,
79 IpAddress destIpAddress,
80 byte allowedHops) {
81 int destSid;
82 if (destIpAddress.isIp4()) {
83 destSid = config.getIPv4SegmentId(payload.getDestinationMAC());
84 } else {
85 destSid = config.getIPv6SegmentId(payload.getDestinationMAC());
86 }
87
88 if (sid == -1 || destSid == sid ||
89 config.inSameSubnet(outport.deviceId(), destIpAddress)) {
90 TrafficTreatment treatment = DefaultTrafficTreatment.builder().
91 setOutput(outport.port()).build();
92 OutboundPacket packet = new DefaultOutboundPacket(outport.deviceId(),
93 treatment, ByteBuffer.wrap(payload.serialize()));
94 srManager.packetService.emit(packet);
95 } else {
Saurav Dasceccf242017-08-03 18:30:35 -070096 log.trace("Send a MPLS packet as a ICMP response");
Pier Ventre735b8c82016-12-02 08:16:05 -080097 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
98 .setOutput(outport.port())
99 .build();
100
101 payload.setEtherType(Ethernet.MPLS_UNICAST);
102 MPLS mplsPkt = new MPLS();
103 mplsPkt.setLabel(sid);
104 mplsPkt.setTtl(allowedHops);
105 mplsPkt.setPayload(payload.getPayload());
106 payload.setPayload(mplsPkt);
107
108 OutboundPacket packet = new DefaultOutboundPacket(outport.deviceId(),
109 treatment, ByteBuffer.wrap(payload.serialize()));
110
111 srManager.packetService.emit(packet);
112 }
sanghob35a6192015-04-01 13:05:26 -0700113 }
114
Pier Ventref4b5fce2016-11-28 16:48:06 -0800115 //////////////////////////////////////
116 // ICMP Echo/Reply Protocol //
117 //////////////////////////////////////
118
sanghob35a6192015-04-01 13:05:26 -0700119 /**
120 * Process incoming ICMP packet.
Charles Chand4f3a622017-03-29 17:24:39 -0700121 * If it is an ICMP request to router, then sends an ICMP response.
122 * Otherwise ignore the packet.
sanghob35a6192015-04-01 13:05:26 -0700123 *
Pier Ventre735b8c82016-12-02 08:16:05 -0800124 * @param eth inbound ICMP packet
125 * @param inPort the input port
sanghob35a6192015-04-01 13:05:26 -0700126 */
Pier Ventre735b8c82016-12-02 08:16:05 -0800127 public void processIcmp(Ethernet eth, ConnectPoint inPort) {
128 DeviceId deviceId = inPort.deviceId();
129 IPv4 ipv4Packet = (IPv4) eth.getPayload();
130 Ip4Address destinationAddress = Ip4Address.valueOf(ipv4Packet.getDestinationAddress());
Pier Ventre10bd8d12016-11-26 21:05:22 -0800131 Set<IpAddress> gatewayIpAddresses = config.getPortIPs(deviceId);
Pier Ventree0ae7a32016-11-23 09:57:42 -0800132 IpAddress routerIp;
Charles Chan0b4e6182015-11-03 10:42:14 -0800133 try {
Pier Ventree0ae7a32016-11-23 09:57:42 -0800134 routerIp = config.getRouterIpv4(deviceId);
Charles Chan0b4e6182015-11-03 10:42:14 -0800135 } catch (DeviceConfigNotFoundException e) {
136 log.warn(e.getMessage() + " Aborting processPacketIn.");
137 return;
138 }
sanghob35a6192015-04-01 13:05:26 -0700139 // ICMP to the router IP or gateway IP
Pier Ventre735b8c82016-12-02 08:16:05 -0800140 if (((ICMP) ipv4Packet.getPayload()).getIcmpType() == ICMP.TYPE_ECHO_REQUEST &&
141 (destinationAddress.equals(routerIp.getIp4Address()) ||
Srikanth Vavilapalli4db76e32015-04-07 15:12:32 -0700142 gatewayIpAddresses.contains(destinationAddress))) {
Pier Ventre735b8c82016-12-02 08:16:05 -0800143 sendIcmpResponse(eth, inPort);
sanghob35a6192015-04-01 13:05:26 -0700144 } else {
Charles Chand4f3a622017-03-29 17:24:39 -0700145 log.trace("Ignore ICMP that targets for {}", destinationAddress);
sanghob35a6192015-04-01 13:05:26 -0700146 }
Charles Chand4f3a622017-03-29 17:24:39 -0700147 // We remove the packet from the queue
148 srManager.ipHandler.dequeuePacket(ipv4Packet, destinationAddress);
sanghob35a6192015-04-01 13:05:26 -0700149 }
150
Charles Chan68aa62d2015-11-09 16:37:23 -0800151 /**
152 * Sends an ICMP reply message.
153 *
Charles Chan68aa62d2015-11-09 16:37:23 -0800154 * @param icmpRequest the original ICMP request
155 * @param outport the output port where the ICMP reply should be sent to
156 */
Pier Ventree0ae7a32016-11-23 09:57:42 -0800157 private void sendIcmpResponse(Ethernet icmpRequest, ConnectPoint outport) {
Pier Ventre735b8c82016-12-02 08:16:05 -0800158 Ethernet icmpReplyEth = ICMP.buildIcmpReply(icmpRequest);
sanghob35a6192015-04-01 13:05:26 -0700159 IPv4 icmpRequestIpv4 = (IPv4) icmpRequest.getPayload();
Pier Ventre735b8c82016-12-02 08:16:05 -0800160 IPv4 icmpReplyIpv4 = (IPv4) icmpReplyEth.getPayload();
161 Ip4Address destIpAddress = Ip4Address.valueOf(icmpRequestIpv4.getSourceAddress());
sangho666cd6d2015-04-14 16:27:13 -0700162 Ip4Address destRouterAddress = config.getRouterIpAddressForASubnetHost(destIpAddress);
Charles Chan40abef42017-06-02 19:23:51 -0700163
164 // Note: Source IP of the ICMP request doesn't belong to any configured subnet.
165 // The source might be an indirectly attached host (e.g. behind a router)
166 // Lookup the route store for the nexthop instead.
167 if (destRouterAddress == null) {
Charles Chan9640c812017-08-23 13:55:39 -0700168 Optional<DeviceId> deviceId = srManager.routeService
169 .longestPrefixLookup(destIpAddress).map(srManager::nextHopLocations)
170 .flatMap(locations -> locations.stream().findFirst())
171 .map(ConnectPoint::deviceId);
172 if (deviceId.isPresent()) {
Charles Chan40abef42017-06-02 19:23:51 -0700173 try {
Charles Chan9640c812017-08-23 13:55:39 -0700174 destRouterAddress = config.getRouterIpv4(deviceId.get());
Charles Chan40abef42017-06-02 19:23:51 -0700175 } catch (DeviceConfigNotFoundException e) {
Charles Chan9640c812017-08-23 13:55:39 -0700176 log.warn("Device config for {} not found. Abort ICMP processing", deviceId);
Charles Chan40abef42017-06-02 19:23:51 -0700177 return;
178 }
179 }
180 }
181
Pier Ventree0ae7a32016-11-23 09:57:42 -0800182 int destSid = config.getIPv4SegmentId(destRouterAddress);
Charles Chan68aaad52016-12-09 12:54:49 -0800183 if (destSid < 0) {
Charles Chan40abef42017-06-02 19:23:51 -0700184 log.warn("Failed to lookup SID of the switch that {} attaches to. " +
185 "Unable to process ICMP request.", destIpAddress);
sanghob35a6192015-04-01 13:05:26 -0700186 return;
187 }
Pier Ventre735b8c82016-12-02 08:16:05 -0800188 sendPacketOut(outport, icmpReplyEth, destSid, destIpAddress, icmpReplyIpv4.getTtl());
sanghob35a6192015-04-01 13:05:26 -0700189 }
190
Pier Ventre735b8c82016-12-02 08:16:05 -0800191 ///////////////////////////////////////////
192 // ICMPv6 Echo/Reply Protocol //
193 ///////////////////////////////////////////
sanghob35a6192015-04-01 13:05:26 -0700194
Pier Ventre735b8c82016-12-02 08:16:05 -0800195 /**
196 * Process incoming ICMPv6 packet.
Charles Chand4f3a622017-03-29 17:24:39 -0700197 * If it is an ICMPv6 request to router, then sends an ICMPv6 response.
198 * Otherwise ignore the packet.
Pier Ventre735b8c82016-12-02 08:16:05 -0800199 *
200 * @param eth the incoming ICMPv6 packet
201 * @param inPort the input port
202 */
203 public void processIcmpv6(Ethernet eth, ConnectPoint inPort) {
204 DeviceId deviceId = inPort.deviceId();
205 IPv6 ipv6Packet = (IPv6) eth.getPayload();
Charles Chan419f5aa2017-10-23 12:43:06 -0700206 ICMP6 icmp6 = (ICMP6) ipv6Packet.getPayload();
Pier Ventre735b8c82016-12-02 08:16:05 -0800207 Ip6Address destinationAddress = Ip6Address.valueOf(ipv6Packet.getDestinationAddress());
208 Set<IpAddress> gatewayIpAddresses = config.getPortIPs(deviceId);
209 IpAddress routerIp;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400210
Charles Chan419f5aa2017-10-23 12:43:06 -0700211 // Only proceed with echo request
212 if (icmp6.getIcmpType() != ICMP6.ECHO_REQUEST) {
213 return;
214 }
215
Pier Ventre735b8c82016-12-02 08:16:05 -0800216 try {
217 routerIp = config.getRouterIpv6(deviceId);
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400218
Charles Chanc9e92242018-03-06 15:45:03 -0800219 Optional<Ip6Address> linkLocalIp = getLinkLocalIp(inPort);
Charles Chan419f5aa2017-10-23 12:43:06 -0700220 // Ensure ICMP to the router IP, EUI-64 link-local IP, or gateway IP
221 if (destinationAddress.equals(routerIp.getIp6Address()) ||
222 (linkLocalIp.isPresent() && destinationAddress.equals(linkLocalIp.get())) ||
223 gatewayIpAddresses.contains(destinationAddress)) {
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400224 sendIcmpv6Response(eth, inPort);
225 } else {
226 log.trace("Ignore ICMPv6 that targets for {}", destinationAddress);
227 }
Pier Ventre735b8c82016-12-02 08:16:05 -0800228 } catch (DeviceConfigNotFoundException e) {
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400229 log.warn(e.getMessage() + " Ignore ICMPv6 that targets to {}.", destinationAddress);
Pier Ventre735b8c82016-12-02 08:16:05 -0800230 }
231 }
232
233 /**
234 * Sends an ICMPv6 reply message.
235 *
Pier Ventre735b8c82016-12-02 08:16:05 -0800236 * @param ethRequest the original ICMP request
237 * @param outport the output port where the ICMP reply should be sent to
238 */
239 private void sendIcmpv6Response(Ethernet ethRequest, ConnectPoint outport) {
Charles Chanc9e92242018-03-06 15:45:03 -0800240 int sid = -1;
Pier Ventre735b8c82016-12-02 08:16:05 -0800241 Ethernet ethReply = ICMP6.buildIcmp6Reply(ethRequest);
242 IPv6 icmpRequestIpv6 = (IPv6) ethRequest.getPayload();
243 IPv6 icmpReplyIpv6 = (IPv6) ethRequest.getPayload();
Charles Chanc9e92242018-03-06 15:45:03 -0800244 // Source IP of the echo "reply"
245 Ip6Address srcIpAddress = Ip6Address.valueOf(icmpRequestIpv6.getDestinationAddress());
246 // Destination IP of the echo "reply"
Pier Ventre735b8c82016-12-02 08:16:05 -0800247 Ip6Address destIpAddress = Ip6Address.valueOf(icmpRequestIpv6.getSourceAddress());
Charles Chanc9e92242018-03-06 15:45:03 -0800248 Optional<Ip6Address> linkLocalIp = getLinkLocalIp(outport);
Pier Ventre735b8c82016-12-02 08:16:05 -0800249 Ip6Address destRouterAddress = config.getRouterIpAddressForASubnetHost(destIpAddress);
Charles Chan40abef42017-06-02 19:23:51 -0700250
Charles Chanc9e92242018-03-06 15:45:03 -0800251 // Fast path if the echo request targets the link-local address of switch interface
252 if (linkLocalIp.isPresent() && srcIpAddress.equals(linkLocalIp.get())) {
253 sendPacketOut(outport, ethReply, sid, destIpAddress, icmpReplyIpv6.getHopLimit());
254 return;
255 }
256
Charles Chan40abef42017-06-02 19:23:51 -0700257 // Note: Source IP of the ICMP request doesn't belong to any configured subnet.
258 // The source might be an indirect host behind a router.
259 // Lookup the route store for the nexthop instead.
260 if (destRouterAddress == null) {
Charles Chan9640c812017-08-23 13:55:39 -0700261 Optional<DeviceId> deviceId = srManager.routeService
262 .longestPrefixLookup(destIpAddress).map(srManager::nextHopLocations)
263 .flatMap(locations -> locations.stream().findFirst())
264 .map(ConnectPoint::deviceId);
265 if (deviceId.isPresent()) {
Charles Chan40abef42017-06-02 19:23:51 -0700266 try {
Charles Chan9640c812017-08-23 13:55:39 -0700267 destRouterAddress = config.getRouterIpv6(deviceId.get());
Charles Chan40abef42017-06-02 19:23:51 -0700268 } catch (DeviceConfigNotFoundException e) {
Charles Chan9640c812017-08-23 13:55:39 -0700269 log.warn("Device config for {} not found. Abort ICMPv6 processing", deviceId);
Charles Chan40abef42017-06-02 19:23:51 -0700270 return;
271 }
272 }
273 }
274
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400275 // Search SID only if store lookup is success otherwise proceed with "sid=-1"
Charles Chanc9e92242018-03-06 15:45:03 -0800276 if (destRouterAddress != null) {
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400277 sid = config.getIPv6SegmentId(destRouterAddress);
278 if (sid < 0) {
279 log.warn("Failed to lookup SID of the switch that {} attaches to. " +
280 "Unable to process ICMPv6 request.", destIpAddress);
281 return;
282 }
Pier Ventre735b8c82016-12-02 08:16:05 -0800283 }
284 sendPacketOut(outport, ethReply, sid, destIpAddress, icmpReplyIpv6.getHopLimit());
sanghob35a6192015-04-01 13:05:26 -0700285 }
sangho666cd6d2015-04-14 16:27:13 -0700286
Pier Ventref4b5fce2016-11-28 16:48:06 -0800287 ///////////////////////////////////////////
288 // ICMPv6 Neighbour Discovery Protocol //
289 ///////////////////////////////////////////
sangho666cd6d2015-04-14 16:27:13 -0700290
Pier Ventref4b5fce2016-11-28 16:48:06 -0800291 /**
292 * Process incoming NDP packet.
293 *
294 * If it is an NDP request for the router or for the gateway, then sends a NDP reply.
295 * If it is an NDP request to unknown host flood in the subnet.
296 * If it is an NDP packet to known host forward the packet to the host.
297 *
298 * FIXME If the NDP packets use link local addresses we fail.
299 *
300 * @param pkt inbound packet
301 * @param hostService the host service
302 */
303 public void processPacketIn(NeighbourMessageContext pkt, HostService hostService) {
Charles Chand4f3a622017-03-29 17:24:39 -0700304 // First we validate the ndp packet
Pier Ventref4b5fce2016-11-28 16:48:06 -0800305 SegmentRoutingAppConfig appConfig = srManager.cfgService
306 .getConfig(srManager.appId, SegmentRoutingAppConfig.class);
307 if (appConfig != null && appConfig.suppressSubnet().contains(pkt.inPort())) {
308 // Ignore NDP packets come from suppressed ports
309 pkt.drop();
310 return;
311 }
Pier Ventref4b5fce2016-11-28 16:48:06 -0800312
313 if (pkt.type() == NeighbourMessageType.REQUEST) {
314 handleNdpRequest(pkt, hostService);
315 } else {
316 handleNdpReply(pkt, hostService);
317 }
318
319 }
320
321 /**
Pier Ventref4b5fce2016-11-28 16:48:06 -0800322 * Helper method to handle the ndp requests.
Pier Ventref4b5fce2016-11-28 16:48:06 -0800323 * @param pkt the ndp packet request and context information
324 * @param hostService the host service
325 */
326 private void handleNdpRequest(NeighbourMessageContext pkt, HostService hostService) {
Charles Chan563a7812017-02-27 15:50:43 -0800327 // ND request for the gateway. We have to reply on behalf of the gateway.
Pier Ventref4b5fce2016-11-28 16:48:06 -0800328 if (isNdpForGateway(pkt)) {
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400329 log.trace("Sending NDP reply on behalf of gateway IP for pkt: {}", pkt.target());
330 MacAddress routerMac = config.getRouterMacForAGatewayIp(pkt.target());
Charles Chane5e0c9a2018-03-30 12:11:34 -0700331 if (routerMac == null) {
332 log.warn("Router MAC of {} is not configured. Cannot handle NDP request from {}",
333 pkt.inPort().deviceId(), pkt.sender());
334 return;
335 }
Charles Chaneded6882018-06-29 14:28:39 -0700336 sendResponse(pkt, routerMac, hostService, true);
Pier Ventref4b5fce2016-11-28 16:48:06 -0800337 } else {
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400338
339 // Process NDP targets towards EUI-64 address.
340 try {
341 DeviceId deviceId = pkt.inPort().deviceId();
Charles Chan419f5aa2017-10-23 12:43:06 -0700342
Charles Chanc9e92242018-03-06 15:45:03 -0800343 Optional<Ip6Address> linkLocalIp = getLinkLocalIp(pkt.inPort());
Charles Chan419f5aa2017-10-23 12:43:06 -0700344 if (linkLocalIp.isPresent() && pkt.target().equals(linkLocalIp.get())) {
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400345 MacAddress routerMac = config.getDeviceMac(deviceId);
Charles Chaneded6882018-06-29 14:28:39 -0700346 sendResponse(pkt, routerMac, hostService, true);
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400347 }
348 } catch (DeviceConfigNotFoundException e) {
349 log.warn(e.getMessage() + " Unable to handle NDP packet to {}. Aborting.", pkt.target());
350 return;
351 }
352
Charles Chan563a7812017-02-27 15:50:43 -0800353 // NOTE: Ignore NDP packets except those target for the router
354 // We will reconsider enabling this when we have host learning support
Pier Ventref4b5fce2016-11-28 16:48:06 -0800355 /*
Charles Chan563a7812017-02-27 15:50:43 -0800356 // ND request for an host. We do a search by Ip.
Pier Ventref4b5fce2016-11-28 16:48:06 -0800357 Set<Host> hosts = hostService.getHostsByIp(pkt.target());
Charles Chan563a7812017-02-27 15:50:43 -0800358 // Possible misconfiguration ? In future this case
359 // should be handled we can have same hosts in different VLANs.
Pier Ventref4b5fce2016-11-28 16:48:06 -0800360 if (hosts.size() > 1) {
361 log.warn("More than one host with IP {}", pkt.target());
362 }
363 Host targetHost = hosts.stream().findFirst().orElse(null);
Charles Chan563a7812017-02-27 15:50:43 -0800364 // If we know the host forward to its attachment point.
Pier Ventref4b5fce2016-11-28 16:48:06 -0800365 if (targetHost != null) {
366 log.debug("Forward NDP request to the target host");
367 pkt.forward(targetHost.location());
368 } else {
Charles Chan563a7812017-02-27 15:50:43 -0800369 // Flood otherwise.
Pier Ventref4b5fce2016-11-28 16:48:06 -0800370 log.debug("Flood NDP request to the target subnet");
371 flood(pkt);
372 }
Charles Chan563a7812017-02-27 15:50:43 -0800373 */
Pier Ventref4b5fce2016-11-28 16:48:06 -0800374 }
375 }
376
377 /**
378 * Helper method to handle the ndp replies.
379 *
380 * @param pkt the ndp packet reply and context information
381 * @param hostService the host service
382 */
383 private void handleNdpReply(NeighbourMessageContext pkt, HostService hostService) {
384 if (isNdpForGateway(pkt)) {
385 log.debug("Forwarding all the ip packets we stored");
386 Ip6Address hostIpAddress = pkt.sender().getIp6Address();
387 srManager.ipHandler.forwardPackets(pkt.inPort().deviceId(), hostIpAddress);
388 } else {
Charles Chan563a7812017-02-27 15:50:43 -0800389 // NOTE: Ignore NDP packets except those target for the router
390 // We will reconsider enabling this when we have host learning support
391 /*
Pier Ventref4b5fce2016-11-28 16:48:06 -0800392 HostId hostId = HostId.hostId(pkt.dstMac(), pkt.vlan());
393 Host targetHost = hostService.getHost(hostId);
394 if (targetHost != null) {
395 log.debug("Forwarding the reply to the host");
396 pkt.forward(targetHost.location());
397 } else {
Charles Chan563a7812017-02-27 15:50:43 -0800398 // We don't have to flood towards spine facing ports.
Charles Chan59cc16d2017-02-02 16:20:42 -0800399 if (pkt.vlan().equals(SegmentRoutingManager.INTERNAL_VLAN)) {
Pier Ventref4b5fce2016-11-28 16:48:06 -0800400 return;
401 }
402 log.debug("Flooding the reply to the subnet");
403 flood(pkt);
404 }
Charles Chan563a7812017-02-27 15:50:43 -0800405 */
Pier Ventref4b5fce2016-11-28 16:48:06 -0800406 }
407 }
408
409 /**
410 * Utility to verify if the ND are for the gateway.
411 *
412 * @param pkt the ndp packet
413 * @return true if the ndp is for the gateway. False otherwise
414 */
415 private boolean isNdpForGateway(NeighbourMessageContext pkt) {
416 DeviceId deviceId = pkt.inPort().deviceId();
417 Set<IpAddress> gatewayIpAddresses = null;
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400418
Pier Ventref4b5fce2016-11-28 16:48:06 -0800419 try {
420 if (pkt.target().equals(config.getRouterIpv6(deviceId))) {
421 return true;
422 }
423 gatewayIpAddresses = config.getPortIPs(deviceId);
424 } catch (DeviceConfigNotFoundException e) {
425 log.warn(e.getMessage() + " Aborting check for router IP in processing ndp");
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400426 return false;
Pier Ventref4b5fce2016-11-28 16:48:06 -0800427 }
Charles Chan0ed44fb2017-03-13 13:10:30 -0700428 return gatewayIpAddresses != null && gatewayIpAddresses.stream()
429 .filter(IpAddress::isIp6)
430 .anyMatch(gatewayIp -> gatewayIp.equals(pkt.target()) ||
431 Arrays.equals(IPv6.getSolicitNodeAddress(gatewayIp.toOctets()),
Jayakumar Thazhath3ec6aa22017-09-11 02:00:20 -0400432 pkt.target().toOctets()));
Pier Ventref4b5fce2016-11-28 16:48:06 -0800433 }
434
435 /**
Pier Ventre735b8c82016-12-02 08:16:05 -0800436 * Sends a NDP request for the target IP address to all ports except in-port.
Pier Ventref4b5fce2016-11-28 16:48:06 -0800437 *
Pier Ventre735b8c82016-12-02 08:16:05 -0800438 * @param deviceId Switch device ID
439 * @param targetAddress target IP address for ARP
440 * @param inPort in-port
Pier Ventref4b5fce2016-11-28 16:48:06 -0800441 */
Pier Ventre735b8c82016-12-02 08:16:05 -0800442 public void sendNdpRequest(DeviceId deviceId, IpAddress targetAddress, ConnectPoint inPort) {
443 byte[] senderMacAddress = new byte[MacAddress.MAC_ADDRESS_LENGTH];
444 byte[] senderIpAddress = new byte[Ip6Address.BYTE_LENGTH];
Charles Chand4f3a622017-03-29 17:24:39 -0700445 // Retrieves device info.
Pier Luigia905c0c2017-01-29 12:38:48 -0800446 if (!getSenderInfo(senderMacAddress, senderIpAddress, deviceId, targetAddress)) {
447 log.warn("Aborting sendNdpRequest, we cannot get all the information needed");
448 return;
449 }
Charles Chand4f3a622017-03-29 17:24:39 -0700450 // We have to compute the dst mac address and dst ip address.
Pier Ventre735b8c82016-12-02 08:16:05 -0800451 byte[] dstIp = IPv6.getSolicitNodeAddress(targetAddress.toOctets());
452 byte[] dstMac = IPv6.getMCastMacAddress(dstIp);
Charles Chand4f3a622017-03-29 17:24:39 -0700453 // Creates the request.
Pier Ventre735b8c82016-12-02 08:16:05 -0800454 Ethernet ndpRequest = NeighborSolicitation.buildNdpSolicit(
Ray Milkey49724162019-01-25 15:59:36 -0800455 targetAddress.getIp6Address(),
456 Ip6Address.valueOf(senderIpAddress),
457 Ip6Address.valueOf(dstIp),
458 MacAddress.valueOf(senderMacAddress),
459 MacAddress.valueOf(dstMac),
Pier Ventre735b8c82016-12-02 08:16:05 -0800460 VlanId.NONE
461 );
462 flood(ndpRequest, inPort, targetAddress);
Pier Ventref4b5fce2016-11-28 16:48:06 -0800463 }
Charles Chanc9e92242018-03-06 15:45:03 -0800464
465 /**
466 * Returns link-local IP of given connect point.
467 *
468 * @param cp connect point
469 * @return optional link-local IP
470 */
471 private Optional<Ip6Address> getLinkLocalIp(ConnectPoint cp) {
472 return srManager.interfaceService.getInterfacesByPort(cp)
473 .stream()
474 .map(Interface::mac)
475 .map(MacAddress::toBytes)
476 .map(IPv6::getLinkLocalAddress)
477 .map(Ip6Address::valueOf)
478 .findFirst();
479 }
sanghob35a6192015-04-01 13:05:26 -0700480}