summaryrefslogtreecommitdiff
path: root/common/device/com/android/net/module/util/netlink/RtNetlinkRouteMessage.java
blob: 9acac69cc29fbe01d07533c27e030aa77b7a8e8d (plain)
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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
/*
 * Copyright (C) 2021 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 com.android.net.module.util.netlink;

import static android.system.OsConstants.AF_INET;
import static android.system.OsConstants.AF_INET6;

import static com.android.net.module.util.NetworkStackConstants.IPV4_ADDR_ANY;
import static com.android.net.module.util.NetworkStackConstants.IPV6_ADDR_ANY;

import android.annotation.SuppressLint;
import android.net.IpPrefix;
import android.system.OsConstants;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;

import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.nio.ByteBuffer;

/**
 * A NetlinkMessage subclass for rtnetlink route messages.
 *
 * RtNetlinkRouteMessage.parse() must be called with a ByteBuffer that contains exactly one
 * netlink message.
 *
 * see also:
 *
 *     include/uapi/linux/rtnetlink.h
 *
 * @hide
 */
public class RtNetlinkRouteMessage extends NetlinkMessage {
    public static final short RTA_DST           = 1;
    public static final short RTA_OIF           = 4;
    public static final short RTA_GATEWAY       = 5;
    public static final short RTA_CACHEINFO     = 12;

    private int mIfindex;
    @NonNull
    private StructRtMsg mRtmsg;
    @NonNull
    private IpPrefix mDestination;
    @Nullable
    private InetAddress mGateway;
    @Nullable
    private StructRtaCacheInfo mRtaCacheInfo;

    private RtNetlinkRouteMessage(StructNlMsgHdr header) {
        super(header);
        mRtmsg = null;
        mDestination = null;
        mGateway = null;
        mIfindex = 0;
        mRtaCacheInfo = null;
    }

    public int getInterfaceIndex() {
        return mIfindex;
    }

    @NonNull
    public StructRtMsg getRtMsgHeader() {
        return mRtmsg;
    }

    @NonNull
    public IpPrefix getDestination() {
        return mDestination;
    }

    @Nullable
    public InetAddress getGateway() {
        return mGateway;
    }

    @Nullable
    public StructRtaCacheInfo getRtaCacheInfo() {
        return mRtaCacheInfo;
    }

    /**
     * Check whether the address families of destination and gateway match rtm_family in
     * StructRtmsg.
     *
     * For example, IPv4-mapped IPv6 addresses as an IPv6 address will be always converted to IPv4
     * address, that's incorrect when upper layer creates a new {@link RouteInfo} class instance
     * for IPv6 route with the converted IPv4 gateway.
     */
    private static boolean matchRouteAddressFamily(@NonNull final InetAddress address,
            int family) {
        return ((address instanceof Inet4Address) && (family == AF_INET))
                || ((address instanceof Inet6Address) && (family == AF_INET6));
    }

