2 * Copyright (c) 2016 Red Hat, Inc. and others. All rights reserved.
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
8 package org.opendaylight.netvirt.ipv6service;
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;
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();
56 public Ipv6PktHandler(PacketProcessingService pktService) {
57 this.pktService = pktService;
58 ifMgr = IfMgr.getIfMgrInstance();
59 this.ipv6Utils = Ipv6ServiceUtils.getInstance();
63 public void onPacketReceived(PacketReceived packetReceived) {
67 if (packetReceived == null) {
68 LOG.debug("Received null packet. Returning without any processing");
72 byte[] data = packetReceived.getPayload();
73 if (data.length <= 0) {
74 LOG.debug("Received packet with invalid length {}", data.length);
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));
91 LOG.debug("IPv6 Pdu received on port {} with next-header {} ",
92 packetReceived.getIngress(), v6NxtHdr);
97 } catch (BufferException e) {
98 LOG.warn("Failed to decode packet: {}", e.getMessage());
103 public long getPacketProcessedCounter() {
104 return pktProccessedCounter;
107 private class PacketHandler implements Runnable {
109 PacketReceived packet;
111 PacketHandler(int icmpv6Type, PacketReceived packet) {
112 this.type = icmpv6Type;
113 this.packet = packet;
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();
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());
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));
143 pktProccessedCounter++;
144 LOG.warn("Port {} not found, skipping.", port);
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);
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());
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++;
179 private NeighborSolicitationPacket deserializeNSPacket(byte[] data) {
180 NeighborSolicitationPacketBuilder nsPdu = new NeighborSolicitationPacketBuilder();
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)));
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;
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());
223 return nsPdu.build();
226 private void updateNAResponse(NeighborSolicitationPacket pdu,
227 VirtualPort port, NeighborAdvertisePacketBuilder naPacket) {
229 if (!pdu.getSourceIpv6().equals(ipv6Utils.UNSPECIFIED_ADDR)) {
230 naPacket.setDestinationIpv6(pdu.getSourceIpv6());
231 flag = 0xE0; // Set Router, Solicited and Override Flag.
233 naPacket.setDestinationIpv6(ipv6Utils.ALL_NODES_MCAST_ADDR);
234 flag = 0xA0; // Set Router and Override Flag.
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());
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);
257 private byte[] icmp6NAPayloadtoByte(NeighborAdvertisePacket pdu) {
258 byte[] data = new byte[36];
259 Arrays.fill(data, (byte)0);
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());
267 byte[] address = null;
268 address = InetAddress.getByName(pdu.getTargetAddress().getValue()).getAddress();
270 } catch (UnknownHostException e) {
271 LOG.error("Serializing NA target address failed", e);
273 buf.put((byte)pdu.getOptionType().shortValue());
274 buf.put((byte)pdu.getTargetAddrLength().shortValue());
275 buf.put(ipv6Utils.bytesFromHexString(pdu.getTargetLlAddress().getValue().toString()));
279 private byte[] fillNeighborAdvertisementPacket(NeighborAdvertisePacket pdu) {
280 ByteBuffer buf = ByteBuffer.allocate(Ipv6Constants.ICMPV6_OFFSET + pdu.getIpv6Length());
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());
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());
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));
306 pktProccessedCounter++;
307 LOG.info("Port {} not found, skipping.", port);
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);
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;
325 private RouterSolicitationPacket deserializeRSPacket(byte[] data) {
326 RouterSolicitationPacketBuilder rsPdu = new RouterSolicitationPacketBuilder();
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)));
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;
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;
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;
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))));
376 } catch (BufferException | UnknownHostException e) {
377 LOG.warn("Exception obtained when deserializing Router Solicitation packet", e.toString());
379 return rsPdu.build();
384 public void close() throws Exception {
385 packetProcessor.shutdown();