diff --git a/include/pfs/net.hpp b/include/pfs/net.hpp index 723dbd4..6e66238 100644 --- a/include/pfs/net.hpp +++ b/include/pfs/net.hpp @@ -48,6 +48,7 @@ class net final using netlink_socket_filter = std::function; using unix_socket_filter = std::function; using net_route_filter = std::function; + using net_arp_filter = std::function; public: std::vector get_dev(net_device_filter filter = nullptr) const; @@ -69,6 +70,8 @@ class net final std::vector get_route(net_route_filter filter = nullptr) const; + std::vector get_arp(net_arp_filter filter = nullptr) const; + private: friend class task; net(const std::string& parent_root); diff --git a/include/pfs/parsers/net_arp.hpp b/include/pfs/parsers/net_arp.hpp new file mode 100644 index 0000000..5681b90 --- /dev/null +++ b/include/pfs/parsers/net_arp.hpp @@ -0,0 +1,34 @@ +/* + * Copyright 2020-present Daniel Trugman + * + * 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. + */ + +#ifndef PFS_PARSERS_NET_ARP_HPP +#define PFS_PARSERS_NET_ARP_HPP + +#include + +#include "pfs/types.hpp" + +namespace pfs { +namespace impl { +namespace parsers { + +net_arp parse_net_arp_line(const std::string& line); + +} // namespace parsers +} // namespace impl +} // namespace pfs + +#endif // PFS_PARSERS_NET_ARP_HPP diff --git a/include/pfs/types.hpp b/include/pfs/types.hpp index 7fd20c7..cbd5b3a 100644 --- a/include/pfs/types.hpp +++ b/include/pfs/types.hpp @@ -620,6 +620,16 @@ struct net_route unsigned int irtt; }; +struct net_arp +{ + std::string ip_address; + unsigned int type; + unsigned int flags; + std::string hw_address; + std::string mask; + std::string device; +}; + // Hint: See 'https://docs.kernel.org/block/stat.html' struct block_stat { diff --git a/sample/enum_net.cpp b/sample/enum_net.cpp index 77809b2..9111f9f 100644 --- a/sample/enum_net.cpp +++ b/sample/enum_net.cpp @@ -74,6 +74,9 @@ int enum_net(std::vector&& args) auto routes = net.get_route(); print(routes); + + auto arp = net.get_arp(); + print(arp); } catch (const std::runtime_error& ex) { diff --git a/sample/format.hpp b/sample/format.hpp index 6ac6905..e7ed06d 100644 --- a/sample/format.hpp +++ b/sample/format.hpp @@ -728,6 +728,18 @@ inline std::ostream& operator<<(std::ostream& out, const pfs::net_route& route) return out; } +inline std::ostream& operator<<(std::ostream& out, const pfs::net_arp& arp) +{ + out << "ip_address[" << arp.ip_address << "] "; + out << "type[" << arp.type << "] "; + out << "flags[" << arp.flags << "] "; + out << "hw_address[" << arp.hw_address << "] "; + out << "mask[" << arp.mask << "] "; + out << "device[" << arp.device << "] "; + + return out; +} + inline std::ostream& operator<<(std::ostream& out, const pfs::block_stat& stat) { diff --git a/src/net.cpp b/src/net.cpp index 00ebd9d..3007cef 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -16,6 +16,7 @@ #include "pfs/net.hpp" #include "pfs/parsers/net_route.hpp" +#include "pfs/parsers/net_arp.hpp" #include "pfs/parsers/net_device.hpp" #include "pfs/parsers/net_socket.hpp" #include "pfs/parsers/unix_socket.hpp" @@ -166,4 +167,19 @@ std::vector net::get_route(net_route_filter filter) const return output; } +std::vector net::get_arp(net_arp_filter filter) const +{ + static const std::string ARP_FILE("arp"); + auto path = _net_root + ARP_FILE; + + static const size_t HEADER_LINES = 1; + + std::vector output; + parsers::parse_file_lines(path, std::back_inserter(output), + parsers::parse_net_arp_line, + filter, HEADER_LINES); + return output; +} + + } // namespace pfs diff --git a/src/parsers/net_arp.cpp b/src/parsers/net_arp.cpp new file mode 100644 index 0000000..2435c93 --- /dev/null +++ b/src/parsers/net_arp.cpp @@ -0,0 +1,79 @@ +/* + * Copyright 2020-present Daniel Trugman + * + * 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 "pfs/parsers/net_arp.hpp" +#include "pfs/parser_error.hpp" +#include "pfs/utils.hpp" +#include "pfs/types.hpp" + +namespace pfs { +namespace impl { +namespace parsers { + +net_arp parse_net_arp_line(const std::string& line) +{ + // Some examples: + // clang-format off + // IP address HW type Flags HW address Mask Device + // 192.168.10.1 0x1 0x2 10:20:30:40:50:60 * eth0 + // clang-format on + + enum token + { + IP_ADDRESS = 0, + TYPE = 1, + FLAGS = 2, + HW_ADDRESS = 3, + MASK = 4, + DEVICE = 5, + COUNT + }; + + net_arp arp; + + static const char DELIM = ' '; + + auto tokens = utils::split(line, DELIM); + if (tokens.size() != COUNT) + { + throw parser_error("Corrupted net arp - Unexpected tokens count", line); + } + + try + { + // Parse tokens into arp struct + arp.ip_address = tokens[IP_ADDRESS]; + utils::stot(tokens[TYPE], arp.type, utils::base::hex); + utils::stot(tokens[FLAGS], arp.flags, utils::base::hex); + arp.hw_address = tokens[HW_ADDRESS]; + arp.mask = tokens[MASK]; + arp.device = tokens[DEVICE]; + } + catch (const std::invalid_argument& ex) + { + throw parser_error("Corrupted net arp - Invalid argument", line); + } + catch (const std::out_of_range& ex) + { + throw parser_error("Corrupted net arp - Out of range", line); + } + + return arp; +} + +} // namespace parsers +} // namespace impl +} // namespace pfs diff --git a/src/parsers/net_socket.cpp b/src/parsers/net_socket.cpp index 1581a45..76e5326 100644 --- a/src/parsers/net_socket.cpp +++ b/src/parsers/net_socket.cpp @@ -26,8 +26,6 @@ namespace parsers { namespace { -static const size_t HEX_BYTE_LEN = 8; - net_socket::net_state parse_state(const std::string& state_str) { int state_int; diff --git a/test/test_net_arp.cpp b/test/test_net_arp.cpp new file mode 100644 index 0000000..cc8f218 --- /dev/null +++ b/test/test_net_arp.cpp @@ -0,0 +1,47 @@ +#include + +#include "catch.hpp" + +#include "pfs/parsers/net_arp.hpp" +#include "pfs/parser_error.hpp" +#include "pfs/types.hpp" + +using namespace pfs::impl::parsers; + +TEST_CASE("Parse corrupted net arp", "[net][arp]") +{ + SECTION("Missing token") + { + std::string line = "192.168.10.1 0x1 0x2 10:20:30:40:50:60 *"; + REQUIRE_THROWS_AS(parse_net_arp_line(line), pfs::parser_error); + } + + SECTION("Extra token") + { + std::string line = "192.168.10.1 0x1 0x2 10:20:30:40:50:60 * eth0 0x4"; + REQUIRE_THROWS_AS(parse_net_arp_line(line), pfs::parser_error); + } +} + +TEST_CASE("Parse net arp", "[net][arp]") +{ + pfs::net_arp expected; + + std::string line = "192.168.10.1 0x1 0x2 10:20:30:40:50:60 * eth0"; + + expected.ip_address = "192.168.10.1"; + expected.type = 0x1; + expected.flags = 0x2; + expected.hw_address = "10:20:30:40:50:60"; + expected.mask = "*"; + expected.device = "eth0"; + + auto arp = parse_net_arp_line(line); + + REQUIRE(arp.ip_address == expected.ip_address); + REQUIRE(arp.type == expected.type); + REQUIRE(arp.flags == expected.flags); + REQUIRE(arp.hw_address == expected.hw_address); + REQUIRE(arp.mask == expected.mask); + REQUIRE(arp.device == expected.device); +}