1 /* 2 * Copyright (C) 2020 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 android.net.netlink; 18 19 import static android.net.InetAddresses.parseNumericAddress; 20 import static android.system.OsConstants.AF_INET6; 21 import static android.system.OsConstants.NETLINK_ROUTE; 22 23 import static org.junit.Assert.assertEquals; 24 import static org.junit.Assert.assertNotNull; 25 import static org.junit.Assert.assertNull; 26 import static org.junit.Assert.assertTrue; 27 28 import android.net.IpPrefix; 29 30 import androidx.test.filters.SmallTest; 31 import androidx.test.runner.AndroidJUnit4; 32 33 import libcore.util.HexEncoding; 34 35 import org.junit.Test; 36 import org.junit.runner.RunWith; 37 38 import java.net.InetAddress; 39 import java.nio.ByteBuffer; 40 import java.nio.ByteOrder; 41 42 @RunWith(AndroidJUnit4.class) 43 @SmallTest 44 public class NduseroptMessageTest { 45 46 private static final byte ICMP_TYPE_RA = (byte) 134; 47 48 private static final int IFINDEX1 = 15715755; 49 private static final int IFINDEX2 = 1431655765; 50 51 // IPv6, 0 bytes of options, interface index 15715755, type 134 (RA), code 0, padding. 52 private static final String HDR_EMPTY = "0a00" + "0000" + "abcdef00" + "8600000000000000"; 53 54 // IPv6, 16 bytes of options, interface index 1431655765, type 134 (RA), code 0, padding. 55 private static final String HDR_16BYTE = "0a00" + "1000" + "55555555" + "8600000000000000"; 56 57 // IPv6, 32 bytes of options, interface index 1431655765, type 134 (RA), code 0, padding. 58 private static final String HDR_32BYTE = "0a00" + "2000" + "55555555" + "8600000000000000"; 59 60 // PREF64 option, 2001:db8:3:4:5:6::/96, lifetime=10064 61 private static final String OPT_PREF64 = "2602" + "2750" + "20010db80003000400050006"; 62 63 // Length 20, NDUSEROPT_SRCADDR, fe80:2:3:4:5:6:7:8 64 private static final String NLA_SRCADDR = "1400" + "0100" + "fe800002000300040005000600070008"; 65 66 private static final InetAddress SADDR1 = parseNumericAddress("fe80:2:3:4:5:6:7:8%" + IFINDEX1); 67 private static final InetAddress SADDR2 = parseNumericAddress("fe80:2:3:4:5:6:7:8%" + IFINDEX2); 68 69 private static final String MSG_EMPTY = HDR_EMPTY + NLA_SRCADDR; 70 private static final String MSG_PREF64 = HDR_16BYTE + OPT_PREF64 + NLA_SRCADDR; 71 72 @Test testParsing()73 public void testParsing() { 74 NduseroptMessage msg = parseNduseroptMessage(toBuffer(MSG_EMPTY)); 75 assertMatches(AF_INET6, 0, IFINDEX1, ICMP_TYPE_RA, (byte) 0, SADDR1, msg); 76 assertNull(msg.option); 77 78 msg = parseNduseroptMessage(toBuffer(MSG_PREF64)); 79 assertMatches(AF_INET6, 16, IFINDEX2, ICMP_TYPE_RA, (byte) 0, SADDR2, msg); 80 assertPref64Option("2001:db8:3:4:5:6::/96", msg.option); 81 } 82 83 @Test testParseWithinNetlinkMessage()84 public void testParseWithinNetlinkMessage() throws Exception { 85 // A NduseroptMessage inside a netlink message. Ensure that it parses the same way both by 86 // parsing the netlink message via NetlinkMessage.parse() and by parsing the option itself 87 // with NduseroptMessage.parse(). 88 final String hexBytes = 89 "44000000440000000000000000000000" // len=68, RTM_NEWNDUSEROPT 90 + "0A0010001E0000008600000000000000" // IPv6, opt_bytes=16, ifindex=30, RA 91 + "260202580064FF9B0000000000000000" // pref64, prefix=64:ff9b::/96, 600 92 + "14000100FE800000000000000250B6FFFEB7C499"; // srcaddr=fe80::250:b6ff:feb7:c499 93 94 ByteBuffer buf = toBuffer(hexBytes); 95 assertEquals(68, buf.limit()); 96 buf.order(ByteOrder.nativeOrder()); 97 98 NetlinkMessage nlMsg = NetlinkMessage.parse(buf, NETLINK_ROUTE); 99 assertNotNull(nlMsg); 100 assertTrue(nlMsg instanceof NduseroptMessage); 101 102 NduseroptMessage msg = (NduseroptMessage) nlMsg; 103 InetAddress srcaddr = InetAddress.getByName("fe80::250:b6ff:feb7:c499%30"); 104 assertMatches(AF_INET6, 16, 30, ICMP_TYPE_RA, (byte) 0, srcaddr, msg); 105 assertPref64Option("64:ff9b::/96", msg.option); 106 107 final String hexBytesWithoutHeader = hexBytes.substring(StructNlMsgHdr.STRUCT_SIZE * 2); 108 ByteBuffer bufWithoutHeader = toBuffer(hexBytesWithoutHeader); 109 assertEquals(52, bufWithoutHeader.limit()); 110 msg = parseNduseroptMessage(bufWithoutHeader); 111 assertMatches(AF_INET6, 16, 30, ICMP_TYPE_RA, (byte) 0, srcaddr, msg); 112 assertPref64Option("64:ff9b::/96", msg.option); 113 } 114 115 @Test testParseUnknownOptionWithinNetlinkMessage()116 public void testParseUnknownOptionWithinNetlinkMessage() throws Exception { 117 final String hexBytes = 118 "4C0000004400000000000000000000000" 119 + "A0018001E0000008600000000000000" 120 + "1903000000001770FD123456789000000000000000000001" // RDNSS option 121 + "14000100FE800000000000000250B6FFFEB7C499"; 122 123 ByteBuffer buf = toBuffer(hexBytes); 124 assertEquals(76, buf.limit()); 125 buf.order(ByteOrder.nativeOrder()); 126 127 NetlinkMessage nlMsg = NetlinkMessage.parse(buf, NETLINK_ROUTE); 128 assertNotNull(nlMsg); 129 assertTrue(nlMsg instanceof NduseroptMessage); 130 131 NduseroptMessage msg = (NduseroptMessage) nlMsg; 132 InetAddress srcaddr = InetAddress.getByName("fe80::250:b6ff:feb7:c499%30"); 133 assertMatches(AF_INET6, 24, 30, ICMP_TYPE_RA, (byte) 0, srcaddr, msg); 134 assertEquals(NdOption.UNKNOWN, msg.option); 135 } 136 137 @Test testUnknownOption()138 public void testUnknownOption() { 139 ByteBuffer buf = toBuffer(MSG_PREF64); 140 // Replace the PREF64 option type (38) with an unknown option number. 141 final int optionStart = NduseroptMessage.STRUCT_SIZE; 142 assertEquals(38, buf.get(optionStart)); 143 buf.put(optionStart, (byte) 42); 144 145 NduseroptMessage msg = parseNduseroptMessage(buf); 146 assertMatches(AF_INET6, 16, IFINDEX2, ICMP_TYPE_RA, (byte) 0, SADDR2, msg); 147 assertEquals(NdOption.UNKNOWN, msg.option); 148 149 buf.flip(); 150 assertEquals(42, buf.get(optionStart)); 151 buf.put(optionStart, (byte) 38); 152 153 msg = parseNduseroptMessage(buf); 154 assertMatches(AF_INET6, 16, IFINDEX2, ICMP_TYPE_RA, (byte) 0, SADDR2, msg); 155 assertPref64Option("2001:db8:3:4:5:6::/96", msg.option); 156 } 157 158 @Test testZeroLengthOption()159 public void testZeroLengthOption() { 160 // Make sure an unknown option with a 0-byte length is ignored and parsing continues with 161 // the address, which comes after it. 162 final String hexString = HDR_16BYTE + "00000000000000000000000000000000" + NLA_SRCADDR; 163 ByteBuffer buf = toBuffer(hexString); 164 assertEquals(52, buf.limit()); 165 NduseroptMessage msg = parseNduseroptMessage(buf); 166 assertMatches(AF_INET6, 16, IFINDEX2, ICMP_TYPE_RA, (byte) 0, SADDR2, msg); 167 assertNull(msg.option); 168 } 169 170 @Test testTooLongOption()171 public void testTooLongOption() { 172 // Make sure that if an option's length is too long, it's ignored and parsing continues with 173 // the address, which comes after it. 174 final String hexString = HDR_16BYTE + "26030000000000000000000000000000" + NLA_SRCADDR; 175 ByteBuffer buf = toBuffer(hexString); 176 assertEquals(52, buf.limit()); 177 NduseroptMessage msg = parseNduseroptMessage(buf); 178 assertMatches(AF_INET6, 16, IFINDEX2, ICMP_TYPE_RA, (byte) 0, SADDR2, msg); 179 assertNull(msg.option); 180 } 181 182 @Test testOptionsTooLong()183 public void testOptionsTooLong() { 184 // Header claims 32 bytes of options. Buffer ends before options end. 185 String hexString = HDR_32BYTE + OPT_PREF64; 186 ByteBuffer buf = toBuffer(hexString); 187 assertEquals(32, buf.limit()); 188 assertNull(NduseroptMessage.parse(toBuffer(hexString), NETLINK_ROUTE)); 189 190 // Header claims 32 bytes of options. Buffer ends at end of options with no source address. 191 hexString = HDR_32BYTE + OPT_PREF64 + OPT_PREF64; 192 buf = toBuffer(hexString); 193 assertEquals(48, buf.limit()); 194 assertNull(NduseroptMessage.parse(toBuffer(hexString), NETLINK_ROUTE)); 195 } 196 197 @Test testTruncation()198 public void testTruncation() { 199 final int optLen = MSG_PREF64.length() / 2; // 1 byte = 2 hex chars 200 for (int len = 0; len < optLen; len++) { 201 ByteBuffer buf = toBuffer(MSG_PREF64.substring(0, len * 2)); 202 NduseroptMessage msg = parseNduseroptMessage(buf); 203 if (len < optLen) { 204 assertNull(msg); 205 } else { 206 assertNotNull(msg); 207 assertPref64Option("2001:db8:3:4:5:6::/96", msg.option); 208 } 209 } 210 } 211 212 @Test testToString()213 public void testToString() { 214 NduseroptMessage msg = parseNduseroptMessage(toBuffer(MSG_PREF64)); 215 assertNotNull(msg); 216 assertEquals("Nduseroptmsg(10, 16, 1431655765, 134, 0, fe80:2:3:4:5:6:7:8%1431655765)", 217 msg.toString()); 218 } 219 220 // Convenience method to parse a NduseroptMessage that's not part of a netlink message. parseNduseroptMessage(ByteBuffer buf)221 private NduseroptMessage parseNduseroptMessage(ByteBuffer buf) { 222 return NduseroptMessage.parse(null, buf); 223 } 224 toBuffer(String hexString)225 private ByteBuffer toBuffer(String hexString) { 226 return ByteBuffer.wrap(HexEncoding.decode(hexString)); 227 } 228 assertMatches(int family, int optsLen, int ifindex, byte icmpType, byte icmpCode, InetAddress srcaddr, NduseroptMessage msg)229 private void assertMatches(int family, int optsLen, int ifindex, byte icmpType, 230 byte icmpCode, InetAddress srcaddr, NduseroptMessage msg) { 231 assertNotNull(msg); 232 assertEquals(family, msg.family); 233 assertEquals(ifindex, msg.ifindex); 234 assertEquals(optsLen, msg.opts_len); 235 assertEquals(icmpType, msg.icmp_type); 236 assertEquals(icmpCode, msg.icmp_code); 237 assertEquals(srcaddr, msg.srcaddr); 238 } 239 assertPref64Option(String prefix, NdOption opt)240 private void assertPref64Option(String prefix, NdOption opt) { 241 assertNotNull(opt); 242 assertTrue(opt instanceof StructNdOptPref64); 243 StructNdOptPref64 pref64Opt = (StructNdOptPref64) opt; 244 assertEquals(new IpPrefix(prefix), pref64Opt.prefix); 245 } 246 } 247