INET Sockets Tour: ioctl()

版权声明:本文为博主原创文章,仅供学习参考,转载请注明出处,谢谢! https://blog.csdn.net/Rong_Toa/article/details/86654487

目录

arch/i386/kernel/entry.S

fs/ioctl.c/sys_ioctl()

net/socket.c/sock_ioctl()

net/ipv4/af_inet.c

net/ipv4/af_inet.c/inet_ioctl()

net/ipv4/devinet.c/devinet_ioctl()

net/core/dev.c/dev_ioctl()

net/core/dev.c/dev_ifsioc()

net/ipv4/fib_frontend.c/ip_rt_ioctl()


 ioctl() tour

 Anatomy of ioctl() thread

PLEASE WAIT FOR THE EXPLANATION.

arch/i386/kernel/entry.S

ENTRY(sys_call_table)
.....
        .long SYMBOL_NAME(sys_ioctl)         /* 54 */

fs/ioctl.c/sys_ioctl()

asmlinkage int sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg)
{
        struct file * filp;
        unsigned int flag;
        int on, error = -EBADF;

        lock_kernel();
        filp = fget(fd);
        if (!filp)
                goto out;
        error = 0;
        switch (cmd) {
.....
                default:
.....
                        else if (filp->f_op && filp->f_op->ioctl)
                                error = filp->f_op->ioctl(filp->f_dentry->d_inode, filp, cmd, arg);
        }
        fput(filp);

out:
        unlock_kernel();
        return error;
}

net/socket.c/sock_ioctl()

/*
 *      With an ioctl arg may well be a user mode pointer, but we don't know what to do
 *      with it - that's up to the protocol still.
 */

int sock_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
           unsigned long arg)
{
        struct socket *sock = socki_lookup(inode);
        return sock->ops->ioctl(sock, cmd, arg);
}

net/ipv4/af_inet.c

struct proto_ops inet_stream_ops = {
        PF_INET,

        sock_no_dup,
        inet_release,
        inet_bind,
        inet_stream_connect,
        sock_no_socketpair,
        inet_accept,
        inet_getname,
        inet_poll,
        inet_ioctl,
        inet_listen,
        inet_shutdown,
        inet_setsockopt,
        inet_getsockopt,
        sock_no_fcntl,
        inet_sendmsg,
        inet_recvmsg
};

struct proto_ops inet_dgram_ops = {
        PF_INET,

        sock_no_dup,
        inet_release,
        inet_bind,
        inet_dgram_connect,
        sock_no_socketpair,
        sock_no_accept,
        inet_getname,
        datagram_poll,
        inet_ioctl,
        sock_no_listen,
        inet_shutdown,
        inet_setsockopt,
        inet_getsockopt,
        sock_no_fcntl,
        inet_sendmsg,
        inet_recvmsg
};

net/ipv4/af_inet.c/inet_ioctl()

static int inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
{
        struct sock *sk = sock->sk;
        int err;
        int pid;

        switch(cmd)
        {
.....
                case SIOCADDRT:
                case SIOCDELRT:
                case SIOCRTMSG:
                        return(ip_rt_ioctl(cmd,(void *) arg));

                case SIOCDARP:
                case SIOCGARP:
                case SIOCSARP:
                        return(arp_ioctl(cmd,(void *) arg));
.....
                case SIOCGIFADDR:
                case SIOCSIFADDR:
                case SIOCGIFBRDADDR:
                case SIOCSIFBRDADDR:
                case SIOCGIFNETMASK:
                case SIOCSIFNETMASK:
                case SIOCGIFDSTADDR:
                case SIOCSIFDSTADDR:
                case SIOCSIFPFLAGS:
                case SIOCGIFPFLAGS:
                case SIOCSIFFLAGS:
                        return(devinet_ioctl(cmd,(void *) arg));
.....
                default:
//              case SIOCGIFFLAGS:
//              case SIOCSIFHWADDR:
//              case SIOCGIFHWADDR:
// etc.
.....
                        if (sk->prot->ioctl==NULL || (err=sk->prot->ioctl(sk, cmd, arg))==-ENOIOCTLC
MD)
                                return(dev_ioctl(cmd,(void *) arg));
                        return err;
        }
        /*NOTREACHED*/
        return(0);
}

