内核网络子系统--devinet_ioctl

Kernel: 4.12.6

deinet_ioctl:获取或者设置接口的地址,掩码,标记等信息;

注意,使用SIOCSIFFLAGS关闭设备,如果使用了别名,则删除对应ip,如果其为主ip,并且从ip未设置提升主ip,则所有从ip也会删除;

int devinet_ioctl(struct net *net, unsigned int cmd, void __user *arg)
{
    struct ifreq ifr;
    struct sockaddr_in sin_orig;
    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 net_device *dev;
    char *colon;
    int ret = -EFAULT;
    int tryaddrmatch = 0;

    /*
     *    Fetch the caller‘s info block into kernel space
     */

    //从用户空间拷贝配置
    if (copy_from_user(&ifr, arg, sizeof(struct ifreq)))
        goto out;
    ifr.ifr_name[IFNAMSIZ - 1] = 0;

    /* save original address for comparison */
    //存储原地址
    memcpy(&sin_orig, sin, sizeof(*sin));

    //如果配置了别名,则将别名后缀去掉,后续恢复
    colon = strchr(ifr.ifr_name, ‘:‘);
    if (colon)
        *colon = 0;

    //加载驱动
    dev_load(net, ifr.ifr_name);

    //参数检查
    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 these 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)
         */
        //记录原地址协议族是否为AF_INET
        tryaddrmatch = (sin_orig.sin_family == AF_INET);

        //设置协议族为AF_INET
        memset(sin, 0, sizeof(*sin));
        sin->sin_family = AF_INET;
        break;

    //设置接口flag
    case SIOCSIFFLAGS:
        ret = -EPERM;

        //检查权限,不足则退出
        if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
            goto out;
        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 */

        //检查权限,不足则退出

        ret = -EPERM;
        if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
            goto out;

        //检查协议族,不是AF_INET则退出
        ret = -EINVAL;
        if (sin->sin_family != AF_INET)
            goto out;
        break;

    //其他情况参数非法
    default:
        ret = -EINVAL;
        goto out;
    }

    rtnl_lock();

    //根据名称查找设备
    ret = -ENODEV;
    dev = __dev_get_by_name(net, ifr.ifr_name);

    //未找到退出
    if (!dev)
        goto done;

    //恢复别名分隔符
    if (colon)
        *colon = ‘:‘;

    //获取in_device结构
    in_dev = __in_dev_get_rtnl(dev);

    //若存在
    if (in_dev) {

        //如果为AF_INET,则根据ip和标签查找ifa
        if (tryaddrmatch) {
            /* Matthias Andree */
            /* compare label and address (4.4BSD style) */
            /* note: we only do this for a limited set of ioctls
               and only if the original address family was AF_INET.
               This is checked above. */
            for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL;
                 ifap = &ifa->ifa_next) {
                if (!strcmp(ifr.ifr_name, ifa->ifa_label) &&
                    sin_orig.sin_addr.s_addr ==
                            ifa->ifa_local) {
                    break; /* found */
                }
            }
        }
        /* we didn‘t get a match, maybe the application is
           4.3BSD-style and passed in junk so we fall back to
           comparing just the label */
        //如果没找到,则之查找标签
        if (!ifa) {
            for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL;
                 ifap = &ifa->ifa_next)
                if (!strcmp(ifr.ifr_name, ifa->ifa_label))
                    break;
        }
    }

    //若ifa不存在,设置地址和设置标志以外的命令不合法
    ret = -EADDRNOTAVAIL;
    if (!ifa && cmd != SIOCSIFADDR && cmd != SIOCSIFFLAGS)
        goto done;

    switch (cmd) {
        //获取ip地址
    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;
        //设置flags
    case SIOCSIFFLAGS:
        //别名
        if (colon) {
            ret = -EADDRNOTAVAIL;
            if (!ifa)
                break;
            ret = 0;

            //关闭网络设备,则删除ip
            if (!(ifr.ifr_flags & IFF_UP))
                inet_del_ifa(in_dev, ifap, 1);
            break;
        }

        //修改标记
        ret = dev_change_flags(dev, ifr.ifr_flags);
        break;
    //设置地址
    case SIOCSIFADDR:    /* Set interface address (and family) */
        ret = -EINVAL;
        //检查掩码长度
        if (inet_abc_len(sin->sin_addr.s_addr) < 0)
            break;

        //地址不存在
        if (!ifa) {
            ret = -ENOBUFS;
            ifa = inet_alloc_ifa();
            if (!ifa)
                break;
            INIT_HLIST_NODE(&ifa->hash);

            //拷贝名称
            if (colon)
                memcpy(ifa->ifa_label, ifr.ifr_name, IFNAMSIZ);
            else
                memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
        } else {
            ret = 0;
            //地址相同
            if (ifa->ifa_local == sin->sin_addr.s_addr)
                break;

            //地址不同,则删除原地址
            inet_del_ifa(in_dev, ifap, 0);
            ifa->ifa_broadcast = 0;
            ifa->ifa_scope = 0;
        }

        //设置地址
        ifa->ifa_address = ifa->ifa_local = sin->sin_addr.s_addr;

        //如果不是点对点
        if (!(dev->flags & IFF_POINTOPOINT)) {

            //设置掩码
            ifa->ifa_prefixlen = inet_abc_len(ifa->ifa_address);
            ifa->ifa_mask = inet_make_mask(ifa->ifa_prefixlen);

            //设置广播地址
            if ((dev->flags & IFF_BROADCAST) &&
                ifa->ifa_prefixlen < 31)
                ifa->ifa_broadcast = ifa->ifa_address |
                             ~ifa->ifa_mask;
        } else {
            //设置掩码
            ifa->ifa_prefixlen = 32;
            ifa->ifa_mask = inet_make_mask(32);
        }

        //设置生命周期
        set_ifa_lifetime(ifa, INFINITY_LIFE_TIME, INFINITY_LIFE_TIME);

        //添加ip地址
        ret = inet_set_ifa(dev, ifa);
        break;
    //设置广播地址
    case SIOCSIFBRDADDR:    /* Set the broadcast address */
        ret = 0;

        //删除重新设置
        if (ifa->ifa_broadcast != sin->sin_addr.s_addr) {
            inet_del_ifa(in_dev, ifap, 0);
            ifa->ifa_broadcast = sin->sin_addr.s_addr;
            inet_insert_ifa(ifa);
        }
        break;
    //设置点对点目的地址
    case SIOCSIFDSTADDR:    /* Set the destination address */
        ret = 0;
        //相同
        if (ifa->ifa_address == sin->sin_addr.s_addr)
            break;
        ret = -EINVAL;

        //校验地址
        if (inet_abc_len(sin->sin_addr.s_addr) < 0)
            break;
        ret = 0;

        //删除重置地址
        inet_del_ifa(in_dev, ifap, 0);
        ifa->ifa_address = sin->sin_addr.s_addr;
        inet_insert_ifa(ifa);
        break;
    //设置掩码
    case SIOCSIFNETMASK:     /* Set the netmask for the interface */

        /*
         *    The mask we set must be legal.
         */

        //检查掩码
        ret = -EINVAL;
        if (bad_mask(sin->sin_addr.s_addr, 0))
            break;
        ret = 0;
        if (ifa->ifa_mask != sin->sin_addr.s_addr) {
            //设置掩码
            __be32 old_mask = ifa->ifa_mask;
            inet_del_ifa(in_dev, ifap, 0);
            ifa->ifa_mask = sin->sin_addr.s_addr;
            ifa->ifa_prefixlen = inet_mask_len(ifa->ifa_mask);

            /* See if current broadcast address matches
             * with current netmask, then recalculate
             * the broadcast address. Otherwise it‘s a
             * funny address, so don‘t touch it since
             * the user seems to know what (s)he‘s doing...
             */
             //如果之前广播地址与掩码匹配,
             //则重新按照此方式计算广播地址
            if ((dev->flags & IFF_BROADCAST) &&
                (ifa->ifa_prefixlen < 31) &&
                (ifa->ifa_broadcast ==
                 (ifa->ifa_local|~old_mask))) {
                ifa->ifa_broadcast = (ifa->ifa_local |
                              ~sin->sin_addr.s_addr);
            }
            //重新设置ip
            inet_insert_ifa(ifa);
        }
        break;
    }
