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