devinet_ioctl() 根据用户空间提供的ifreq参数和cmd操作对网卡进行设置或参数的获取
cmd:大体分为两种,即set和get
p.s. 有意思的是,用户空间的ifreq指针指向的参数需要拷贝到内存空间中,让后函数才能对参数进行解读和分析。
copy_from_user(&ifr, arg, sizeof(struct ifreq)
-
struct ifreq {
-
char ifr_name[IFNAMSIZ];
-
union
-
{
-
struct sockaddr ifru_addr;
-
struct sockaddr ifru_dstaddr;
-
struct sockaddr ifru_broadaddr;
-
struct sockaddr ifru_netmask;
-
struct sockaddr ifru_hwaddr;
-
short int ifru_flags;
-
int ifru_ivalue;
-
int ifru_mtu;
-
struct ifmap ifru_map;
-
char ifru_slave[IFNAMSIZ]; /* Just fits the size */
-
char ifru_newname[IFNAMSIZ];
-
__caddr_t ifru_data;
-
} ifr_ifru;
-
};
-
-
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 net_device *dev;
-
char *colon;
-
int ret = 0;
-
-
/*
-
* Fetch the caller's info block into kernel space
-
*/
-
-
if (copy_from_user(&ifr, arg, sizeof(struct ifreq)))
-
return -EFAULT;
-
ifr.ifr_name[IFNAMSIZ-1] = 0;
-
-
colon = strchr(ifr.ifr_name, ':');
-
if (colon)
-
*colon = 0;
-
-
#ifdef CONFIG_KMOD
-
dev_load(ifr.ifr_name);
-
#endif
-
-
switch(cmd) { //根據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;
-
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;
-
break;
-
default:
-
return -EINVAL;
-
}
-
//鎖
-
dev_probe_lock();
-
rtnl_lock();
-
//獲取設備名對應的設備的數據結構(描述一個設備)
-
if ((dev = __dev_get_by_name(ifr.ifr_name)) == NULL) {
-
ret = -ENODEV;
-
goto done;
-
}
-
-
if (colon)
-
*colon = ':';
-
-
if ((in_dev=__in_dev_get(dev)) != NULL) { //查找對應的設備名
-
for (ifap=&in_dev->ifa_list; (ifa=*ifap) != NULL; ifap=&ifa->ifa_next)
-
if (strcmp(ifr.ifr_name, ifa->ifa_label) == 0)
-
break;
-
}
-
-
if (ifa == NULL && cmd != SIOCSIFADDR && cmd != SIOCSIFFLAGS) {
-
ret = -EADDRNOTAVAIL;
-
goto done;
-
}
-
-
switch(cmd) { //在對應的設備上進行操作
-
case SIOCGIFADDR: /* Get interface address */ //獲取IP
-
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:
-
if (colon) {
-
if (ifa == NULL) {
-
ret = -EADDRNOTAVAIL;
-
break;
-
}
-
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) */
-
if (inet_abc_len(sin->sin_addr.s_addr) < 0) {
-
ret = -EINVAL;
-
break;
-
}
-
-
if (!ifa) {
-
if ((ifa = inet_alloc_ifa()) == NULL) {
-
ret = -ENOBUFS;
-
break;
-
}
-
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_anycast = 0;
-
}
-
-
ifa->ifa_address =
-
ifa->ifa_local = sin->sin_addr.s_addr; //設置IP
-
-
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);
-
}
-
ret = inet_set_ifa(dev, ifa);
-
break;
-
-
case SIOCSIFBRDADDR: /* Set the broadcast address */
-
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 */
-
if (ifa->ifa_address != sin->sin_addr.s_addr) {
-
if (inet_abc_len(sin->sin_addr.s_addr) < 0) {
-
ret = -EINVAL;
-
break;
-
}
-
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.
-
*/
-
if (bad_mask(sin->sin_addr.s_addr, 0)) {
-
ret = -EINVAL;
-
break;
-
}
-
-
if (ifa->ifa_mask != sin->sin_addr.s_addr) {
-
inet_del_ifa(in_dev, ifap, 0);
-
ifa->ifa_mask = sin->sin_addr.s_addr;
-
ifa->ifa_prefixlen = inet_mask_len(ifa->ifa_mask);
-
inet_insert_ifa(ifa);
-
}
-
break;
-
}
-
done:
-
rtnl_unlock();
-
dev_probe_unlock();
-
return ret;
-
-
rarok:
-
rtnl_unlock();
-
dev_probe_unlock();
-
if (copy_to_user(arg, &ifr, sizeof(struct ifreq)))
-
return -EFAULT;
-
return 0;
-
}
阅读(4177) | 评论(0) | 转发(1) |