1 /*
2 * Copyright (C) 2020 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 #include <libnl++/printer.h>
18
19 #include "common.h"
20 #include "protocols/all.h"
21
22 #include <android-base/logging.h>
23 #include <libnl++/Buffer.h>
24
25 #include <algorithm>
26 #include <iomanip>
27 #include <sstream>
28
29 // should be in linux/netlink.h
30 #define NLM_F_DUMP_FILTERED 0x20
31 #define NLM_F_NONREC 0x100
32 #define NLM_F_CAPPED 0x100
33 #define NLM_F_ACK_TLVS 0x200
34
35 namespace android::nl {
36
flagsToStream(std::stringstream & ss,__u16 nlmsg_flags,protocols::MessageGenre genre)37 static void flagsToStream(std::stringstream& ss, __u16 nlmsg_flags, protocols::MessageGenre genre) {
38 bool first = true;
39 auto printFlag = [&ss, &first, &nlmsg_flags](__u16 flag, const std::string& name) {
40 if ((nlmsg_flags & flag) != flag) return;
41 nlmsg_flags &= ~flag;
42
43 if (first) {
44 first = false;
45 } else {
46 ss << '|';
47 }
48
49 ss << name;
50 };
51
52 printFlag(NLM_F_REQUEST, "REQUEST");
53 printFlag(NLM_F_MULTI, "MULTI");
54 printFlag(NLM_F_ACK, "ACK");
55 printFlag(NLM_F_ECHO, "ECHO");
56 printFlag(NLM_F_DUMP_INTR, "DUMP_INTR");
57 printFlag(NLM_F_DUMP_FILTERED, "DUMP_FILTERED");
58
59 switch (genre) {
60 case protocols::MessageGenre::Unknown:
61 break;
62 case protocols::MessageGenre::Get:
63 printFlag(NLM_F_DUMP, "DUMP"); // ROOT | MATCH
64 printFlag(NLM_F_ROOT, "ROOT");
65 printFlag(NLM_F_MATCH, "MATCH");
66 printFlag(NLM_F_ATOMIC, "ATOMIC");
67 break;
68 case protocols::MessageGenre::New:
69 printFlag(NLM_F_REPLACE, "REPLACE");
70 printFlag(NLM_F_EXCL, "EXCL");
71 printFlag(NLM_F_CREATE, "CREATE");
72 printFlag(NLM_F_APPEND, "APPEND");
73 break;
74 case protocols::MessageGenre::Delete:
75 printFlag(NLM_F_NONREC, "NONREC");
76 break;
77 case protocols::MessageGenre::Ack:
78 printFlag(NLM_F_CAPPED, "CAPPED");
79 printFlag(NLM_F_ACK_TLVS, "ACK_TLVS");
80 break;
81 }
82
83 if (nlmsg_flags != 0) {
84 if (!first) ss << '|';
85 ss << std::hex << nlmsg_flags << std::dec;
86 }
87 }
88
toStream(std::stringstream & ss,const Buffer<uint8_t> data)89 static void toStream(std::stringstream& ss, const Buffer<uint8_t> data) {
90 const auto rawData = data.getRaw();
91 const auto dataLen = rawData.len();
92 ss << std::hex;
93 int i = 0;
94 for (const auto byte : rawData) {
95 if (i % 16 == 0 && dataLen > 16) {
96 ss << std::endl << ' ' << std::dec << std::setw(4) << i << std::hex;
97 }
98 if (i++ > 0 || dataLen > 16) ss << ' ';
99 ss << std::setw(2) << unsigned(byte);
100 }
101 ss << std::dec;
102 if (dataLen > 16) ss << std::endl;
103 }
104
toStream(std::stringstream & ss,const Buffer<nlattr> attr,const protocols::AttributeMap & attrMap)105 static void toStream(std::stringstream& ss, const Buffer<nlattr> attr,
106 const protocols::AttributeMap& attrMap) {
107 using DataType = protocols::AttributeDefinition::DataType;
108 using Flags = protocols::AttributeDefinition::Flags;
109 const auto attrtype = attrMap[attr->nla_type];
110
111 ss << attrtype.name;
112
113 if (attrtype.dataType == DataType::Flag && attr.data<uint8_t>().getRaw().len() == 0) return;
114 ss << ": ";
115
116 if (attrtype.flags == Flags::Verbose) {
117 const auto raw = attr.data<uint8_t>();
118 ss << "{len=" << raw.getRaw().len();
119 ss << ", crc=" << std::hex << std::setw(4) << crc16(raw) << std::dec;
120 ss << "}";
121 return;
122 }
123
124 switch (attrtype.dataType) {
125 case DataType::Raw:
126 case DataType::Flag:
127 toStream(ss, attr.data<uint8_t>());
128 break;
129 case DataType::Nested: {
130 ss << '{';
131 bool first = true;
132 for (const auto childattr : attr.data<nlattr>()) {
133 if (!first) ss << ", ";
134 first = false;
135 toStream(ss, childattr, std::get<protocols::AttributeMap>(attrtype.ops));
136 }
137 ss << '}';
138 break;
139 }
140 case DataType::StringNul:
141 case DataType::String: {
142 const auto str = attr.data<char>().getRaw();
143 auto len = str.len();
144 if (attrtype.dataType == DataType::StringNul && len > 0 && str.ptr()[len - 1] == '\0') {
145 len--;
146 }
147
148 ss << '"' << printableOnly({str.ptr(), len}) << '"';
149 break;
150 }
151 case DataType::Uint:
152 ss << attr.data<uint64_t>().copyFirst();
153 break;
154 case DataType::Struct: {
155 const auto structToStream =
156 std::get<protocols::AttributeDefinition::ToStream>(attrtype.ops);
157 structToStream(ss, attr);
158 break;
159 }
160 }
161 }
162
toStream(std::stringstream & ss,const Buffer<nlmsghdr> hdr,int protocol,bool printPayload)163 static void toStream(std::stringstream& ss, const Buffer<nlmsghdr> hdr, int protocol,
164 bool printPayload) {
165 if (!hdr.firstOk()) {
166 ss << "nlmsg{buffer overflow}";
167 return;
168 }
169
170 ss << std::setfill('0');
171
172 auto protocolMaybe = protocols::get(protocol);
173 if (!protocolMaybe.has_value()) {
174 ss << "nlmsg{protocol=" << protocol << "}";
175 return;
176 }
177 protocols::NetlinkProtocol& protocolDescr = *protocolMaybe;
178
179 auto msgDescMaybe = protocolDescr.getMessageDescriptor(hdr->nlmsg_type);
180 const auto msgDetails =
181 protocols::MessageDescriptor::getMessageDetails(msgDescMaybe, hdr->nlmsg_type);
182
183 if (msgDescMaybe.has_value()) msgDescMaybe->get().track(hdr);
184
185 ss << "nlmsg{" << protocolDescr.getName() << " ";
186
187 ss << "hdr={";
188 ss << "type=" << msgDetails.name;
189 if (hdr->nlmsg_flags != 0) {
190 ss << ", flags=";
191 flagsToStream(ss, hdr->nlmsg_flags, msgDetails.genre);
192 }
193 if (hdr->nlmsg_seq != 0) ss << ", seq=" << hdr->nlmsg_seq;
194 if (hdr->nlmsg_pid != 0) ss << ", pid=" << hdr->nlmsg_pid;
195 ss << ", len=" << hdr->nlmsg_len;
196 ss << ", crc=" << std::hex << std::setw(4) << crc16(hdr.data<uint8_t>()) << std::dec;
197 ss << '}';
198
199 if (!printPayload) return;
200 ss << ' ';
201
202 if (!msgDescMaybe.has_value()) {
203 toStream(ss, hdr.data<uint8_t>());
204 } else {
205 const protocols::MessageDescriptor& msgDesc = *msgDescMaybe;
206 msgDesc.dataToStream(ss, hdr);
207
208 bool first = true;
209 for (auto attr : hdr.data<nlattr>(msgDesc.getContentsSize())) {
210 if (first) {
211 ss << " attributes: {";
212 first = false;
213 } else {
214 ss << ", ";
215 }
216 toStream(ss, attr, msgDesc.getAttributeMap());
217 }
218 if (!first) ss << '}';
219 }
220
221 ss << "}";
222 }
223
toString(const Buffer<nlmsghdr> hdrs,int protocol,bool printPayload)224 std::string toString(const Buffer<nlmsghdr> hdrs, int protocol, bool printPayload) {
225 std::stringstream ss;
226 bool first = true;
227 for (const auto hdr : hdrs) {
228 if (!first) ss << std::endl;
229 first = false;
230
231 toStream(ss, hdr, protocol, printPayload);
232 }
233
234 return ss.str();
235 }
236
237 } // namespace android::nl
238