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;
}
原文地址:https://www.cnblogs.com/wanpengcoder/p/7396169.html