• 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;
    }
  • 相关阅读:
    操作系统要点总结
    ARP的通信过程
    判断网段、子网、网络号
    C++要点总结
    枚举类型
    C指针总结
    C运算符总结
    替换空格
    WCF编写时候的测试
    WCF创建到使用到发布
  • 原文地址:https://www.cnblogs.com/wanpengcoder/p/7396169.html
Copyright © 2020-2023  润新知