zoukankan      html  css  js  c++  java
  • 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;
    }
  • 相关阅读:
    Linux下sed,awk,grep,cut,find学习笔记
    Python文件处理(1)
    KMP详解
    Java引用详解
    解决安卓中页脚被输入法顶起的问题
    解决swfupload上传控件文件名中文乱码问题 三种方法 flash及最新版本11.8.800.168
    null id in entry (don't flush the Session after an exception occurs)
    HQL中的Like查询需要注意的地方
    spring mvc controller间跳转 重定向 传参
    node to traverse cannot be null!
  • 原文地址:https://www.cnblogs.com/wanpengcoder/p/7396169.html
Copyright © 2011-2022 走看看