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