forked from mirrors/linux
		
	mctp: Add device handling and netlink interface
This change adds the infrastructure for managing MCTP netdevices; we add a pointer to the AF_MCTP-specific data to struct netdevice, and hook up the rtnetlink operations for adding and removing addresses. Includes changes from Matt Johnston <matt@codeconstruct.com.au>. Signed-off-by: Jeremy Kerr <jk@codeconstruct.com.au> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
		
							parent
							
								
									4b2e69305c
								
							
						
					
					
						commit
						583be982d9
					
				
					 10 changed files with 491 additions and 1 deletions
				
			
		|  | @ -11039,6 +11039,7 @@ L:	netdev@vger.kernel.org | ||||||
| S:	Maintained | S:	Maintained | ||||||
| F:	drivers/net/mctp/ | F:	drivers/net/mctp/ | ||||||
| F:	include/net/mctp.h | F:	include/net/mctp.h | ||||||
|  | F:	include/net/mctpdevice.h | ||||||
| F:	net/mctp/ | F:	net/mctp/ | ||||||
| 
 | 
 | ||||||
| MAN-PAGES: MANUAL PAGES FOR LINUX -- Sections 2, 3, 4, 5, and 7 | MAN-PAGES: MANUAL PAGES FOR LINUX -- Sections 2, 3, 4, 5, and 7 | ||||||
|  |  | ||||||
|  | @ -1823,6 +1823,7 @@ enum netdev_ml_priv_type { | ||||||
|  *	@ieee802154_ptr: IEEE 802.15.4 low-rate Wireless Personal Area Network |  *	@ieee802154_ptr: IEEE 802.15.4 low-rate Wireless Personal Area Network | ||||||
|  *			 device struct |  *			 device struct | ||||||
|  *	@mpls_ptr:	mpls_dev struct pointer |  *	@mpls_ptr:	mpls_dev struct pointer | ||||||
|  |  *	@mctp_ptr:	MCTP specific data | ||||||
|  * |  * | ||||||
|  *	@dev_addr:	Hw address (before bcast, |  *	@dev_addr:	Hw address (before bcast, | ||||||
|  *			because most packets are unicast) |  *			because most packets are unicast) | ||||||
|  | @ -2110,6 +2111,9 @@ struct net_device { | ||||||
| #if IS_ENABLED(CONFIG_MPLS_ROUTING) | #if IS_ENABLED(CONFIG_MPLS_ROUTING) | ||||||
| 	struct mpls_dev __rcu	*mpls_ptr; | 	struct mpls_dev __rcu	*mpls_ptr; | ||||||
| #endif | #endif | ||||||
|  | #if IS_ENABLED(CONFIG_MCTP) | ||||||
|  | 	struct mctp_dev __rcu	*mctp_ptr; | ||||||
|  | #endif | ||||||
| 
 | 
 | ||||||
| /*
 | /*
 | ||||||
|  * Cache lines mostly used on receive path (including eth_type_trans()) |  * Cache lines mostly used on receive path (including eth_type_trans()) | ||||||
|  |  | ||||||
|  | @ -10,6 +10,7 @@ | ||||||
| #define __NET_MCTP_H | #define __NET_MCTP_H | ||||||
| 
 | 
 | ||||||
| #include <linux/bits.h> | #include <linux/bits.h> | ||||||
|  | #include <linux/mctp.h> | ||||||
| 
 | 
 | ||||||
| /* MCTP packet definitions */ | /* MCTP packet definitions */ | ||||||
| struct mctp_hdr { | struct mctp_hdr { | ||||||
|  | @ -32,4 +33,17 @@ struct mctp_hdr { | ||||||
| #define MCTP_HDR_TAG_SHIFT	0 | #define MCTP_HDR_TAG_SHIFT	0 | ||||||
| #define MCTP_HDR_TAG_MASK	GENMASK(2, 0) | #define MCTP_HDR_TAG_MASK	GENMASK(2, 0) | ||||||
| 
 | 
 | ||||||
|  | static inline bool mctp_address_ok(mctp_eid_t eid) | ||||||
|  | { | ||||||
|  | 	return eid >= 8 && eid < 255; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline struct mctp_hdr *mctp_hdr(struct sk_buff *skb) | ||||||
|  | { | ||||||
|  | 	return (struct mctp_hdr *)skb_network_header(skb); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void mctp_device_init(void); | ||||||
|  | void mctp_device_exit(void); | ||||||
|  | 
 | ||||||
| #endif /* __NET_MCTP_H */ | #endif /* __NET_MCTP_H */ | ||||||
|  |  | ||||||
							
								
								
									
										35
									
								
								include/net/mctpdevice.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								include/net/mctpdevice.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,35 @@ | ||||||
|  | /* SPDX-License-Identifier: GPL-2.0 */ | ||||||
|  | /*
 | ||||||
|  |  * Management Component Transport Protocol (MCTP) - device | ||||||
|  |  * definitions. | ||||||
|  |  * | ||||||
|  |  * Copyright (c) 2021 Code Construct | ||||||
|  |  * Copyright (c) 2021 Google | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #ifndef __NET_MCTPDEVICE_H | ||||||
|  | #define __NET_MCTPDEVICE_H | ||||||
|  | 
 | ||||||
|  | #include <linux/list.h> | ||||||
|  | #include <linux/types.h> | ||||||
|  | #include <linux/refcount.h> | ||||||
|  | 
 | ||||||
|  | struct mctp_dev { | ||||||
|  | 	struct net_device	*dev; | ||||||
|  | 
 | ||||||
|  | 	unsigned int		net; | ||||||
|  | 
 | ||||||
|  | 	/* Only modified under RTNL. Reads have addrs_lock held */ | ||||||
|  | 	u8			*addrs; | ||||||
|  | 	size_t			num_addrs; | ||||||
|  | 	spinlock_t		addrs_lock; | ||||||
|  | 
 | ||||||
|  | 	struct rcu_head		rcu; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | #define MCTP_INITIAL_DEFAULT_NET	1 | ||||||
|  | 
 | ||||||
|  | struct mctp_dev *mctp_dev_get_rtnl(const struct net_device *dev); | ||||||
|  | struct mctp_dev *__mctp_dev_get(const struct net_device *dev); | ||||||
|  | 
 | ||||||
|  | #endif /* __NET_MCTPDEVICE_H */ | ||||||
|  | @ -151,6 +151,9 @@ | ||||||
| #define ETH_P_MAP	0x00F9		/* Qualcomm multiplexing and | #define ETH_P_MAP	0x00F9		/* Qualcomm multiplexing and | ||||||
| 					 * aggregation protocol | 					 * aggregation protocol | ||||||
| 					 */ | 					 */ | ||||||
|  | #define ETH_P_MCTP	0x00FA		/* Management component transport | ||||||
|  | 					 * protocol packets | ||||||
|  | 					 */ | ||||||
| 
 | 
 | ||||||
| /*
 | /*
 | ||||||
|  *	This is an Ethernet frame header. |  *	This is an Ethernet frame header. | ||||||
|  |  | ||||||
|  | @ -1260,4 +1260,14 @@ struct ifla_rmnet_flags { | ||||||
| 	__u32	mask; | 	__u32	mask; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | /* MCTP section */ | ||||||
|  | 
 | ||||||
|  | enum { | ||||||
|  | 	IFLA_MCTP_UNSPEC, | ||||||
|  | 	IFLA_MCTP_NET, | ||||||
|  | 	__IFLA_MCTP_MAX, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | #define IFLA_MCTP_MAX (__IFLA_MCTP_MAX - 1) | ||||||
|  | 
 | ||||||
| #endif /* _UAPI_LINUX_IF_LINK_H */ | #endif /* _UAPI_LINUX_IF_LINK_H */ | ||||||
|  |  | ||||||
|  | @ -26,6 +26,7 @@ struct sockaddr_mctp { | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| #define MCTP_NET_ANY		0x0 | #define MCTP_NET_ANY		0x0 | ||||||
|  | #define MCTP_NET_DEFAULT	0x0 | ||||||
| 
 | 
 | ||||||
| #define MCTP_ADDR_NULL		0x00 | #define MCTP_ADDR_NULL		0x00 | ||||||
| #define MCTP_ADDR_ANY		0xff | #define MCTP_ADDR_ANY		0xff | ||||||
|  |  | ||||||
|  | @ -1,3 +1,3 @@ | ||||||
| # SPDX-License-Identifier: GPL-2.0
 | # SPDX-License-Identifier: GPL-2.0
 | ||||||
| obj-$(CONFIG_MCTP) += mctp.o | obj-$(CONFIG_MCTP) += mctp.o | ||||||
| mctp-objs := af_mctp.o | mctp-objs := af_mctp.o device.o | ||||||
|  |  | ||||||
|  | @ -6,13 +6,18 @@ | ||||||
|  * Copyright (c) 2021 Google |  * Copyright (c) 2021 Google | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
|  | #include <linux/if_arp.h> | ||||||
| #include <linux/net.h> | #include <linux/net.h> | ||||||
| #include <linux/mctp.h> | #include <linux/mctp.h> | ||||||
| #include <linux/module.h> | #include <linux/module.h> | ||||||
| #include <linux/socket.h> | #include <linux/socket.h> | ||||||
| 
 | 
 | ||||||
|  | #include <net/mctp.h> | ||||||
|  | #include <net/mctpdevice.h> | ||||||
| #include <net/sock.h> | #include <net/sock.h> | ||||||
| 
 | 
 | ||||||
|  | /* socket implementation */ | ||||||
|  | 
 | ||||||
| struct mctp_sock { | struct mctp_sock { | ||||||
| 	struct sock	sk; | 	struct sock	sk; | ||||||
| }; | }; | ||||||
|  | @ -152,6 +157,8 @@ static __init int mctp_init(void) | ||||||
| 	if (rc) | 	if (rc) | ||||||
| 		goto err_unreg_sock; | 		goto err_unreg_sock; | ||||||
| 
 | 
 | ||||||
|  | 	mctp_device_init(); | ||||||
|  | 
 | ||||||
| 	return 0; | 	return 0; | ||||||
| 
 | 
 | ||||||
| err_unreg_sock: | err_unreg_sock: | ||||||
|  | @ -162,6 +169,7 @@ static __init int mctp_init(void) | ||||||
| 
 | 
 | ||||||
| static __exit void mctp_exit(void) | static __exit void mctp_exit(void) | ||||||
| { | { | ||||||
|  | 	mctp_device_exit(); | ||||||
| 	proto_unregister(&mctp_proto); | 	proto_unregister(&mctp_proto); | ||||||
| 	sock_unregister(PF_MCTP); | 	sock_unregister(PF_MCTP); | ||||||
| } | } | ||||||
|  |  | ||||||
							
								
								
									
										414
									
								
								net/mctp/device.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										414
									
								
								net/mctp/device.c
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,414 @@ | ||||||
|  | // SPDX-License-Identifier: GPL-2.0
 | ||||||
|  | /*
 | ||||||
|  |  * Management Component Transport Protocol (MCTP) - device implementation. | ||||||
|  |  * | ||||||
|  |  * Copyright (c) 2021 Code Construct | ||||||
|  |  * Copyright (c) 2021 Google | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include <linux/if_link.h> | ||||||
|  | #include <linux/mctp.h> | ||||||
|  | #include <linux/netdevice.h> | ||||||
|  | #include <linux/rcupdate.h> | ||||||
|  | #include <linux/rtnetlink.h> | ||||||
|  | 
 | ||||||
|  | #include <net/addrconf.h> | ||||||
|  | #include <net/netlink.h> | ||||||
|  | #include <net/mctp.h> | ||||||
|  | #include <net/mctpdevice.h> | ||||||
|  | #include <net/sock.h> | ||||||
|  | 
 | ||||||
|  | struct mctp_dump_cb { | ||||||
|  | 	int h; | ||||||
|  | 	int idx; | ||||||
|  | 	size_t a_idx; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /* unlocked: caller must hold rcu_read_lock */ | ||||||
|  | struct mctp_dev *__mctp_dev_get(const struct net_device *dev) | ||||||
|  | { | ||||||
|  | 	return rcu_dereference(dev->mctp_ptr); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | struct mctp_dev *mctp_dev_get_rtnl(const struct net_device *dev) | ||||||
|  | { | ||||||
|  | 	return rtnl_dereference(dev->mctp_ptr); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void mctp_dev_destroy(struct mctp_dev *mdev) | ||||||
|  | { | ||||||
|  | 	struct net_device *dev = mdev->dev; | ||||||
|  | 
 | ||||||
|  | 	dev_put(dev); | ||||||
|  | 	kfree_rcu(mdev, rcu); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int mctp_fill_addrinfo(struct sk_buff *skb, struct netlink_callback *cb, | ||||||
|  | 			      struct mctp_dev *mdev, mctp_eid_t eid) | ||||||
|  | { | ||||||
|  | 	struct ifaddrmsg *hdr; | ||||||
|  | 	struct nlmsghdr *nlh; | ||||||
|  | 
 | ||||||
|  | 	nlh = nlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, | ||||||
|  | 			RTM_NEWADDR, sizeof(*hdr), NLM_F_MULTI); | ||||||
|  | 	if (!nlh) | ||||||
|  | 		return -EMSGSIZE; | ||||||
|  | 
 | ||||||
|  | 	hdr = nlmsg_data(nlh); | ||||||
|  | 	hdr->ifa_family = AF_MCTP; | ||||||
|  | 	hdr->ifa_prefixlen = 0; | ||||||
|  | 	hdr->ifa_flags = 0; | ||||||
|  | 	hdr->ifa_scope = 0; | ||||||
|  | 	hdr->ifa_index = mdev->dev->ifindex; | ||||||
|  | 
 | ||||||
|  | 	if (nla_put_u8(skb, IFA_LOCAL, eid)) | ||||||
|  | 		goto cancel; | ||||||
|  | 
 | ||||||
|  | 	if (nla_put_u8(skb, IFA_ADDRESS, eid)) | ||||||
|  | 		goto cancel; | ||||||
|  | 
 | ||||||
|  | 	nlmsg_end(skb, nlh); | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
|  | 
 | ||||||
|  | cancel: | ||||||
|  | 	nlmsg_cancel(skb, nlh); | ||||||
|  | 	return -EMSGSIZE; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int mctp_dump_dev_addrinfo(struct mctp_dev *mdev, struct sk_buff *skb, | ||||||
|  | 				  struct netlink_callback *cb) | ||||||
|  | { | ||||||
|  | 	struct mctp_dump_cb *mcb = (void *)cb->ctx; | ||||||
|  | 	int rc = 0; | ||||||
|  | 
 | ||||||
|  | 	for (; mcb->a_idx < mdev->num_addrs; mcb->a_idx++) { | ||||||
|  | 		rc = mctp_fill_addrinfo(skb, cb, mdev, mdev->addrs[mcb->a_idx]); | ||||||
|  | 		if (rc < 0) | ||||||
|  | 			break; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return rc; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int mctp_dump_addrinfo(struct sk_buff *skb, struct netlink_callback *cb) | ||||||
|  | { | ||||||
|  | 	struct mctp_dump_cb *mcb = (void *)cb->ctx; | ||||||
|  | 	struct net *net = sock_net(skb->sk); | ||||||
|  | 	struct hlist_head *head; | ||||||
|  | 	struct net_device *dev; | ||||||
|  | 	struct ifaddrmsg *hdr; | ||||||
|  | 	struct mctp_dev *mdev; | ||||||
|  | 	int ifindex; | ||||||
|  | 	int idx, rc; | ||||||
|  | 
 | ||||||
|  | 	hdr = nlmsg_data(cb->nlh); | ||||||
|  | 	// filter by ifindex if requested
 | ||||||
|  | 	ifindex = hdr->ifa_index; | ||||||
|  | 
 | ||||||
|  | 	rcu_read_lock(); | ||||||
|  | 	for (; mcb->h < NETDEV_HASHENTRIES; mcb->h++, mcb->idx = 0) { | ||||||
|  | 		idx = 0; | ||||||
|  | 		head = &net->dev_index_head[mcb->h]; | ||||||
|  | 		hlist_for_each_entry_rcu(dev, head, index_hlist) { | ||||||
|  | 			if (idx >= mcb->idx && | ||||||
|  | 			    (ifindex == 0 || ifindex == dev->ifindex)) { | ||||||
|  | 				mdev = __mctp_dev_get(dev); | ||||||
|  | 				if (mdev) { | ||||||
|  | 					rc = mctp_dump_dev_addrinfo(mdev, | ||||||
|  | 								    skb, cb); | ||||||
|  | 					// Error indicates full buffer, this
 | ||||||
|  | 					// callback will get retried.
 | ||||||
|  | 					if (rc < 0) | ||||||
|  | 						goto out; | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			idx++; | ||||||
|  | 			// reset for next iteration
 | ||||||
|  | 			mcb->a_idx = 0; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | out: | ||||||
|  | 	rcu_read_unlock(); | ||||||
|  | 	mcb->idx = idx; | ||||||
|  | 
 | ||||||
|  | 	return skb->len; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static const struct nla_policy ifa_mctp_policy[IFA_MAX + 1] = { | ||||||
|  | 	[IFA_ADDRESS]		= { .type = NLA_U8 }, | ||||||
|  | 	[IFA_LOCAL]		= { .type = NLA_U8 }, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static int mctp_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, | ||||||
|  | 			    struct netlink_ext_ack *extack) | ||||||
|  | { | ||||||
|  | 	struct net *net = sock_net(skb->sk); | ||||||
|  | 	struct nlattr *tb[IFA_MAX + 1]; | ||||||
|  | 	struct net_device *dev; | ||||||
|  | 	struct mctp_addr *addr; | ||||||
|  | 	struct mctp_dev *mdev; | ||||||
|  | 	struct ifaddrmsg *ifm; | ||||||
|  | 	unsigned long flags; | ||||||
|  | 	u8 *tmp_addrs; | ||||||
|  | 	int rc; | ||||||
|  | 
 | ||||||
|  | 	rc = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_mctp_policy, | ||||||
|  | 			 extack); | ||||||
|  | 	if (rc < 0) | ||||||
|  | 		return rc; | ||||||
|  | 
 | ||||||
|  | 	ifm = nlmsg_data(nlh); | ||||||
|  | 
 | ||||||
|  | 	if (tb[IFA_LOCAL]) | ||||||
|  | 		addr = nla_data(tb[IFA_LOCAL]); | ||||||
|  | 	else if (tb[IFA_ADDRESS]) | ||||||
|  | 		addr = nla_data(tb[IFA_ADDRESS]); | ||||||
|  | 	else | ||||||
|  | 		return -EINVAL; | ||||||
|  | 
 | ||||||
|  | 	/* find device */ | ||||||
|  | 	dev = __dev_get_by_index(net, ifm->ifa_index); | ||||||
|  | 	if (!dev) | ||||||
|  | 		return -ENODEV; | ||||||
|  | 
 | ||||||
|  | 	mdev = mctp_dev_get_rtnl(dev); | ||||||
|  | 	if (!mdev) | ||||||
|  | 		return -ENODEV; | ||||||
|  | 
 | ||||||
|  | 	if (!mctp_address_ok(addr->s_addr)) | ||||||
|  | 		return -EINVAL; | ||||||
|  | 
 | ||||||
|  | 	/* Prevent duplicates. Under RTNL so don't need to lock for reading */ | ||||||
|  | 	if (memchr(mdev->addrs, addr->s_addr, mdev->num_addrs)) | ||||||
|  | 		return -EEXIST; | ||||||
|  | 
 | ||||||
|  | 	tmp_addrs = kmalloc(mdev->num_addrs + 1, GFP_KERNEL); | ||||||
|  | 	if (!tmp_addrs) | ||||||
|  | 		return -ENOMEM; | ||||||
|  | 	memcpy(tmp_addrs, mdev->addrs, mdev->num_addrs); | ||||||
|  | 	tmp_addrs[mdev->num_addrs] = addr->s_addr; | ||||||
|  | 
 | ||||||
|  | 	/* Lock to write */ | ||||||
|  | 	spin_lock_irqsave(&mdev->addrs_lock, flags); | ||||||
|  | 	mdev->num_addrs++; | ||||||
|  | 	swap(mdev->addrs, tmp_addrs); | ||||||
|  | 	spin_unlock_irqrestore(&mdev->addrs_lock, flags); | ||||||
|  | 
 | ||||||
|  | 	kfree(tmp_addrs); | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int mctp_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh, | ||||||
|  | 			    struct netlink_ext_ack *extack) | ||||||
|  | { | ||||||
|  | 	struct net *net = sock_net(skb->sk); | ||||||
|  | 	struct nlattr *tb[IFA_MAX + 1]; | ||||||
|  | 	struct net_device *dev; | ||||||
|  | 	struct mctp_addr *addr; | ||||||
|  | 	struct mctp_dev *mdev; | ||||||
|  | 	struct ifaddrmsg *ifm; | ||||||
|  | 	unsigned long flags; | ||||||
|  | 	u8 *pos; | ||||||
|  | 	int rc; | ||||||
|  | 
 | ||||||
|  | 	rc = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_mctp_policy, | ||||||
|  | 			 extack); | ||||||
|  | 	if (rc < 0) | ||||||
|  | 		return rc; | ||||||
|  | 
 | ||||||
|  | 	ifm = nlmsg_data(nlh); | ||||||
|  | 
 | ||||||
|  | 	if (tb[IFA_LOCAL]) | ||||||
|  | 		addr = nla_data(tb[IFA_LOCAL]); | ||||||
|  | 	else if (tb[IFA_ADDRESS]) | ||||||
|  | 		addr = nla_data(tb[IFA_ADDRESS]); | ||||||
|  | 	else | ||||||
|  | 		return -EINVAL; | ||||||
|  | 
 | ||||||
|  | 	/* find device */ | ||||||
|  | 	dev = __dev_get_by_index(net, ifm->ifa_index); | ||||||
|  | 	if (!dev) | ||||||
|  | 		return -ENODEV; | ||||||
|  | 
 | ||||||
|  | 	mdev = mctp_dev_get_rtnl(dev); | ||||||
|  | 	if (!mdev) | ||||||
|  | 		return -ENODEV; | ||||||
|  | 
 | ||||||
|  | 	pos = memchr(mdev->addrs, addr->s_addr, mdev->num_addrs); | ||||||
|  | 	if (!pos) | ||||||
|  | 		return -ENOENT; | ||||||
|  | 
 | ||||||
|  | 	spin_lock_irqsave(&mdev->addrs_lock, flags); | ||||||
|  | 	memmove(pos, pos + 1, mdev->num_addrs - 1 - (pos - mdev->addrs)); | ||||||
|  | 	mdev->num_addrs--; | ||||||
|  | 	spin_unlock_irqrestore(&mdev->addrs_lock, flags); | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static struct mctp_dev *mctp_add_dev(struct net_device *dev) | ||||||
|  | { | ||||||
|  | 	struct mctp_dev *mdev; | ||||||
|  | 
 | ||||||
|  | 	ASSERT_RTNL(); | ||||||
|  | 
 | ||||||
|  | 	mdev = kzalloc(sizeof(*mdev), GFP_KERNEL); | ||||||
|  | 	if (!mdev) | ||||||
|  | 		return ERR_PTR(-ENOMEM); | ||||||
|  | 
 | ||||||
|  | 	spin_lock_init(&mdev->addrs_lock); | ||||||
|  | 
 | ||||||
|  | 	mdev->net = MCTP_INITIAL_DEFAULT_NET; | ||||||
|  | 
 | ||||||
|  | 	/* associate to net_device */ | ||||||
|  | 	rcu_assign_pointer(dev->mctp_ptr, mdev); | ||||||
|  | 	dev_hold(dev); | ||||||
|  | 	mdev->dev = dev; | ||||||
|  | 
 | ||||||
|  | 	return mdev; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int mctp_fill_link_af(struct sk_buff *skb, | ||||||
|  | 			     const struct net_device *dev, u32 ext_filter_mask) | ||||||
|  | { | ||||||
|  | 	struct mctp_dev *mdev; | ||||||
|  | 
 | ||||||
|  | 	mdev = mctp_dev_get_rtnl(dev); | ||||||
|  | 	if (!mdev) | ||||||
|  | 		return -ENODATA; | ||||||
|  | 	if (nla_put_u32(skb, IFLA_MCTP_NET, mdev->net)) | ||||||
|  | 		return -EMSGSIZE; | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static size_t mctp_get_link_af_size(const struct net_device *dev, | ||||||
|  | 				    u32 ext_filter_mask) | ||||||
|  | { | ||||||
|  | 	struct mctp_dev *mdev; | ||||||
|  | 	unsigned int ret; | ||||||
|  | 
 | ||||||
|  | 	/* caller holds RCU */ | ||||||
|  | 	mdev = __mctp_dev_get(dev); | ||||||
|  | 	if (!mdev) | ||||||
|  | 		return 0; | ||||||
|  | 	ret = nla_total_size(4); /* IFLA_MCTP_NET */ | ||||||
|  | 	return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static const struct nla_policy ifla_af_mctp_policy[IFLA_MCTP_MAX + 1] = { | ||||||
|  | 	[IFLA_MCTP_NET]		= { .type = NLA_U32 }, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static int mctp_set_link_af(struct net_device *dev, const struct nlattr *attr, | ||||||
|  | 			    struct netlink_ext_ack *extack) | ||||||
|  | { | ||||||
|  | 	struct nlattr *tb[IFLA_MCTP_MAX + 1]; | ||||||
|  | 	struct mctp_dev *mdev; | ||||||
|  | 	int rc; | ||||||
|  | 
 | ||||||
|  | 	rc = nla_parse_nested(tb, IFLA_MCTP_MAX, attr, ifla_af_mctp_policy, | ||||||
|  | 			      NULL); | ||||||
|  | 	if (rc) | ||||||
|  | 		return rc; | ||||||
|  | 
 | ||||||
|  | 	mdev = mctp_dev_get_rtnl(dev); | ||||||
|  | 	if (!mdev) | ||||||
|  | 		return 0; | ||||||
|  | 
 | ||||||
|  | 	if (tb[IFLA_MCTP_NET]) | ||||||
|  | 		WRITE_ONCE(mdev->net, nla_get_u32(tb[IFLA_MCTP_NET])); | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void mctp_unregister(struct net_device *dev) | ||||||
|  | { | ||||||
|  | 	struct mctp_dev *mdev; | ||||||
|  | 
 | ||||||
|  | 	mdev = mctp_dev_get_rtnl(dev); | ||||||
|  | 
 | ||||||
|  | 	if (!mdev) | ||||||
|  | 		return; | ||||||
|  | 
 | ||||||
|  | 	RCU_INIT_POINTER(mdev->dev->mctp_ptr, NULL); | ||||||
|  | 
 | ||||||
|  | 	kfree(mdev->addrs); | ||||||
|  | 
 | ||||||
|  | 	mctp_dev_destroy(mdev); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int mctp_register(struct net_device *dev) | ||||||
|  | { | ||||||
|  | 	struct mctp_dev *mdev; | ||||||
|  | 
 | ||||||
|  | 	/* Already registered? */ | ||||||
|  | 	if (rtnl_dereference(dev->mctp_ptr)) | ||||||
|  | 		return 0; | ||||||
|  | 
 | ||||||
|  | 	/* only register specific types; MCTP-specific and loopback for now */ | ||||||
|  | 	if (dev->type != ARPHRD_MCTP && dev->type != ARPHRD_LOOPBACK) | ||||||
|  | 		return 0; | ||||||
|  | 
 | ||||||
|  | 	mdev = mctp_add_dev(dev); | ||||||
|  | 	if (IS_ERR(mdev)) | ||||||
|  | 		return PTR_ERR(mdev); | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int mctp_dev_notify(struct notifier_block *this, unsigned long event, | ||||||
|  | 			   void *ptr) | ||||||
|  | { | ||||||
|  | 	struct net_device *dev = netdev_notifier_info_to_dev(ptr); | ||||||
|  | 	int rc; | ||||||
|  | 
 | ||||||
|  | 	switch (event) { | ||||||
|  | 	case NETDEV_REGISTER: | ||||||
|  | 		rc = mctp_register(dev); | ||||||
|  | 		if (rc) | ||||||
|  | 			return notifier_from_errno(rc); | ||||||
|  | 		break; | ||||||
|  | 	case NETDEV_UNREGISTER: | ||||||
|  | 		mctp_unregister(dev); | ||||||
|  | 		break; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return NOTIFY_OK; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static struct rtnl_af_ops mctp_af_ops = { | ||||||
|  | 	.family = AF_MCTP, | ||||||
|  | 	.fill_link_af = mctp_fill_link_af, | ||||||
|  | 	.get_link_af_size = mctp_get_link_af_size, | ||||||
|  | 	.set_link_af = mctp_set_link_af, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static struct notifier_block mctp_dev_nb = { | ||||||
|  | 	.notifier_call = mctp_dev_notify, | ||||||
|  | 	.priority = ADDRCONF_NOTIFY_PRIORITY, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | void __init mctp_device_init(void) | ||||||
|  | { | ||||||
|  | 	register_netdevice_notifier(&mctp_dev_nb); | ||||||
|  | 
 | ||||||
|  | 	rtnl_register_module(THIS_MODULE, PF_MCTP, RTM_GETADDR, | ||||||
|  | 			     NULL, mctp_dump_addrinfo, 0); | ||||||
|  | 	rtnl_register_module(THIS_MODULE, PF_MCTP, RTM_NEWADDR, | ||||||
|  | 			     mctp_rtm_newaddr, NULL, 0); | ||||||
|  | 	rtnl_register_module(THIS_MODULE, PF_MCTP, RTM_DELADDR, | ||||||
|  | 			     mctp_rtm_deladdr, NULL, 0); | ||||||
|  | 	rtnl_af_register(&mctp_af_ops); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void __exit mctp_device_exit(void) | ||||||
|  | { | ||||||
|  | 	rtnl_af_unregister(&mctp_af_ops); | ||||||
|  | 	rtnl_unregister(PF_MCTP, RTM_DELADDR); | ||||||
|  | 	rtnl_unregister(PF_MCTP, RTM_NEWADDR); | ||||||
|  | 	rtnl_unregister(PF_MCTP, RTM_GETADDR); | ||||||
|  | 
 | ||||||
|  | 	unregister_netdevice_notifier(&mctp_dev_nb); | ||||||
|  | } | ||||||
		Loading…
	
		Reference in a new issue
	
	 Jeremy Kerr
						Jeremy Kerr