xref: /aosp_15_r20/hardware/interfaces/automotive/can/1.0/default/libnl++/printer.cpp (revision 4d7e907c777eeecc4c5bd7cf640a754fac206ff7)
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