Browse Source

IPv4: Implement socket ioctls SIOCGIFADDR and SIOCSIFADDR

This allows userspace programs to get and set (superuser-only) the IPv4
address of a network adapter. :^)
Andreas Kling 5 years ago
parent
commit
2482fc3538

+ 34 - 1
Kernel/Net/IPv4Socket.cpp

@@ -13,6 +13,7 @@
 #include <Kernel/Process.h>
 #include <Kernel/Process.h>
 #include <Kernel/UnixTypes.h>
 #include <Kernel/UnixTypes.h>
 #include <LibC/errno_numbers.h>
 #include <LibC/errno_numbers.h>
+#include <LibC/sys/ioctl_numbers.h>
 
 
 //#define IPV4_SOCKET_DEBUG
 //#define IPV4_SOCKET_DEBUG
 
 
@@ -337,7 +338,6 @@ KResult IPv4Socket::getsockopt(int level, int option, void* value, socklen_t* va
     if (level != IPPROTO_IP)
     if (level != IPPROTO_IP)
         return Socket::getsockopt(level, option, value, value_size);
         return Socket::getsockopt(level, option, value, value_size);
 
 
-
     switch (option) {
     switch (option) {
     case IP_TTL:
     case IP_TTL:
         if (*value_size < sizeof(int))
         if (*value_size < sizeof(int))
@@ -348,3 +348,36 @@ KResult IPv4Socket::getsockopt(int level, int option, void* value, socklen_t* va
         return KResult(-ENOPROTOOPT);
         return KResult(-ENOPROTOOPT);
     }
     }
 }
 }
