diff --git a/Kernel/Net/IPv4Socket.cpp b/Kernel/Net/IPv4Socket.cpp index 33064ac88cb..99b522b1ed5 100644 --- a/Kernel/Net/IPv4Socket.cpp +++ b/Kernel/Net/IPv4Socket.cpp @@ -465,50 +465,100 @@ KResult IPv4Socket::getsockopt(FileDescription& description, int level, int opti int IPv4Socket::ioctl(FileDescription&, unsigned request, unsigned arg) { REQUIRE_PROMISE(inet); - auto* ifr = (ifreq*)arg; - if (!Process::current->validate_read_typed(ifr)) - return -EFAULT; - char namebuf[IFNAMSIZ + 1]; - memcpy(namebuf, ifr->ifr_name, IFNAMSIZ); - namebuf[sizeof(namebuf) - 1] = '\0'; - auto adapter = NetworkAdapter::lookup_by_name(namebuf); - if (!adapter) - return -ENODEV; + auto ioctl_route = [request, arg]() { + auto* route = (rtentry*)arg; + if (!Process::current->validate_read_typed(route)) + return -EFAULT; + + char namebuf[IFNAMSIZ + 1]; + memcpy(namebuf, route->rt_dev, IFNAMSIZ); + namebuf[sizeof(namebuf) - 1] = '\0'; + + auto adapter = NetworkAdapter::lookup_by_name(namebuf); + if (!adapter) + return -ENODEV; + + switch (request) { + case SIOCADDRT: + if (!Process::current->is_superuser()) + return -EPERM; + if (route->rt_gateway.sa_family != AF_INET) + return -EAFNOSUPPORT; + if ((route->rt_flags & (RTF_UP | RTF_GATEWAY)) != (RTF_UP | RTF_GATEWAY)) + return -EINVAL; // FIXME: Find the correct value to return + adapter->set_ipv4_gateway(IPv4Address(((sockaddr_in&)route->rt_gateway).sin_addr.s_addr)); + return 0; + + case SIOCDELRT: + // FIXME: Support gateway deletion + return 0; + } + + return -EINVAL; + }; + + auto ioctl_interface = [request, arg]() { + auto* ifr = (ifreq*)arg; + if (!Process::current->validate_read_typed(ifr)) + return -EFAULT; + + char namebuf[IFNAMSIZ + 1]; + memcpy(namebuf, ifr->ifr_name, IFNAMSIZ); + namebuf[sizeof(namebuf) - 1] = '\0'; + + auto adapter = NetworkAdapter::lookup_by_name(namebuf); + if (!adapter) + return -ENODEV; + + switch (request) { + case SIOCSIFADDR: + if (!Process::current->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)); + return 0; + + case SIOCSIFNETMASK: + if (!Process::current->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)); + return 0; + + case SIOCGIFADDR: + if (!Process::current->validate_write_typed(ifr)) + return -EFAULT; + ifr->ifr_addr.sa_family = AF_INET; + ((sockaddr_in&)ifr->ifr_addr).sin_addr.s_addr = adapter->ipv4_address().to_u32(); + return 0; + + case SIOCGIFHWADDR: + if (!Process::current->validate_write_typed(ifr)) + return -EFAULT; + ifr->ifr_hwaddr.sa_family = AF_INET; + { + auto mac_address = adapter->mac_address(); + memcpy(ifr->ifr_hwaddr.sa_data, &mac_address, sizeof(MACAddress)); + } + return 0; + } + + return -EINVAL; + }; switch (request) { case SIOCSIFADDR: - if (!Process::current->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)); - return 0; - case SIOCSIFNETMASK: - if (!Process::current->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)); - return 0; - case SIOCGIFADDR: - if (!Process::current->validate_write_typed(ifr)) - return -EFAULT; - ifr->ifr_addr.sa_family = AF_INET; - ((sockaddr_in&)ifr->ifr_addr).sin_addr.s_addr = adapter->ipv4_address().to_u32(); - return 0; - case SIOCGIFHWADDR: - if (!Process::current->validate_write_typed(ifr)) - return -EFAULT; - ifr->ifr_hwaddr.sa_family = AF_INET; - { - auto mac_address = adapter->mac_address(); - memcpy(ifr->ifr_hwaddr.sa_data, &mac_address, sizeof(MACAddress)); - } - return 0; + return ioctl_interface(); + + case SIOCADDRT: + case SIOCDELRT: + return ioctl_route(); } return -EINVAL; diff --git a/Kernel/UnixTypes.h b/Kernel/UnixTypes.h index 282679fbf94..66c07c6496e 100644 --- a/Kernel/UnixTypes.h +++ b/Kernel/UnixTypes.h @@ -528,6 +528,17 @@ struct ifreq { #define ifr_hwaddr ifr_ifru.ifru_hwaddr // MAC address }; +struct rtentry { + struct sockaddr rt_gateway; /* the gateway address */ + struct sockaddr rt_genmask; /* the target network mask */ + unsigned short int rt_flags; + char* rt_dev; + /* FIXME: complete the struct */ +}; + +#define RTF_UP 0x1 /* do not delete the route */ +#define RTF_GATEWAY 0x2 /* the route is a gateway and not an end host */ + #define AT_FDCWD -100 #define PURGE_ALL_VOLATILE 0x1 diff --git a/Libraries/LibC/net/route.h b/Libraries/LibC/net/route.h index 1434243aab4..200d97ecb67 100644 --- a/Libraries/LibC/net/route.h +++ b/Libraries/LibC/net/route.h @@ -28,6 +28,12 @@ #include struct rtentry { - struct sockaddr* rt_gateway; + struct sockaddr rt_gateway; /* the gateway address */ + struct sockaddr rt_genmask; /* the target network mask */ + unsigned short int rt_flags; + char* rt_dev; /* FIXME: complete the struct */ }; + +#define RTF_UP 0x1 /* do not delete the route */ +#define RTF_GATEWAY 0x2 /* the route is a gateway and not an end host */ diff --git a/Libraries/LibC/sys/ioctl_numbers.h b/Libraries/LibC/sys/ioctl_numbers.h index c41463a8917..f0179bb7914 100644 --- a/Libraries/LibC/sys/ioctl_numbers.h +++ b/Libraries/LibC/sys/ioctl_numbers.h @@ -62,6 +62,7 @@ enum IOCtlNumber { SIOCSIFADDR, SIOCGIFADDR, SIOCGIFHWADDR, + SIOCSIFNETMASK, SIOCADDRT, - SIOCSIFNETMASK + SIOCDELRT }; diff --git a/Userland/ifconfig.cpp b/Userland/ifconfig.cpp index b53ddd8cf6a..65fc7f5a985 100644 --- a/Userland/ifconfig.cpp +++ b/Userland/ifconfig.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -164,9 +165,27 @@ int main(int argc, char** argv) } if (value_gateway) { - // ioctl does not support rtentry yet - fprintf(stderr, "Changing the gateway is not supported yet\n"); - return 1; + auto address = IPv4Address::from_string(value_gateway); + + int fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); + if (fd < 0) { + perror("socket"); + return 1; + } + + struct rtentry rt; + memset(&rt, 0, sizeof(rt)); + + rt.rt_dev = const_cast(ifname.characters()); + rt.rt_gateway.sa_family = AF_INET; + ((sockaddr_in&)rt.rt_gateway).sin_addr.s_addr = address.value().to_in_addr_t(); + rt.rt_flags = RTF_UP | RTF_GATEWAY; + + int rc = ioctl(fd, SIOCADDRT, &rt); + if (rc < 0) { + perror("ioctl(SIOCADDRT)"); + return 1; + } } } return 0;