1 /*
2  * Copyright (C) 2019 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 android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.system.OsConstants;
22 
23 import java.nio.ByteBuffer;
24 
25 
26 /**
27  * NetlinkMessage base class for other, more specific netlink message types.
28  *
29  * Classes that extend NetlinkMessage should:
30  *     - implement a public static parse(StructNlMsgHdr, ByteBuffer) method
31  *     - returning either null (parse errors) or a new object of the subclass
32  *       type (cast-able to NetlinkMessage)
33  *
34  * NetlinkMessage.parse() should be updated to know which nlmsg_type values
35  * correspond with which message subclasses.
36  *
37  * @hide
38  */
39 public class NetlinkMessage {
40     private final static String TAG = "NetlinkMessage";
41 
42     /**
43      * Parsing netlink messages for reserved control message or specific netlink message. The
44      * netlink family is required for parsing specific netlink message. See man-pages/netlink.
45      */
46     @Nullable
parse(@onNull ByteBuffer byteBuffer, int nlFamily)47     public static NetlinkMessage parse(@NonNull ByteBuffer byteBuffer, int nlFamily) {
48         final int startPosition = (byteBuffer != null) ? byteBuffer.position() : -1;
49         final StructNlMsgHdr nlmsghdr = StructNlMsgHdr.parse(byteBuffer);
50         if (nlmsghdr == null) {
51             return null;
52         }
53 
54         int payloadLength = NetlinkConstants.alignedLengthOf(nlmsghdr.nlmsg_len);
55         payloadLength -= StructNlMsgHdr.STRUCT_SIZE;
56         if (payloadLength < 0 || payloadLength > byteBuffer.remaining()) {
57             // Malformed message or runt buffer.  Pretend the buffer was consumed.
58             byteBuffer.position(byteBuffer.limit());
59             return null;
60         }
61 
62         // Reserved control messages. The netlink family is ignored.
63         // See NLMSG_MIN_TYPE in include/uapi/linux/netlink.h.
64         if (nlmsghdr.nlmsg_type <= NetlinkConstants.NLMSG_MAX_RESERVED) {
65             return parseCtlMessage(nlmsghdr, byteBuffer, payloadLength);
66         }
67 
68         // Netlink family messages. The netlink family is required. Note that the reason for using
69         // if-statement is that switch-case can't be used because the OsConstants.NETLINK_* are
70         // not constant.
71         if (nlFamily == OsConstants.NETLINK_ROUTE) {
72             return parseRtMessage(nlmsghdr, byteBuffer);
73         } else if (nlFamily == OsConstants.NETLINK_INET_DIAG) {
74             return parseInetDiagMessage(nlmsghdr, byteBuffer);
75         } else if (nlFamily == OsConstants.NETLINK_NETFILTER) {
76             return parseNfMessage(nlmsghdr, byteBuffer);
77         }
78 
79         return null;
80     }
81 
82     protected StructNlMsgHdr mHeader;
83 
NetlinkMessage(StructNlMsgHdr nlmsghdr)84     public NetlinkMessage(StructNlMsgHdr nlmsghdr) {
85         mHeader = nlmsghdr;
86     }
87 
getHeader()88     public StructNlMsgHdr getHeader() {
89         return mHeader;
90     }
91 
92     @Override
toString()93     public String toString() {
94         // The netlink family is not provided to StructNlMsgHdr#toString because NetlinkMessage
95         // doesn't store the information. So the netlink message type can't be transformed into
96         // a string by StructNlMsgHdr#toString and just keep as an integer. The specific message
97         // which inherits NetlinkMessage could override NetlinkMessage#toString and provide the
98         // specific netlink family to StructNlMsgHdr#toString.
99         return "NetlinkMessage{" + (mHeader == null ? "" : mHeader.toString()) + "}";
100     }
101 
102     @NonNull
parseCtlMessage(@onNull StructNlMsgHdr nlmsghdr, @NonNull ByteBuffer byteBuffer, int payloadLength)103     private static NetlinkMessage parseCtlMessage(@NonNull StructNlMsgHdr nlmsghdr,
104             @NonNull ByteBuffer byteBuffer, int payloadLength) {
105         switch (nlmsghdr.nlmsg_type) {
106             case NetlinkConstants.NLMSG_ERROR:
107                 return (NetlinkMessage) NetlinkErrorMessage.parse(nlmsghdr, byteBuffer);
108             default: {
109                 // Other netlink control messages. Just parse the header for now,
110                 // pretending the whole message was consumed.
111                 byteBuffer.position(byteBuffer.position() + payloadLength);
112                 return new NetlinkMessage(nlmsghdr);
113             }
114         }
115     }
116 
117     @Nullable
parseRtMessage(@onNull StructNlMsgHdr nlmsghdr, @NonNull ByteBuffer byteBuffer)118     private static NetlinkMessage parseRtMessage(@NonNull StructNlMsgHdr nlmsghdr,
119             @NonNull ByteBuffer byteBuffer) {
120         switch (nlmsghdr.nlmsg_type) {
121             case NetlinkConstants.RTM_NEWNEIGH:
122             case NetlinkConstants.RTM_DELNEIGH:
123             case NetlinkConstants.RTM_GETNEIGH:
124                 return (NetlinkMessage) RtNetlinkNeighborMessage.parse(nlmsghdr, byteBuffer);
125             case NetlinkConstants.RTM_NEWNDUSEROPT:
126                 return (NetlinkMessage) NduseroptMessage.parse(nlmsghdr, byteBuffer);
127             default: return null;
128         }
129     }
130 
131     @Nullable
parseInetDiagMessage(@onNull StructNlMsgHdr nlmsghdr, @NonNull ByteBuffer byteBuffer)132     private static NetlinkMessage parseInetDiagMessage(@NonNull StructNlMsgHdr nlmsghdr,
133             @NonNull ByteBuffer byteBuffer) {
134         switch (nlmsghdr.nlmsg_type) {
135             case NetlinkConstants.SOCK_DIAG_BY_FAMILY:
136                 return (NetlinkMessage) InetDiagMessage.parse(nlmsghdr, byteBuffer);
137             default: return null;
138         }
139     }
140 
141     @Nullable
parseNfMessage(@onNull StructNlMsgHdr nlmsghdr, @NonNull ByteBuffer byteBuffer)142     private static NetlinkMessage parseNfMessage(@NonNull StructNlMsgHdr nlmsghdr,
143             @NonNull ByteBuffer byteBuffer) {
144         switch (nlmsghdr.nlmsg_type) {
145             case NetlinkConstants.NFNL_SUBSYS_CTNETLINK << 8
146                     | NetlinkConstants.IPCTNL_MSG_CT_NEW:
147             case NetlinkConstants.NFNL_SUBSYS_CTNETLINK << 8
148                     | NetlinkConstants.IPCTNL_MSG_CT_DELETE:
149                 return (NetlinkMessage) ConntrackMessage.parse(nlmsghdr, byteBuffer);
150             default: return null;
151         }
152     }
153 }
154