e74ea8b97d7da9834a0ae6bce2a82b47d4efba06
[netvirt.git] / vpnservice / ipv6service / impl / src / main / java / org / opendaylight / netvirt / ipv6service / Ipv6PktHandler.java
1 /*
2  * Copyright (c) 2016 Red Hat, Inc. and others. All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.netvirt.ipv6service;
9
10 import java.math.BigInteger;
11 import java.net.InetAddress;
12 import java.net.UnknownHostException;
13 import java.nio.ByteBuffer;
14 import java.util.ArrayList;
15 import java.util.Arrays;
16 import java.util.List;
17 import java.util.concurrent.ExecutorService;
18 import java.util.concurrent.Executors;
19 import org.opendaylight.controller.liblldp.BitBufferHelper;
20 import org.opendaylight.controller.liblldp.BufferException;
21 import org.opendaylight.genius.mdsalutil.MetaDataUtil;
22 import org.opendaylight.netvirt.ipv6service.utils.Ipv6Constants;
23 import org.opendaylight.netvirt.ipv6service.utils.Ipv6Constants.Ipv6RtrAdvertType;
24 import org.opendaylight.netvirt.ipv6service.utils.Ipv6ServiceUtils;
25 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Address;
26 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
27 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.ipv6service.nd.packet.rev160620.EthernetHeader;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.ipv6service.nd.packet.rev160620.Ipv6Header;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.ipv6service.nd.packet.rev160620.NeighborAdvertisePacket;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.ipv6service.nd.packet.rev160620.NeighborAdvertisePacketBuilder;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.ipv6service.nd.packet.rev160620.NeighborSolicitationPacket;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.ipv6service.nd.packet.rev160620.NeighborSolicitationPacketBuilder;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.ipv6service.nd.packet.rev160620.RouterSolicitationPacket;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.ipv6service.nd.packet.rev160620.RouterSolicitationPacketBuilder;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingListener;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingService;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketReceived;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInput;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInputBuilder;
44 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
45 import org.slf4j.Logger;
46 import org.slf4j.LoggerFactory;
47
48 public class Ipv6PktHandler implements AutoCloseable, PacketProcessingListener {
49     private static final Logger LOG = LoggerFactory.getLogger(Ipv6PktHandler.class);
50     private long pktProccessedCounter = 0;
51     private final PacketProcessingService pktService;
52     private final IfMgr ifMgr;
53     private final Ipv6ServiceUtils ipv6Utils;
54     private final ExecutorService packetProcessor = Executors.newCachedThreadPool();
55
56     public Ipv6PktHandler(PacketProcessingService pktService) {
57         this.pktService = pktService;
58         ifMgr = IfMgr.getIfMgrInstance();
59         this.ipv6Utils = Ipv6ServiceUtils.getInstance();
60     }
61
62     @Override
63     public void onPacketReceived(PacketReceived packetReceived) {
64         int     ethType;
65         int     v6NxtHdr;
66
67         if (packetReceived == null) {
68             LOG.debug("Received null packet. Returning without any processing");
69             return;
70         }
71
72         byte[] data = packetReceived.getPayload();
73         if (data.length <= 0) {
74             LOG.debug("Received packet with invalid length {}", data.length);
75             return;
76         }
77         try {
78             ethType = BitBufferHelper.getInt(BitBufferHelper.getBits(data, Ipv6Constants.ETHTYPE_START,
79                     Ipv6Constants.TWO_BYTES));
80             if (ethType == Ipv6Constants.IP_V6_ETHTYPE) {
81                 v6NxtHdr = BitBufferHelper.getByte(BitBufferHelper.getBits(data,
82                         (Ipv6Constants.IP_V6_HDR_START + Ipv6Constants.IP_V6_NEXT_HDR), Ipv6Constants.ONE_BYTE));
83                 if (v6NxtHdr == Ipv6Constants.ICMP_V6_TYPE) {
84                     int icmpv6Type = BitBufferHelper.getInt(BitBufferHelper.getBits(data,
85                             Ipv6Constants.ICMPV6_HDR_START, Ipv6Constants.ONE_BYTE));
86                     if ((icmpv6Type == Ipv6Constants.ICMP_V6_RS_CODE)
87                             || (icmpv6Type == Ipv6Constants.ICMP_V6_NS_CODE)) {
88                         packetProcessor.submit(new PacketHandler(icmpv6Type, packetReceived));
89                     }
90                 } else {
91                     LOG.debug("IPv6 Pdu received on port {} with next-header {} ",
92                             packetReceived.getIngress(), v6NxtHdr);
93                 }
94             } else {
95                 return;
96             }
97         } catch (BufferException e) {
98             LOG.warn("Failed to decode packet: {}", e.getMessage());
99             return;
100         }
101     }
102
103     public long getPacketProcessedCounter() {
104         return pktProccessedCounter;
105     }
106
107     private class PacketHandler implements Runnable {
108         int type;
109         PacketReceived packet;
110
111         PacketHandler(int icmpv6Type, PacketReceived packet) {
112             this.type = icmpv6Type;
113             this.packet = packet;
114         }
115
116         @Override
117         public void run() {
118             if (type == Ipv6Constants.ICMP_V6_NS_CODE) {
119                 LOG.info("Received Neighbor Solicitation request");
120                 processNeighborSolicitationRequest();
121             } else if (type == Ipv6Constants.ICMP_V6_RS_CODE) {
122                 LOG.info("Received Router Solicitation request");
123                 processRouterSolicitationRequest();
124             }
125         }
126
127         private void processNeighborSolicitationRequest() {
128             byte[] data = packet.getPayload();
129             NeighborSolicitationPacket nsPdu = deserializeNSPacket(data);
130             Ipv6Header ipv6Header = (Ipv6Header) nsPdu;
131             if (ipv6Utils.validateChecksum(data, ipv6Header, nsPdu.getIcmp6Chksum()) == false) {
132                 pktProccessedCounter++;
133                 LOG.warn("Received Neighbor Solicitation with invalid checksum on {}. Ignoring the packet.",
134                         packet.getIngress());
135                 return;
136             }
137
138             BigInteger metadata = packet.getMatch().getMetadata().getMetadata();
139             long portTag = MetaDataUtil.getLportFromMetadata(metadata).intValue();
140             String interfaceName = ifMgr.getInterfaceNameFromTag(portTag);
141             VirtualPort port = ifMgr.obtainV6Interface(new Uuid(interfaceName));
142             if (port == null) {
143                 pktProccessedCounter++;
144                 LOG.warn("Port {} not found, skipping.", port);
145                 return;
146             }
147
148             VirtualPort routerPort = ifMgr.getRouterV6InterfaceForNetwork(port.getNetworkID());
149             if (routerPort == null) {
150                 pktProccessedCounter++;
151                 LOG.warn("Port {} is not associated to a Router, skipping NS request.", routerPort);
152                 return;
153             }
154
155             if (!routerPort.getIpv6Addresses().contains(nsPdu.getTargetIpAddress())) {
156                 pktProccessedCounter++;
157                 LOG.warn("No Router interface with address {} on the network {}, skipping NS request.",
158                         nsPdu.getTargetIpAddress(), port.getNetworkID());
159                 return;
160             }
161
162             //formulate the NA response
163             NeighborAdvertisePacketBuilder naPacket = new NeighborAdvertisePacketBuilder();
164             updateNAResponse(nsPdu, routerPort, naPacket);
165             // serialize the response packet
166             byte[] txPayload = fillNeighborAdvertisementPacket(naPacket.build());
167             InstanceIdentifier<Node> outNode = packet.getIngress().getValue().firstIdentifierOf(Node.class);
168             TransmitPacketInput input = new TransmitPacketInputBuilder().setPayload(txPayload)
169                     .setNode(new NodeRef(outNode))
170                     .setEgress(packet.getIngress()).build();
171             // Tx the packet out of the controller.
172             if (pktService != null) {
173                 LOG.debug("Transmitting the Neighbor Advt packet out on {}", packet.getIngress());
174                 pktService.transmitPacket(input);
175                 pktProccessedCounter++;
176             }
177         }
178
179         private NeighborSolicitationPacket deserializeNSPacket(byte[] data) {
180             NeighborSolicitationPacketBuilder nsPdu = new NeighborSolicitationPacketBuilder();
181             int bitOffset = 0;
182
183             try {
184                 nsPdu.setDestinationMac(new MacAddress(
185                         ipv6Utils.bytesToHexString(BitBufferHelper.getBits(data, bitOffset, 48))));
186                 bitOffset = bitOffset + 48;
187                 nsPdu.setSourceMac(new MacAddress(
188                         ipv6Utils.bytesToHexString(BitBufferHelper.getBits(data, bitOffset, 48))));
189                 bitOffset = bitOffset + 48;
190                 nsPdu.setEthertype(BitBufferHelper.getInt(BitBufferHelper.getBits(data, bitOffset, 16)));
191
192                 bitOffset = Ipv6Constants.IP_V6_HDR_START;
193                 nsPdu.setVersion(BitBufferHelper.getShort(BitBufferHelper.getBits(data, bitOffset, 4)));
194                 bitOffset = bitOffset + 4;
195                 nsPdu.setFlowLabel(BitBufferHelper.getLong(BitBufferHelper.getBits(data, bitOffset, 28)));
196                 bitOffset = bitOffset + 28;
197                 nsPdu.setIpv6Length(BitBufferHelper.getInt(BitBufferHelper.getBits(data, bitOffset, 16)));
198                 bitOffset = bitOffset + 16;
199                 nsPdu.setNextHeader(BitBufferHelper.getShort(BitBufferHelper.getBits(data, bitOffset, 8)));
200                 bitOffset = bitOffset + 8;
201                 nsPdu.setHopLimit(BitBufferHelper.getShort(BitBufferHelper.getBits(data, bitOffset, 8)));
202                 bitOffset = bitOffset + 8;
203                 nsPdu.setSourceIpv6(Ipv6Address.getDefaultInstance(
204                         InetAddress.getByAddress(BitBufferHelper.getBits(data, bitOffset, 128)).getHostAddress()));
205                 bitOffset = bitOffset + 128;
206                 nsPdu.setDestinationIpv6(Ipv6Address.getDefaultInstance(
207                         InetAddress.getByAddress(BitBufferHelper.getBits(data, bitOffset, 128)).getHostAddress()));
208                 bitOffset = bitOffset + 128;
209
210                 nsPdu.setIcmp6Type(BitBufferHelper.getShort(BitBufferHelper.getBits(data, bitOffset, 8)));
211                 bitOffset = bitOffset + 8;
212                 nsPdu.setIcmp6Code(BitBufferHelper.getShort(BitBufferHelper.getBits(data, bitOffset, 8)));
213                 bitOffset = bitOffset + 8;
214                 nsPdu.setIcmp6Chksum(BitBufferHelper.getInt(BitBufferHelper.getBits(data, bitOffset, 16)));
215                 bitOffset = bitOffset + 16;
216                 nsPdu.setReserved(Long.valueOf(0));
217                 bitOffset = bitOffset + 32;
218                 nsPdu.setTargetIpAddress(Ipv6Address.getDefaultInstance(
219                         InetAddress.getByAddress(BitBufferHelper.getBits(data, bitOffset, 128)).getHostAddress()));
220             } catch (BufferException | UnknownHostException  e) {
221                 LOG.warn("Exception obtained when deserializing NS packet", e.toString());
222             }
223             return nsPdu.build();
224         }
225
226         private void updateNAResponse(NeighborSolicitationPacket pdu,
227                                       VirtualPort port, NeighborAdvertisePacketBuilder naPacket) {
228             long flag = 0;
229             if (!pdu.getSourceIpv6().equals(ipv6Utils.UNSPECIFIED_ADDR)) {
230                 naPacket.setDestinationIpv6(pdu.getSourceIpv6());
231                 flag = 0xE0; // Set Router, Solicited and Override Flag.
232             } else {
233                 naPacket.setDestinationIpv6(ipv6Utils.ALL_NODES_MCAST_ADDR);
234                 flag = 0xA0; // Set Router and Override Flag.
235             }
236             naPacket.setDestinationMac(pdu.getSourceMac());
237             naPacket.setEthertype(pdu.getEthertype());
238             naPacket.setSourceIpv6(pdu.getTargetIpAddress());
239             naPacket.setSourceMac(new MacAddress(port.getMacAddress()));
240             naPacket.setHopLimit(Ipv6Constants.ICMP_V6_MAX_HOP_LIMIT);
241             naPacket.setIcmp6Type(Ipv6Constants.ICMP_V6_NA_CODE);
242             naPacket.setIcmp6Code(pdu.getIcmp6Code());
243             flag = flag << 24;
244             naPacket.setFlags(flag);
245             naPacket.setFlowLabel(pdu.getFlowLabel());
246             naPacket.setIpv6Length(32);
247             naPacket.setNextHeader(pdu.getNextHeader());
248             naPacket.setOptionType((short)2);
249             naPacket.setTargetAddrLength((short)1);
250             naPacket.setTargetAddress(pdu.getTargetIpAddress());
251             naPacket.setTargetLlAddress(new MacAddress(port.getMacAddress()));
252             naPacket.setVersion(pdu.getVersion());
253             naPacket.setIcmp6Chksum(0);
254             return;
255         }
256
257         private byte[] icmp6NAPayloadtoByte(NeighborAdvertisePacket pdu) {
258             byte[] data = new byte[36];
259             Arrays.fill(data, (byte)0);
260
261             ByteBuffer buf = ByteBuffer.wrap(data);
262             buf.put((byte)pdu.getIcmp6Type().shortValue());
263             buf.put((byte)pdu.getIcmp6Code().shortValue());
264             buf.putShort((short)pdu.getIcmp6Chksum().intValue());
265             buf.putInt((int)pdu.getFlags().longValue());
266             try {
267                 byte[] address = null;
268                 address = InetAddress.getByName(pdu.getTargetAddress().getValue()).getAddress();
269                 buf.put(address);
270             } catch (UnknownHostException e) {
271                 LOG.error("Serializing NA target address failed", e);
272             }
273             buf.put((byte)pdu.getOptionType().shortValue());
274             buf.put((byte)pdu.getTargetAddrLength().shortValue());
275             buf.put(ipv6Utils.bytesFromHexString(pdu.getTargetLlAddress().getValue().toString()));
276             return data;
277         }
278
279         private byte[] fillNeighborAdvertisementPacket(NeighborAdvertisePacket pdu) {
280             ByteBuffer buf = ByteBuffer.allocate(Ipv6Constants.ICMPV6_OFFSET + pdu.getIpv6Length());
281
282             buf.put(ipv6Utils.convertEthernetHeaderToByte((EthernetHeader)pdu), 0, 14);
283             buf.put(ipv6Utils.convertIpv6HeaderToByte((Ipv6Header)pdu), 0, 40);
284             buf.put(icmp6NAPayloadtoByte(pdu), 0, pdu.getIpv6Length());
285             int checksum = ipv6Utils.calcIcmpv6Checksum(buf.array(), (Ipv6Header) pdu);
286             buf.putShort((Ipv6Constants.ICMPV6_OFFSET + 2), (short)checksum);
287             return (buf.array());
288         }
289
290         private void processRouterSolicitationRequest() {
291             byte[] data = packet.getPayload();
292             RouterSolicitationPacket rsPdu = deserializeRSPacket(data);
293             Ipv6Header ipv6Header = (Ipv6Header) rsPdu;
294             if (ipv6Utils.validateChecksum(data, ipv6Header, rsPdu.getIcmp6Chksum()) == false) {
295                 pktProccessedCounter++;
296                 LOG.warn("Received RS packet with invalid checksum on {}. Ignoring the packet.",
297                         packet.getIngress());
298                 return;
299             }
300
301             BigInteger metadata = packet.getMatch().getMetadata().getMetadata();
302             long portTag = MetaDataUtil.getLportFromMetadata(metadata).intValue();
303             String interfaceName = ifMgr.getInterfaceNameFromTag(portTag);
304             VirtualPort port = ifMgr.obtainV6Interface(new Uuid(interfaceName));
305             if (port == null) {
306                 pktProccessedCounter++;
307                 LOG.info("Port {} not found, skipping.", port);
308                 return;
309             }
310
311             VirtualPort routerPort = ifMgr.getRouterV6InterfaceForNetwork(port.getNetworkID());
312             if (routerPort == null) {
313                 pktProccessedCounter++;
314                 LOG.warn("Port {} is not associated to a Router, skipping.", routerPort);
315                 return;
316             }
317             Ipv6RouterAdvt ipv6RouterAdvert = new Ipv6RouterAdvt();
318             List<NodeConnectorRef> ncRefList = new ArrayList<>();
319             ncRefList.add(packet.getIngress());
320             ipv6RouterAdvert.transmitRtrAdvertisement(Ipv6RtrAdvertType.SOLICITED_ADVERTISEMENT,
321                                                       routerPort, ncRefList, rsPdu);
322             pktProccessedCounter = pktProccessedCounter + 1;
323         }
324
325         private RouterSolicitationPacket deserializeRSPacket(byte[] data) {
326             RouterSolicitationPacketBuilder rsPdu = new RouterSolicitationPacketBuilder();
327             int bitOffset = 0;
328
329             try {
330                 rsPdu.setDestinationMac(new MacAddress(
331                         ipv6Utils.bytesToHexString(BitBufferHelper.getBits(data, bitOffset, 48))));
332                 bitOffset = bitOffset + 48;
333                 rsPdu.setSourceMac(new MacAddress(
334                         ipv6Utils.bytesToHexString(BitBufferHelper.getBits(data, bitOffset, 48))));
335                 bitOffset = bitOffset + 48;
336                 rsPdu.setEthertype(BitBufferHelper.getInt(BitBufferHelper.getBits(data, bitOffset, 16)));
337
338                 bitOffset = Ipv6Constants.IP_V6_HDR_START;
339                 rsPdu.setVersion(BitBufferHelper.getShort(BitBufferHelper.getBits(data, bitOffset, 4)));
340                 bitOffset = bitOffset + 4;
341                 rsPdu.setFlowLabel(BitBufferHelper.getLong(BitBufferHelper.getBits(data, bitOffset, 28)));
342                 bitOffset = bitOffset + 28;
343
344                 rsPdu.setIpv6Length(BitBufferHelper.getInt(BitBufferHelper.getBits(data, bitOffset, 16)));
345                 bitOffset = bitOffset + 16;
346                 rsPdu.setNextHeader(BitBufferHelper.getShort(BitBufferHelper.getBits(data, bitOffset, 8)));
347                 bitOffset = bitOffset + 8;
348                 rsPdu.setHopLimit(BitBufferHelper.getShort(BitBufferHelper.getBits(data, bitOffset, 8)));
349                 bitOffset = bitOffset + 8;
350                 rsPdu.setSourceIpv6(Ipv6Address.getDefaultInstance(
351                         InetAddress.getByAddress(BitBufferHelper.getBits(data, bitOffset, 128)).getHostAddress()));
352                 bitOffset = bitOffset + 128;
353                 rsPdu.setDestinationIpv6(Ipv6Address.getDefaultInstance(
354                         InetAddress.getByAddress(BitBufferHelper.getBits(data, bitOffset, 128)).getHostAddress()));
355                 bitOffset = bitOffset + 128;
356
357                 rsPdu.setIcmp6Type(BitBufferHelper.getShort(BitBufferHelper.getBits(data, bitOffset, 8)));
358                 bitOffset = bitOffset + 8;
359                 rsPdu.setIcmp6Code(BitBufferHelper.getShort(BitBufferHelper.getBits(data, bitOffset, 8)));
360                 bitOffset = bitOffset + 8;
361                 rsPdu.setIcmp6Chksum(BitBufferHelper.getInt(BitBufferHelper.getBits(data, bitOffset, 16)));
362                 bitOffset = bitOffset + 16;
363                 rsPdu.setReserved(Long.valueOf(0));
364                 bitOffset = bitOffset + 32;
365
366                 if (rsPdu.getIpv6Length() > Ipv6Constants.ICMPV6_RA_LENGTH_WO_OPTIONS) {
367                     rsPdu.setOptionType(BitBufferHelper.getShort(BitBufferHelper.getBits(data, bitOffset, 8)));
368                     bitOffset = bitOffset + 8;
369                     rsPdu.setSourceAddrLength(BitBufferHelper.getShort(BitBufferHelper.getBits(data, bitOffset, 8)));
370                     bitOffset = bitOffset + 8;
371                     if (rsPdu.getOptionType() == 1) {
372                         rsPdu.setSourceLlAddress(new MacAddress(
373                                 ipv6Utils.bytesToHexString(BitBufferHelper.getBits(data, bitOffset, 48))));
374                     }
375                 }
376             } catch (BufferException | UnknownHostException e) {
377                 LOG.warn("Exception obtained when deserializing Router Solicitation packet", e.toString());
378             }
379             return rsPdu.build();
380         }
381     }
382
383     @Override
384     public void close() throws Exception {
385         packetProcessor.shutdown();
386     }
387 }