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