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_ROOT) != 0) { 75 if (sb.length() > 0) { 76 sb.append("|"); 77 } 78 sb.append("NLM_F_ROOT"); 79 } 80 if ((flags & NLM_F_MATCH) != 0) { 81 if (sb.length() > 0) { 82 sb.append("|"); 83 } 84 sb.append("NLM_F_MATCH"); 85 } 86 return sb.toString(); 87 } 88 hasAvailableSpace(ByteBuffer byteBuffer)89 private static boolean hasAvailableSpace(ByteBuffer byteBuffer) { 90 return byteBuffer != null && byteBuffer.remaining() >= STRUCT_SIZE; 91 } 92 93 /** 94 * Parse netlink message header from buffer. 95 */ 96 @Nullable parse(@onNull ByteBuffer byteBuffer)97 public static StructNlMsgHdr parse(@NonNull ByteBuffer byteBuffer) { 98 if (!hasAvailableSpace(byteBuffer)) return null; 99 100 // The ByteOrder must have already been set by the caller. In most 101 // cases ByteOrder.nativeOrder() is correct, with the exception 102 // of usage within unittests. 103 final StructNlMsgHdr struct = new StructNlMsgHdr(); 104 struct.nlmsg_len = byteBuffer.getInt(); 105 struct.nlmsg_type = byteBuffer.getShort(); 106 struct.nlmsg_flags = byteBuffer.getShort(); 107 struct.nlmsg_seq = byteBuffer.getInt(); 108 struct.nlmsg_pid = byteBuffer.getInt(); 109 110 if (struct.nlmsg_len < STRUCT_SIZE) { 111 // Malformed. 112 return null; 113 } 114 return struct; 115 } 116 117 public int nlmsg_len; 118 public short nlmsg_type; 119 public short nlmsg_flags; 120 public int nlmsg_seq; 121 public int nlmsg_pid; 122 StructNlMsgHdr()123 public StructNlMsgHdr() { 124 nlmsg_len = 0; 125 nlmsg_type = 0; 126 nlmsg_flags = 0; 127 nlmsg_seq = 0; 128 nlmsg_pid = 0; 129 } 130 131 /** 132 * Write netlink message header to ByteBuffer. 133 */ pack(ByteBuffer byteBuffer)134 public void pack(ByteBuffer byteBuffer) { 135 // The ByteOrder must have already been set by the caller. In most 136 // cases ByteOrder.nativeOrder() is correct, with the possible 137 // exception of usage within unittests. 138 byteBuffer.putInt(nlmsg_len); 139 byteBuffer.putShort(nlmsg_type); 140 byteBuffer.putShort(nlmsg_flags); 141 byteBuffer.putInt(nlmsg_seq); 142 byteBuffer.putInt(nlmsg_pid); 143 } 144 145 @Override toString()146 public String toString() { 147 return toString(null /* unknown netlink family */); 148 } 149 150 /** 151 * Transform a netlink header into a string. The netlink family is required for transforming 152 * a netlink type integer into a string. 153 * @param nlFamily netlink family. Using Integer will not incur autoboxing penalties because 154 * family values are small, and all Integer objects between -128 and 127 are 155 * statically cached. See Integer.IntegerCache. 156 * @return A list of header elements. 157 */ 158 @NonNull toString(@ullable Integer nlFamily)159 public String toString(@Nullable Integer nlFamily) { 160 final String typeStr = "" + nlmsg_type 161 + "(" + (nlFamily == null 162 ? "" : NetlinkConstants.stringForNlMsgType(nlmsg_type, nlFamily)) 163 + ")"; 164 final String flagsStr = "" + nlmsg_flags 165 + "(" + stringForNlMsgFlags(nlmsg_flags) + ")"; 166 return "StructNlMsgHdr{ " 167 + "nlmsg_len{" + nlmsg_len + "}, " 168 + "nlmsg_type{" + typeStr + "}, " 169 + "nlmsg_flags{" + flagsStr + "}, " 170 + "nlmsg_seq{" + nlmsg_seq + "}, " 171 + "nlmsg_pid{" + nlmsg_pid + "} " 172 + "}"; 173 } 174 } 175