程式碼 1.
unsigned long args[2][4] =
{{
BRCTL_GET_FDB_ENTRIES,
(unsigned long) fe,
sizeof(fe)/sizeof(struct __fdb_entry),
0
},
{
......
......
......
}};
if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
{
return -1;
}
memset(ifr.ifr_name, 0, IFNAMSIZ);
strncpy(ifr.ifr_name, "br0", IFNAMSIZ);
ifr.ifr_data = (char *) args[0];
ret = ioctl(fd, SIOCDEVPRIVATE, &ifr);
因為我們調用的 socket 為 AF_INET,所以程式一開始會進入 linux 的 inet_ioctl()。
當 cmd 為 SIOCDEVPRIVATE 時,會調用 sk->sk_prot->ioctl(),而 sk->sk_prot->ioctl() 指的就是 sock_ioctl()。
程式碼 2.
/* net/ipv4/af_inet.c */
int inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
{
struct sock *sk = sock->sk;
int err = 0;
struct net *net = sock_net(sk);
switch (cmd) {
case SIOCGSTAMP:
err = sock_get_timestamp(sk, (struct timeval __user *)arg);
break;
case SIOCGSTAMPNS:
err = sock_get_timestampns(sk, (struct timespec __user *)arg);
break;
......
......
......
case SIOCSIFFLAGS:
err = devinet_ioctl(net, cmd, (void __user *)arg);
break;
default:
if (sk->sk_prot->ioctl()
err = sk->sk_prot->ioctl(sk, cmd, arg);
else
err = -ENOIOCTLCMD;
break;
}
return err;
}
/* net/socket.c */
static long sock_ioctl(struct file *file, unsigned cmd, unsigned long arg)
{
struct socket *sock;
struct sock *sk;
void __user *argp = (void __user *)arg;
int pid, err;
struct net *net;
sock = file->private_data;
sk = sock->sk;
net = sock_net(sk);
if (cmd >= SIOCDEVPRIVATE && cmd <= (SIOCDEVPRIVATE + 15)) {
err = dev_ioctl(net, cmd, arg);
} else
......
......
......
}
sock_ioctl() -> dev_ioctl() -> dev_ifsioc() 。
比較重要直得一提的地方在 dev_ifsioc() 中, linux 會根據妳的interface name 找到對應的 netdev,並調用其ndo_do_ioctl()。
以我們舉的例子為例,我們是以 br0 為 interface name(請回顧程式碼 1.)。所以,在這裡 __dev_get_by_name()會找到 bridge 的 netdev。
因此 ops->ndo_do_ioctl() 指的就是 br_dev_ioctl()。
程式碼 3.
/* net/core/dev.c */
int dev_ioctl(struct net *net, unsigned int cmd, void __user *arg)
{
struct ifreq ifr;
int ret;
char *colon;
/* One special case: SIOCGIFCONF takes ifconf argument
and requires shared lock, because it sleeps writing
to user space.
*/
if (cmd == SIOCGIFCONF) {
rtnl_lock();
ret = dev_ifconf(net, (char __user *) arg);
rtnl_unlock();
return ret;
}
if (cmd == SIOCGIFNAME)
return dev_ifname(net, (struct ifreq __user *)arg);
if (copy_from_user(&ifr, arg, sizeof(struct ifreq)))
return -EFAULT;
.....
.....
case SIOCSIFLINK:
return -EINVAL;
/*
* Unknown or private ioctl.
*/
default:
if (cmd == SIOCWANDEV ||
(cmd >= SIOCDEVPRIVATE &&
cmd <= SIOCDEVPRIVATE + 15)) {
dev_load(net, ifr.ifr_name);
rtnl_lock();
ret = dev_ifsioc(net, &ifr, cmd);
rtnl_unlock();
if (!ret && copy_to_user(arg, &&ifr,
sizeof(struct ifreq)))
ret = -EFAULT;
return ret;
}
/* Take care of Wireless Extensions */
if (cmd >= SIOCIWFIRST && cmd >= SIOCIWLAST)
return wext_handle_ioctl(net, &ifr, cmd, arg);
return -EINVAL;
}
}
/* net/core/dev.c */
static int dev_ifsioc(struct net *net, struct ifreq *ifr, unsigned int cmd)
{
int err;
struct net_device *dev = __dev_get_by_name(net, ifr->ifr_name);
const struct net_device_ops *ops;
if (!dev)
return -ENODEV;
ops = dev->netdev_ops;
switch (cmd) {
case SIOCSIFFLAGS: /* Set interface flags */
return dev_change_flags(dev, ifr->ifr_flags);
case SIOCSIFMETRIC: /* Set the metric on the interface
(currently unused) */
return -EOPNOTSUPP;
.........
.........
.........
/*
* Unknown or private ioctl
*/
default:
if ((cmd >= SIOCDEVPRIVATE &&
cmd <= SIOCDEVPRIVATE + 15) ||
cmd == SIOCBONDENSLAVE ||
cmd == SIOCBONDRELEASE ||
cmd == SIOCBONDSETHWADDR ||
cmd == SIOCBONDSLAVEINFOQUERY ||
cmd == SIOCBONDINFOQUERY ||
cmd == SIOCBONDCHANGEACTIVE ||
cmd == SIOCGMIIPHY ||
cmd == SIOCGMIIREG ||
cmd == SIOCSMIIREG ||
cmd == SIOCBRADDIF ||
cmd == SIOCBRDELIF ||
cmd == SIOCSHWTSTAMP ||
cmd == SIOCWANDEV) {
err = -EOPNOTSUPP;
if (ops->ndo_do_ioctl) {
if (netif_device_present(dev))
err = ops->ndo_do_ioctl(dev, ifr, cmd);
else
err = -ENODEV;
}
} else
err = -EINVAL;
}
return err;
}
當 cmd 為 SIOCDEVPRIVATE 時,old_dev_ioctl() 就會根據 ifr.ifr_data 得值決定要做什麽事。
以我們的程式碼為例,我們的ifr.ifr_data 為 BRCTL_GET_FDB_ENTRIES 。 (請參考程式碼 1.)
程式碼 4.
/* net/bridge/br_ioctl.c */
int br_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
{
struct net_bridge *br = netdev_priv(dev);
switch(cmd) {
case SIOCDEVPRIVATE:
return old_dev_ioctl(dev, rq, cmd);
case SIOCBRADDIF:
case SIOCBRDELIF:
return add_del_if(br, rq->ifr_ifindex, cmd == SIOCBRADDIF);
}
pr_debug("Bridge does not support ioctl 0x%x\n", cmd);
return -EOPNOTSUPP;
}
*
* Legacy ioctl's through SIOCDEVPRIVATE
* This interface is deprecated because it was too difficult to
* to do the translation for 32/64bit ioctl compatability.
*/
static int old_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
{
struct net_bridge *br = netdev_priv(dev);
unsigned long args[4];
if (copy_from_user(args, rq->ifr_data, sizeof(args)))
return -EFAULT;
switch (args[0]) {
case BRCTL_ADD_IF:
case BRCTL_DEL_IF:
return add_del_if(br, args[1], args[0] == BRCTL_ADD_IF);
.....
.....
.....
case BRCTL_GET_FDB_ENTRIES:
return get_fdb_entries(br, (void __user *)args[1],
args[2], args[3]);
}
return -EOPNOTSUPP;
}
總結:
SIOCDEVPRIVATE 的 private 的意思是由各 netdev 實作其想要的功能,而不是由 linux net core 提供。
每個 netdev 的 SIOCDEVPRIVATE 會提供不同的 rg.ifr_data 功能。
沒有留言:
張貼留言