    /**
     * Parse rtnetlink route message from {@link ByteBuffer}. This method must be called with a
     * ByteBuffer that contains exactly one netlink message.
     *
     * @param header netlink message header.
     * @param byteBuffer the ByteBuffer instance that wraps the raw netlink message bytes.
     */
    @SuppressLint("NewApi")
    @Nullable
    public static RtNetlinkRouteMessage parse(@NonNull final StructNlMsgHdr header,
            @NonNull final ByteBuffer byteBuffer) {
        final RtNetlinkRouteMessage routeMsg = new RtNetlinkRouteMessage(header);

        routeMsg.mRtmsg = StructRtMsg.parse(byteBuffer);
        if (routeMsg.mRtmsg == null) return null;
        int rtmFamily = routeMsg.mRtmsg.family;

        // RTA_DST
        final int baseOffset = byteBuffer.position();
        StructNlAttr nlAttr = StructNlAttr.findNextAttrOfType(RTA_DST, byteBuffer);
        if (nlAttr != null) {
            final InetAddress destination = nlAttr.getValueAsInetAddress();
            // If the RTA_DST attribute is malformed, return null.
            if (destination == null) return null;
            // If the address family of destination doesn't match rtm_family, return null.
            if (!matchRouteAddressFamily(destination, rtmFamily)) return null;
            routeMsg.mDestination = new IpPrefix(destination, routeMsg.mRtmsg.dstLen);
        } else if (rtmFamily == AF_INET) {
            routeMsg.mDestination = new IpPrefix(IPV4_ADDR_ANY, 0);
        } else if (rtmFamily == AF_INET6) {
            routeMsg.mDestination = new IpPrefix(IPV6_ADDR_ANY, 0);
        } else {
            return null;
        }

        // RTA_GATEWAY
        byteBuffer.position(baseOffset);
        nlAttr = StructNlAttr.findNextAttrOfType(RTA_GATEWAY, byteBuffer);
        if (nlAttr != null) {
            routeMsg.mGateway = nlAttr.getValueAsInetAddress();
            // If the RTA_GATEWAY attribute is malformed, return null.
            if (routeMsg.mGateway == null) return null;
            // If the address family of gateway doesn't match rtm_family, return null.
            if (!matchRouteAddressFamily(routeMsg.mGateway, rtmFamily)) return null;
        }

        // RTA_OIF
        byteBuffer.position(baseOffset);
        nlAttr = StructNlAttr.findNextAttrOfType(RTA_OIF, byteBuffer);
        if (nlAttr != null) {
            // Any callers that deal with interface names are responsible for converting
            // the interface index to a name themselves. This may not succeed or may be
            // incorrect, because the interface might have been deleted, or even deleted
            // and re-added with a different index, since the netlink message was sent.
            routeMsg.mIfindex = nlAttr.getValueAsInt(0 /* 0 isn't a valid ifindex */);
        }

        // RTA_CACHEINFO
        byteBuffer.position(baseOffset);
        nlAttr = StructNlAttr.findNextAttrOfType(RTA_CACHEINFO, byteBuffer);
        if (nlAttr != null) {
            routeMsg.mRtaCacheInfo = StructRtaCacheInfo.parse(nlAttr.getValueAsByteBuffer());
        }

        return routeMsg;
    }

    /**
     * Write a rtnetlink address message to {@link ByteBuffer}.
     */
    @VisibleForTesting
    protected void pack(ByteBuffer byteBuffer) {
        getHeader().pack(byteBuffer);
        mRtmsg.pack(byteBuffer);

        final StructNlAttr destination = new StructNlAttr(RTA_DST, mDestination.getAddress());
        destination.pack(byteBuffer);

        if (mGateway != null) {
            final StructNlAttr gateway = new StructNlAttr(RTA_GATEWAY, mGateway.getAddress());
            gateway.pack(byteBuffer);
        }
        if (mIfindex != 0) {
            final StructNlAttr ifindex = new StructNlAttr(RTA_OIF, mIfindex);
            ifindex.pack(byteBuffer);
        }
        if (mRtaCacheInfo != null) {
            final StructNlAttr cacheInfo = new StructNlAttr(RTA_CACHEINFO,
                    mRtaCacheInfo.writeToBytes());
            cacheInfo.pack(byteBuffer);
        }
    }

    @Override
    public String toString() {
        return "RtNetlinkRouteMessage{ "
                + "nlmsghdr{" + mHeader.toString(OsConstants.NETLINK_ROUTE) + "}, "
                + "Rtmsg{" + mRtmsg.toString() + "}, "
                + "destination{" + mDestination.getAddress().getHostAddress() + "}, "
                + "gateway{" + (mGateway == null ? "" : mGateway.getHostAddress()) + "}, "
                + "ifindex{" + mIfindex + "}, "
                + "rta_cacheinfo{" + (mRtaCacheInfo == null ? "" : mRtaCacheInfo.toString()) + "} "
                + "}";
    }
}