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