/* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "common.h" #include "protocols/all.h" #include #include #include #include #include namespace android::nl { static void flagsToStream(std::stringstream& ss, __u16 nlmsg_flags, protocols::MessageGenre genre) { bool first = true; auto printFlag = [&ss, &first, &nlmsg_flags](__u16 flag, const std::string& name) { if ((nlmsg_flags & flag) != flag) return; nlmsg_flags &= ~flag; if (first) { first = false; } else { ss << '|'; } ss << name; }; printFlag(NLM_F_REQUEST, "REQUEST"); printFlag(NLM_F_MULTI, "MULTI"); printFlag(NLM_F_ACK, "ACK"); printFlag(NLM_F_ECHO, "ECHO"); printFlag(NLM_F_DUMP_INTR, "DUMP_INTR"); printFlag(NLM_F_DUMP_FILTERED, "DUMP_FILTERED"); switch (genre) { case protocols::MessageGenre::Unknown: break; case protocols::MessageGenre::Get: printFlag(NLM_F_DUMP, "DUMP"); // ROOT | MATCH printFlag(NLM_F_ROOT, "ROOT"); printFlag(NLM_F_MATCH, "MATCH"); printFlag(NLM_F_ATOMIC, "ATOMIC"); break; case protocols::MessageGenre::New: printFlag(NLM_F_REPLACE, "REPLACE"); printFlag(NLM_F_EXCL, "EXCL"); printFlag(NLM_F_CREATE, "CREATE"); printFlag(NLM_F_APPEND, "APPEND"); break; case protocols::MessageGenre::Delete: printFlag(NLM_F_NONREC, "NONREC"); break; case protocols::MessageGenre::Ack: printFlag(NLM_F_CAPPED, "CAPPED"); printFlag(NLM_F_ACK_TLVS, "ACK_TLVS"); break; } if (nlmsg_flags != 0) { if (!first) ss << '|'; ss << std::hex << nlmsg_flags << std::dec; } } static void toStream(std::stringstream& ss, const Buffer data) { const auto rawData = data.getRaw(); const auto dataLen = rawData.len(); ss << std::hex; int i = 0; for (const auto byte : rawData) { if (i % 16 == 0 && dataLen > 16) { ss << std::endl << ' ' << std::dec << std::setw(4) << i << std::hex; } if (i++ > 0 || dataLen > 16) ss << ' '; ss << std::setw(2) << unsigned(byte); } ss << std::dec; if (dataLen > 16) ss << std::endl; } static void toStream(std::stringstream& ss, const Buffer attr, const protocols::AttributeMap& attrMap) { using DataType = protocols::AttributeDefinition::DataType; using Flags = protocols::AttributeDefinition::Flags; const auto attrtype = attrMap[attr->nla_type]; ss << attrtype.name; if (attrtype.dataType == DataType::Flag && attr.data().getRaw().len() == 0) return; ss << ": "; if (attrtype.flags == Flags::Verbose) { const auto raw = attr.data(); ss << "{len=" << raw.getRaw().len(); ss << ", crc=" << std::hex << std::setw(4) << crc16(raw) << std::dec; ss << "}"; return; } switch (attrtype.dataType) { case DataType::Raw: case DataType::Flag: toStream(ss, attr.data()); break; case DataType::Nested: { ss << '{'; bool first = true; for (const auto childattr : attr.data()) { if (!first) ss << ", "; first = false; toStream(ss, childattr, std::get(attrtype.ops)); } ss << '}'; break; } case DataType::StringNul: case DataType::String: { const auto str = attr.data().getRaw(); auto len = str.len(); if (attrtype.dataType == DataType::StringNul && len > 0 && str.ptr()[len - 1] == '\0') { len--; } ss << '"' << printableOnly({str.ptr(), len}) << '"'; break; } case DataType::Uint: ss << attr.data().copyFirst(); break; case DataType::Struct: { const auto structToStream = std::get(attrtype.ops); structToStream(ss, attr); break; } } } std::string toString(const Buffer hdr, int protocol, bool printPayload) { if (!hdr.firstOk()) return "nlmsg{buffer overflow}"; std::stringstream ss; ss << std::setfill('0'); auto protocolMaybe = protocols::get(protocol); if (!protocolMaybe.has_value()) { ss << "nlmsg{protocol=" << protocol << "}"; return ss.str(); } protocols::NetlinkProtocol& protocolDescr = *protocolMaybe; auto msgDescMaybe = protocolDescr.getMessageDescriptor(hdr->nlmsg_type); const auto msgDetails = protocols::MessageDescriptor::getMessageDetails(msgDescMaybe, hdr->nlmsg_type); if (msgDescMaybe.has_value()) msgDescMaybe->get().track(hdr); ss << "nlmsg{" << protocolDescr.getName() << " "; ss << "hdr={"; ss << "type=" << msgDetails.name; if (hdr->nlmsg_flags != 0) { ss << ", flags="; flagsToStream(ss, hdr->nlmsg_flags, msgDetails.genre); } if (hdr->nlmsg_seq != 0) ss << ", seq=" << hdr->nlmsg_seq; if (hdr->nlmsg_pid != 0) ss << ", pid=" << hdr->nlmsg_pid; ss << ", len=" << hdr->nlmsg_len; ss << ", crc=" << std::hex << std::setw(4) << crc16(hdr.data()) << std::dec; ss << '}'; if (!printPayload) return ss.str(); ss << ' '; if (!msgDescMaybe.has_value()) { toStream(ss, hdr.data()); } else { const protocols::MessageDescriptor& msgDesc = *msgDescMaybe; msgDesc.dataToStream(ss, hdr); bool first = true; for (auto attr : hdr.data(msgDesc.getContentsSize())) { if (first) { ss << " attributes: {"; first = false; } else { ss << ", "; } toStream(ss, attr, msgDesc.getAttributeMap()); } if (!first) ss << '}'; } ss << "}"; return ss.str(); } } // namespace android::nl