+
+int IPv4Socket::ioctl(FileDescription&, unsigned request, unsigned arg)
+{
+    auto* ifr = (ifreq*)arg;
+    if (!current->process().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 (!current->process().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 SIOCGIFADDR:
+        if (!current->process().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;
+    }
+
+    return -EINVAL;
+}

+ 2 - 0
Kernel/Net/IPv4Socket.h

@@ -34,6 +34,8 @@ public:
     virtual KResult setsockopt(int level, int option, const void*, socklen_t) override;
     virtual KResult setsockopt(int level, int option, const void*, socklen_t) override;
     virtual KResult getsockopt(int level, int option, void*, socklen_t*) override;
     virtual KResult getsockopt(int level, int option, void*, socklen_t*) override;
 
 
+    virtual int ioctl(FileDescription&, unsigned request, unsigned arg) override;
+
     void did_receive(const IPv4Address& peer_address, u16 peer_port, KBuffer&&);
     void did_receive(const IPv4Address& peer_address, u16 peer_port, KBuffer&&);
 
 
     const IPv4Address& local_address() const { return m_local_address; }
     const IPv4Address& local_address() const { return m_local_address; }

+ 10 - 0
Kernel/Net/NetworkAdapter.cpp

@@ -32,6 +32,16 @@ WeakPtr<NetworkAdapter> NetworkAdapter::from_ipv4_address(const IPv4Address& add
     return nullptr;
     return nullptr;
 }
 }
 
 
+WeakPtr<NetworkAdapter> NetworkAdapter::lookup_by_name(const StringView& name)
+{
+    NetworkAdapter* found_adapter = nullptr;
+    for_each([&](auto& adapter) {
+        if (adapter.name() == name)
+            found_adapter = &adapter;
+    });
+    return found_adapter ? found_adapter->make_weak_ptr() : nullptr;
+}
+
 NetworkAdapter::NetworkAdapter()
 NetworkAdapter::NetworkAdapter()
 {
 {
     // FIXME: I wanna lock :(
     // FIXME: I wanna lock :(

+ 1 - 0
Kernel/Net/NetworkAdapter.h

@@ -18,6 +18,7 @@ class NetworkAdapter : public Weakable<NetworkAdapter> {
 public:
 public:
     static void for_each(Function<void(NetworkAdapter&)>);
     static void for_each(Function<void(NetworkAdapter&)>);
     static WeakPtr<NetworkAdapter> from_ipv4_address(const IPv4Address&);
     static WeakPtr<NetworkAdapter> from_ipv4_address(const IPv4Address&);
+    static WeakPtr<NetworkAdapter> lookup_by_name(const StringView&);
     virtual ~NetworkAdapter();
     virtual ~NetworkAdapter();
 
 
     virtual const char* class_name() const = 0;
     virtual const char* class_name() const = 0;

+ 30 - 0
Kernel/UnixTypes.h

@@ -406,3 +406,33 @@ struct iovec {
 struct sched_param {
 struct sched_param {
     int sched_priority;
     int sched_priority;
 };
 };
+
+struct ifreq {
+#define IFNAMSIZ 16
+    char ifr_name[IFNAMSIZ];
+    union {
+        struct sockaddr ifru_addr;
+        struct sockaddr ifru_dstaddr;
+        struct sockaddr ifru_broadaddr;
+        short ifru_flags;
+        int ifru_metric;
+        int64_t ifru_vnetid;
+        uint64_t ifru_media;
+        void* ifru_data;
+        unsigned int ifru_index;
+    } ifr_ifru;
+#define ifr_addr ifr_ifru.ifru_addr           // address
+#define ifr_dstaddr ifr_ifru.ifru_dstaddr     // other end of p-to-p link
+#define ifr_broadaddr ifr_ifru.ifru_broadaddr // broadcast address
+#define ifr_flags ifr_ifru.ifru_flags         // flags
+#define ifr_metric ifr_ifru.ifru_metric       // metric
+#define ifr_mtu ifr_ifru.ifru_metric          // mtu (overload)
+#define ifr_hardmtu ifr_ifru.ifru_metric      // hardmtu (overload)
+#define ifr_media ifr_ifru.ifru_media         // media options
+#define ifr_rdomainid ifr_ifru.ifru_metric    // VRF instance (overload)
+#define ifr_vnetid ifr_ifru.ifru_vnetid       // Virtual Net Id
+#define ifr_ttl ifr_ifru.ifru_metric          // tunnel TTL (overload)
+#define ifr_data ifr_ifru.ifru_data           // for use by interface
+#define ifr_index ifr_ifru.ifru_index         // interface index
+#define ifr_llprio ifr_ifru.ifru_metric       // link layer priority
+};

+ 38 - 0
Libraries/LibC/net/if.h

@@ -0,0 +1,38 @@
+#pragma once
+
+#include <sys/cdefs.h>
+#include <sys/socket.h>
+
+__BEGIN_DECLS
+
+struct ifreq {
+#define IFNAMSIZ 16
+    char ifr_name[IFNAMSIZ];
+    union {
+        struct sockaddr ifru_addr;
+        struct sockaddr ifru_dstaddr;
+        struct sockaddr ifru_broadaddr;
+        short ifru_flags;
+        int ifru_metric;
+        int64_t ifru_vnetid;
+        uint64_t ifru_media;
+        void* ifru_data;
+        unsigned int ifru_index;
+    } ifr_ifru;
+#define ifr_addr ifr_ifru.ifru_addr           // address
+#define ifr_dstaddr ifr_ifru.ifru_dstaddr     // other end of p-to-p link
+#define ifr_broadaddr ifr_ifru.ifru_broadaddr // broadcast address
+#define ifr_flags ifr_ifru.ifru_flags         // flags
+#define ifr_metric ifr_ifru.ifru_metric       // metric
+#define ifr_mtu ifr_ifru.ifru_metric          // mtu (overload)
+#define ifr_hardmtu ifr_ifru.ifru_metric      // hardmtu (overload)
+#define ifr_media ifr_ifru.ifru_media         // media options
+#define ifr_rdomainid ifr_ifru.ifru_metric    // VRF instance (overload)
+#define ifr_vnetid ifr_ifru.ifru_vnetid       // Virtual Net Id
+#define ifr_ttl ifr_ifru.ifru_metric          // tunnel TTL (overload)
+#define ifr_data ifr_ifru.ifru_data           // for use by interface
+#define ifr_index ifr_ifru.ifru_index         // interface index
+#define ifr_llprio ifr_ifru.ifru_metric       // link layer priority
+};
+
+__END_DECLS

+ 2 - 0
Libraries/LibC/sys/ioctl_numbers.h

@@ -33,4 +33,6 @@ enum IOCtlNumber {
     FB_IOCTL_SET_RESOLUTION,
     FB_IOCTL_SET_RESOLUTION,
     FB_IOCTL_GET_BUFFER,
     FB_IOCTL_GET_BUFFER,
     FB_IOCTL_SET_BUFFER,
     FB_IOCTL_SET_BUFFER,
+    SIOCSIFADDR,
+    SIOCGIFADDR,
 };
 };