Serenity Operating System
at master 220 lines 8.2 kB view raw
1/* 2 * Copyright (c) 2022, Brandon Pruitt <brapru@pm.me> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#include <AK/DeprecatedString.h> 8#include <AK/IPv4Address.h> 9#include <AK/JsonArray.h> 10#include <AK/JsonObject.h> 11#include <AK/QuickSort.h> 12#include <AK/StringView.h> 13#include <LibCore/ArgsParser.h> 14#include <LibCore/File.h> 15#include <LibCore/ProcessStatisticsReader.h> 16#include <LibCore/System.h> 17#include <LibMain/Main.h> 18#include <net/if.h> 19#include <net/route.h> 20#include <netinet/in.h> 21#include <sys/ioctl.h> 22#include <sys/socket.h> 23#include <unistd.h> 24 25ErrorOr<int> serenity_main(Main::Arguments arguments) 26{ 27 TRY(Core::System::pledge("stdio rpath inet")); 28 TRY(Core::System::unveil("/sys/kernel/net", "r")); 29 TRY(Core::System::unveil(nullptr, nullptr)); 30 31 StringView modify_action; 32 StringView value_host_address; 33 StringView value_network_address; 34 StringView value_gateway_address; 35 StringView value_netmask_address; 36 StringView value_interface; 37 38 Core::ArgsParser args_parser; 39 args_parser.set_general_help("Display kernel routing table"); 40 args_parser.add_positional_argument(modify_action, "Modify the global routing table { add | del }", "action", Core::ArgsParser::Required::No); 41 args_parser.add_option(value_host_address, "Target destination is an IPv4 address", "host", 'h', "host"); 42 args_parser.add_option(value_network_address, "Target destination is a network address", "net", 'n', "net"); 43 args_parser.add_option(value_gateway_address, "Route packets via a gateway", "gw", 'g', "gw"); 44 args_parser.add_option(value_netmask_address, "The netmask to be used when adding a network route", "netmask", 'm', "netmask"); 45 args_parser.add_option(value_interface, "Force the route to be associated with the specified device interface", "interface", 'i', "interface"); 46 args_parser.parse(arguments); 47 48 enum class Alignment { 49 Left, 50 Right 51 }; 52 53 struct Column { 54 DeprecatedString title; 55 Alignment alignment { Alignment::Left }; 56 int width { 0 }; 57 DeprecatedString buffer; 58 }; 59 60 Vector<Column> columns; 61 62 int destination_column = -1; 63 int gateway_column = -1; 64 int genmask_column = -1; 65 int flags_column = -1; 66 int interface_column = -1; 67 68 auto add_column = [&](auto title, auto alignment, auto width) { 69 columns.append({ title, alignment, width, {} }); 70 return columns.size() - 1; 71 }; 72 73 destination_column = add_column("Destination", Alignment::Left, 15); 74 gateway_column = add_column("Gateway", Alignment::Left, 15); 75 genmask_column = add_column("Genmask", Alignment::Left, 15); 76 flags_column = add_column("Flags", Alignment::Left, 5); 77 interface_column = add_column("Interface", Alignment::Left, 9); 78 79 auto print_column = [](auto& column, auto& string) { 80 if (!column.width) { 81 out("{}", string); 82 return; 83 } 84 if (column.alignment == Alignment::Right) { 85 out("{:>{1}} "sv, string, column.width); 86 } else { 87 out("{:<{1}} "sv, string, column.width); 88 } 89 }; 90 91 if (modify_action.is_empty()) { 92 auto file = TRY(Core::File::open("/sys/kernel/net/route"sv, Core::File::OpenMode::Read)); 93 auto file_contents = TRY(file->read_until_eof()); 94 auto json = TRY(JsonValue::from_string(file_contents)); 95 96 outln("Kernel IP routing table"); 97 98 for (auto& column : columns) 99 print_column(column, column.title); 100 outln(); 101 102 Vector<JsonValue> sorted_regions = json.as_array().values(); 103 quick_sort(sorted_regions, [](auto& a, auto& b) { 104 return a.as_object().get_deprecated_string("destination"sv).value_or({}) < b.as_object().get_deprecated_string("destination"sv).value_or({}); 105 }); 106 107 for (auto& value : sorted_regions) { 108 auto& if_object = value.as_object(); 109 110 auto destination = if_object.get_deprecated_string("destination"sv).value_or({}); 111 auto gateway = if_object.get_deprecated_string("gateway"sv).value_or({}); 112 auto genmask = if_object.get_deprecated_string("genmask"sv).value_or({}); 113 auto interface = if_object.get_deprecated_string("interface"sv).value_or({}); 114 auto flags = if_object.get_u32("flags"sv).value_or(0); 115 116 StringBuilder flags_builder; 117 if (flags & RTF_UP) 118 flags_builder.append('U'); 119 if (flags & RTF_GATEWAY) 120 flags_builder.append('G'); 121 if (flags & RTF_HOST) 122 flags_builder.append('H'); 123 124 if (destination_column != -1) 125 columns[destination_column].buffer = destination; 126 if (gateway_column != -1) 127 columns[gateway_column].buffer = gateway; 128 if (genmask_column != -1) 129 columns[genmask_column].buffer = genmask; 130 if (flags_column != -1) 131 columns[flags_column].buffer = flags_builder.string_view(); 132 if (interface_column != -1) 133 columns[interface_column].buffer = interface; 134 135 for (auto& column : columns) 136 print_column(column, column.buffer); 137 outln(); 138 }; 139 } 140 141 if (!modify_action.is_empty()) { 142 bool const action_add = (modify_action == "add"); 143 bool const action_del = (modify_action == "del"); 144 145 if (!action_add && !action_del) { 146 warnln("Invalid modify action: {}", modify_action); 147 return 1; 148 } 149 150 if (value_host_address.is_empty() && value_network_address.is_empty()) { 151 warnln("No target host or network specified"); 152 return 1; 153 } 154 155 Optional<IPv4Address> destination; 156 157 if (!value_host_address.is_empty()) 158 destination = AK::IPv4Address::from_string(value_host_address); 159 160 StringView address; 161 StringView cidr; 162 if (!value_network_address.is_empty()) { 163 // Check if a CIDR notation was provided and parse accordingly 164 if (auto position = value_network_address.find('/'); position.has_value()) { 165 address = value_network_address.substring_view(0, position.value()); 166 cidr = value_network_address.substring_view(position.value() + 1); 167 } else { 168 address = value_network_address; 169 } 170 destination = AK::IPv4Address::from_string(address); 171 } 172 173 if (!destination.has_value()) { 174 warnln("Invalid destination IPv4 address"); 175 return 1; 176 } 177 178 auto gateway = AK::IPv4Address::from_string(value_gateway_address); 179 if (action_add && !gateway.has_value()) { 180 warnln("Invalid gateway IPv4 address: '{}'", value_gateway_address); 181 return 1; 182 } 183 184 Optional<IPv4Address> genmask; 185 if (auto cidr_int = cidr.to_int(); cidr_int.has_value()) 186 genmask = AK::IPv4Address::netmask_from_cidr(cidr_int.value()); 187 else 188 genmask = AK::IPv4Address::from_string(value_netmask_address); 189 190 if (!genmask.has_value()) { 191 warnln("Invalid genmask IPv4 address: '{}'", value_netmask_address); 192 return 1; 193 } 194 195 int fd = TRY(Core::System::socket(AF_INET, SOCK_DGRAM, IPPROTO_IP)); 196 197 rtentry rt {}; 198 memset(&rt, 0, sizeof(rt)); 199 200 rt.rt_dev = const_cast<char*>(value_interface.characters_without_null_termination()); 201 rt.rt_gateway.sa_family = AF_INET; 202 ((sockaddr_in&)rt.rt_dst).sin_addr.s_addr = destination.value().to_in_addr_t(); 203 ((sockaddr_in&)rt.rt_gateway).sin_addr.s_addr = gateway.value_or(IPv4Address {}).to_in_addr_t(); 204 ((sockaddr_in&)rt.rt_genmask).sin_addr.s_addr = genmask.value().to_in_addr_t(); 205 rt.rt_flags = RTF_UP; 206 207 if (!value_host_address.is_empty()) 208 rt.rt_flags |= RTF_HOST; 209 210 rt.rt_flags |= RTF_GATEWAY; 211 212 if (action_add) 213 TRY(Core::System::ioctl(fd, SIOCADDRT, &rt)); 214 215 if (action_del) 216 TRY(Core::System::ioctl(fd, SIOCDELRT, &rt)); 217 } 218 219 return 0; 220}