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