done:
    rtnl_unlock();
out:
    return ret;
rarok:
    rtnl_unlock();

    //拷贝到用户空间
    ret = copy_to_user(arg, &ifr, sizeof(struct ifreq)) ? -EFAULT : 0;
    goto out;
}
时间: 2024-08-29 00:45:22

内核网络子系统--devinet_ioctl的相关文章

内核网络子系统--添加/删除ip地址

kernel: 4.12.6 添加ip地址:主从ip的判断,并且插入到合适的位置中: static int __inet_insert_ifa(struct in_ifaddr *ifa, struct nlmsghdr *nlh, u32 portid) { struct in_device *in_dev = ifa->ifa_dev; struct in_ifaddr *ifa1, **ifap, **last_primary; ASSERT_RTNL(); //ifa_local地址不存

精通Linux内核网络

这篇是计算机中操作系统Linux类的优质预售推荐<精通Linux内核网络>. 最详尽的Linux内核网络专著,深入剖析IPsec.Wireless.InfiniBand等重要内核网络子系统. 编辑推荐 专注于各网络协议实现技术的精髓及其遵循的指导方针和原则. 重点讲解数据包在Linux内核网络栈中的传输过程,阐述其与网络各层及各子系统之间的交互. 从网络开发者视角,配合清晰图表,深入剖析Linux内核网络子系统的内部细节及核心实现 内容简介 本书讨论Linux 内核网络栈的实现及其原理,深入而

Linux内核分析(四)----进程管理|网络子系统|虚拟文件系统|驱动简介

Linux内核分析(四) 两天没有更新了,上次博文我们分析了linux的内存管理子系统,本来我不想对接下来的进程管理子系统.网络子系统.虚拟文件系统在这个阶段进行分析的,但是为了让大家对内核有个整体的把握,今天还是简单的介绍一下剩余的几个子系统,我们对这几个子系统的分析,只要了解其作用和部分内容即可,不必深究,等我们写上几个驱动,到时候按照驱动再来分析这几个子系统我们就清晰多了. 在http://www.cnblogs.com/wrjvszq/p/4257164.html一文我们提到过linux

Linux 内核无线子系统

Linux 内核无线子系统 浅谈 Linux 内核无线子系统 Table of Contents 1. 全局概览 2. 模块间接口 3. 数据路径与管理路径 4. 数据包是如何被发送? 5. 谈谈管理路径 6. 数据包又是如何被接收? 7. 总结一下 Linux 内核是如何实现无线网络接口呢?数据包是通过怎样的方式被发送和接收呢?刚开始工作接触 Linux 无线网络时,我曾迷失在浩瀚的基础代码中,寻找具有介绍性的材料来回答如上面提到的那些高层次的问题.跟踪探索了一段时间的源代码后,我写下了这篇总

linux内核DCB子系统

网络设备是怎么利用linux内核的DCB子系统,来达到融合网络流量的各种各样的QoS需求的?融合网卡或者存储流量是否也可以使用到DCB子系统的,他们是怎样工作的?本文将对上面这两个问题进行解答:本文首先大体介绍了DCB机制和它的使用环境:然后介绍一个使用DCB的应用程序lldpad的例子:再然后介绍一个DCB子系统中重要的数据结构:最后介绍DCB内核模块和驱动的具体实现. Overview 在整个DCB过程中,是要把各种各样的流量,可能是FCoE流量,可能是一般的TCP流量,还可能是其他的视频流

(转)浅谈 Linux 内核无线子系统

前言 Linux 内核是如何实现无线网络接口呢?数据包是通过怎样的方式被发送和接收呢? 刚开始工作接触 Linux 无线网络时,我曾迷失在浩瀚的基础代码中,寻找具有介绍性的材料来回答如上面提到的那些高层次的问题. 跟踪探索了一段时间的源代码后,我写下了这篇总结,希望在 Linux 无线网络的工作原理上,读者能从这篇文章获得一个具有帮助性的概览. 1.全局概览 在开始探索 Linux 无线具体细节之前,让我们先来把握一下 Linux 无线子系统整体结构.如图1,展示了 Linux 无线子系统各个模

linux 内核网络协议栈阅读理解--带详尽注释以及相关流程调用注释,附 github 注释后源码

linux 内核网络协议栈阅读理解--带详尽注释以及相关流程调用注释,对理解内核协议栈源码很有帮助 对理解阅读 linux 协议栈源码很用帮助 github 地址: https://github.com/y123456yz/Reading-and-comprehense-linux-Kernel-network-protocol-stack

Linux内核--网络栈实现分析(一)--网络栈初始化

本文分析基于内核Linux Kernel 1.2.13 原创作品,转载请标明http://blog.csdn.net/yming0221/article/details/7488828 更多请看专栏,地址http://blog.csdn.net/column/details/linux-kernel-net.html 作者:闫明 以后的系列博文将深入分析Linux内核的网络栈实现原理,这里看到曹桂平博士的分析后,也决定选择Linux内核1.2.13版本进行分析. 原因如下: 1.功能和网络栈层次

Atitit.软件开发概念(11)--网络子系统--url编码 空格问题URLEncoder java js php

Atitit.软件开发概念(11)--网络子系统--url编码 空格问题URLEncoder java js php 1. RFC2396标准 including HTML 4.01 section 17.13.4, and also RFC 1866 (which is supercededby the W3C HTML recommendations). 在form的ContextType是[x-www-form-urlencoded]的时候会对form中的键/值对进行编码,空格被转义成+,