User Tools

Site Tools


ip_up_down

Overview

Control flow when running one of:

  • ip link show
  • ip link show eth0
  • ip link set eth0 up
  • ip link set eth0 down

Links:

Issues:

  • iplink.c redefines iplink_req for no reason (line 218)
  • Define HAVE_LIBMNL for error support.

Structures

From ip/ip_common.h:

struct iplink_req {
	struct nlmsghdr		n;
	struct ifinfomsg	i;
	char			buf[1024];
};

struct ifinfomsg

From include/uapi/linux/rtnetlink.h:

/* struct ifinfomsg
 * passes link level specific information, not dependent
 * on network protocol.
 */

struct ifinfomsg {
	unsigned char	ifi_family;
	unsigned char	__ifi_pad;
	unsigned short	ifi_type;		/* ARPHRD_* */
	int		ifi_index;		/* Link index	*/
	unsigned	ifi_flags;		/* IFF_* flags	*/
	unsigned	ifi_change;		/* IFF_* change mask */
};

ifi_flags

From include/uapi/linux/if.h:

enum net_device_flags {
/* for compatibility with glibc net/if.h */
#if __UAPI_DEF_IF_NET_DEVICE_FLAGS
	IFF_UP				= 1<<0,  /* sysfs */
	IFF_BROADCAST			= 1<<1,  /* volatile */
	IFF_DEBUG			= 1<<2,  /* sysfs */
	IFF_LOOPBACK			= 1<<3,  /* volatile */
	IFF_POINTOPOINT			= 1<<4,  /* volatile */
	IFF_NOTRAILERS			= 1<<5,  /* sysfs */
	IFF_RUNNING			= 1<<6,  /* volatile */
	IFF_NOARP			= 1<<7,  /* sysfs */
	IFF_PROMISC			= 1<<8,  /* sysfs */
	IFF_ALLMULTI			= 1<<9,  /* sysfs */
	IFF_MASTER			= 1<<10, /* volatile */
	IFF_SLAVE			= 1<<11, /* volatile */
	IFF_MULTICAST			= 1<<12, /* sysfs */
	IFF_PORTSEL			= 1<<13, /* sysfs */
	IFF_AUTOMEDIA			= 1<<14, /* sysfs */
	IFF_DYNAMIC			= 1<<15, /* sysfs */
#endif /* __UAPI_DEF_IF_NET_DEVICE_FLAGS */
#if __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO
	IFF_LOWER_UP			= 1<<16, /* volatile */
	IFF_DORMANT			= 1<<17, /* volatile */
	IFF_ECHO			= 1<<18, /* volatile */
#endif /* __UAPI_DEF_IF_NET_DEVICE_FLAGS_LOWER_UP_DORMANT_ECHO */
};

Usage

NAME
       ip-link - network device configuration

SYNOPSIS
       ip link  { COMMAND | help }

       ip link add [ link DEVICE ] [ name ] NAME
               [ txqueuelen PACKETS ]
               [ address LLADDR ] [ broadcast LLADDR ]
               [ mtu MTU ] [ index IDX ]
               [ numtxqueues QUEUE_COUNT ] [ numrxqueues QUEUE_COUNT ]
               [ gso_max_size BYTES ] [ gso_max_segs SEGMENTS ]
               type TYPE [ ARGS ]

       ip link delete { DEVICE | group GROUP } type TYPE [ ARGS ]

       ip link set { DEVICE | group GROUP }
               [ { up | down } ]
               [ type ETYPE TYPE_ARGS ]
               [ arp { on | off } ]
               [ dynamic { on | off } ]
               [ multicast { on | off } ]
               [ allmulticast { on | off } ]
               [ promisc { on | off } ]
               [ protodown { on | off } ]
               [ trailers { on | off } ]
               [ txqueuelen PACKETS ]
               [ name NEWNAME ]
               [ address LLADDR ]
               [ broadcast LLADDR ]
               [ mtu MTU ]
               [ netns { PID | NETNSNAME } ]
               [ link-netnsid ID ]
               [ alias NAME ]
               [ vf NUM [ mac LLADDR ]
                        [ VFVLAN-LIST ]
                        [ rate TXRATE ]
                        [ max_tx_rate TXRATE ]
                        [ min_tx_rate TXRATE ]
                        [ spoofchk { on | off } ]
                        [ query_rss { on | off } ]
                        [ state { auto | enable | disable } ]
                        [ trust { on | off } ]
                        [ node_guid eui64 ]
                        [ port_guid eui64 ] ]
               [ { xdp | xdpgeneric | xdpdrv | xdpoffload } { off |
                       object FILE [ section NAME ] [ verbose ] |
                       pinned FILE } ]
               [ master DEVICE ]
               [ nomaster ]
               [ vrf NAME ]
               [ addrgenmode { eui64 | none | stable_secret | random } ]
               [ macaddr { flush | { add | del } MACADDR | set [ MACADDR [ MACADDR [ ... ] ]
               ] } ]

       ip link show [ DEVICE | group GROUP ] [ up ] [ master DEVICE ] [ type ETYPE ] [ vrf
               NAME ]

       ip link xstats type TYPE [ ARGS ]

       ip link afstats [ dev DEVICE ]

       ip link help [ TYPE ]

       TYPE := [ bridge | bond | can | dummy | hsr | ifb | ipoib | macvlan | macvtap | vcan
               | vxcan | veth | vlan | vxlan | ip6tnl | ipip | sit | gre | gretap | erspan |
               ip6gre | ip6gretap | ip6erspan | vti | nlmon | ipvlan | lowpan | geneve | vrf
               | macsec | netdevsim | rmnet ]

       ETYPE := [ TYPE | bridge_slave | bond_slave ]

       VFVLAN-LIST := [ VFVLAN-LIST ] VFVLAN

       VFVLAN := [ vlan VLANID [ qos VLAN-QOS ] [ proto VLAN-PROTO ] ]

