| 1 | // Copyright (C) Microsoft Corporation. All rights reserved. |
| 2 | #pragma once |
| 3 | |
| 4 | #include <format> |
| 5 | #include <string> |
| 6 | #include <linux/if_link.h> |
| 7 | |
| 8 | #include "NetlinkMessage.h" |
| 9 | #include "NetlinkParseException.h" |
| 10 | |
| 11 | template <typename TMessage> |
| 12 | NetlinkMessage<TMessage>::NetlinkMessage(const NetlinkResponse& response, Titerator responseBegin, Titerator begin, Titerator end) : |
| 13 | m_response(response), m_responseBegin(responseBegin), m_begin(begin), m_end(end) |
| 14 | { |
| 15 | } |
| 16 | |
| 17 | template <typename TMessage> |
| 18 | const TMessage* NetlinkMessage<TMessage>::Payload() const |
| 19 | { |
| 20 | const auto* data = reinterpret_cast<const char*>(NLMSG_DATA(&*m_begin)); |
| 21 | if (data + sizeof(TMessage) > &*m_end) |
| 22 | { |
| 23 | throw NetlinkParseException( |
| 24 | m_response, |
| 25 | std::format( |
| 26 | "Message at offset {}: attempted to access beyond message offset ({} > {})", |
| 27 | (m_begin - m_responseBegin), |
| 28 | sizeof(TMessage), |
| 29 | (m_end - m_begin))); |
| 30 | } |
| 31 | |
| 32 | return reinterpret_cast<const TMessage*>(NLMSG_DATA(&*m_begin)); |
| 33 | } |
| 34 | |
| 35 | template <> |
| 36 | inline const rtattr* NetlinkMessage<rtmsg>::FirstAttribute() const |
| 37 | { |
| 38 | return RTM_RTA(NLMSG_DATA(&*m_begin)); |
| 39 | } |
| 40 | |
| 41 | template <> |
| 42 | inline const rtattr* NetlinkMessage<ifaddrmsg>::FirstAttribute() const |
| 43 | { |
| 44 | return IFA_RTA(NLMSG_DATA(&*m_begin)); |
| 45 | } |
| 46 | |
| 47 | template <typename TAttribute> |
| 48 | const rtattr* NetlinkMessage<TAttribute>::FirstAttribute() const |
| 49 | { |
| 50 | throw RuntimeErrorWithSourceLocation("Tried listing attributes for a message without attributes"); |
| 51 | } |
| 52 | |
| 53 | template <typename TMessage> |
| 54 | template <typename TAttribute> |
| 55 | std::vector<const TAttribute*> NetlinkMessage<TMessage>::Attributes(int type) const |
| 56 | { |
| 57 | std::vector<const TAttribute*> attributes; |
| 58 | |
| 59 | auto len = NLMSG_PAYLOAD(Header(), sizeof(TMessage)); |
| 60 | for (const rtattr* e = FirstAttribute(); RTA_OK(e, len); e = RTA_NEXT(e, len)) |
| 61 | { |
| 62 | if (e->rta_type == type) |
| 63 | { |
| 64 | const auto* ptr = reinterpret_cast<const TAttribute*>(RTA_DATA(e)); |
| 65 | |
| 66 | if (sizeof(TAttribute) > e->rta_len) |
| 67 | { |
| 68 | throw NetlinkParseException( |
| 69 | m_response, |
| 70 | std::format( |
| 71 | "Attribute at offset {}: attempted to access beyond attribute offset ({} > {})", |
| 72 | (reinterpret_cast<const char*>(e) - &*m_responseBegin), |
| 73 | sizeof(TAttribute), |
| 74 | e->rta_len)); |
| 75 | } |
| 76 | |
| 77 | attributes.push_back(ptr); |
| 78 | } |
| 79 | } |
| 80 | |
| 81 | return attributes; |
| 82 | } |
| 83 | |
| 84 | template <typename TMessage> |
| 85 | template <typename TAttribute> |
| 86 | std::optional<const TAttribute*> NetlinkMessage<TMessage>::UniqueAttribute(int type) const |
| 87 | { |
| 88 | auto attributes = Attributes<TAttribute>(type); |
| 89 | |
| 90 | if (attributes.empty()) |
| 91 | { |
| 92 | return {}; |
| 93 | } |
| 94 | else if (attributes.size() == 1) |
| 95 | { |
| 96 | return attributes[0]; |
| 97 | } |
| 98 | |
| 99 | throw RuntimeErrorWithSourceLocation(std::format("Unexpected attribute count: {} for attribute type: {}", attributes.size(), type)); |
| 100 | } |
| 101 | |
| 102 | template <typename TMessage> |
| 103 | const nlmsghdr* NetlinkMessage<TMessage>::Header() const |
| 104 | { |
| 105 | return reinterpret_cast<const nlmsghdr*>(&*m_begin); |
| 106 | } |