Merge "Supporting DHCP as a Service"
[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.Ipv6ServiceUtils;
24 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IetfInetUtil;
25 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
26 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv6Address;
27 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv6Prefix;
28 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
29 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.ipv6service.nd.packet.rev160620.EthernetHeader;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.ipv6service.nd.packet.rev160620.Ipv6Header;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.ipv6service.nd.packet.rev160620.RouterAdvertisementPacket;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.ipv6service.nd.packet.rev160620.RouterAdvertisementPacketBuilder;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.ipv6service.nd.packet.rev160620.RouterSolicitationPacket;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.ipv6service.nd.packet.rev160620.RouterSolicitationPacketBuilder;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.ipv6service.nd.packet.rev160620.router.advertisement.packet.PrefixList;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.ipv6service.nd.packet.rev160620.router.advertisement.packet.PrefixListBuilder;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingListener;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingService;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketReceived;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInput;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInputBuilder;
45 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
46 import org.slf4j.Logger;
47 import org.slf4j.LoggerFactory;
48
49 public class Ipv6PktHandler implements AutoCloseable, PacketProcessingListener {
50     private static final Logger LOG = LoggerFactory.getLogger(Ipv6PktHandler.class);
51     private long pktProccessedCounter = 0;
52     private PacketProcessingService pktService;
53     private IfMgr ifMgr;
54     private Ipv6ServiceUtils ipv6Utils;
55     private final ExecutorService packetProcessor = Executors.newCachedThreadPool();
56
57     public Ipv6PktHandler() {
58         this.ipv6Utils = Ipv6ServiceUtils.getInstance();
59     }
60
61     public void setPacketProcessingService(PacketProcessingService packetService) {
62         this.pktService = packetService;
63     }
64
65     public void setIfMgrInstance(IfMgr instance) {
66         this.ifMgr = instance;
67     }
68
69     @Override
70     public void onPacketReceived(PacketReceived packetReceived) {
71         int     ethType;
72         int     v6NxtHdr;
73
74         if (packetReceived == null) {
75             LOG.debug("Received null packet. Returning without any processing");
76             return;
77         }
78
79         byte[] data = packetReceived.getPayload();
80         if (data.length <= 0) {
81             LOG.debug("Received packet with invalid length {}", data.length);
82             return;
83         }
84         try {
85             ethType = BitBufferHelper.getInt(BitBufferHelper.getBits(data, Ipv6Constants.ETHTYPE_START,
86                     Ipv6Constants.TWO_BYTES));
87             if (ethType == Ipv6Constants.IPv6_ETHTYPE) {
88                 v6NxtHdr = BitBufferHelper.getByte(BitBufferHelper.getBits(data,
89                         (Ipv6Constants.IPv6_HDR_START + Ipv6Constants.IPv6_NEXT_HDR), Ipv6Constants.ONE_BYTE));
90                 if (v6NxtHdr == Ipv6Constants.ICMPv6_TYPE) {
91                     int icmpv6Type = BitBufferHelper.getInt(BitBufferHelper.getBits(data,
92                             Ipv6Constants.ICMPV6_HDR_START, Ipv6Constants.ONE_BYTE));
93                     if ((icmpv6Type == Ipv6Constants.ICMPv6_RS_CODE) || (icmpv6Type == Ipv6Constants.ICMPv6_NS_CODE)) {
94                         packetProcessor.submit(new PacketHandler(icmpv6Type, packetReceived));
95                     }
96                 } else {
97                     LOG.debug("IPv6 Pdu received on port {} with next-header {} ",
98                             packetReceived.getIngress(), v6NxtHdr);
99                 }
100             } else {
101                 return;
102             }
103         } catch (BufferException e) {
104             LOG.warn("Failed to decode packet: {}", e.getMessage());
105             return;
106         }
107     }
108
109     public long getPacketProcessedCounter() {
110         return pktProccessedCounter;
111     }
112
113     private class PacketHandler implements Runnable {
114         int type;
115         PacketReceived packet;
116
117         PacketHandler(int icmpv6Type, PacketReceived packet) {
118             this.type = icmpv6Type;
119             this.packet = packet;
120         }
121
122         @Override
123         public void run() {
124             if (type == Ipv6Constants.ICMPv6_NS_CODE) {
125                 LOG.info("Received Neighbor Solicitation request");
126             } else if (type == Ipv6Constants.ICMPv6_RS_CODE) {
127                 LOG.info("Received Router Solicitation request");
128                 processRouterSolicitationRequest();
129             }
130         }
131
132         private void processRouterSolicitationRequest() {
133             byte[] data = packet.getPayload();
134             List<String> prefixList;
135             RouterSolicitationPacket rsPdu = deserializeRSPacket(data);
136             Ipv6Header ipv6Header = (Ipv6Header) rsPdu;
137             if (ipv6Utils.validateChecksum(data, ipv6Header, rsPdu.getIcmp6Chksum()) == false) {
138                 pktProccessedCounter++;
139                 LOG.warn("Received RS packet with invalid checksum on {}. Ignoring the packet.",
140                         packet.getIngress());
141                 return;
142             }
143
144             BigInteger metadata = packet.getMatch().getMetadata().getMetadata();
145             long portTag = MetaDataUtil.getLportFromMetadata(metadata).intValue();
146             String interfaceName = ifMgr.getInterfaceNameFromTag(portTag);
147             VirtualPort port = ifMgr.obtainV6Interface(new Uuid(interfaceName));
148             if (port == null) {
149                 pktProccessedCounter++;
150                 LOG.warn("Port {} not found, skipping.", port);
151                 return;
152             }
153
154             VirtualPort routerPort = ifMgr.getRouterV6InterfaceForNetwork(port.getNetworkID());
155             if (routerPort == null) {
156                 pktProccessedCounter++;
157                 LOG.warn("Port {} is not associated to a Router, skipping.", routerPort);
158                 return;
159             }
160
161             RouterAdvertisementPacketBuilder raPacket = new RouterAdvertisementPacketBuilder();
162             updateRAResponse(rsPdu, raPacket, rsPdu.getSourceMac(), routerPort);
163             // Serialize the response packet
164             byte[] txPayload = fillRouterAdvertisementPacket(raPacket.build());
165             InstanceIdentifier<Node> outNode = packet.getIngress().getValue().firstIdentifierOf(Node.class);
166             TransmitPacketInput input = new TransmitPacketInputBuilder().setPayload(txPayload)
167                     .setNode(new NodeRef(outNode))
168                     .setEgress(packet.getIngress()).build();
169             if (pktService != null) {
170                 LOG.debug("Transmitting the Router Advt packet out {}", packet.getIngress());
171                 pktService.transmitPacket(input);
172                 pktProccessedCounter++;
173             }
174         }
175
176         private RouterSolicitationPacket deserializeRSPacket(byte[] data) {
177             RouterSolicitationPacketBuilder rsPdu = new RouterSolicitationPacketBuilder();
178             int bitOffset = 0;
179
180             try {
181                 rsPdu.setDestinationMac(new MacAddress(
182                         ipv6Utils.bytesToHexString(BitBufferHelper.getBits(data, bitOffset, 48))));
183                 bitOffset = bitOffset + 48;
184                 rsPdu.setSourceMac(new MacAddress(
185                         ipv6Utils.bytesToHexString(BitBufferHelper.getBits(data, bitOffset, 48))));
186                 bitOffset = bitOffset + 48;
187                 rsPdu.setEthertype(BitBufferHelper.getInt(BitBufferHelper.getBits(data, bitOffset, 16)));
188
189                 bitOffset = Ipv6Constants.IPv6_HDR_START;
190                 rsPdu.setVersion(BitBufferHelper.getShort(BitBufferHelper.getBits(data, bitOffset, 4)));
191                 bitOffset = bitOffset + 4;
192                 rsPdu.setFlowLabel(BitBufferHelper.getLong(BitBufferHelper.getBits(data, bitOffset, 28)));
193                 bitOffset = bitOffset + 28;
194
195                 rsPdu.setIpv6Length(BitBufferHelper.getInt(BitBufferHelper.getBits(data, bitOffset, 16)));
196                 bitOffset = bitOffset + 16;
197                 rsPdu.setNextHeader(BitBufferHelper.getShort(BitBufferHelper.getBits(data, bitOffset, 8)));
198                 bitOffset = bitOffset + 8;
199                 rsPdu.setHopLimit(BitBufferHelper.getShort(BitBufferHelper.getBits(data, bitOffset, 8)));
200                 bitOffset = bitOffset + 8;
201                 rsPdu.setSourceIpv6(Ipv6Address.getDefaultInstance(
202                         InetAddress.getByAddress(BitBufferHelper.getBits(data, bitOffset, 128)).getHostAddress()));
203                 bitOffset = bitOffset + 128;
204                 rsPdu.setDestinationIpv6(Ipv6Address.getDefaultInstance(
205                         InetAddress.getByAddress(BitBufferHelper.getBits(data, bitOffset, 128)).getHostAddress()));
206                 bitOffset = bitOffset + 128;
207
208                 rsPdu.setIcmp6Type(BitBufferHelper.getShort(BitBufferHelper.getBits(data, bitOffset, 8)));
209                 bitOffset = bitOffset + 8;
210                 rsPdu.setIcmp6Code(BitBufferHelper.getShort(BitBufferHelper.getBits(data, bitOffset, 8)));
211                 bitOffset = bitOffset + 8;
212                 rsPdu.setIcmp6Chksum(BitBufferHelper.getInt(BitBufferHelper.getBits(data, bitOffset, 16)));
213                 bitOffset = bitOffset + 16;
214                 rsPdu.setReserved(Long.valueOf(0));
215                 bitOffset = bitOffset + 32;
216
217                 if (rsPdu.getIpv6Length() > Ipv6Constants.ICMPV6_RA_LENGTH_WO_OPTIONS) {
218                     rsPdu.setOptionType(BitBufferHelper.getShort(BitBufferHelper.getBits(data, bitOffset, 8)));
219                     bitOffset = bitOffset + 8;
220                     rsPdu.setSourceAddrLength(BitBufferHelper.getShort(BitBufferHelper.getBits(data, bitOffset, 8)));
221                     bitOffset = bitOffset + 8;
222                     if (rsPdu.getOptionType() == 1) {
223                         rsPdu.setSourceLlAddress(new MacAddress(
224                                 ipv6Utils.bytesToHexString(BitBufferHelper.getBits(data, bitOffset, 48))));
225                     }
226                 }
227             } catch (BufferException | UnknownHostException e) {
228                 LOG.warn("Exception obtained when deserializing Router Solicitation packet", e.toString());
229             }
230             return rsPdu.build();
231         }
232
233         private void updateRAResponse(RouterSolicitationPacket pdu,
234                                       RouterAdvertisementPacketBuilder raPacket,
235                                       MacAddress vmMac, VirtualPort routerPort) {
236             short icmpv6RaFlags = 0;
237             String gatewayMac = null;
238             IpAddress gatewayIp;
239             List<String> autoConfigPrefixList = new ArrayList<String>();
240             List<String> statefulConfigPrefixList = new ArrayList<String>();
241
242             for (VirtualSubnet subnet : routerPort.getSubnets()) {
243                 gatewayIp = subnet.getGatewayIp();
244                 // Skip if its a v4 subnet.
245                 if (gatewayIp.getIpv4Address() != null) {
246                     continue;
247                 }
248
249                 if (!subnet.getIpv6RAMode().isEmpty()) {
250                     if (Ipv6Constants.IPV6_AUTO_ADDRESS_SUBNETS.contains(subnet.getIpv6RAMode())) {
251                         autoConfigPrefixList.add(String.valueOf(subnet.getSubnetCidr().getValue()));
252                     }
253
254                     if (subnet.getIpv6RAMode().equalsIgnoreCase(Ipv6Constants.IPV6_DHCPV6_STATEFUL)) {
255                         statefulConfigPrefixList.add(String.valueOf(subnet.getSubnetCidr().getValue()));
256                     }
257                 }
258
259                 if (subnet.getIpv6RAMode().equalsIgnoreCase(Ipv6Constants.IPV6_DHCPV6_STATELESS)) {
260                     icmpv6RaFlags = (short) (icmpv6RaFlags | (1 << 6)); // Other Configuration.
261                 } else if (subnet.getIpv6RAMode().equalsIgnoreCase(Ipv6Constants.IPV6_DHCPV6_STATEFUL)) {
262                     icmpv6RaFlags = (short) (icmpv6RaFlags | (1 << 7)); // Managed Address Conf.
263                 }
264             }
265
266             gatewayMac = routerPort.getMacAddress();
267
268             MacAddress sourceMac = MacAddress.getDefaultInstance(gatewayMac);
269             raPacket.setSourceMac(sourceMac);
270             raPacket.setDestinationMac(vmMac);
271             raPacket.setEthertype(pdu.getEthertype());
272
273             raPacket.setVersion(pdu.getVersion());
274             raPacket.setFlowLabel(pdu.getFlowLabel());
275             int prefixListLength = autoConfigPrefixList.size() + statefulConfigPrefixList.size();
276             raPacket.setIpv6Length(Ipv6Constants.ICMPV6_RA_LENGTH_WO_OPTIONS
277                     + Ipv6Constants.ICMPV6_OPTION_SOURCE_LLA_LENGTH
278                     + prefixListLength * Ipv6Constants.ICMPV6_OPTION_PREFIX_LENGTH);
279             raPacket.setNextHeader(pdu.getNextHeader());
280             raPacket.setHopLimit(Ipv6Constants.ICMPv6_MAX_HOP_LIMIT);
281             raPacket.setSourceIpv6(ipv6Utils.getIpv6LinkLocalAddressFromMac(sourceMac));
282             raPacket.setDestinationIpv6(pdu.getSourceIpv6());
283
284             raPacket.setIcmp6Type(Ipv6Constants.ICMPv6_RA_CODE);
285             raPacket.setIcmp6Code((short)0);
286             raPacket.setIcmp6Chksum(0);
287
288             raPacket.setCurHopLimit((short) Ipv6Constants.IPV6_DEFAULT_HOP_LIMIT);
289             raPacket.setFlags((short) icmpv6RaFlags);
290             raPacket.setRouterLifetime(Ipv6Constants.IPV6_ROUTER_LIFETIME);
291             raPacket.setReachableTime((long) 0);
292             raPacket.setRetransTime((long) 0);
293
294             raPacket.setOptionSourceAddr((short)1);
295             raPacket.setSourceAddrLength((short)1);
296             raPacket.setSourceLlAddress(MacAddress.getDefaultInstance(gatewayMac));
297
298             List<PrefixList> prefixList = new ArrayList<PrefixList>();
299             PrefixListBuilder prefix = new PrefixListBuilder();
300             prefix.setOptionType((short)3);
301             prefix.setOptionLength((short)4);
302             // Note: EUI-64 auto-configuration requires 64 bits.
303             prefix.setPrefixLength((short)64);
304             prefix.setValidLifetime((long) Ipv6Constants.IPV6_RA_VALID_LIFETIME);
305             prefix.setPreferredLifetime((long) Ipv6Constants.IPV6_RA_PREFERRED_LIFETIME);
306             prefix.setReserved((long) 0);
307
308             short autoConfPrefixFlags = 0;
309             autoConfPrefixFlags = (short) (autoConfPrefixFlags | (1 << 7)); // On-link flag
310             autoConfPrefixFlags = (short) (autoConfPrefixFlags | (1 << 6)); // Autonomous address-configuration flag.
311             for (String v6Prefix : autoConfigPrefixList) {
312                 prefix.setFlags((short)autoConfPrefixFlags);
313                 prefix.setPrefix(new Ipv6Prefix(v6Prefix));
314                 prefixList.add(prefix.build());
315             }
316
317             short statefulPrefixFlags = 0;
318             statefulPrefixFlags = (short) (statefulPrefixFlags | (1 << 7)); // On-link flag
319             for (String v6Prefix : statefulConfigPrefixList) {
320                 prefix.setFlags((short)statefulPrefixFlags);
321                 prefix.setPrefix(new Ipv6Prefix(v6Prefix));
322                 prefixList.add(prefix.build());
323             }
324
325             raPacket.setPrefixList((List<PrefixList>) prefixList);
326
327             return;
328         }
329
330         private byte[] fillRouterAdvertisementPacket(RouterAdvertisementPacket pdu) {
331             ByteBuffer buf = ByteBuffer.allocate(Ipv6Constants.ICMPV6_OFFSET + pdu.getIpv6Length());
332
333             buf.put(ipv6Utils.convertEthernetHeaderToByte((EthernetHeader)pdu), 0, 14);
334             buf.put(ipv6Utils.convertIpv6HeaderToByte((Ipv6Header)pdu), 0, 40);
335             buf.put(icmp6RAPayloadtoByte(pdu), 0, pdu.getIpv6Length());
336             int checksum = ipv6Utils.calcIcmpv6Checksum(buf.array(), (Ipv6Header) pdu);
337             buf.putShort((Ipv6Constants.ICMPV6_OFFSET + 2), (short)checksum);
338             return (buf.array());
339         }
340
341         private byte[] icmp6RAPayloadtoByte(RouterAdvertisementPacket pdu) {
342             byte[] data = new byte[pdu.getIpv6Length()];
343             Arrays.fill(data, (byte)0);
344
345             ByteBuffer buf = ByteBuffer.wrap(data);
346             buf.put((byte)pdu.getIcmp6Type().shortValue());
347             buf.put((byte)pdu.getIcmp6Code().shortValue());
348             buf.putShort((short)pdu.getIcmp6Chksum().intValue());
349             buf.put((byte)pdu.getCurHopLimit().shortValue());
350             buf.put((byte)pdu.getFlags().shortValue());
351             buf.putShort((short)pdu.getRouterLifetime().intValue());
352             buf.putInt((int)pdu.getReachableTime().longValue());
353             buf.putInt((int)pdu.getRetransTime().longValue());
354             buf.put((byte)pdu.getOptionSourceAddr().shortValue());
355             buf.put((byte)pdu.getSourceAddrLength().shortValue());
356             buf.put(ipv6Utils.bytesFromHexString(pdu.getSourceLlAddress().getValue().toString()));
357
358             for (PrefixList prefix : pdu.getPrefixList()) {
359                 buf.put((byte)prefix.getOptionType().shortValue());
360                 buf.put((byte)prefix.getOptionLength().shortValue());
361                 buf.put((byte)prefix.getPrefixLength().shortValue());
362                 buf.put((byte)prefix.getFlags().shortValue());
363                 buf.putInt((int)prefix.getValidLifetime().longValue());
364                 buf.putInt((int)prefix.getPreferredLifetime().longValue());
365                 buf.putInt((int)prefix.getReserved().longValue());
366                 buf.put(IetfInetUtil.INSTANCE.ipv6PrefixToBytes(new Ipv6Prefix(prefix.getPrefix())),0,16);
367             }
368             return data;
369         }
370
371     }
372
373     @Override
374     public void close() throws Exception {
375         packetProcessor.shutdown();
376     }
377 }