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.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;
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;
54 private Ipv6ServiceUtils ipv6Utils;
55 private final ExecutorService packetProcessor = Executors.newCachedThreadPool();
57 public Ipv6PktHandler() {
58 this.ipv6Utils = Ipv6ServiceUtils.getInstance();
61 public void setPacketProcessingService(PacketProcessingService packetService) {
62 this.pktService = packetService;
65 public void setIfMgrInstance(IfMgr instance) {
66 this.ifMgr = instance;
70 public void onPacketReceived(PacketReceived packetReceived) {
74 if (packetReceived == null) {
75 LOG.debug("Received null packet. Returning without any processing");
79 byte[] data = packetReceived.getPayload();
80 if (data.length <= 0) {
81 LOG.debug("Received packet with invalid length {}", data.length);
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));
97 LOG.debug("IPv6 Pdu received on port {} with next-header {} ",
98 packetReceived.getIngress(), v6NxtHdr);
103 } catch (BufferException e) {
104 LOG.warn("Failed to decode packet: {}", e.getMessage());
109 public long getPacketProcessedCounter() {
110 return pktProccessedCounter;
113 private class PacketHandler implements Runnable {
115 PacketReceived packet;
117 PacketHandler(int icmpv6Type, PacketReceived packet) {
118 this.type = icmpv6Type;
119 this.packet = packet;
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();
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());
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));
149 pktProccessedCounter++;
150 LOG.warn("Port {} not found, skipping.", port);
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);
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++;
176 private RouterSolicitationPacket deserializeRSPacket(byte[] data) {
177 RouterSolicitationPacketBuilder rsPdu = new RouterSolicitationPacketBuilder();
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)));
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;
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;
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;
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))));
227 } catch (BufferException | UnknownHostException e) {
228 LOG.warn("Exception obtained when deserializing Router Solicitation packet", e.toString());
230 return rsPdu.build();
233 private void updateRAResponse(RouterSolicitationPacket pdu,
234 RouterAdvertisementPacketBuilder raPacket,
235 MacAddress vmMac, VirtualPort routerPort) {
236 short icmpv6RaFlags = 0;
237 String gatewayMac = null;
239 List<String> autoConfigPrefixList = new ArrayList<String>();
240 List<String> statefulConfigPrefixList = new ArrayList<String>();
242 for (VirtualSubnet subnet : routerPort.getSubnets()) {
243 gatewayIp = subnet.getGatewayIp();
244 // Skip if its a v4 subnet.
245 if (gatewayIp.getIpv4Address() != null) {
249 if (!subnet.getIpv6RAMode().isEmpty()) {
250 if (Ipv6Constants.IPV6_AUTO_ADDRESS_SUBNETS.contains(subnet.getIpv6RAMode())) {
251 autoConfigPrefixList.add(String.valueOf(subnet.getSubnetCidr().getValue()));
254 if (subnet.getIpv6RAMode().equalsIgnoreCase(Ipv6Constants.IPV6_DHCPV6_STATEFUL)) {
255 statefulConfigPrefixList.add(String.valueOf(subnet.getSubnetCidr().getValue()));
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.
266 gatewayMac = routerPort.getMacAddress();
268 MacAddress sourceMac = MacAddress.getDefaultInstance(gatewayMac);
269 raPacket.setSourceMac(sourceMac);
270 raPacket.setDestinationMac(vmMac);
271 raPacket.setEthertype(pdu.getEthertype());
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());
284 raPacket.setIcmp6Type(Ipv6Constants.ICMPv6_RA_CODE);
285 raPacket.setIcmp6Code((short)0);
286 raPacket.setIcmp6Chksum(0);
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);
294 raPacket.setOptionSourceAddr((short)1);
295 raPacket.setSourceAddrLength((short)1);
296 raPacket.setSourceLlAddress(MacAddress.getDefaultInstance(gatewayMac));
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);
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());
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());
325 raPacket.setPrefixList((List<PrefixList>) prefixList);
330 private byte[] fillRouterAdvertisementPacket(RouterAdvertisementPacket pdu) {
331 ByteBuffer buf = ByteBuffer.allocate(Ipv6Constants.ICMPV6_OFFSET + pdu.getIpv6Length());
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());
341 private byte[] icmp6RAPayloadtoByte(RouterAdvertisementPacket pdu) {
342 byte[] data = new byte[pdu.getIpv6Length()];
343 Arrays.fill(data, (byte)0);
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()));
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);
374 public void close() throws Exception {
375 packetProcessor.shutdown();