net/ipv4/devinet.c/devinet_ioctl()

int devinet_ioctl(unsigned int cmd, void *arg)
{
        struct ifreq ifr;
        struct sockaddr_in *sin = (struct sockaddr_in *)&ifr.ifr_addr;
        struct in_device *in_dev;
        struct in_ifaddr **ifap = NULL;
        struct in_ifaddr *ifa = NULL;
        struct device *dev;
.....
        switch(cmd) {
        case SIOCGIFADDR:       /* Get interface address */
        case SIOCGIFBRDADDR:    /* Get the broadcast address */
        case SIOCGIFDSTADDR:    /* Get the destination address */
        case SIOCGIFNETMASK:    /* Get the netmask for the interface */
                /* Note that this ioctls will not sleep,
                   so that we do not impose a lock.
                   One day we will be forced to put shlock here (I mean SMP)
                 */
                memset(sin, 0, sizeof(*sin));
                sin->sin_family = AF_INET;
                break;

        case SIOCSIFFLAGS:
                if (!capable(CAP_NET_ADMIN))
                        return -EACCES;
                rtnl_lock();
                exclusive = 1;
                break;
        case SIOCSIFADDR:       /* Set interface address (and family) */
        case SIOCSIFBRDADDR:    /* Set the broadcast address */
        case SIOCSIFDSTADDR:    /* Set the destination address */
        case SIOCSIFNETMASK:    /* Set the netmask for the interface */
                if (!capable(CAP_NET_ADMIN))
                        return -EACCES;
                if (sin->sin_family != AF_INET)
                        return -EINVAL;
                rtnl_lock();
                exclusive = 1;
                break;
        default:
                return -EINVAL;
        }
.....
        switch(cmd) {
                case SIOCGIFADDR:       /* Get interface address */
                        sin->sin_addr.s_addr = ifa->ifa_local;
                        goto rarok;

                case SIOCGIFBRDADDR:    /* Get the broadcast address */
                        sin->sin_addr.s_addr = ifa->ifa_broadcast;
                        goto rarok;

                case SIOCGIFDSTADDR:    /* Get the destination address */
                        sin->sin_addr.s_addr = ifa->ifa_address;
                        goto rarok;

                case SIOCGIFNETMASK:    /* Get the netmask for the interface */
                        sin->sin_addr.s_addr = ifa->ifa_mask;
                        goto rarok;

                case SIOCSIFFLAGS:
.....
                        ret = dev_change_flags(dev, ifr.ifr_flags);
                        break;

                case SIOCSIFADDR:       /* Set interface address (and family) */
.....
                        ifa->ifa_address =
                        ifa->ifa_local = sin->sin_addr.s_addr;
.....
                        ret = inet_set_ifa(dev, ifa);
                        break;

                case SIOCSIFBRDADDR:    /* Set the broadcast address */
.....
                                ifa->ifa_broadcast = sin->sin_addr.s_addr;
                                inet_insert_ifa(in_dev, ifa);
                        }
                        break;

                case SIOCSIFDSTADDR:    /* Set the destination address */
.....
                                ifa->ifa_address = sin->sin_addr.s_addr;
                                inet_insert_ifa(in_dev, ifa);
                        }
                        break;

                case SIOCSIFNETMASK:    /* Set the netmask for the interface */
.....
                                ifa->ifa_mask = sin->sin_addr.s_addr;
                                ifa->ifa_prefixlen = inet_mask_len(ifa->ifa_mask);
                                inet_set_ifa(dev, ifa);
                        }
                        break;
        }
done:
        if (exclusive)
                rtnl_unlock();
        return ret;

rarok:
        if (copy_to_user(arg, &ifr, sizeof(struct ifreq)))
                return -EFAULT;
        return 0;
}

net/core/dev.c/dev_ioctl()

/*
 *      This function handles all "interface"-type I/O control requests. The actual
 *      'doing' part of this is dev_ifsioc above.
 */

