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 com.android.net.module.util.netlink;
18 
19 import androidx.annotation.NonNull;
20 import androidx.annotation.Nullable;
21 
22 import java.nio.ByteBuffer;
23 
24 /**
25  * struct nlmsghdr
26  *
27  * see <linux_src>/include/uapi/linux/netlink.h
28  *
29  * @hide
30  */
31 public class StructNlMsgHdr {
32     // Already aligned.
33     public static final int STRUCT_SIZE = 16;
34 
35     public static final short NLM_F_REQUEST = 0x0001;
36     public static final short NLM_F_MULTI   = 0x0002;
37     public static final short NLM_F_ACK     = 0x0004;
38     public static final short NLM_F_ECHO    = 0x0008;
39     // Flags for a GET request.
40     public static final short NLM_F_ROOT    = 0x0100;
41     public static final short NLM_F_MATCH   = 0x0200;
42     public static final short NLM_F_DUMP    = NLM_F_ROOT | NLM_F_MATCH;
43     // Flags for a NEW request.
44     public static final short NLM_F_REPLACE   = 0x100;
45     public static final short NLM_F_EXCL      = 0x200;
46     public static final short NLM_F_CREATE    = 0x400;
47     public static final short NLM_F_APPEND    = 0x800;
48 
49     // TODO: Probably need to distinguish the flags which have the same value. For example,
50     // NLM_F_MATCH (0x200) and NLM_F_EXCL (0x200).
stringForNlMsgFlags(short flags)51     private static String stringForNlMsgFlags(short flags) {
52         final StringBuilder sb = new StringBuilder();
53         if ((flags & NLM_F_REQUEST) != 0) {
54             sb.append("NLM_F_REQUEST");
55         }
56         if ((flags & NLM_F_MULTI) != 0) {
57             if (sb.length() > 0) {
58                 sb.append("|");
59             }
60             sb.append("NLM_F_MULTI");
61         }
62         if ((flags & NLM_F_ACK) != 0) {
63             if (sb.length() > 0) {
64                 sb.append("|");
65             }
66             sb.append("NLM_F_ACK");
67         }
68         if ((flags & NLM_F_ECHO) != 0) {
69             if (sb.length() > 0) {
70                 sb.append("|");
71             }
72             sb.append("NLM_F_ECHO");
73         }
74         if ((flags & NLM_F_DUMP) == NLM_F_DUMP) {
75             if (sb.length() > 0) {
76                 sb.append("|");
77             }
78             sb.append("NLM_F_DUMP");
79         } else if ((flags & NLM_F_ROOT) != 0) { // NLM_F_DUMP = NLM_F_ROOT | NLM_F_MATCH
80             if (sb.length() > 0) {
81                 sb.append("|");
82             }
83             sb.append("NLM_F_ROOT");
84         } else if ((flags & NLM_F_MATCH) != 0) {
85             if (sb.length() > 0) {
86                 sb.append("|");
87             }
88             sb.append("NLM_F_MATCH");
89         }
90         return sb.toString();
91     }
92 
hasAvailableSpace(ByteBuffer byteBuffer)93     private static boolean hasAvailableSpace(ByteBuffer byteBuffer) {
94         return byteBuffer != null && byteBuffer.remaining() >= STRUCT_SIZE;
95     }
96 
97     /**
98      * Parse netlink message header from buffer.
99      */
100     @Nullable
parse(@onNull ByteBuffer byteBuffer)101     public static StructNlMsgHdr parse(@NonNull ByteBuffer byteBuffer) {
102         if (!hasAvailableSpace(byteBuffer)) return null;
103 
104         // The ByteOrder must have already been set by the caller.  In most
105         // cases ByteOrder.nativeOrder() is correct, with the exception
106         // of usage within unittests.
107         final StructNlMsgHdr struct = new StructNlMsgHdr();
108         struct.nlmsg_len = byteBuffer.getInt();
109         struct.nlmsg_type = byteBuffer.getShort();
110         struct.nlmsg_flags = byteBuffer.getShort();
111         struct.nlmsg_seq = byteBuffer.getInt();
112         struct.nlmsg_pid = byteBuffer.getInt();
113 
114         if (struct.nlmsg_len < STRUCT_SIZE) {
115             // Malformed.
116             return null;
117         }
118         return struct;
119     }
120 
121     public int nlmsg_len;
122     public short nlmsg_type;
123     public short nlmsg_flags;
124     public int nlmsg_seq;
125     public int nlmsg_pid;
126 
StructNlMsgHdr()127     public StructNlMsgHdr() {
128         nlmsg_len = 0;
129         nlmsg_type = 0;
130         nlmsg_flags = 0;
131         nlmsg_seq = 0;
132         nlmsg_pid = 0;
133     }
134 
StructNlMsgHdr(int payloadLen, short type, short flags, int seq)135     public StructNlMsgHdr(int payloadLen, short type, short flags, int seq) {
136         nlmsg_len = StructNlMsgHdr.STRUCT_SIZE + payloadLen;
137         nlmsg_type = type;
138         nlmsg_flags = flags;
139         nlmsg_seq = seq;
140         nlmsg_pid = 0;
141     }
142 
143     /**
144      * Write netlink message header to ByteBuffer.
145      */
pack(ByteBuffer byteBuffer)146     public void pack(ByteBuffer byteBuffer) {
147         // The ByteOrder must have already been set by the caller.  In most
148         // cases ByteOrder.nativeOrder() is correct, with the possible
149         // exception of usage within unittests.
150         byteBuffer.putInt(nlmsg_len);
151         byteBuffer.putShort(nlmsg_type);
152         byteBuffer.putShort(nlmsg_flags);
153         byteBuffer.putInt(nlmsg_seq);
154         byteBuffer.putInt(nlmsg_pid);
155     }
156 
157     @Override
toString()158     public String toString() {
159         return toString(null /* unknown netlink family */);
160     }
161 
162     /**
163      * Transform a netlink header into a string. The netlink family is required for transforming
164      * a netlink type integer into a string.
165      * @param nlFamily netlink family. Using Integer will not incur autoboxing penalties because
166      *                 family values are small, and all Integer objects between -128 and 127 are
167      *                 statically cached. See Integer.IntegerCache.
168      * @return A list of header elements.
169      */
170     @NonNull
toString(@ullable Integer nlFamily)171     public String toString(@Nullable Integer nlFamily) {
172         final String typeStr = "" + nlmsg_type
173                 + "(" + (nlFamily == null
174                 ? "" : NetlinkConstants.stringForNlMsgType(nlmsg_type, nlFamily))
175                 + ")";
176         final String flagsStr = "" + nlmsg_flags
177                 + "(" + stringForNlMsgFlags(nlmsg_flags) + ")";
178         return "StructNlMsgHdr{ "
179                 + "nlmsg_len{" + nlmsg_len + "}, "
180                 + "nlmsg_type{" + typeStr + "}, "
181                 + "nlmsg_flags{" + flagsStr + "}, "
182                 + "nlmsg_seq{" + nlmsg_seq + "}, "
183                 + "nlmsg_pid{" + nlmsg_pid + "} "
184                 + "}";
185     }
186 }
187