Serenity Operating System
1/*
2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
3 * Copyright (c) 2022, Alex Major
4 *
5 * SPDX-License-Identifier: BSD-2-Clause
6 */
7
8#include <AK/Assertions.h>
9#include <AK/IPv4Address.h>
10#include <AK/JsonArray.h>
11#include <AK/JsonObject.h>
12#include <AK/NumberFormat.h>
13#include <LibCore/ArgsParser.h>
14#include <LibCore/File.h>
15#include <LibCore/System.h>
16#include <LibMain/Main.h>
17#include <net/if.h>
18#include <netinet/in.h>
19
20ErrorOr<int> serenity_main(Main::Arguments arguments)
21{
22 StringView value_ipv4 {};
23 StringView value_adapter {};
24 StringView value_mask {};
25
26 Core::ArgsParser args_parser;
27 args_parser.set_general_help("Display or modify the configuration of each network interface.");
28 args_parser.add_option(value_ipv4, "Set the IP address of the selected network", "ipv4", 'i', "ip");
29 args_parser.add_option(value_adapter, "Select a specific network adapter to configure", "adapter", 'a', "adapter");
30 args_parser.add_option(value_mask, "Set the network mask of the selected network", "mask", 'm', "mask");
31 args_parser.parse(arguments);
32
33 if (value_ipv4.is_empty() && value_adapter.is_empty() && value_mask.is_empty()) {
34 auto file = TRY(Core::File::open("/sys/kernel/net/adapters"sv, Core::File::OpenMode::Read));
35 auto file_contents = TRY(file->read_until_eof());
36 auto json = TRY(JsonValue::from_string(file_contents));
37
38 json.as_array().for_each([](auto& value) {
39 auto& if_object = value.as_object();
40
41 auto name = if_object.get_deprecated_string("name"sv).value_or({});
42 auto class_name = if_object.get_deprecated_string("class_name"sv).value_or({});
43 auto mac_address = if_object.get_deprecated_string("mac_address"sv).value_or({});
44 auto ipv4_address = if_object.get_deprecated_string("ipv4_address"sv).value_or({});
45 auto netmask = if_object.get_deprecated_string("ipv4_netmask"sv).value_or({});
46 auto packets_in = if_object.get_u32("packets_in"sv).value_or(0);
47 auto bytes_in = if_object.get_u32("bytes_in"sv).value_or(0);
48 auto packets_out = if_object.get_u32("packets_out"sv).value_or(0);
49 auto bytes_out = if_object.get_u32("bytes_out"sv).value_or(0);
50 auto mtu = if_object.get_u32("mtu"sv).value_or(0);
51
52 outln("{}:", name);
53 outln("\tmac: {}", mac_address);
54 outln("\tipv4: {}", ipv4_address);
55 outln("\tnetmask: {}", netmask);
56 outln("\tclass: {}", class_name);
57 outln("\tRX: {} packets {} bytes ({})", packets_in, bytes_in, human_readable_size(bytes_in));
58 outln("\tTX: {} packets {} bytes ({})", packets_out, bytes_out, human_readable_size(bytes_out));
59 outln("\tMTU: {}", mtu);
60 outln();
61 });
62 } else {
63
64 if (value_adapter.is_empty()) {
65 warnln("No network adapter was specified.");
66 return 1;
67 }
68
69 DeprecatedString ifname = value_adapter;
70
71 if (!value_ipv4.is_empty()) {
72 auto address = IPv4Address::from_string(value_ipv4);
73
74 if (!address.has_value()) {
75 warnln("Invalid IPv4 address: '{}'", value_ipv4);
76 return 1;
77 }
78
79 auto fd = TRY(Core::System::socket(AF_INET, SOCK_DGRAM, IPPROTO_IP));
80
81 struct ifreq ifr;
82 memset(&ifr, 0, sizeof(ifr));
83
84 bool fits = ifname.copy_characters_to_buffer(ifr.ifr_name, IFNAMSIZ);
85 if (!fits) {
86 warnln("Interface name '{}' is too long", ifname);
87 return 1;
88 }
89 ifr.ifr_addr.sa_family = AF_INET;
90 ((sockaddr_in&)ifr.ifr_addr).sin_addr.s_addr = address.value().to_in_addr_t();
91
92 TRY(Core::System::ioctl(fd, SIOCSIFADDR, &ifr));
93 }
94
95 if (!value_mask.is_empty()) {
96 auto address = IPv4Address::from_string(value_mask);
97
98 if (!address.has_value()) {
99 warnln("Invalid IPv4 mask: '{}'", value_mask);
100 return 1;
101 }
102
103 auto fd = TRY(Core::System::socket(AF_INET, SOCK_DGRAM, IPPROTO_IP));
104
105 struct ifreq ifr;
106 memset(&ifr, 0, sizeof(ifr));
107
108 bool fits = ifname.copy_characters_to_buffer(ifr.ifr_name, IFNAMSIZ);
109 if (!fits) {
110 warnln("Interface name '{}' is too long", ifname);
111 return 1;
112 }
113 ifr.ifr_netmask.sa_family = AF_INET;
114 ((sockaddr_in&)ifr.ifr_netmask).sin_addr.s_addr = address.value().to_in_addr_t();
115
116 TRY(Core::System::ioctl(fd, SIOCSIFNETMASK, &ifr));
117 }
118 }
119 return 0;
120}