int dev_ioctl(unsigned int cmd, void *arg)
{
        struct ifreq ifr;
        int ret;
        char *colon;

.....
        if (copy_from_user(&ifr, arg, sizeof(struct ifreq)))
                return -EFAULT;

.....
        /*
         *      See which interface the caller is talking about.
         */

        switch(cmd)
        {
                /*
                 *      These ioctl calls:
                 *      - can be done by all.
                 *      - atomic and do not require locking.
                 *      - return a value
                 */

                case SIOCGIFFLAGS:
.....
                case SIOCGIFHWADDR:
.....
                        dev_load(ifr.ifr_name);
                        ret = dev_ifsioc(&ifr, cmd);
                        if (!ret) {
                                if (colon)
                                        *colon = ':';
                                if (copy_to_user(arg, &ifr, sizeof(struct ifreq)))
                                        return -EFAULT;
                        }
                        return ret;

                /*
                 *      These ioctl calls:
                 *      - require superuser power.
                 *      - require strict serialization.
                 *      - do not return a value
                 */

.....
                case SIOCSIFHWADDR:
.....
                        if (!capable(CAP_NET_ADMIN))
                                return -EPERM;
                        dev_load(ifr.ifr_name);
                        rtnl_lock();
                        ret = dev_ifsioc(&ifr, cmd);
                        rtnl_unlock();
                        return ret;
.....
        }
}

net/core/dev.c/dev_ifsioc()

/*
 *      Perform the SIOCxIFxxx calls.
 */

static int dev_ifsioc(struct ifreq *ifr, unsigned int cmd)
{
        struct device *dev;
        int err;

        if ((dev = dev_get(ifr->ifr_name)) == NULL)
                return -ENODEV;

        switch(cmd)
        {
                case SIOCGIFFLAGS:      /* Get interface flags */
                        ifr->ifr_flags = (dev->flags&~(IFF_PROMISC|IFF_ALLMULTI))
                                |(dev->gflags&(IFF_PROMISC|IFF_ALLMULTI));
                        return 0;

.....
                case SIOCGIFHWADDR:
                        memcpy(ifr->ifr_hwaddr.sa_data,dev->dev_addr, MAX_ADDR_LEN);
                        ifr->ifr_hwaddr.sa_family=dev->type;
                        return 0;

                case SIOCSIFHWADDR:
                        if(dev->set_mac_address==NULL)
                                return -EOPNOTSUPP;
                        if(ifr->ifr_hwaddr.sa_family!=dev->type)
                                return -EINVAL;
                        err=dev->set_mac_address(dev,&ifr->ifr_hwaddr);
                        if (!err)
                                notifier_call_chain(&netdev_chain, NETDEV_CHANGEADDR, dev);
                        return err;
.....
        }
}

net/ipv4/fib_frontend.c/ip_rt_ioctl()

/*
 *      Handle IP routing ioctl calls. These are used to manipulate the routing tables
 */

int ip_rt_ioctl(unsigned int cmd, void *arg)
{
        int err;
        struct kern_rta rta;
        struct rtentry  r;
        struct {
                struct nlmsghdr nlh;
                struct rtmsg    rtm;
        } req;

        switch (cmd) {
        case SIOCADDRT:         /* Add a route */
        case SIOCDELRT:         /* Delete a route */
                if (!capable(CAP_NET_ADMIN))
                        return -EPERM;
                if (copy_from_user(&r, arg, sizeof(struct rtentry)))
                        return -EFAULT;
                rtnl_lock();
                err = fib_convert_rtentry(cmd, &req.nlh, &req.rtm, &rta, &r);
                if (err == 0) {
                        if (cmd == SIOCDELRT) {
                                struct fib_table *tb = fib_get_table(req.rtm.rtm_table);
                                err = -ESRCH;
                                if (tb)
                                        err = tb->tb_delete(tb, &req.rtm, &rta, &req.nlh, NULL);
                        } else {
                                struct fib_table *tb = fib_new_table(req.rtm.rtm_table);
                                err = -ENOBUFS;
                                if (tb)
                                        err = tb->tb_insert(tb, &req.rtm, &rta, &req.nlh, NULL);
                        }
                        if (rta.rta_mx)
                                kfree(rta.rta_mx);
                }
                rtnl_unlock();
                return err;
        }
        return -EINVAL;
}

To be continued.

http://www.skyfree.org/linux/kernel_network/ioctl.html

猜你喜欢

转载自blog.csdn.net/Rong_Toa/article/details/86654487
今日推荐