• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 com.android.net.module.util.netlink;
18 
19 import static android.system.OsConstants.AF_INET6;
20 
21 import androidx.annotation.NonNull;
22 import androidx.annotation.Nullable;
23 
24 import java.net.Inet6Address;
25 import java.net.InetAddress;
26 import java.net.UnknownHostException;
27 import java.nio.BufferUnderflowException;
28 import java.nio.ByteBuffer;
29 import java.nio.ByteOrder;
30 
31 /**
32  * A NetlinkMessage subclass for RTM_NEWNDUSEROPT messages.
33  */
34 public class NduseroptMessage extends NetlinkMessage {
35     public static final int STRUCT_SIZE = 16;
36 
37     static final int NDUSEROPT_SRCADDR = 1;
38 
39     /** The address family. Presumably always AF_INET6. */
40     public final byte family;
41     /**
42      * The total length in bytes of the options that follow this structure.
43      * Actually a 16-bit unsigned integer.
44      */
45     public final int opts_len;
46     /** The interface index on which the options were received. */
47     public final int ifindex;
48     /** The ICMP type of the packet that contained the options. */
49     public final byte icmp_type;
50     /** The ICMP code of the packet that contained the options. */
51     public final byte icmp_code;
52 
53     /**
54      * ND option that was in this message.
55      * Even though the length field is called "opts_len", the kernel only ever sends one option per
56      * message. It is unlikely that this will ever change as it would break existing userspace code.
57      * But if it does, we can simply update this code, since userspace is typically newer than the
58      * kernel.
59      */
60     @Nullable
61     public final NdOption option;
62 
63     /** The IP address that sent the packet containing the option. */
64     public final InetAddress srcaddr;
65 
NduseroptMessage(@onNull StructNlMsgHdr header, @NonNull ByteBuffer buf)66     NduseroptMessage(@NonNull StructNlMsgHdr header, @NonNull ByteBuffer buf)
67             throws UnknownHostException {
68         super(header);
69 
70         // The structure itself.
71         buf.order(ByteOrder.nativeOrder());  // Restored in the finally clause inside parse().
72         final int start = buf.position();
73         family = buf.get();
74         buf.get();  // Skip 1 byte of padding.
75         opts_len = Short.toUnsignedInt(buf.getShort());
76         ifindex = buf.getInt();
77         icmp_type = buf.get();
78         icmp_code = buf.get();
79         buf.position(buf.position() + 6);  // Skip 6 bytes of padding.
80 
81         // The ND option.
82         // Ensure we don't read past opts_len even if the option length is invalid.
83         // Note that this check is not really necessary since if the option length is not valid,
84         // this struct won't be very useful to the caller.
85         //
86         // It's safer to pass the slice of original ByteBuffer to just parse the ND option field,
87         // although parsing ND option might throw exception or return null, it won't break the
88         // original ByteBuffer position.
89         buf.order(ByteOrder.BIG_ENDIAN);
90         try {
91             final ByteBuffer slice = buf.slice();
92             slice.limit(opts_len);
93             option = NdOption.parse(slice);
94         } finally {
95             // Advance buffer position according to opts_len in the header. ND option length might
96             // be incorrect in the malformed packet.
97             int newPosition = start + STRUCT_SIZE + opts_len;
98             if (newPosition >= buf.limit()) {
99                 throw new IllegalArgumentException("ND option extends past end of buffer");
100             }
101             buf.position(newPosition);
102         }
103 
104         // The source address attribute.
105         StructNlAttr nla = StructNlAttr.parse(buf);
106         if (nla == null || nla.nla_type != NDUSEROPT_SRCADDR || nla.nla_value == null) {
107             throw new IllegalArgumentException("Invalid source address in ND useropt");
108         }
109         if (family == AF_INET6) {
110             // InetAddress.getByAddress only looks at the ifindex if the address type needs one.
111             srcaddr = Inet6Address.getByAddress(null /* hostname */, nla.nla_value, ifindex);
112         } else {
113             srcaddr = InetAddress.getByAddress(nla.nla_value);
114         }
115     }
116 
117     /**
118      * Parses a StructNduseroptmsg from a {@link ByteBuffer}.
119      *
120      * @param header the netlink message header.
121      * @param buf The buffer from which to parse the option. The buffer's byte order must be
122      *            {@link java.nio.ByteOrder#BIG_ENDIAN}.
123      * @return the parsed option, or {@code null} if the option could not be parsed successfully
124      *         (for example, if it was truncated, or if the prefix length code was wrong).
125      */
126     @Nullable
parse(@onNull StructNlMsgHdr header, @NonNull ByteBuffer buf)127     public static NduseroptMessage parse(@NonNull StructNlMsgHdr header, @NonNull ByteBuffer buf) {
128         if (buf == null || buf.remaining() < STRUCT_SIZE) return null;
129         ByteOrder oldOrder = buf.order();
130         try {
131             return new NduseroptMessage(header, buf);
132         } catch (IllegalArgumentException | UnknownHostException | BufferUnderflowException e) {
133             // Not great, but better than throwing an exception that might crash the caller.
134             // Convention in this package is that null indicates that the option was truncated, so
135             // callers must already handle it.
136             return null;
137         } finally {
138             buf.order(oldOrder);
139         }
140     }
141 
142     @Override
toString()143     public String toString() {
144         return String.format("Nduseroptmsg(%d, %d, %d, %d, %d, %s)",
145                 family, opts_len, ifindex, Byte.toUnsignedInt(icmp_type),
146                 Byte.toUnsignedInt(icmp_code), srcaddr.getHostAddress());
147     }
148 }
149