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