1 /* 2 * Copyright (C) 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.net.module.util; 18 19 import static android.system.OsConstants.IPPROTO_ICMPV6; 20 21 import static com.android.net.module.util.IpUtils.icmpv6Checksum; 22 import static com.android.net.module.util.NetworkStackConstants.ETHER_TYPE_IPV6; 23 import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ECHO_REPLY_TYPE; 24 import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ECHO_REQUEST_TYPE; 25 import static com.android.net.module.util.NetworkStackConstants.ICMPV6_NEIGHBOR_ADVERTISEMENT; 26 import static com.android.net.module.util.NetworkStackConstants.ICMPV6_NEIGHBOR_SOLICITATION; 27 import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ROUTER_ADVERTISEMENT; 28 import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ROUTER_SOLICITATION; 29 30 import android.net.MacAddress; 31 32 import com.android.net.module.util.structs.EthernetHeader; 33 import com.android.net.module.util.structs.Icmpv6Header; 34 import com.android.net.module.util.structs.Ipv6Header; 35 import com.android.net.module.util.structs.NaHeader; 36 import com.android.net.module.util.structs.NsHeader; 37 import com.android.net.module.util.structs.RaHeader; 38 import com.android.net.module.util.structs.RsHeader; 39 40 import java.net.Inet6Address; 41 import java.nio.ByteBuffer; 42 import java.nio.ByteOrder; 43 44 /** 45 * Utilities for IPv6 packets. 46 */ 47 public class Ipv6Utils { 48 /** 49 * Build a generic ICMPv6 packet without Ethernet header. 50 */ buildIcmpv6Packet(final Inet6Address srcIp, final Inet6Address dstIp, short type, short code, final ByteBuffer... options)51 public static ByteBuffer buildIcmpv6Packet(final Inet6Address srcIp, final Inet6Address dstIp, 52 short type, short code, final ByteBuffer... options) { 53 final int ipv6HeaderLen = Struct.getSize(Ipv6Header.class); 54 final int icmpv6HeaderLen = Struct.getSize(Icmpv6Header.class); 55 int payloadLen = 0; 56 for (ByteBuffer option: options) { 57 payloadLen += option.limit(); 58 } 59 final ByteBuffer packet = ByteBuffer.allocate(ipv6HeaderLen + icmpv6HeaderLen + payloadLen); 60 final Ipv6Header ipv6Header = 61 new Ipv6Header((int) 0x60000000 /* version, traffic class, flowlabel */, 62 icmpv6HeaderLen + payloadLen /* payload length */, 63 (byte) IPPROTO_ICMPV6 /* next header */, (byte) 0xff /* hop limit */, srcIp, dstIp); 64 final Icmpv6Header icmpv6Header = new Icmpv6Header(type, code, (short) 0 /* checksum */); 65 66 ipv6Header.writeToByteBuffer(packet); 67 icmpv6Header.writeToByteBuffer(packet); 68 for (ByteBuffer option : options) { 69 packet.put(option); 70 // in case option might be reused by caller, restore the position and 71 // limit of bytebuffer. 72 option.clear(); 73 } 74 packet.flip(); 75 76 // Populate the ICMPv6 checksum field. 77 packet.putShort(ipv6HeaderLen + 2, icmpv6Checksum(packet, 0 /* ipOffset */, 78 ipv6HeaderLen /* transportOffset */, 79 (short) (icmpv6HeaderLen + payloadLen) /* transportLen */)); 80 return packet; 81 82 } 83 84 /** 85 * Build a generic ICMPv6 packet(e.g., packet used in the neighbor discovery protocol). 86 */ buildIcmpv6Packet(final MacAddress srcMac, final MacAddress dstMac, final Inet6Address srcIp, final Inet6Address dstIp, short type, short code, final ByteBuffer... options)87 public static ByteBuffer buildIcmpv6Packet(final MacAddress srcMac, final MacAddress dstMac, 88 final Inet6Address srcIp, final Inet6Address dstIp, short type, short code, 89 final ByteBuffer... options) { 90 final ByteBuffer payload = buildIcmpv6Packet(srcIp, dstIp, type, code, options); 91 92 final int etherHeaderLen = Struct.getSize(EthernetHeader.class); 93 final ByteBuffer packet = ByteBuffer.allocate(etherHeaderLen + payload.limit()); 94 final EthernetHeader ethHeader = 95 new EthernetHeader(dstMac, srcMac, (short) ETHER_TYPE_IPV6); 96 97 ethHeader.writeToByteBuffer(packet); 98 packet.put(payload); 99 packet.flip(); 100 101 return packet; 102 } 103 104 /** 105 * Build the ICMPv6 packet payload including payload header and specific options. 106 */ buildIcmpv6Payload(final ByteBuffer payloadHeader, final ByteBuffer... options)107 private static ByteBuffer[] buildIcmpv6Payload(final ByteBuffer payloadHeader, 108 final ByteBuffer... options) { 109 final ByteBuffer[] payload = new ByteBuffer[options.length + 1]; 110 payload[0] = payloadHeader; 111 System.arraycopy(options, 0, payload, 1, options.length); 112 return payload; 113 } 114 115 /** 116 * Build an ICMPv6 Router Advertisement packet from the required specified parameters. 117 */ buildRaPacket(final MacAddress srcMac, final MacAddress dstMac, final Inet6Address srcIp, final Inet6Address dstIp, final byte flags, final int lifetime, final long reachableTime, final long retransTimer, final ByteBuffer... options)118 public static ByteBuffer buildRaPacket(final MacAddress srcMac, final MacAddress dstMac, 119 final Inet6Address srcIp, final Inet6Address dstIp, final byte flags, 120 final int lifetime, final long reachableTime, final long retransTimer, 121 final ByteBuffer... options) { 122 final RaHeader raHeader = new RaHeader((byte) 0 /* hopLimit, unspecified */, 123 flags, lifetime, reachableTime, retransTimer); 124 final ByteBuffer[] payload = buildIcmpv6Payload( 125 ByteBuffer.wrap(raHeader.writeToBytes(ByteOrder.BIG_ENDIAN)), options); 126 return buildIcmpv6Packet(srcMac, dstMac, srcIp, dstIp, 127 (byte) ICMPV6_ROUTER_ADVERTISEMENT /* type */, (byte) 0 /* code */, payload); 128 } 129 130 /** 131 * Build an ICMPv6 Neighbor Advertisement packet from the required specified parameters. 132 */ buildNaPacket(final MacAddress srcMac, final MacAddress dstMac, final Inet6Address srcIp, final Inet6Address dstIp, final int flags, final Inet6Address target, final ByteBuffer... options)133 public static ByteBuffer buildNaPacket(final MacAddress srcMac, final MacAddress dstMac, 134 final Inet6Address srcIp, final Inet6Address dstIp, final int flags, 135 final Inet6Address target, final ByteBuffer... options) { 136 final NaHeader naHeader = new NaHeader(flags, target); 137 final ByteBuffer[] payload = buildIcmpv6Payload( 138 ByteBuffer.wrap(naHeader.writeToBytes(ByteOrder.BIG_ENDIAN)), options); 139 return buildIcmpv6Packet(srcMac, dstMac, srcIp, dstIp, 140 (byte) ICMPV6_NEIGHBOR_ADVERTISEMENT /* type */, (byte) 0 /* code */, payload); 141 } 142 143 /** 144 * Build an ICMPv6 Neighbor Solicitation packet from the required specified parameters. 145 */ buildNsPacket(final MacAddress srcMac, final MacAddress dstMac, final Inet6Address srcIp, final Inet6Address dstIp, final Inet6Address target, final ByteBuffer... options)146 public static ByteBuffer buildNsPacket(final MacAddress srcMac, final MacAddress dstMac, 147 final Inet6Address srcIp, final Inet6Address dstIp, 148 final Inet6Address target, final ByteBuffer... options) { 149 final NsHeader nsHeader = new NsHeader(target); 150 final ByteBuffer[] payload = buildIcmpv6Payload( 151 ByteBuffer.wrap(nsHeader.writeToBytes(ByteOrder.BIG_ENDIAN)), options); 152 return buildIcmpv6Packet(srcMac, dstMac, srcIp, dstIp, 153 (byte) ICMPV6_NEIGHBOR_SOLICITATION /* type */, (byte) 0 /* code */, payload); 154 } 155 156 /** 157 * Build an ICMPv6 Router Solicitation packet from the required specified parameters. 158 */ buildRsPacket(final MacAddress srcMac, final MacAddress dstMac, final Inet6Address srcIp, final Inet6Address dstIp, final ByteBuffer... options)159 public static ByteBuffer buildRsPacket(final MacAddress srcMac, final MacAddress dstMac, 160 final Inet6Address srcIp, final Inet6Address dstIp, final ByteBuffer... options) { 161 final RsHeader rsHeader = new RsHeader((int) 0 /* reserved */); 162 final ByteBuffer[] payload = buildIcmpv6Payload( 163 ByteBuffer.wrap(rsHeader.writeToBytes(ByteOrder.BIG_ENDIAN)), options); 164 return buildIcmpv6Packet(srcMac, dstMac, srcIp, dstIp, 165 (byte) ICMPV6_ROUTER_SOLICITATION /* type */, (byte) 0 /* code */, payload); 166 } 167 168 /** 169 * Build an ICMPv6 Router Solicitation packet from the required specified parameters without 170 * ethernet header. 171 */ buildRsPacket( final Inet6Address srcIp, final Inet6Address dstIp, final ByteBuffer... options)172 public static ByteBuffer buildRsPacket( 173 final Inet6Address srcIp, final Inet6Address dstIp, final ByteBuffer... options) { 174 final RsHeader rsHeader = new RsHeader((int) 0 /* reserved */); 175 final ByteBuffer[] payload = 176 buildIcmpv6Payload( 177 ByteBuffer.wrap(rsHeader.writeToBytes(ByteOrder.BIG_ENDIAN)), options); 178 return buildIcmpv6Packet( 179 srcIp, 180 dstIp, 181 (byte) ICMPV6_ROUTER_SOLICITATION /* type */, 182 (byte) 0 /* code */, 183 payload); 184 } 185 186 /** 187 * Build an ICMPv6 Echo Request packet from the required specified parameters. 188 */ buildEchoRequestPacket(final MacAddress srcMac, final MacAddress dstMac, final Inet6Address srcIp, final Inet6Address dstIp)189 public static ByteBuffer buildEchoRequestPacket(final MacAddress srcMac, 190 final MacAddress dstMac, final Inet6Address srcIp, final Inet6Address dstIp) { 191 final ByteBuffer payload = ByteBuffer.allocate(4); // ID and Sequence number may be zero. 192 return buildIcmpv6Packet(srcMac, dstMac, srcIp, dstIp, 193 (byte) ICMPV6_ECHO_REQUEST_TYPE /* type */, (byte) 0 /* code */, payload); 194 } 195 196 /** 197 * Build an ICMPv6 Echo Request packet from the required specified parameters without ethernet 198 * header. 199 */ buildEchoRequestPacket(final Inet6Address srcIp, final Inet6Address dstIp)200 public static ByteBuffer buildEchoRequestPacket(final Inet6Address srcIp, 201 final Inet6Address dstIp) { 202 final ByteBuffer payload = ByteBuffer.allocate(4); // ID and Sequence number may be zero. 203 return buildIcmpv6Packet(srcIp, dstIp, (byte) ICMPV6_ECHO_REQUEST_TYPE /* type */, 204 (byte) 0 /* code */, 205 payload); 206 } 207 208 /** Build an ICMPv6 Echo Reply packet without ethernet header. */ buildEchoReplyPacket( final Inet6Address srcIp, final Inet6Address dstIp)209 public static ByteBuffer buildEchoReplyPacket( 210 final Inet6Address srcIp, final Inet6Address dstIp) { 211 final ByteBuffer payload = ByteBuffer.allocate(4); // ID and Sequence number may be zero. 212 return buildIcmpv6Packet(srcIp, dstIp, (byte) ICMPV6_ECHO_REPLY_TYPE /* type */, 213 (byte) 0 /* code */, payload); 214 } 215 } 216