diff --git a/automotive/can/1.0/default/libnl++/Android.bp b/automotive/can/1.0/default/libnl++/Android.bp index 90e1002508..a69e302658 100644 --- a/automotive/can/1.0/default/libnl++/Android.bp +++ b/automotive/can/1.0/default/libnl++/Android.bp @@ -22,6 +22,7 @@ cc_library_static { "protocols/common/Empty.cpp", "protocols/common/Error.cpp", "protocols/generic/Ctrl.cpp", + "protocols/generic/FamilyTracker.cpp", "protocols/generic/Generic.cpp", "protocols/generic/GenericMessageBase.cpp", "protocols/generic/Unknown.cpp", @@ -34,6 +35,7 @@ cc_library_static { "protocols/all.cpp", "Attributes.cpp", "MessageFactory.cpp", + "MessageMutator.cpp", "Socket.cpp", "common.cpp", "printer.cpp", diff --git a/automotive/can/1.0/default/libnl++/MessageMutator.cpp b/automotive/can/1.0/default/libnl++/MessageMutator.cpp new file mode 100644 index 0000000000..00b48a66ae --- /dev/null +++ b/automotive/can/1.0/default/libnl++/MessageMutator.cpp @@ -0,0 +1,50 @@ +/* + * 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 + +namespace android::nl { + +MessageMutator::MessageMutator(nlmsghdr* buffer, size_t totalLen) + : mConstBuffer(buffer, totalLen), mMutableBuffer(buffer) { + CHECK(totalLen >= sizeof(nlmsghdr)); +} + +nlmsghdr* MessageMutator::operator->() const { + return mMutableBuffer; +} + +MessageMutator::operator Buffer() const { + return mConstBuffer; +} + +uint64_t MessageMutator::read(Buffer attr) const { + return attr.data().copyFirst(); +} + +void MessageMutator::write(Buffer attr, uint64_t val) const { + const auto attrData = attr.data(); + const auto offset = mConstBuffer.getOffset(attrData); + CHECK(offset.has_value()) << "Trying to write attribute that's not a member of this message"; + + const auto writeableBuffer = reinterpret_cast(mMutableBuffer) + *offset; + const auto attrSize = attrData.getRaw().len(); + + if (attrSize > sizeof(val)) memset(writeableBuffer, 0, attrSize); + memcpy(writeableBuffer, &val, std::min(sizeof(val), attrSize)); +} + +} // namespace android::nl diff --git a/automotive/can/1.0/default/libnl++/include/libnl++/Attributes.h b/automotive/can/1.0/default/libnl++/include/libnl++/Attributes.h index f16d997899..a438a69932 100644 --- a/automotive/can/1.0/default/libnl++/include/libnl++/Attributes.h +++ b/automotive/can/1.0/default/libnl++/include/libnl++/Attributes.h @@ -93,14 +93,29 @@ class Attributes : private Buffer { */ template T get(nlattrtype_t attrtype) const { - const auto& ind = index(); - const auto it = ind.find(attrtype); - if (it == ind.end()) { + const auto buffer = getBuffer(attrtype); + if (!buffer.has_value()) { LOG(WARNING) << "Netlink attribute is missing: " << attrtype; return T{}; } - return parse(it->second); + return parse(*buffer); + } + + /** + * Fetches underlying buffer of a given attribute. + * + * This is a low-level access method unlikely to be useful in most cases. Please consider + * using #get instead. + * + * \param attrtype Attribute to fetch + * \return Attribute buffer. + */ + std::optional> getBuffer(nlattrtype_t attrtype) const { + const auto& ind = index(); + const auto it = ind.find(attrtype); + if (it == ind.end()) return std::nullopt; + return it->second; } /** diff --git a/automotive/can/1.0/default/libnl++/include/libnl++/Buffer.h b/automotive/can/1.0/default/libnl++/include/libnl++/Buffer.h index bf83fbcc20..d759a0a570 100644 --- a/automotive/can/1.0/default/libnl++/include/libnl++/Buffer.h +++ b/automotive/can/1.0/default/libnl++/include/libnl++/Buffer.h @@ -91,6 +91,18 @@ class Buffer { return {impl::data(mData, offset), dataEnd()}; } + template + std::optional getOffset(Buffer inner) const { + const auto selfStart = uintptr_t(mData); + const auto selfEnd = uintptr_t(mBufferEnd); + const auto innerStart = uintptr_t(inner.mData); + const auto innerEnd = uintptr_t(inner.mBufferEnd); + + if (innerStart < selfStart || innerEnd > selfEnd) return std::nullopt; + + return innerStart - selfStart; + } + class iterator { public: iterator() : mCurrent(nullptr, size_t(0)) { diff --git a/automotive/can/1.0/default/libnl++/include/libnl++/MessageMutator.h b/automotive/can/1.0/default/libnl++/include/libnl++/MessageMutator.h new file mode 100644 index 0000000000..7d495e9a5b --- /dev/null +++ b/automotive/can/1.0/default/libnl++/include/libnl++/MessageMutator.h @@ -0,0 +1,61 @@ +/* + * 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. + */ + +#pragma once + +#include + +namespace android::nl { + +/** + * In-place message mutator. + * + * Useful for making small changes (such as adjusting const-sized attributes or struct fields) + * efficiently and in-place. However, if you need to rebuild the message (e.g. to modify variable + * sized attributes or add/remove them), you need to use MessageFactory instead. + */ +class MessageMutator { + public: + /** + * Construct message mutator object from editable buffer. + */ + MessageMutator(nlmsghdr* buffer, size_t totalLen); + + nlmsghdr* operator->() const; + operator Buffer() const; + + /** + * Read current attribute value. + * + * \param Read-only attribute buffer. + * \returns Attribute value. + */ + uint64_t read(Buffer attr) const; + + /** + * Write new attribute value. + * + * \param Read-only attribute buffer. + * \param val New value to set. + */ + void write(Buffer attr, uint64_t val) const; + + private: + const Buffer mConstBuffer; + nlmsghdr* mMutableBuffer; +}; + +} // namespace android::nl diff --git a/automotive/can/1.0/default/libnl++/include/libnl++/generic/FamilyTracker.h b/automotive/can/1.0/default/libnl++/include/libnl++/generic/FamilyTracker.h new file mode 100644 index 0000000000..253003527f --- /dev/null +++ b/automotive/can/1.0/default/libnl++/include/libnl++/generic/FamilyTracker.h @@ -0,0 +1,62 @@ +/* + * 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. + */ + +#pragma once + +#include +#include + +#include + +#include + +namespace android::nl::generic { + +/** + * Tracker of Netlink family ID registrations. + */ +class FamilyTracker { + public: + /** + * Try parsing NL80211 message. + * + * Proper parsing of NL80211 nessages requires prior parsing of control message for Generic + * Netlink protocol. + * + * \param msg Message to parse + * \returns Parsed NL80211 message or std::nullopt if it wasn't one + */ + std::optional> parseNl80211(Buffer msg); + + private: + /* For efficiency, we use a single hardcoded family ID. However, if we supported multiple family + * types, this should probably be a map. + */ + std::optional mNl80211FamilyId; + + /** + * Track Generic protocol messages. + * + * This method is looking for family registration messages. + * + * \param msg Message to track + * \returns True, if the message was a control message (regardless of carrying + * family info or not) + */ + bool track(const Buffer& msg); +}; + +} // namespace android::nl::generic diff --git a/automotive/can/1.0/default/libnl++/protocols/generic/FamilyTracker.cpp b/automotive/can/1.0/default/libnl++/protocols/generic/FamilyTracker.cpp new file mode 100644 index 0000000000..900560e5b9 --- /dev/null +++ b/automotive/can/1.0/default/libnl++/protocols/generic/FamilyTracker.cpp @@ -0,0 +1,50 @@ +/* + * 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 + +namespace android::nl::generic { + +bool FamilyTracker::track(const Buffer& buffer) { + const auto msgMaybe = nl::Message::parse(buffer, {GENL_ID_CTRL}); + if (!msgMaybe.has_value()) return false; + + const auto msg = *msgMaybe; + if (msg->cmd != CTRL_CMD_NEWFAMILY) return true; + + const auto familyName = msg.attributes.get(CTRL_ATTR_FAMILY_NAME); + const auto familyId = msg.attributes.get(CTRL_ATTR_FAMILY_ID); + + if (familyId < GENL_START_ALLOC) { + LOG(WARNING) << "Invalid family ID: " << familyId; + return true; + } + + if (familyName == "nl80211") mNl80211FamilyId = familyId; + + return true; +} + +std::optional> FamilyTracker::parseNl80211(Buffer msg) { + if (track(msg)) return std::nullopt; + if (!mNl80211FamilyId.has_value()) return std::nullopt; + + return nl::Message::parse(msg, {*mNl80211FamilyId}); +} + +} // namespace android::nl::generic