INET Sockets Tour: ioctl()

Last updated 2002-01-31 10:43 am


ioctl() tour

Anatomy of ioctl() thread

PLEASE WAIT FOR THE EXPLANATION.

arch/i386/kernel/entry.S

ENTRY(sys_call_table) ..... .long SYMBOL_NAME(sys_ioctl) /* 54 */

fs/ioctl.c/sys_ioctl()

asmlinkage int sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) { struct file * filp; unsigned int flag; int on, error = -EBADF; lock_kernel(); filp = fget(fd); if (!filp) goto out; error = 0; switch (cmd) { ..... default: ..... else if (filp->f_op && filp->f_op->ioctl) error = filp->f_op->ioctl(filp->f_dentry->d_inode, filp, cmd, arg); } fput(filp); out: unlock_kernel(); return error; }

net/socket.c/sock_ioctl()

/* * With an ioctl arg may well be a user mode pointer, but we don't know what to do * with it - that's up to the protocol still. */ int sock_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { struct socket *sock = socki_lookup(inode); return sock->ops->ioctl(sock, cmd, arg); }

net/ipv4/af_inet.c

struct proto_ops inet_stream_ops = { PF_INET, sock_no_dup, inet_release, inet_bind, inet_stream_connect, sock_no_socketpair, inet_accept, inet_getname, inet_poll, inet_ioctl, inet_listen, inet_shutdown, inet_setsockopt, inet_getsockopt, sock_no_fcntl, inet_sendmsg, inet_recvmsg }; struct proto_ops inet_dgram_ops = { PF_INET, sock_no_dup, inet_release, inet_bind, inet_dgram_connect, sock_no_socketpair, sock_no_accept, inet_getname, datagram_poll, inet_ioctl, sock_no_listen, inet_shutdown, inet_setsockopt, inet_getsockopt, sock_no_fcntl, inet_sendmsg, inet_recvmsg };

net/ipv4/af_inet.c/inet_ioctl()

static int inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) { struct sock *sk = sock->sk; int err; int pid; switch(cmd) { ..... case SIOCADDRT: case SIOCDELRT: case SIOCRTMSG: return(ip_rt_ioctl(cmd,(void *) arg)); case SIOCDARP: case SIOCGARP: case SIOCSARP: return(arp_ioctl(cmd,(void *) arg)); ..... case SIOCGIFADDR: case SIOCSIFADDR: case SIOCGIFBRDADDR: case SIOCSIFBRDADDR: case SIOCGIFNETMASK: case SIOCSIFNETMASK: case SIOCGIFDSTADDR: case SIOCSIFDSTADDR: case SIOCSIFPFLAGS: case SIOCGIFPFLAGS: case SIOCSIFFLAGS: return(devinet_ioctl(cmd,(void *) arg)); ..... default: // case SIOCGIFFLAGS: // case SIOCSIFHWADDR: // case SIOCGIFHWADDR: // etc. ..... if (sk->prot->ioctl==NULL || (err=sk->prot->ioctl(sk, cmd, arg))==-ENOIOCTLC MD) return(dev_ioctl(cmd,(void *) arg)); return err; } /*NOTREACHED*/ return(0); }

net/ipv4/devinet.c/devinet_ioctl()

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 device *dev; ..... 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 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; rtnl_lock(); exclusive = 1; 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; rtnl_lock(); exclusive = 1; break; default: return -EINVAL; } ..... switch(cmd) { 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; case SIOCSIFFLAGS: ..... ret = dev_change_flags(dev, ifr.ifr_flags); break; case SIOCSIFADDR: /* Set interface address (and family) */ ..... ifa->ifa_address = ifa->ifa_local = sin->sin_addr.s_addr; ..... ret = inet_set_ifa(dev, ifa); break; case SIOCSIFBRDADDR: /* Set the broadcast address */ ..... ifa->ifa_broadcast = sin->sin_addr.s_addr; inet_insert_ifa(in_dev, ifa); } break; case SIOCSIFDSTADDR: /* Set the destination address */ ..... ifa->ifa_address = sin->sin_addr.s_addr; inet_insert_ifa(in_dev, ifa); } break; case SIOCSIFNETMASK: /* Set the netmask for the interface */ ..... ifa->ifa_mask = sin->sin_addr.s_addr; ifa->ifa_prefixlen = inet_mask_len(ifa->ifa_mask); inet_set_ifa(dev, ifa); } break; } done: if (exclusive) rtnl_unlock(); return ret; rarok: if (copy_to_user(arg, &ifr, sizeof(struct ifreq))) return -EFAULT; return 0; }

