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