mirror of
https://github.com/SerenityOS/serenity
synced 2026-04-25 17:15:42 +02:00
Kernel+ifconfig: Allow setting an IPv6 address on an interface
Since we take a socket address with an address type, this allows us to support setting an IPv6 address using an IPv4 socket without a particularly hacky API. This deviates from Linux's behavior (see https://www.man7.org/linux/man-pages/man7/netdevice.7.html ) where AF_INET6 uses a completely different control structure, but this doesn't seem necessary. This requires changing the sockaddr size to fit sockaddr_in6, as the network ioctl's are the only place where sockaddr is used with a fixed size (and not with variable size data like in POSIX APIs, which would support sockaddr_in6 without changes). ifconfig takes a new parameter for setting the IPv6 address of an interface. The IPv4 address short option '-i' is removed, as it only yields confusion (which IP version is the default? and usually such options are called -4 or -6, if they exist) and isn't necessary thanks to the brief long option name. This commit's main purpose for now is to allow participating in IPv6 NDP and pings without requiring SLAC. Co-authored-by: Dominique Liberda <ja@sdomi.pl>
This commit is contained in:
committed by
Nico Weber
parent
81eae154b7
commit
ac44ec5ebc
@@ -109,7 +109,8 @@ static inline void* CMSG_DATA(struct cmsghdr* cmsg)
|
||||
|
||||
struct sockaddr {
|
||||
sa_family_t sa_family;
|
||||
char sa_data[14];
|
||||
// For network interface ioctl(), this needs to fit all sockaddr_* structures (excluding Unix domain sockets).
|
||||
char sa_data[26];
|
||||
};
|
||||
|
||||
struct ucred {
|
||||
|
||||
@@ -755,17 +755,22 @@ ErrorOr<void> IPv4Socket::ioctl(OpenFileDescription&, unsigned request, Userspac
|
||||
case SIOCSIFADDR:
|
||||
if (!current_process_credentials->is_superuser())
|
||||
return EPERM;
|
||||
if (ifr.ifr_addr.sa_family != AF_INET)
|
||||
return EAFNOSUPPORT;
|
||||
adapter->set_ipv4_address(IPv4Address(((sockaddr_in&)ifr.ifr_addr).sin_addr.s_addr));
|
||||
if (ifr.ifr_addr.sa_family == AF_INET) {
|
||||
adapter->set_ipv4_address(IPv4Address(bit_cast<sockaddr_in*>(&ifr.ifr_addr)->sin_addr.s_addr));
|
||||
return {};
|
||||
} else if (ifr.ifr_addr.sa_family == AF_INET6) {
|
||||
adapter->set_ipv6_address(IPv6Address(bit_cast<sockaddr_in6*>(&ifr.ifr_addr)->sin6_addr.s6_addr));
|
||||
return {};
|
||||
} else {
|
||||
return EAFNOSUPPORT;
|
||||
}
|
||||
|
||||
case SIOCSIFNETMASK:
|
||||
if (!current_process_credentials->is_superuser())
|
||||
return EPERM;
|
||||
if (ifr.ifr_addr.sa_family != AF_INET)
|
||||
return EAFNOSUPPORT;
|
||||
adapter->set_ipv4_netmask(IPv4Address(((sockaddr_in&)ifr.ifr_netmask).sin_addr.s_addr));
|
||||
adapter->set_ipv4_netmask(IPv4Address(bit_cast<sockaddr_in*>(&ifr.ifr_netmask)->sin_addr.s_addr));
|
||||
return {};
|
||||
|
||||
case SIOCGIFADDR: {
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include <AK/Assertions.h>
|
||||
#include <AK/IPv4Address.h>
|
||||
#include <AK/IPv6Address.h>
|
||||
#include <AK/JsonArray.h>
|
||||
#include <AK/JsonObject.h>
|
||||
#include <AK/NumberFormat.h>
|
||||
@@ -20,12 +21,14 @@
|
||||
ErrorOr<int> serenity_main(Main::Arguments arguments)
|
||||
{
|
||||
StringView value_ipv4 {};
|
||||
StringView value_ipv6 {};
|
||||
StringView value_adapter {};
|
||||
StringView value_mask {};
|
||||
|
||||
Core::ArgsParser args_parser;
|
||||
args_parser.set_general_help("Display or modify the configuration of each network interface.");
|
||||
args_parser.add_option(value_ipv4, "Set the IP address of the selected network", "ipv4", 'i', "ip");
|
||||
args_parser.add_option(value_ipv4, "Set the IPv4 address of the selected network", "ipv4", 0, "ipv4");
|
||||
args_parser.add_option(value_ipv6, "Set the IPv6 address of the selected network", "ipv6", 0, "ipv6");
|
||||
args_parser.add_option(value_adapter, "Select a specific network adapter to configure", "adapter", 'a', "adapter");
|
||||
args_parser.add_option(value_mask, "Set the network mask of the selected network", "mask", 'm', "mask");
|
||||
args_parser.parse(arguments);
|
||||
@@ -92,6 +95,31 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
|
||||
TRY(Core::System::ioctl(fd, SIOCSIFADDR, &ifr));
|
||||
}
|
||||
|
||||
if (!value_ipv6.is_empty()) {
|
||||
auto address = IPv6Address::from_string(value_ipv6);
|
||||
|
||||
if (!address.has_value()) {
|
||||
warnln("Invalid IPv6 address: '{}'", value_ipv6);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// FIXME: should be an AF_INET6 socket (once we support it), but the Kernel doesn't care either way.
|
||||
auto fd = TRY(Core::System::socket(AF_INET, SOCK_DGRAM, IPPROTO_IP));
|
||||
|
||||
struct ifreq ifr;
|
||||
memset(&ifr, 0, sizeof(ifr));
|
||||
|
||||
bool fits = ifname.copy_characters_to_buffer(ifr.ifr_name, IFNAMSIZ);
|
||||
if (!fits) {
|
||||
warnln("Interface name '{}' is too long", ifname);
|
||||
return 1;
|
||||
}
|
||||
ifr.ifr_addr.sa_family = AF_INET6;
|
||||
AK::TypedTransfer<u8>::copy(((sockaddr_in6&)ifr.ifr_addr).sin6_addr.s6_addr, address.value().to_in6_addr_t(), sizeof(in6_addr));
|
||||
|
||||
TRY(Core::System::ioctl(fd, SIOCSIFADDR, &ifr));
|
||||
}
|
||||
|
||||
if (!value_mask.is_empty()) {
|
||||
auto address = IPv4Address::from_string(value_mask);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user