811 lines · 19.5 KB · C
RawDownload
/* * INET		An implementation of the TCP/IP protocol suite for the LINUX *		operating system.  INET is implemented using the  BSD Socket *		interface as the means of communication with the user level. * *		Routing netlink socket interface: protocol independent part. * * Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> * *		This program is free software; you can redistribute it and/or *		modify it under the terms of the GNU General Public License *		as published by the Free Software Foundation; either version *		2 of the License, or (at your option) any later version. * *	Fixes: *	Vitaly E. Lavrov		RTA_OK arithmetics was wrong. */ #include <linux/config.h>#include <linux/errno.h>#include <linux/module.h>#include <linux/types.h>#include <linux/socket.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/timer.h>#include <linux/string.h>#include <linux/sockios.h>#include <linux/net.h>#include <linux/fcntl.h>#include <linux/mm.h>#include <linux/slab.h>#include <linux/interrupt.h>#include <linux/capability.h>#include <linux/skbuff.h>#include <linux/init.h>#include <linux/security.h>#include <linux/mutex.h> #include <asm/uaccess.h>#include <asm/system.h>#include <asm/string.h> #include <linux/inet.h>#include <linux/netdevice.h>#include <net/ip.h>#include <net/protocol.h>#include <net/arp.h>#include <net/route.h>#include <net/udp.h>#include <net/sock.h>#include <net/pkt_sched.h>#include <net/netlink.h>#ifdef CONFIG_NET_WIRELESS_RTNETLINK#include <linux/wireless.h>#include <net/iw_handler.h>#endif	/* CONFIG_NET_WIRELESS_RTNETLINK */ static DEFINE_MUTEX(rtnl_mutex); void rtnl_lock(void){	mutex_lock(&rtnl_mutex);} void __rtnl_unlock(void){	mutex_unlock(&rtnl_mutex);} void rtnl_unlock(void){	mutex_unlock(&rtnl_mutex);	if (rtnl && rtnl->sk_receive_queue.qlen)		rtnl->sk_data_ready(rtnl, 0);	netdev_run_todo();} int rtnl_trylock(void){	return mutex_trylock(&rtnl_mutex);} int rtattr_parse(struct rtattr *tb[], int maxattr, struct rtattr *rta, int len){	memset(tb, 0, sizeof(struct rtattr*)*maxattr); 	while (RTA_OK(rta, len)) {		unsigned flavor = rta->rta_type;		if (flavor && flavor <= maxattr)			tb[flavor-1] = rta;		rta = RTA_NEXT(rta, len);	}	return 0;} struct sock *rtnl; struct rtnetlink_link * rtnetlink_links[NPROTO]; static const int rtm_min[RTM_NR_FAMILIES] ={	[RTM_FAM(RTM_NEWLINK)]      = NLMSG_LENGTH(sizeof(struct ifinfomsg)),	[RTM_FAM(RTM_NEWADDR)]      = NLMSG_LENGTH(sizeof(struct ifaddrmsg)),	[RTM_FAM(RTM_NEWROUTE)]     = NLMSG_LENGTH(sizeof(struct rtmsg)),	[RTM_FAM(RTM_NEWNEIGH)]     = NLMSG_LENGTH(sizeof(struct ndmsg)),	[RTM_FAM(RTM_NEWRULE)]      = NLMSG_LENGTH(sizeof(struct rtmsg)),	[RTM_FAM(RTM_NEWQDISC)]     = NLMSG_LENGTH(sizeof(struct tcmsg)),	[RTM_FAM(RTM_NEWTCLASS)]    = NLMSG_LENGTH(sizeof(struct tcmsg)),	[RTM_FAM(RTM_NEWTFILTER)]   = NLMSG_LENGTH(sizeof(struct tcmsg)),	[RTM_FAM(RTM_NEWACTION)]    = NLMSG_LENGTH(sizeof(struct tcamsg)),	[RTM_FAM(RTM_NEWPREFIX)]    = NLMSG_LENGTH(sizeof(struct rtgenmsg)),	[RTM_FAM(RTM_GETMULTICAST)] = NLMSG_LENGTH(sizeof(struct rtgenmsg)),	[RTM_FAM(RTM_GETANYCAST)]   = NLMSG_LENGTH(sizeof(struct rtgenmsg)),	[RTM_FAM(RTM_NEWNEIGHTBL)]  = NLMSG_LENGTH(sizeof(struct ndtmsg)),}; static const int rta_max[RTM_NR_FAMILIES] ={	[RTM_FAM(RTM_NEWLINK)]      = IFLA_MAX,	[RTM_FAM(RTM_NEWADDR)]      = IFA_MAX,	[RTM_FAM(RTM_NEWROUTE)]     = RTA_MAX,	[RTM_FAM(RTM_NEWNEIGH)]     = NDA_MAX,	[RTM_FAM(RTM_NEWRULE)]      = RTA_MAX,	[RTM_FAM(RTM_NEWQDISC)]     = TCA_MAX,	[RTM_FAM(RTM_NEWTCLASS)]    = TCA_MAX,	[RTM_FAM(RTM_NEWTFILTER)]   = TCA_MAX,	[RTM_FAM(RTM_NEWACTION)]    = TCAA_MAX,	[RTM_FAM(RTM_NEWNEIGHTBL)]  = NDTA_MAX,}; void __rta_fill(struct sk_buff *skb, int attrtype, int attrlen, const void *data){	struct rtattr *rta;	int size = RTA_LENGTH(attrlen); 	rta = (struct rtattr*)skb_put(skb, RTA_ALIGN(size));	rta->rta_type = attrtype;	rta->rta_len = size;	memcpy(RTA_DATA(rta), data, attrlen);	memset(RTA_DATA(rta) + attrlen, 0, RTA_ALIGN(size) - size);} size_t rtattr_strlcpy(char *dest, const struct rtattr *rta, size_t size){	size_t ret = RTA_PAYLOAD(rta);	char *src = RTA_DATA(rta); 	if (ret > 0 && src[ret - 1] == '\0')		ret--;	if (size > 0) {		size_t len = (ret >= size) ? size - 1 : ret;		memset(dest, 0, size);		memcpy(dest, src, len);	}	return ret;} int rtnetlink_send(struct sk_buff *skb, u32 pid, unsigned group, int echo){	int err = 0; 	NETLINK_CB(skb).dst_group = group;	if (echo)		atomic_inc(&skb->users);	netlink_broadcast(rtnl, skb, pid, group, GFP_KERNEL);	if (echo)		err = netlink_unicast(rtnl, skb, pid, MSG_DONTWAIT);	return err;} int rtnetlink_put_metrics(struct sk_buff *skb, u32 *metrics){	struct rtattr *mx = (struct rtattr*)skb->tail;	int i; 	RTA_PUT(skb, RTA_METRICS, 0, NULL);	for (i=0; i<RTAX_MAX; i++) {		if (metrics[i])			RTA_PUT(skb, i+1, sizeof(u32), metrics+i);	}	mx->rta_len = skb->tail - (u8*)mx;	if (mx->rta_len == RTA_LENGTH(0))		skb_trim(skb, (u8*)mx - skb->data);	return 0; rtattr_failure:	skb_trim(skb, (u8*)mx - skb->data);	return -1;}  static void set_operstate(struct net_device *dev, unsigned char transition){	unsigned char operstate = dev->operstate; 	switch(transition) {	case IF_OPER_UP:		if ((operstate == IF_OPER_DORMANT ||		     operstate == IF_OPER_UNKNOWN) &&		    !netif_dormant(dev))			operstate = IF_OPER_UP;		break; 	case IF_OPER_DORMANT:		if (operstate == IF_OPER_UP ||		    operstate == IF_OPER_UNKNOWN)			operstate = IF_OPER_DORMANT;		break;	}; 	if (dev->operstate != operstate) {		write_lock_bh(&dev_base_lock);		dev->operstate = operstate;		write_unlock_bh(&dev_base_lock);		netdev_state_change(dev);	}} static int rtnetlink_fill_ifinfo(struct sk_buff *skb, struct net_device *dev,				 int type, u32 pid, u32 seq, u32 change, 				 unsigned int flags){	struct ifinfomsg *r;	struct nlmsghdr  *nlh;	unsigned char	 *b = skb->tail; 	nlh = NLMSG_NEW(skb, pid, seq, type, sizeof(*r), flags);	r = NLMSG_DATA(nlh);	r->ifi_family = AF_UNSPEC;	r->__ifi_pad = 0;	r->ifi_type = dev->type;	r->ifi_index = dev->ifindex;	r->ifi_flags = dev_get_flags(dev);	r->ifi_change = change; 	RTA_PUT(skb, IFLA_IFNAME, strlen(dev->name)+1, dev->name); 	if (1) {		u32 txqlen = dev->tx_queue_len;		RTA_PUT(skb, IFLA_TXQLEN, sizeof(txqlen), &txqlen);	} 	if (1) {		u32 weight = dev->weight;		RTA_PUT(skb, IFLA_WEIGHT, sizeof(weight), &weight);	} 	if (1) {		u8 operstate = netif_running(dev)?dev->operstate:IF_OPER_DOWN;		u8 link_mode = dev->link_mode;		RTA_PUT(skb, IFLA_OPERSTATE, sizeof(operstate), &operstate);		RTA_PUT(skb, IFLA_LINKMODE, sizeof(link_mode), &link_mode);	} 	if (1) {		struct rtnl_link_ifmap map = {			.mem_start   = dev->mem_start,			.mem_end     = dev->mem_end,			.base_addr   = dev->base_addr,			.irq         = dev->irq,			.dma         = dev->dma,			.port        = dev->if_port,		};		RTA_PUT(skb, IFLA_MAP, sizeof(map), &map);	} 	if (dev->addr_len) {		RTA_PUT(skb, IFLA_ADDRESS, dev->addr_len, dev->dev_addr);		RTA_PUT(skb, IFLA_BROADCAST, dev->addr_len, dev->broadcast);	} 	if (1) {		u32 mtu = dev->mtu;		RTA_PUT(skb, IFLA_MTU, sizeof(mtu), &mtu);	} 	if (dev->ifindex != dev->iflink) {		u32 iflink = dev->iflink;		RTA_PUT(skb, IFLA_LINK, sizeof(iflink), &iflink);	} 	if (dev->qdisc_sleeping)		RTA_PUT(skb, IFLA_QDISC,			strlen(dev->qdisc_sleeping->ops->id) + 1,			dev->qdisc_sleeping->ops->id);		if (dev->master) {		u32 master = dev->master->ifindex;		RTA_PUT(skb, IFLA_MASTER, sizeof(master), &master);	} 	if (dev->get_stats) {		unsigned long *stats = (unsigned long*)dev->get_stats(dev);		if (stats) {			struct rtattr  *a;			__u32	       *s;			int		i;			int		n = sizeof(struct rtnl_link_stats)/4; 			a = __RTA_PUT(skb, IFLA_STATS, n*4);			s = RTA_DATA(a);			for (i=0; i<n; i++)				s[i] = stats[i];		}	}	nlh->nlmsg_len = skb->tail - b;	return skb->len; nlmsg_failure:rtattr_failure:	skb_trim(skb, b - skb->data);	return -1;} static int rtnetlink_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb){	int idx;	int s_idx = cb->args[0];	struct net_device *dev; 	read_lock(&dev_base_lock);	for (dev=dev_base, idx=0; dev; dev = dev->next, idx++) {		if (idx < s_idx)			continue;		if (rtnetlink_fill_ifinfo(skb, dev, RTM_NEWLINK,					  NETLINK_CB(cb->skb).pid,					  cb->nlh->nlmsg_seq, 0,					  NLM_F_MULTI) <= 0)			break;	}	read_unlock(&dev_base_lock);	cb->args[0] = idx; 	return skb->len;} static int do_setlink(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg){	struct ifinfomsg  *ifm = NLMSG_DATA(nlh);	struct rtattr    **ida = arg;	struct net_device *dev;	int err, send_addr_notify = 0; 	if (ifm->ifi_index >= 0)		dev = dev_get_by_index(ifm->ifi_index);	else if (ida[IFLA_IFNAME - 1]) {		char ifname[IFNAMSIZ]; 		if (rtattr_strlcpy(ifname, ida[IFLA_IFNAME - 1],		                   IFNAMSIZ) >= IFNAMSIZ)			return -EINVAL;		dev = dev_get_by_name(ifname);	} else		return -EINVAL; 	if (!dev)		return -ENODEV; 	err = -EINVAL; 	if (ifm->ifi_flags)		dev_change_flags(dev, ifm->ifi_flags); 	if (ida[IFLA_MAP - 1]) {		struct rtnl_link_ifmap *u_map;		struct ifmap k_map; 		if (!dev->set_config) {			err = -EOPNOTSUPP;			goto out;		} 		if (!netif_device_present(dev)) {			err = -ENODEV;			goto out;		}				if (ida[IFLA_MAP - 1]->rta_len != RTA_LENGTH(sizeof(*u_map)))			goto out; 		u_map = RTA_DATA(ida[IFLA_MAP - 1]); 		k_map.mem_start = (unsigned long) u_map->mem_start;		k_map.mem_end = (unsigned long) u_map->mem_end;		k_map.base_addr = (unsigned short) u_map->base_addr;		k_map.irq = (unsigned char) u_map->irq;		k_map.dma = (unsigned char) u_map->dma;		k_map.port = (unsigned char) u_map->port; 		err = dev->set_config(dev, &k_map); 		if (err)			goto out;	} 	if (ida[IFLA_ADDRESS - 1]) {		if (!dev->set_mac_address) {			err = -EOPNOTSUPP;			goto out;		}		if (!netif_device_present(dev)) {			err = -ENODEV;			goto out;		}		if (ida[IFLA_ADDRESS - 1]->rta_len != RTA_LENGTH(dev->addr_len))			goto out; 		err = dev->set_mac_address(dev, RTA_DATA(ida[IFLA_ADDRESS - 1]));		if (err)			goto out;		send_addr_notify = 1;	} 	if (ida[IFLA_BROADCAST - 1]) {		if (ida[IFLA_BROADCAST - 1]->rta_len != RTA_LENGTH(dev->addr_len))			goto out;		memcpy(dev->broadcast, RTA_DATA(ida[IFLA_BROADCAST - 1]),		       dev->addr_len);		send_addr_notify = 1;	} 	if (ida[IFLA_MTU - 1]) {		if (ida[IFLA_MTU - 1]->rta_len != RTA_LENGTH(sizeof(u32)))			goto out;		err = dev_set_mtu(dev, *((u32 *) RTA_DATA(ida[IFLA_MTU - 1]))); 		if (err)			goto out; 	} 	if (ida[IFLA_TXQLEN - 1]) {		if (ida[IFLA_TXQLEN - 1]->rta_len != RTA_LENGTH(sizeof(u32)))			goto out; 		dev->tx_queue_len = *((u32 *) RTA_DATA(ida[IFLA_TXQLEN - 1]));	} 	if (ida[IFLA_WEIGHT - 1]) {		if (ida[IFLA_WEIGHT - 1]->rta_len != RTA_LENGTH(sizeof(u32)))			goto out; 		dev->weight = *((u32 *) RTA_DATA(ida[IFLA_WEIGHT - 1]));	} 	if (ida[IFLA_OPERSTATE - 1]) {		if (ida[IFLA_OPERSTATE - 1]->rta_len != RTA_LENGTH(sizeof(u8)))			goto out; 		set_operstate(dev, *((u8 *) RTA_DATA(ida[IFLA_OPERSTATE - 1])));	} 	if (ida[IFLA_LINKMODE - 1]) {		if (ida[IFLA_LINKMODE - 1]->rta_len != RTA_LENGTH(sizeof(u8)))			goto out; 		write_lock_bh(&dev_base_lock);		dev->link_mode = *((u8 *) RTA_DATA(ida[IFLA_LINKMODE - 1]));		write_unlock_bh(&dev_base_lock);	} 	if (ifm->ifi_index >= 0 && ida[IFLA_IFNAME - 1]) {		char ifname[IFNAMSIZ]; 		if (rtattr_strlcpy(ifname, ida[IFLA_IFNAME - 1],		                   IFNAMSIZ) >= IFNAMSIZ)			goto out;		err = dev_change_name(dev, ifname);		if (err)			goto out;	} #ifdef CONFIG_NET_WIRELESS_RTNETLINK	if (ida[IFLA_WIRELESS - 1]) { 		/* Call Wireless Extensions.		 * Various stuff checked in there... */		err = wireless_rtnetlink_set(dev, RTA_DATA(ida[IFLA_WIRELESS - 1]), ida[IFLA_WIRELESS - 1]->rta_len);		if (err)			goto out;	}#endif	/* CONFIG_NET_WIRELESS_RTNETLINK */ 	err = 0; out:	if (send_addr_notify)		call_netdevice_notifiers(NETDEV_CHANGEADDR, dev); 	dev_put(dev);	return err;} #ifdef CONFIG_NET_WIRELESS_RTNETLINKstatic int do_getlink(struct sk_buff *in_skb, struct nlmsghdr* in_nlh, void *arg){	struct ifinfomsg  *ifm = NLMSG_DATA(in_nlh);	struct rtattr    **ida = arg;	struct net_device *dev;

Lines 1–500 of 811.