ip/iplink.c

int do_iplink(int argc, char **argv)
{
        if (argc < 1)
                return ipaddr_list_link(0, NULL);

        if (iplink_have_newlink()) {
                if (matches(*argv, "add") == 0)
                        return iplink_modify(RTM_NEWLINK,
                                             NLM_F_CREATE|NLM_F_EXCL,
                                             argc-1, argv+1);
                if (matches(*argv, "set") == 0 ||
                    matches(*argv, "change") == 0)
                        return iplink_modify(RTM_NEWLINK, 0,
                                             argc-1, argv+1);
                if (matches(*argv, "replace") == 0)
                        return iplink_modify(RTM_NEWLINK,
                                             NLM_F_CREATE|NLM_F_REPLACE,
                                             argc-1, argv+1);
                if (matches(*argv, "delete") == 0)
                        return iplink_modify(RTM_DELLINK, 0,
                                             argc-1, argv+1);
        } else {
#if IPLINK_IOCTL_COMPAT
                if (matches(*argv, "set") == 0)
                        return do_set(argc-1, argv+1);
#endif
... snip ...

do_set()

Modify the appropriate flags:

static int do_set(int argc, char **argv)
{
        char *dev = NULL;
        __u32 mask = 0;
        __u32 flags = 0;
        int qlen = -1;
        int mtu = -1;
        char *newaddr = NULL;
        char *newbrd = NULL;
        struct ifreq ifr0, ifr1;
        char *newname = NULL;
        int htype, halen;

        while (argc > 0) {
                if (strcmp(*argv, "up") == 0) {
                        mask |= IFF_UP;
                        flags |= IFF_UP;
                } else if (strcmp(*argv, "down") == 0) {
                        mask |= IFF_UP;
                        flags &= ~IFF_UP;
... snip ...
        if (mask)
                return do_chflags(dev, flags, mask);
        return 0;
}

do_chflags()

static int do_chflags(const char *dev, __u32 flags, __u32 mask)
{
        struct ifreq ifr;
        int fd;
        int err;

        strlcpy(ifr.ifr_name, dev, IFNAMSIZ);
        fd = get_ctl_fd();
        if (fd < 0)
                return -1;
        err = ioctl(fd, SIOCGIFFLAGS, &ifr);
        if (err) {
                perror("SIOCGIFFLAGS");
                close(fd);
                return -1;
        }
        if ((ifr.ifr_flags^flags)&mask) {
                ifr.ifr_flags &= ~mask;
                ifr.ifr_flags |= mask&flags;
                err = ioctl(fd, SIOCSIFFLAGS, &ifr);
                if (err)
                        perror("SIOCSIFFLAGS");
        }
        close(fd);
        return err;
}

Sockets and ioctl calls (net/socket.c)

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->private_data;
	sk = sock->sk;
	net = sock_net(sk);

... snip ...

		default:
			err = sock_do_ioctl(net, sock, cmd, arg,
					    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->ops->ioctl(sock, cmd, arg);

	/*
	 * 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(&ifc, argp, sizeof(struct ifconf)))
			return -EFAULT;
		rtnl_lock();
		err = dev_ifconf(net, &ifc, sizeof(struct ifreq));
		rtnl_unlock();
		if (!err && copy_to_user(argp, &ifc, sizeof(struct ifconf)))
			err = -EFAULT;
	} else {
		struct ifreq ifr;
		bool need_copyout;
		if (copy_from_user(&ifr, argp, ifreq_size))
			return -EFAULT;
		err = dev_ioctl(net, cmd, &ifr, &need_copyout);
		if (!err && need_copyout)
			if (copy_to_user(argp, &ifr, ifreq_size))
				return -EFAULT;
	}
	return err;
}

struct ifreq

From include/uapi/linux/if.h:

/* for compatibility with glibc net/if.h */
#if __UAPI_DEF_IF_IFREQ
struct ifreq {
#define IFHWADDRLEN	6
	union
	{
		char	ifrn_name[IFNAMSIZ];		/* if name, e.g. "en0" */
	} ifr_ifrn;
	
	union {
		struct	sockaddr ifru_addr;
		struct	sockaddr ifru_dstaddr;
		struct	sockaddr ifru_broadaddr;
		struct	sockaddr ifru_netmask;
		struct  sockaddr ifru_hwaddr;
		short	ifru_flags;
		int	ifru_ivalue;
		int	ifru_mtu;
		struct  ifmap ifru_map;
		char	ifru_slave[IFNAMSIZ];	/* Just fits the size */
		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	/* interface name 	*/
#define ifr_hwaddr	ifr_ifru.ifru_hwaddr	/* MAC address 		*/
#define	ifr_addr	ifr_ifru.ifru_addr	/* address		*/
#define	ifr_dstaddr	ifr_ifru.ifru_dstaddr	/* other end of p-p lnk	*/
#define	ifr_broadaddr	ifr_ifru.ifru_broadaddr	/* broadcast address	*/
#define	ifr_netmask	ifr_ifru.ifru_netmask	/* interface net mask	*/
#define	ifr_flags	ifr_ifru.ifru_flags	/* flags		*/
#define	ifr_metric	ifr_ifru.ifru_ivalue	/* metric		*/
#define	ifr_mtu		ifr_ifru.ifru_mtu	/* mtu			*/
#define ifr_map		ifr_ifru.ifru_map	/* device map		*/
#define ifr_slave	ifr_ifru.ifru_slave	/* slave device		*/
#define	ifr_data	ifr_ifru.ifru_data	/* for use by interface	*/
#define ifr_ifindex	ifr_ifru.ifru_ivalue	/* interface index	*/
#define ifr_bandwidth	ifr_ifru.ifru_ivalue    /* link bandwidth	*/
#define ifr_qlen	ifr_ifru.ifru_ivalue	/* Queue length 	*/
#define ifr_newname	ifr_ifru.ifru_newname	/* New name		*/
#define ifr_settings	ifr_ifru.ifru_settings	/* Device/proto settings*/

net/core/dev_ioctl.c

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, ifr->ifr_name);
		rcu_read_lock();
		ret = dev_ifsioc_locked(net, ifr, cmd);
		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->user_ns, CAP_NET_ADMIN))
			return -EPERM;
		/* fall through */
	case SIOCBONDSLAVEINFOQUERY:
	case SIOCBONDINFOQUERY:
		dev_load(net, ifr->ifr_name);
		rtnl_lock();
		ret = dev_ifsioc(net, ifr, cmd);
		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, 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);

net/core/dev.c

dev_change_flags()

int dev_change_flags(struct net_device *dev, unsigned int flags)
{
	int ret;
	unsigned int changes, old_flags = dev->flags, old_gflags = dev->gflags;

	ret = __dev_change_flags(dev, flags);
	if (ret < 0)
		return ret;

	changes = (old_flags ^ dev->flags) | (old_gflags ^ dev->gflags);
	__dev_notify_flags(dev, old_flags, changes);
	return ret;
}

__dev_change_flags()

int __dev_change_flags(struct net_device *dev, unsigned int flags)
{
	unsigned int old_flags = dev->flags;
	int ret;

	ASSERT_RTNL();

	/*
	 *	Set the flags on our device.
	 */

	dev->flags = (flags & (IFF_DEBUG | IFF_NOTRAILERS | IFF_NOARP |
			       IFF_DYNAMIC | IFF_MULTICAST | IFF_PORTSEL |
			       IFF_AUTOMEDIA)) |
		     (dev->flags & (IFF_UP | IFF_VOLATILE | IFF_PROMISC |
				    IFF_ALLMULTI));

... snip ...

	ret = 0;
	if ((old_flags ^ flags) & IFF_UP) {
		if (old_flags & IFF_UP)
			__dev_close(dev);
		else
			ret = __dev_open(dev);
	}
ip_up_down.txt · Last modified: 2018/10/04 11:09 by rpjday