net/core/dev.c/dev_ioctl()

/* * This function handles all "interface"-type I/O control requests. The actual * 'doing' part of this is dev_ifsioc above. */ int dev_ioctl(unsigned int cmd, void *arg) { struct ifreq ifr; int ret; char *colon; ..... if (copy_from_user(&ifr, arg, sizeof(struct ifreq))) return -EFAULT; ..... /* * See which interface the caller is talking about. */ switch(cmd) { /* * These ioctl calls: * - can be done by all. * - atomic and do not require locking. * - return a value */ case SIOCGIFFLAGS: ..... case SIOCGIFHWADDR: ..... dev_load(ifr.ifr_name); ret = dev_ifsioc(&ifr, cmd); if (!ret) { if (colon) *colon = ':'; if (copy_to_user(arg, &ifr, sizeof(struct ifreq))) return -EFAULT; } return ret; /* * These ioctl calls: * - require superuser power. * - require strict serialization. * - do not return a value */ ..... case SIOCSIFHWADDR: ..... if (!capable(CAP_NET_ADMIN)) return -EPERM; dev_load(ifr.ifr_name); rtnl_lock(); ret = dev_ifsioc(&ifr, cmd); rtnl_unlock(); return ret; ..... } }

net/core/dev.c/dev_ifsioc()

/* * Perform the SIOCxIFxxx calls. */ static int dev_ifsioc(struct ifreq *ifr, unsigned int cmd) { struct device *dev; int err; if ((dev = dev_get(ifr->ifr_name)) == NULL) return -ENODEV; switch(cmd) { case SIOCGIFFLAGS: /* Get interface flags */ ifr->ifr_flags = (dev->flags&~(IFF_PROMISC|IFF_ALLMULTI)) |(dev->gflags&(IFF_PROMISC|IFF_ALLMULTI)); return 0; ..... case SIOCGIFHWADDR: memcpy(ifr->ifr_hwaddr.sa_data,dev->dev_addr, MAX_ADDR_LEN); ifr->ifr_hwaddr.sa_family=dev->type; return 0; case SIOCSIFHWADDR: if(dev->set_mac_address==NULL) return -EOPNOTSUPP; if(ifr->ifr_hwaddr.sa_family!=dev->type) return -EINVAL; err=dev->set_mac_address(dev,&ifr->ifr_hwaddr); if (!err) notifier_call_chain(&netdev_chain, NETDEV_CHANGEADDR, dev); return err; ..... } }

net/ipv4/fib_frontend.c/ip_rt_ioctl()

/* * Handle IP routing ioctl calls. These are used to manipulate the routing tables */ int ip_rt_ioctl(unsigned int cmd, void *arg) { int err; struct kern_rta rta; struct rtentry r; struct { struct nlmsghdr nlh; struct rtmsg rtm; } req; switch (cmd) { case SIOCADDRT: /* Add a route */ case SIOCDELRT: /* Delete a route */ if (!capable(CAP_NET_ADMIN)) return -EPERM; if (copy_from_user(&r, arg, sizeof(struct rtentry))) return -EFAULT; rtnl_lock(); err = fib_convert_rtentry(cmd, &req.nlh, &req.rtm, &rta, &r); if (err == 0) { if (cmd == SIOCDELRT) { struct fib_table *tb = fib_get_table(req.rtm.rtm_table); err = -ESRCH; if (tb) err = tb->tb_delete(tb, &req.rtm, &rta, &req.nlh, NULL); } else { struct fib_table *tb = fib_new_table(req.rtm.rtm_table); err = -ENOBUFS; if (tb) err = tb->tb_insert(tb, &req.rtm, &rta, &req.nlh, NULL); } if (rta.rta_mx) kfree(rta.rta_mx); } rtnl_unlock(); return err; } return -EINVAL; }

To be continued.