blob: 38293d05603d542fde13c89ee8786eece0be1857 [file] [log] [blame]
sangho80f11cb2015-04-01 13:05:26 -07001/*
Brian O'Connor43b53542016-04-09 01:19:45 -07002 * Copyright 2015-present Open Networking Laboratory
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;
Pier Ventreb6a7f342016-11-26 21:05:22 -080030import org.onosproject.incubator.net.neighbour.NeighbourMessageContext;
Pier Ventre1a655962016-11-28 16:48:06 -080031import org.onosproject.incubator.net.neighbour.NeighbourMessageType;
sangho80f11cb2015-04-01 13:05:26 -070032import org.onosproject.net.ConnectPoint;
33import org.onosproject.net.DeviceId;
34import org.onosproject.net.flow.DefaultTrafficTreatment;
35import org.onosproject.net.flow.TrafficTreatment;
Pier Ventre1a655962016-11-28 16:48:06 -080036import org.onosproject.net.host.HostService;
sangho80f11cb2015-04-01 13:05:26 -070037import org.onosproject.net.packet.DefaultOutboundPacket;
sangho80f11cb2015-04-01 13:05:26 -070038import org.onosproject.net.packet.OutboundPacket;
Charles Chan319d1a22015-11-03 10:42:14 -080039import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
Pier Ventre1a655962016-11-28 16:48:06 -080040import org.onosproject.segmentrouting.config.SegmentRoutingAppConfig;
sangho80f11cb2015-04-01 13:05:26 -070041import org.slf4j.Logger;
42import org.slf4j.LoggerFactory;
43
Jonathan Hartd53ebc42015-04-07 16:46:33 -070044import java.nio.ByteBuffer;
Charles Chand3727b72017-03-13 13:10:30 -070045import java.util.Arrays;
Saurav Dasc28b3432015-10-30 17:45:38 -070046import java.util.Set;
Jonathan Hartd53ebc42015-04-07 16:46:33 -070047
Charles Chanb7f75ac2016-01-11 18:28:54 -080048/**
49 * Handler of ICMP packets that responses or forwards ICMP packets that
50 * are sent to the controller.
51 */
Pier Ventreb6b81d52016-12-02 08:16:05 -080052public class IcmpHandler extends SegmentRoutingNeighbourHandler {
sangho80f11cb2015-04-01 13:05:26 -070053
54 private static Logger log = LoggerFactory.getLogger(IcmpHandler.class);
sangho80f11cb2015-04-01 13:05:26 -070055
56 /**
57 * Creates an IcmpHandler object.
58 *
59 * @param srManager SegmentRoutingManager object
60 */
61 public IcmpHandler(SegmentRoutingManager srManager) {
Pier Ventreb6b81d52016-12-02 08:16:05 -080062 super(srManager);
63 }
64
65 /**
66 * Utility function to send packet out.
67 *
68 * @param outport the output port
69 * @param payload the packet to send
70 * @param sid the segment id
71 * @param destIpAddress the destination ip address
72 * @param allowedHops the hop limit/ttl
73 */
74 private void sendPacketOut(ConnectPoint outport,
75 Ethernet payload,
76 int sid,
77 IpAddress destIpAddress,
78 byte allowedHops) {
79 int destSid;
80 if (destIpAddress.isIp4()) {
81 destSid = config.getIPv4SegmentId(payload.getDestinationMAC());
82 } else {
83 destSid = config.getIPv6SegmentId(payload.getDestinationMAC());
84 }
85
86 if (sid == -1 || destSid == sid ||
87 config.inSameSubnet(outport.deviceId(), destIpAddress)) {
88 TrafficTreatment treatment = DefaultTrafficTreatment.builder().
89 setOutput(outport.port()).build();
90 OutboundPacket packet = new DefaultOutboundPacket(outport.deviceId(),
91 treatment, ByteBuffer.wrap(payload.serialize()));
92 srManager.packetService.emit(packet);
93 } else {
94 log.debug("Send a MPLS packet as a ICMP response");
95 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
96 .setOutput(outport.port())
97 .build();
98
99 payload.setEtherType(Ethernet.MPLS_UNICAST);
100 MPLS mplsPkt = new MPLS();
101 mplsPkt.setLabel(sid);
102 mplsPkt.setTtl(allowedHops);
103 mplsPkt.setPayload(payload.getPayload());
104 payload.setPayload(mplsPkt);
105
106 OutboundPacket packet = new DefaultOutboundPacket(outport.deviceId(),
107 treatment, ByteBuffer.wrap(payload.serialize()));
108
109 srManager.packetService.emit(packet);
110 }
sangho80f11cb2015-04-01 13:05:26 -0700111 }
112
Pier Ventre1a655962016-11-28 16:48:06 -0800113 //////////////////////////////////////
114 // ICMP Echo/Reply Protocol //
115 //////////////////////////////////////
116
sangho80f11cb2015-04-01 13:05:26 -0700117 /**
118 * Process incoming ICMP packet.
Charles Chanb78b1132017-03-29 17:24:39 -0700119 * If it is an ICMP request to router, then sends an ICMP response.
120 * Otherwise ignore the packet.
sangho80f11cb2015-04-01 13:05:26 -0700121 *
Pier Ventreb6b81d52016-12-02 08:16:05 -0800122 * @param eth inbound ICMP packet
123 * @param inPort the input port
sangho80f11cb2015-04-01 13:05:26 -0700124 */
Pier Ventreb6b81d52016-12-02 08:16:05 -0800125 public void processIcmp(Ethernet eth, ConnectPoint inPort) {
126 DeviceId deviceId = inPort.deviceId();
127 IPv4 ipv4Packet = (IPv4) eth.getPayload();
128 Ip4Address destinationAddress = Ip4Address.valueOf(ipv4Packet.getDestinationAddress());
Pier Ventreb6a7f342016-11-26 21:05:22 -0800129 Set<IpAddress> gatewayIpAddresses = config.getPortIPs(deviceId);
Pier Ventreadb4ae62016-11-23 09:57:42 -0800130 IpAddress routerIp;
Charles Chan319d1a22015-11-03 10:42:14 -0800131 try {
Pier Ventreadb4ae62016-11-23 09:57:42 -0800132 routerIp = config.getRouterIpv4(deviceId);
Charles Chan319d1a22015-11-03 10:42:14 -0800133 } catch (DeviceConfigNotFoundException e) {
134 log.warn(e.getMessage() + " Aborting processPacketIn.");
135 return;
136 }
sangho80f11cb2015-04-01 13:05:26 -0700137 // ICMP to the router IP or gateway IP
Pier Ventreb6b81d52016-12-02 08:16:05 -0800138 if (((ICMP) ipv4Packet.getPayload()).getIcmpType() == ICMP.TYPE_ECHO_REQUEST &&
139 (destinationAddress.equals(routerIp.getIp4Address()) ||
Srikanth Vavilapalli37a461b2015-04-07 15:12:32 -0700140 gatewayIpAddresses.contains(destinationAddress))) {
Pier Ventreb6b81d52016-12-02 08:16:05 -0800141 sendIcmpResponse(eth, inPort);
sangho80f11cb2015-04-01 13:05:26 -0700142 } else {
Charles Chanb78b1132017-03-29 17:24:39 -0700143 log.trace("Ignore ICMP that targets for {}", destinationAddress);
sangho80f11cb2015-04-01 13:05:26 -0700144 }
Charles Chanb78b1132017-03-29 17:24:39 -0700145 // We remove the packet from the queue
146 srManager.ipHandler.dequeuePacket(ipv4Packet, destinationAddress);
sangho80f11cb2015-04-01 13:05:26 -0700147 }
148
Charles Chanf4586112015-11-09 16:37:23 -0800149 /**
150 * Sends an ICMP reply message.
151 *
152 * Note: we assume that packets sending from the edge switches to the hosts
153 * have untagged VLAN.
154 * @param icmpRequest the original ICMP request
155 * @param outport the output port where the ICMP reply should be sent to
156 */
Pier Ventreadb4ae62016-11-23 09:57:42 -0800157 private void sendIcmpResponse(Ethernet icmpRequest, ConnectPoint outport) {
Charles Chanf4586112015-11-09 16:37:23 -0800158 // Note: We assume that packets arrive at the edge switches have
159 // untagged VLAN.
Pier Ventreb6b81d52016-12-02 08:16:05 -0800160 Ethernet icmpReplyEth = ICMP.buildIcmpReply(icmpRequest);
sangho80f11cb2015-04-01 13:05:26 -0700161 IPv4 icmpRequestIpv4 = (IPv4) icmpRequest.getPayload();
Pier Ventreb6b81d52016-12-02 08:16:05 -0800162 IPv4 icmpReplyIpv4 = (IPv4) icmpReplyEth.getPayload();
163 Ip4Address destIpAddress = Ip4Address.valueOf(icmpRequestIpv4.getSourceAddress());
sangho9b169e32015-04-14 16:27:13 -0700164 Ip4Address destRouterAddress = config.getRouterIpAddressForASubnetHost(destIpAddress);
Pier Ventreadb4ae62016-11-23 09:57:42 -0800165 int destSid = config.getIPv4SegmentId(destRouterAddress);
Charles Chan70661362016-12-09 12:54:49 -0800166 if (destSid < 0) {
Pier Ventreb6b81d52016-12-02 08:16:05 -0800167 log.warn("Cannot find the Segment ID for {}", destIpAddress);
sangho80f11cb2015-04-01 13:05:26 -0700168 return;
169 }
Pier Ventreb6b81d52016-12-02 08:16:05 -0800170 sendPacketOut(outport, icmpReplyEth, destSid, destIpAddress, icmpReplyIpv4.getTtl());
sangho80f11cb2015-04-01 13:05:26 -0700171 }
172
Pier Ventreb6b81d52016-12-02 08:16:05 -0800173 ///////////////////////////////////////////
174 // ICMPv6 Echo/Reply Protocol //
175 ///////////////////////////////////////////
sangho80f11cb2015-04-01 13:05:26 -0700176
Pier Ventreb6b81d52016-12-02 08:16:05 -0800177 /**
178 * Process incoming ICMPv6 packet.
Charles Chanb78b1132017-03-29 17:24:39 -0700179 * If it is an ICMPv6 request to router, then sends an ICMPv6 response.
180 * Otherwise ignore the packet.
Pier Ventreb6b81d52016-12-02 08:16:05 -0800181 *
182 * @param eth the incoming ICMPv6 packet
183 * @param inPort the input port
184 */
185 public void processIcmpv6(Ethernet eth, ConnectPoint inPort) {
186 DeviceId deviceId = inPort.deviceId();
187 IPv6 ipv6Packet = (IPv6) eth.getPayload();
188 Ip6Address destinationAddress = Ip6Address.valueOf(ipv6Packet.getDestinationAddress());
189 Set<IpAddress> gatewayIpAddresses = config.getPortIPs(deviceId);
190 IpAddress routerIp;
191 try {
192 routerIp = config.getRouterIpv6(deviceId);
193 } catch (DeviceConfigNotFoundException e) {
194 log.warn(e.getMessage() + " Aborting processPacketIn.");
195 return;
sangho80f11cb2015-04-01 13:05:26 -0700196 }
Pier Ventreb6b81d52016-12-02 08:16:05 -0800197 ICMP6 icmp6 = (ICMP6) ipv6Packet.getPayload();
198 // ICMP to the router IP or gateway IP
199 if (icmp6.getIcmpType() == ICMP6.ECHO_REQUEST &&
200 (destinationAddress.equals(routerIp.getIp6Address()) ||
201 gatewayIpAddresses.contains(destinationAddress))) {
202 sendIcmpv6Response(eth, inPort);
Pier Ventreb6b81d52016-12-02 08:16:05 -0800203 } else {
Charles Chanb78b1132017-03-29 17:24:39 -0700204 log.trace("Ignore ICMPv6 that targets for {}", destinationAddress);
Pier Ventreb6b81d52016-12-02 08:16:05 -0800205 }
206 }
207
208 /**
209 * Sends an ICMPv6 reply message.
210 *
211 * Note: we assume that packets sending from the edge switches to the hosts
212 * have untagged VLAN.
213 * @param ethRequest the original ICMP request
214 * @param outport the output port where the ICMP reply should be sent to
215 */
216 private void sendIcmpv6Response(Ethernet ethRequest, ConnectPoint outport) {
217 // Note: We assume that packets arrive at the edge switches have
218 // untagged VLAN.
219 Ethernet ethReply = ICMP6.buildIcmp6Reply(ethRequest);
220 IPv6 icmpRequestIpv6 = (IPv6) ethRequest.getPayload();
221 IPv6 icmpReplyIpv6 = (IPv6) ethRequest.getPayload();
222 Ip6Address destIpAddress = Ip6Address.valueOf(icmpRequestIpv6.getSourceAddress());
223 Ip6Address destRouterAddress = config.getRouterIpAddressForASubnetHost(destIpAddress);
224 int sid = config.getIPv6SegmentId(destRouterAddress);
225 if (sid < 0) {
226 log.warn("Cannot find the Segment ID for {}", destIpAddress);
227 return;
228 }
229 sendPacketOut(outport, ethReply, sid, destIpAddress, icmpReplyIpv6.getHopLimit());
sangho80f11cb2015-04-01 13:05:26 -0700230 }
sangho9b169e32015-04-14 16:27:13 -0700231
Pier Ventre1a655962016-11-28 16:48:06 -0800232 ///////////////////////////////////////////
233 // ICMPv6 Neighbour Discovery Protocol //
234 ///////////////////////////////////////////
sangho9b169e32015-04-14 16:27:13 -0700235
Pier Ventre1a655962016-11-28 16:48:06 -0800236 /**
237 * Process incoming NDP packet.
238 *
239 * If it is an NDP request for the router or for the gateway, then sends a NDP reply.
240 * If it is an NDP request to unknown host flood in the subnet.
241 * If it is an NDP packet to known host forward the packet to the host.
242 *
243 * FIXME If the NDP packets use link local addresses we fail.
244 *
245 * @param pkt inbound packet
246 * @param hostService the host service
247 */
248 public void processPacketIn(NeighbourMessageContext pkt, HostService hostService) {
Charles Chanb78b1132017-03-29 17:24:39 -0700249 // First we validate the ndp packet
Pier Ventre1a655962016-11-28 16:48:06 -0800250 SegmentRoutingAppConfig appConfig = srManager.cfgService
251 .getConfig(srManager.appId, SegmentRoutingAppConfig.class);
252 if (appConfig != null && appConfig.suppressSubnet().contains(pkt.inPort())) {
253 // Ignore NDP packets come from suppressed ports
254 pkt.drop();
255 return;
256 }
Pier Ventre1a655962016-11-28 16:48:06 -0800257
258 if (pkt.type() == NeighbourMessageType.REQUEST) {
259 handleNdpRequest(pkt, hostService);
260 } else {
261 handleNdpReply(pkt, hostService);
262 }
263
264 }
265
266 /**
Pier Ventre1a655962016-11-28 16:48:06 -0800267 * Helper method to handle the ndp requests.
268 *
269 * @param pkt the ndp packet request and context information
270 * @param hostService the host service
271 */
272 private void handleNdpRequest(NeighbourMessageContext pkt, HostService hostService) {
Charles Chanb3016ed2017-02-27 15:50:43 -0800273 // ND request for the gateway. We have to reply on behalf of the gateway.
Pier Ventre1a655962016-11-28 16:48:06 -0800274 if (isNdpForGateway(pkt)) {
275 log.debug("Sending NDP reply on behalf of the router");
Pier Ventreb6b81d52016-12-02 08:16:05 -0800276 sendResponse(pkt, config.getRouterMacForAGatewayIp(pkt.target()), hostService);
Pier Ventre1a655962016-11-28 16:48:06 -0800277 } else {
Charles Chanb3016ed2017-02-27 15:50:43 -0800278 // NOTE: Ignore NDP packets except those target for the router
279 // We will reconsider enabling this when we have host learning support
Pier Ventre1a655962016-11-28 16:48:06 -0800280 /*
Charles Chanb3016ed2017-02-27 15:50:43 -0800281 // ND request for an host. We do a search by Ip.
Pier Ventre1a655962016-11-28 16:48:06 -0800282 Set<Host> hosts = hostService.getHostsByIp(pkt.target());
Charles Chanb3016ed2017-02-27 15:50:43 -0800283 // Possible misconfiguration ? In future this case
284 // should be handled we can have same hosts in different VLANs.
Pier Ventre1a655962016-11-28 16:48:06 -0800285 if (hosts.size() > 1) {
286 log.warn("More than one host with IP {}", pkt.target());
287 }
288 Host targetHost = hosts.stream().findFirst().orElse(null);
Charles Chanb3016ed2017-02-27 15:50:43 -0800289 // If we know the host forward to its attachment point.
Pier Ventre1a655962016-11-28 16:48:06 -0800290 if (targetHost != null) {
291 log.debug("Forward NDP request to the target host");
292 pkt.forward(targetHost.location());
293 } else {
Charles Chanb3016ed2017-02-27 15:50:43 -0800294 // Flood otherwise.
Pier Ventre1a655962016-11-28 16:48:06 -0800295 log.debug("Flood NDP request to the target subnet");
296 flood(pkt);
297 }
Charles Chanb3016ed2017-02-27 15:50:43 -0800298 */
Pier Ventre1a655962016-11-28 16:48:06 -0800299 }
300 }
301
302 /**
303 * Helper method to handle the ndp replies.
304 *
305 * @param pkt the ndp packet reply and context information
306 * @param hostService the host service
307 */
308 private void handleNdpReply(NeighbourMessageContext pkt, HostService hostService) {
309 if (isNdpForGateway(pkt)) {
310 log.debug("Forwarding all the ip packets we stored");
311 Ip6Address hostIpAddress = pkt.sender().getIp6Address();
312 srManager.ipHandler.forwardPackets(pkt.inPort().deviceId(), hostIpAddress);
313 } else {
Charles Chanb3016ed2017-02-27 15:50:43 -0800314 // NOTE: Ignore NDP packets except those target for the router
315 // We will reconsider enabling this when we have host learning support
316 /*
Pier Ventre1a655962016-11-28 16:48:06 -0800317 HostId hostId = HostId.hostId(pkt.dstMac(), pkt.vlan());
318 Host targetHost = hostService.getHost(hostId);
319 if (targetHost != null) {
320 log.debug("Forwarding the reply to the host");
321 pkt.forward(targetHost.location());
322 } else {
Charles Chanb3016ed2017-02-27 15:50:43 -0800323 // We don't have to flood towards spine facing ports.
Charles Chan10b0fb72017-02-02 16:20:42 -0800324 if (pkt.vlan().equals(SegmentRoutingManager.INTERNAL_VLAN)) {
Pier Ventre1a655962016-11-28 16:48:06 -0800325 return;
326 }
327 log.debug("Flooding the reply to the subnet");
328 flood(pkt);
329 }
Charles Chanb3016ed2017-02-27 15:50:43 -0800330 */
Pier Ventre1a655962016-11-28 16:48:06 -0800331 }
332 }
333
334 /**
335 * Utility to verify if the ND are for the gateway.
336 *
337 * @param pkt the ndp packet
338 * @return true if the ndp is for the gateway. False otherwise
339 */
340 private boolean isNdpForGateway(NeighbourMessageContext pkt) {
341 DeviceId deviceId = pkt.inPort().deviceId();
342 Set<IpAddress> gatewayIpAddresses = null;
343 try {
344 if (pkt.target().equals(config.getRouterIpv6(deviceId))) {
345 return true;
346 }
347 gatewayIpAddresses = config.getPortIPs(deviceId);
348 } catch (DeviceConfigNotFoundException e) {
349 log.warn(e.getMessage() + " Aborting check for router IP in processing ndp");
350 }
Charles Chand3727b72017-03-13 13:10:30 -0700351
352 return gatewayIpAddresses != null && gatewayIpAddresses.stream()
353 .filter(IpAddress::isIp6)
354 .anyMatch(gatewayIp -> gatewayIp.equals(pkt.target()) ||
355 Arrays.equals(IPv6.getSolicitNodeAddress(gatewayIp.toOctets()),
356 pkt.target().toOctets())
357 );
Pier Ventre1a655962016-11-28 16:48:06 -0800358 }
359
360 /**
Pier Ventreb6b81d52016-12-02 08:16:05 -0800361 * Sends a NDP request for the target IP address to all ports except in-port.
Pier Ventre1a655962016-11-28 16:48:06 -0800362 *
Pier Ventreb6b81d52016-12-02 08:16:05 -0800363 * @param deviceId Switch device ID
364 * @param targetAddress target IP address for ARP
365 * @param inPort in-port
Pier Ventre1a655962016-11-28 16:48:06 -0800366 */
Pier Ventreb6b81d52016-12-02 08:16:05 -0800367 public void sendNdpRequest(DeviceId deviceId, IpAddress targetAddress, ConnectPoint inPort) {
368 byte[] senderMacAddress = new byte[MacAddress.MAC_ADDRESS_LENGTH];
369 byte[] senderIpAddress = new byte[Ip6Address.BYTE_LENGTH];
Charles Chanb78b1132017-03-29 17:24:39 -0700370 // Retrieves device info.
Pier Luigi6a83c4a2017-01-29 12:38:48 -0800371 if (!getSenderInfo(senderMacAddress, senderIpAddress, deviceId, targetAddress)) {
372 log.warn("Aborting sendNdpRequest, we cannot get all the information needed");
373 return;
374 }
Charles Chanb78b1132017-03-29 17:24:39 -0700375 // We have to compute the dst mac address and dst ip address.
Pier Ventreb6b81d52016-12-02 08:16:05 -0800376 byte[] dstIp = IPv6.getSolicitNodeAddress(targetAddress.toOctets());
377 byte[] dstMac = IPv6.getMCastMacAddress(dstIp);
Charles Chanb78b1132017-03-29 17:24:39 -0700378 // Creates the request.
Pier Ventreb6b81d52016-12-02 08:16:05 -0800379 Ethernet ndpRequest = NeighborSolicitation.buildNdpSolicit(
380 targetAddress.toOctets(),
381 senderIpAddress,
382 dstIp,
383 senderMacAddress,
384 dstMac,
385 VlanId.NONE
386 );
387 flood(ndpRequest, inPort, targetAddress);
Pier Ventre1a655962016-11-28 16:48:06 -0800388 }
sangho80f11cb2015-04-01 13:05:26 -0700389}