Differences
This shows you the differences between two versions of the page.
| Both sides previous revision Previous revision Next revision | Previous revision | ||
| ip_up_down [2018/10/04 10:05] – [Overview] rpjday | ip_up_down [2018/10/04 11:09] (current) – [__dev_change_flags()] rpjday | ||
|---|---|---|---|
| Line 15: | Line 15: | ||
| * '' | * '' | ||
| + | * Define '' | ||
| ===== Structures ===== | ===== Structures ===== | ||
| Line 83: | Line 84: | ||
| </ | </ | ||
| - | ==== struct ifreq ==== | ||
| - | |||
| - | From [[https:// | ||
| - | |||
| - | < | ||
| - | /* for compatibility with glibc net/if.h */ | ||
| - | #if __UAPI_DEF_IF_IFREQ | ||
| - | struct ifreq { | ||
| - | #define IFHWADDRLEN 6 | ||
| - | union | ||
| - | { | ||
| - | char ifrn_name[IFNAMSIZ]; | ||
| - | } ifr_ifrn; | ||
| - | |||
| - | union { | ||
| - | struct sockaddr ifru_addr; | ||
| - | struct sockaddr ifru_dstaddr; | ||
| - | struct sockaddr ifru_broadaddr; | ||
| - | struct sockaddr ifru_netmask; | ||
| - | struct | ||
| - | short ifru_flags; | ||
| - | int ifru_ivalue; | ||
| - | int ifru_mtu; | ||
| - | struct | ||
| - | char ifru_slave[IFNAMSIZ]; | ||
| - | char ifru_newname[IFNAMSIZ]; | ||
| - | void __user * ifru_data; | ||
| - | struct if_settings ifru_settings; | ||
| - | } ifr_ifru; | ||
| - | }; | ||
| - | #endif /* __UAPI_DEF_IF_IFREQ */ | ||
| - | |||
| - | #define ifr_name ifr_ifrn.ifrn_name / | ||
| - | #define ifr_hwaddr ifr_ifru.ifru_hwaddr / | ||
| - | # | ||
| - | # | ||
| - | # | ||
| - | # | ||
| - | # | ||
| - | # | ||
| - | # | ||
| - | #define ifr_map ifr_ifru.ifru_map / | ||
| - | #define ifr_slave ifr_ifru.ifru_slave / | ||
| - | # | ||
| - | #define ifr_ifindex ifr_ifru.ifru_ivalue / | ||
| - | #define ifr_bandwidth ifr_ifru.ifru_ivalue | ||
| - | #define ifr_qlen ifr_ifru.ifru_ivalue / | ||
| - | #define ifr_newname ifr_ifru.ifru_newname / | ||
| - | #define ifr_settings ifr_ifru.ifru_settings / | ||
| - | </ | ||
| ===== Usage ===== | ===== Usage ===== | ||
| Line 247: | Line 198: | ||
| ==== do_set() ==== | ==== do_set() ==== | ||
| + | |||
| + | Modify the appropriate flags: | ||
| < | < | ||
| Line 305: | Line 258: | ||
| return err; | return err; | ||
| } | } | ||
| + | </ | ||
| + | |||
| + | ===== Sockets and ioctl calls (net/ | ||
| + | |||
| + | ==== socket_file_ops ==== | ||
| + | |||
| + | < | ||
| + | static const struct file_operations socket_file_ops = { | ||
| + | .owner = THIS_MODULE, | ||
| + | .llseek = no_llseek, | ||
| + | .read_iter = sock_read_iter, | ||
| + | .write_iter = sock_write_iter, | ||
| + | .poll = sock_poll, | ||
| + | .unlocked_ioctl = sock_ioctl, | ||
| + | #ifdef CONFIG_COMPAT | ||
| + | .compat_ioctl = compat_sock_ioctl, | ||
| + | #endif | ||
| + | .mmap = sock_mmap, | ||
| + | .release = sock_close, | ||
| + | .fasync = sock_fasync, | ||
| + | .sendpage = sock_sendpage, | ||
| + | .splice_write = generic_splice_sendpage, | ||
| + | .splice_read = sock_splice_read, | ||
| + | }; | ||
| + | </ | ||
| + | |||
| + | ==== sock_ioctl() ==== | ||
| + | |||
| + | < | ||
| + | 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-> | ||
| + | sk = sock-> | ||
| + | net = sock_net(sk); | ||
| + | |||
| + | ... snip ... | ||
| + | |||
| + | default: | ||
| + | err = sock_do_ioctl(net, | ||
| + | sizeof(struct ifreq)); | ||
| + | break; | ||
| + | } | ||
| + | return err; | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | ==== sock_do_ioctl() ==== | ||
| + | |||
| + | < | ||
| + | static long sock_do_ioctl(struct net *net, struct socket *sock, | ||
| + | unsigned int cmd, unsigned long arg, | ||
| + | unsigned int ifreq_size) | ||
| + | { | ||
| + | int err; | ||
| + | void __user *argp = (void __user *)arg; | ||
| + | |||
| + | err = sock-> | ||
| + | |||
| + | /* | ||
| + | * If this ioctl is unknown try to hand it down | ||
| + | * to the NIC driver. | ||
| + | */ | ||
| + | if (err != -ENOIOCTLCMD) | ||
| + | return err; | ||
| + | |||
| + | if (cmd == SIOCGIFCONF) { | ||
| + | struct ifconf ifc; | ||
| + | if (copy_from_user(& | ||
| + | return -EFAULT; | ||
| + | rtnl_lock(); | ||
| + | err = dev_ifconf(net, | ||
| + | rtnl_unlock(); | ||
| + | if (!err && copy_to_user(argp, | ||
| + | err = -EFAULT; | ||
| + | } else { | ||
| + | struct ifreq ifr; | ||
| + | bool need_copyout; | ||
| + | if (copy_from_user(& | ||
| + | return -EFAULT; | ||
| + | err = dev_ioctl(net, | ||
| + | if (!err && need_copyout) | ||
| + | if (copy_to_user(argp, | ||
| + | return -EFAULT; | ||
| + | } | ||
| + | return err; | ||
| + | } | ||
| + | </ | ||
| + | ===== struct ifreq ===== | ||
| + | |||
| + | From [[https:// | ||
| + | |||
| + | < | ||
| + | /* for compatibility with glibc net/if.h */ | ||
| + | #if __UAPI_DEF_IF_IFREQ | ||
| + | struct ifreq { | ||
| + | #define IFHWADDRLEN 6 | ||
| + | union | ||
| + | { | ||
| + | char ifrn_name[IFNAMSIZ]; | ||
| + | } ifr_ifrn; | ||
| + | |||
| + | union { | ||
| + | struct sockaddr ifru_addr; | ||
| + | struct sockaddr ifru_dstaddr; | ||
| + | struct sockaddr ifru_broadaddr; | ||
| + | struct sockaddr ifru_netmask; | ||
| + | struct | ||
| + | short ifru_flags; | ||
| + | int ifru_ivalue; | ||
| + | int ifru_mtu; | ||
| + | struct | ||
| + | char ifru_slave[IFNAMSIZ]; | ||
| + | char ifru_newname[IFNAMSIZ]; | ||
| + | void __user * ifru_data; | ||
| + | struct if_settings ifru_settings; | ||
| + | } ifr_ifru; | ||
| + | }; | ||
| + | #endif /* __UAPI_DEF_IF_IFREQ */ | ||
| + | |||
| + | #define ifr_name ifr_ifrn.ifrn_name / | ||
| + | #define ifr_hwaddr ifr_ifru.ifru_hwaddr / | ||
| + | # | ||
| + | # | ||
| + | # | ||
| + | # | ||
| + | # | ||
| + | # | ||
| + | # | ||
| + | #define ifr_map ifr_ifru.ifru_map / | ||
| + | #define ifr_slave ifr_ifru.ifru_slave / | ||
| + | # | ||
| + | #define ifr_ifindex ifr_ifru.ifru_ivalue / | ||
| + | #define ifr_bandwidth ifr_ifru.ifru_ivalue | ||
| + | #define ifr_qlen ifr_ifru.ifru_ivalue / | ||
| + | #define ifr_newname ifr_ifru.ifru_newname / | ||
| + | #define ifr_settings ifr_ifru.ifru_settings / | ||
| </ | </ | ||
| ===== net/ | ===== net/ | ||
| + | |||
| + | ==== dev_ioctl() ==== | ||
| + | |||
| + | < | ||
| + | int dev_ioctl(struct net *net, unsigned int cmd, struct ifreq *ifr, bool *need_copyout) | ||
| + | { | ||
| + | |||
| + | ... snip ... | ||
| + | |||
| + | switch (cmd) { | ||
| + | /* | ||
| + | * These ioctl calls: | ||
| + | * - can be done by all. | ||
| + | * - atomic and do not require locking. | ||
| + | * - return a value | ||
| + | */ | ||
| + | case SIOCGIFFLAGS: | ||
| + | case SIOCGIFMETRIC: | ||
| + | case SIOCGIFMTU: | ||
| + | case SIOCGIFHWADDR: | ||
| + | case SIOCGIFSLAVE: | ||
| + | case SIOCGIFMAP: | ||
| + | case SIOCGIFINDEX: | ||
| + | case SIOCGIFTXQLEN: | ||
| + | dev_load(net, | ||
| + | rcu_read_lock(); | ||
| + | ret = dev_ifsioc_locked(net, | ||
| + | rcu_read_unlock(); | ||
| + | if (colon) | ||
| + | *colon = ':'; | ||
| + | return ret; | ||
| + | |||
| + | ... snip ... | ||
| + | |||
| + | case SIOCSIFFLAGS: | ||
| + | case SIOCSIFMETRIC: | ||
| + | case SIOCSIFMTU: | ||
| + | case SIOCSIFHWADDR: | ||
| + | case SIOCSIFSLAVE: | ||
| + | case SIOCADDMULTI: | ||
| + | case SIOCDELMULTI: | ||
| + | case SIOCSIFHWBROADCAST: | ||
| + | case SIOCSMIIREG: | ||
| + | case SIOCBONDENSLAVE: | ||
| + | case SIOCBONDRELEASE: | ||
| + | case SIOCBONDSETHWADDR: | ||
| + | case SIOCBONDCHANGEACTIVE: | ||
| + | case SIOCBRADDIF: | ||
| + | case SIOCBRDELIF: | ||
| + | case SIOCSHWTSTAMP: | ||
| + | if (!ns_capable(net-> | ||
| + | return -EPERM; | ||
| + | /* fall through */ | ||
| + | case SIOCBONDSLAVEINFOQUERY: | ||
| + | case SIOCBONDINFOQUERY: | ||
| + | dev_load(net, | ||
| + | rtnl_lock(); | ||
| + | ret = dev_ifsioc(net, | ||
| + | rtnl_unlock(); | ||
| + | if (need_copyout) | ||
| + | *need_copyout = false; | ||
| + | return ret; | ||
| + | </ | ||
| + | |||
| + | ==== dev_ifsioc() ==== | ||
| + | |||
| + | < | ||
| + | static int dev_ifsioc(struct net *net, struct ifreq *ifr, unsigned int cmd) | ||
| + | { | ||
| + | int err; | ||
| + | struct net_device *dev = __dev_get_by_name(net, | ||
| + | const struct net_device_ops *ops; | ||
| + | |||
| + | if (!dev) | ||
| + | return -ENODEV; | ||
| + | |||
| + | ops = dev-> | ||
| + | |||
| + | switch (cmd) { | ||
| + | case SIOCSIFFLAGS: | ||
| + | return dev_change_flags(dev, | ||
| + | </ | ||
| + | |||
| + | ===== net/ | ||
| + | |||
| + | ==== dev_change_flags() ==== | ||
| + | |||
| + | < | ||
| + | int dev_change_flags(struct net_device *dev, unsigned int flags) | ||
| + | { | ||
| + | int ret; | ||
| + | unsigned int changes, old_flags = dev-> | ||
| + | |||
| + | ret = __dev_change_flags(dev, | ||
| + | if (ret < 0) | ||
| + | return ret; | ||
| + | |||
| + | changes = (old_flags ^ dev-> | ||
| + | __dev_notify_flags(dev, | ||
| + | return ret; | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | ==== __dev_change_flags() ==== | ||
| + | |||
| + | < | ||
| + | int __dev_change_flags(struct net_device *dev, unsigned int flags) | ||
| + | { | ||
| + | unsigned int old_flags = dev-> | ||
| + | int ret; | ||
| + | |||
| + | ASSERT_RTNL(); | ||
| + | |||
| + | /* | ||
| + | * Set the flags on our device. | ||
| + | */ | ||
| + | |||
| + | dev-> | ||
| + | | ||
| + | | ||
| + | | ||
| + | IFF_ALLMULTI)); | ||
| + | |||
| + | ... snip ... | ||
| + | |||
| + | ret = 0; | ||
| + | if ((old_flags ^ flags) & IFF_UP) { | ||
| + | if (old_flags & IFF_UP) | ||
| + | __dev_close(dev); | ||
| + | else | ||
| + | ret = __dev_open(dev); | ||
| + | } | ||
| + | </ | ||