1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
|
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.net.netlink;
import static android.system.OsConstants.AF_INET6;
import androidx.annotation.NonNull;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
/**
* A NetlinkMessage subclass for RTM_NEWNDUSEROPT messages.
*/
public class NduseroptMessage extends NetlinkMessage {
public static final int STRUCT_SIZE = 16;
static final int NDUSEROPT_SRCADDR = 1;
/** The address family. Presumably always AF_INET6. */
public final byte family;
/**
* The total length in bytes of the options that follow this structure.
* Actually a 16-bit unsigned integer.
*/
public final int opts_len;
/** The interface index on which the options were received. */
public final int ifindex;
/** The ICMP type of the packet that contained the options. */
public final byte icmp_type;
/** The ICMP code of the packet that contained the options. */
public final byte icmp_code;
/**
* ND option that was in this message.
* Even though the length field is called "opts_len", the kernel only ever sends one option per
* message. It is unlikely that this will ever change as it would break existing userspace code.
* But if it does, we can simply update this code, since userspace is typically newer than the
* kernel.
*/
public final NdOption option;
/** The IP address that sent the packet containing the option. */
public final InetAddress srcaddr;
NduseroptMessage(@NonNull StructNlMsgHdr header, @NonNull ByteBuffer buf)
throws UnknownHostException {
super(header);
// The structure itself.
buf.order(ByteOrder.nativeOrder()); // Restored in the finally clause inside parse().
final int start = buf.position();
family = buf.get();
buf.get(); // Skip 1 byte of padding.
opts_len = Short.toUnsignedInt(buf.getShort());
ifindex = buf.getInt();
icmp_type = buf.get();
icmp_code = buf.get();
buf.position(buf.position() + 6); // Skip 6 bytes of padding.
// The ND option.
// Ensure we don't read past opts_len even if the option length is invalid.
// Note that this check is not really necessary since if the option length is not valid,
// this struct won't be very useful to the caller.
buf.order(ByteOrder.BIG_ENDIAN);
int oldLimit = buf.limit();
buf.limit(start + STRUCT_SIZE + opts_len);
try {
option = NdOption.parse(buf);
} finally {
buf.limit(oldLimit);
}
// The source address.
int newPosition = start + STRUCT_SIZE + opts_len;
if (newPosition >= buf.limit()) {
throw new IllegalArgumentException("ND options extend past end of buffer");
}
buf.position(newPosition);
StructNlAttr nla = StructNlAttr.parse(buf);
if (nla == null || nla.nla_type != NDUSEROPT_SRCADDR || nla.nla_value == null) {
throw new IllegalArgumentException("Invalid source address in ND useropt");
}
if (family == AF_INET6) {
// InetAddress.getByAddress only looks at the ifindex if the address type needs one.
srcaddr = Inet6Address.getByAddress(null /* hostname */, nla.nla_value, ifindex);
} else {
srcaddr = InetAddress.getByAddress(nla.nla_value);
}
}
/**
* Parses a StructNduseroptmsg from a {@link ByteBuffer}.
*
* @param header the netlink message header.
* @param buf The buffer from which to parse the option. The buffer's byte order must be
* {@link java.nio.ByteOrder#BIG_ENDIAN}.
* @return the parsed option, or {@code null} if the option could not be parsed successfully
* (for example, if it was truncated, or if the prefix length code was wrong).
*/
public static NduseroptMessage parse(@NonNull StructNlMsgHdr header, @NonNull ByteBuffer buf) {
if (buf == null || buf.remaining() < STRUCT_SIZE) return null;
ByteOrder oldOrder = buf.order();
try {
return new NduseroptMessage(header, buf);
} catch (IllegalArgumentException | UnknownHostException | BufferUnderflowException e) {
// Not great, but better than throwing an exception that might crash the caller.
// Convention in this package is that null indicates that the option was truncated, so
// callers must already handle it.
return null;
} finally {
buf.order(oldOrder);
}
}
@Override
public String toString() {
return String.format("Nduseroptmsg(%d, %d, %d, %d, %d, %s)",
family, opts_len, ifindex, Byte.toUnsignedInt(icmp_type),
Byte.toUnsignedInt(icmp_code), srcaddr.getHostAddress());
}
}
|