xref: /openbmc/linux/net/mctp/device.c (revision 7b7fd0ac7dc1ffcaf24d9bca0f051b0168e43cd4)
1583be982SJeremy Kerr // SPDX-License-Identifier: GPL-2.0
2583be982SJeremy Kerr /*
3583be982SJeremy Kerr  * Management Component Transport Protocol (MCTP) - device implementation.
4583be982SJeremy Kerr  *
5583be982SJeremy Kerr  * Copyright (c) 2021 Code Construct
6583be982SJeremy Kerr  * Copyright (c) 2021 Google
7583be982SJeremy Kerr  */
8583be982SJeremy Kerr 
9c78b8b20SJakub Kicinski #include <linux/if_arp.h>
10583be982SJeremy Kerr #include <linux/if_link.h>
11583be982SJeremy Kerr #include <linux/mctp.h>
12583be982SJeremy Kerr #include <linux/netdevice.h>
13583be982SJeremy Kerr #include <linux/rcupdate.h>
14583be982SJeremy Kerr #include <linux/rtnetlink.h>
15583be982SJeremy Kerr 
16583be982SJeremy Kerr #include <net/addrconf.h>
17583be982SJeremy Kerr #include <net/netlink.h>
18583be982SJeremy Kerr #include <net/mctp.h>
19583be982SJeremy Kerr #include <net/mctpdevice.h>
20583be982SJeremy Kerr #include <net/sock.h>
21583be982SJeremy Kerr 
22583be982SJeremy Kerr struct mctp_dump_cb {
23583be982SJeremy Kerr 	int h;
24583be982SJeremy Kerr 	int idx;
25583be982SJeremy Kerr 	size_t a_idx;
26583be982SJeremy Kerr };
27583be982SJeremy Kerr 
28dc121c00SMatt Johnston /* unlocked: caller must hold rcu_read_lock.
29dc121c00SMatt Johnston  * Returned mctp_dev has its refcount incremented, or NULL if unset.
30dc121c00SMatt Johnston  */
__mctp_dev_get(const struct net_device * dev)31583be982SJeremy Kerr struct mctp_dev *__mctp_dev_get(const struct net_device *dev)
32583be982SJeremy Kerr {
33dc121c00SMatt Johnston 	struct mctp_dev *mdev = rcu_dereference(dev->mctp_ptr);
34dc121c00SMatt Johnston 
35dc121c00SMatt Johnston 	/* RCU guarantees that any mdev is still live.
36dc121c00SMatt Johnston 	 * Zero refcount implies a pending free, return NULL.
37dc121c00SMatt Johnston 	 */
38dc121c00SMatt Johnston 	if (mdev)
39dc121c00SMatt Johnston 		if (!refcount_inc_not_zero(&mdev->refs))
40dc121c00SMatt Johnston 			return NULL;
41dc121c00SMatt Johnston 	return mdev;
42583be982SJeremy Kerr }
43583be982SJeremy Kerr 
44dc121c00SMatt Johnston /* Returned mctp_dev does not have refcount incremented. The returned pointer
45dc121c00SMatt Johnston  * remains live while rtnl_lock is held, as that prevents mctp_unregister()
46dc121c00SMatt Johnston  */
mctp_dev_get_rtnl(const struct net_device * dev)47583be982SJeremy Kerr struct mctp_dev *mctp_dev_get_rtnl(const struct net_device *dev)
48583be982SJeremy Kerr {
49583be982SJeremy Kerr 	return rtnl_dereference(dev->mctp_ptr);
50583be982SJeremy Kerr }
51583be982SJeremy Kerr 
mctp_addrinfo_size(void)52dbcefdebSMatt Johnston static int mctp_addrinfo_size(void)
53dbcefdebSMatt Johnston {
54dbcefdebSMatt Johnston 	return NLMSG_ALIGN(sizeof(struct ifaddrmsg))
55dbcefdebSMatt Johnston 		+ nla_total_size(1) // IFA_LOCAL
56dbcefdebSMatt Johnston 		+ nla_total_size(1) // IFA_ADDRESS
57dbcefdebSMatt Johnston 		;
58dbcefdebSMatt Johnston }
59dbcefdebSMatt Johnston 
60dbcefdebSMatt Johnston /* flag should be NLM_F_MULTI for dump calls */
mctp_fill_addrinfo(struct sk_buff * skb,struct mctp_dev * mdev,mctp_eid_t eid,int msg_type,u32 portid,u32 seq,int flag)61dbcefdebSMatt Johnston static int mctp_fill_addrinfo(struct sk_buff *skb,
62dbcefdebSMatt Johnston 			      struct mctp_dev *mdev, mctp_eid_t eid,
63dbcefdebSMatt Johnston 			      int msg_type, u32 portid, u32 seq, int flag)
64583be982SJeremy Kerr {
65583be982SJeremy Kerr 	struct ifaddrmsg *hdr;
66583be982SJeremy Kerr 	struct nlmsghdr *nlh;
67583be982SJeremy Kerr 
68dbcefdebSMatt Johnston 	nlh = nlmsg_put(skb, portid, seq,
69dbcefdebSMatt Johnston 			msg_type, sizeof(*hdr), flag);
70583be982SJeremy Kerr 	if (!nlh)
71583be982SJeremy Kerr 		return -EMSGSIZE;
72583be982SJeremy Kerr 
73583be982SJeremy Kerr 	hdr = nlmsg_data(nlh);
74583be982SJeremy Kerr 	hdr->ifa_family = AF_MCTP;
75583be982SJeremy Kerr 	hdr->ifa_prefixlen = 0;
76583be982SJeremy Kerr 	hdr->ifa_flags = 0;
77583be982SJeremy Kerr 	hdr->ifa_scope = 0;
78583be982SJeremy Kerr 	hdr->ifa_index = mdev->dev->ifindex;
79583be982SJeremy Kerr 
80583be982SJeremy Kerr 	if (nla_put_u8(skb, IFA_LOCAL, eid))
81583be982SJeremy Kerr 		goto cancel;
82583be982SJeremy Kerr 
83583be982SJeremy Kerr 	if (nla_put_u8(skb, IFA_ADDRESS, eid))
84583be982SJeremy Kerr 		goto cancel;
85583be982SJeremy Kerr 
86583be982SJeremy Kerr 	nlmsg_end(skb, nlh);
87583be982SJeremy Kerr 
88583be982SJeremy Kerr 	return 0;
89583be982SJeremy Kerr 
90583be982SJeremy Kerr cancel:
91583be982SJeremy Kerr 	nlmsg_cancel(skb, nlh);
92583be982SJeremy Kerr 	return -EMSGSIZE;
93583be982SJeremy Kerr }
94583be982SJeremy Kerr 
mctp_dump_dev_addrinfo(struct mctp_dev * mdev,struct sk_buff * skb,struct netlink_callback * cb)95583be982SJeremy Kerr static int mctp_dump_dev_addrinfo(struct mctp_dev *mdev, struct sk_buff *skb,
96583be982SJeremy Kerr 				  struct netlink_callback *cb)
97583be982SJeremy Kerr {
98583be982SJeremy Kerr 	struct mctp_dump_cb *mcb = (void *)cb->ctx;
99dbcefdebSMatt Johnston 	u32 portid, seq;
100583be982SJeremy Kerr 	int rc = 0;
101583be982SJeremy Kerr 
102dbcefdebSMatt Johnston 	portid = NETLINK_CB(cb->skb).portid;
103dbcefdebSMatt Johnston 	seq = cb->nlh->nlmsg_seq;
104583be982SJeremy Kerr 	for (; mcb->a_idx < mdev->num_addrs; mcb->a_idx++) {
105dbcefdebSMatt Johnston 		rc = mctp_fill_addrinfo(skb, mdev, mdev->addrs[mcb->a_idx],
106dbcefdebSMatt Johnston 					RTM_NEWADDR, portid, seq, NLM_F_MULTI);
107583be982SJeremy Kerr 		if (rc < 0)
108583be982SJeremy Kerr 			break;
109583be982SJeremy Kerr 	}
110583be982SJeremy Kerr 
111583be982SJeremy Kerr 	return rc;
112583be982SJeremy Kerr }
113583be982SJeremy Kerr 
mctp_dump_addrinfo(struct sk_buff * skb,struct netlink_callback * cb)114583be982SJeremy Kerr static int mctp_dump_addrinfo(struct sk_buff *skb, struct netlink_callback *cb)
115583be982SJeremy Kerr {
116583be982SJeremy Kerr 	struct mctp_dump_cb *mcb = (void *)cb->ctx;
117583be982SJeremy Kerr 	struct net *net = sock_net(skb->sk);
118583be982SJeremy Kerr 	struct hlist_head *head;
119583be982SJeremy Kerr 	struct net_device *dev;
120583be982SJeremy Kerr 	struct ifaddrmsg *hdr;
121583be982SJeremy Kerr 	struct mctp_dev *mdev;
122583be982SJeremy Kerr 	int ifindex;
1238d783197SMatt Johnston 	int idx = 0, rc;
124583be982SJeremy Kerr 
125583be982SJeremy Kerr 	hdr = nlmsg_data(cb->nlh);
126583be982SJeremy Kerr 	// filter by ifindex if requested
127583be982SJeremy Kerr 	ifindex = hdr->ifa_index;
128583be982SJeremy Kerr 
129583be982SJeremy Kerr 	rcu_read_lock();
130583be982SJeremy Kerr 	for (; mcb->h < NETDEV_HASHENTRIES; mcb->h++, mcb->idx = 0) {
131583be982SJeremy Kerr 		idx = 0;
132583be982SJeremy Kerr 		head = &net->dev_index_head[mcb->h];
133583be982SJeremy Kerr 		hlist_for_each_entry_rcu(dev, head, index_hlist) {
134583be982SJeremy Kerr 			if (idx >= mcb->idx &&
135583be982SJeremy Kerr 			    (ifindex == 0 || ifindex == dev->ifindex)) {
136583be982SJeremy Kerr 				mdev = __mctp_dev_get(dev);
137583be982SJeremy Kerr 				if (mdev) {
138583be982SJeremy Kerr 					rc = mctp_dump_dev_addrinfo(mdev,
139583be982SJeremy Kerr 								    skb, cb);
140dc121c00SMatt Johnston 					mctp_dev_put(mdev);
141583be982SJeremy Kerr 					// Error indicates full buffer, this
142583be982SJeremy Kerr 					// callback will get retried.
143583be982SJeremy Kerr 					if (rc < 0)
144583be982SJeremy Kerr 						goto out;
145583be982SJeremy Kerr 				}
146583be982SJeremy Kerr 			}
147583be982SJeremy Kerr 			idx++;
148583be982SJeremy Kerr 			// reset for next iteration
149583be982SJeremy Kerr 			mcb->a_idx = 0;
150583be982SJeremy Kerr 		}
151583be982SJeremy Kerr 	}
152583be982SJeremy Kerr out:
153583be982SJeremy Kerr 	rcu_read_unlock();
154583be982SJeremy Kerr 	mcb->idx = idx;
155583be982SJeremy Kerr 
156583be982SJeremy Kerr 	return skb->len;
157583be982SJeremy Kerr }
158583be982SJeremy Kerr 
mctp_addr_notify(struct mctp_dev * mdev,mctp_eid_t eid,int msg_type,struct sk_buff * req_skb,struct nlmsghdr * req_nlh)159dbcefdebSMatt Johnston static void mctp_addr_notify(struct mctp_dev *mdev, mctp_eid_t eid, int msg_type,
160dbcefdebSMatt Johnston 			     struct sk_buff *req_skb, struct nlmsghdr *req_nlh)
161dbcefdebSMatt Johnston {
162dbcefdebSMatt Johnston 	u32 portid = NETLINK_CB(req_skb).portid;
163dbcefdebSMatt Johnston 	struct net *net = dev_net(mdev->dev);
164dbcefdebSMatt Johnston 	struct sk_buff *skb;
165dbcefdebSMatt Johnston 	int rc = -ENOBUFS;
166dbcefdebSMatt Johnston 
167dbcefdebSMatt Johnston 	skb = nlmsg_new(mctp_addrinfo_size(), GFP_KERNEL);
168dbcefdebSMatt Johnston 	if (!skb)
169dbcefdebSMatt Johnston 		goto out;
170dbcefdebSMatt Johnston 
171dbcefdebSMatt Johnston 	rc = mctp_fill_addrinfo(skb, mdev, eid, msg_type,
172dbcefdebSMatt Johnston 				portid, req_nlh->nlmsg_seq, 0);
173dbcefdebSMatt Johnston 	if (rc < 0) {
174dbcefdebSMatt Johnston 		WARN_ON_ONCE(rc == -EMSGSIZE);
175dbcefdebSMatt Johnston 		goto out;
176dbcefdebSMatt Johnston 	}
177dbcefdebSMatt Johnston 
178dbcefdebSMatt Johnston 	rtnl_notify(skb, net, portid, RTNLGRP_MCTP_IFADDR, req_nlh, GFP_KERNEL);
179dbcefdebSMatt Johnston 	return;
180dbcefdebSMatt Johnston out:
181dbcefdebSMatt Johnston 	kfree_skb(skb);
182dbcefdebSMatt Johnston 	rtnl_set_sk_err(net, RTNLGRP_MCTP_IFADDR, rc);
183dbcefdebSMatt Johnston }
184dbcefdebSMatt Johnston 
185583be982SJeremy Kerr static const struct nla_policy ifa_mctp_policy[IFA_MAX + 1] = {
186583be982SJeremy Kerr 	[IFA_ADDRESS]		= { .type = NLA_U8 },
187583be982SJeremy Kerr 	[IFA_LOCAL]		= { .type = NLA_U8 },
188583be982SJeremy Kerr };
189583be982SJeremy Kerr 
mctp_rtm_newaddr(struct sk_buff * skb,struct nlmsghdr * nlh,struct netlink_ext_ack * extack)190583be982SJeremy Kerr static int mctp_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh,
191583be982SJeremy Kerr 			    struct netlink_ext_ack *extack)
192583be982SJeremy Kerr {
193583be982SJeremy Kerr 	struct net *net = sock_net(skb->sk);
194583be982SJeremy Kerr 	struct nlattr *tb[IFA_MAX + 1];
195583be982SJeremy Kerr 	struct net_device *dev;
196583be982SJeremy Kerr 	struct mctp_addr *addr;
197583be982SJeremy Kerr 	struct mctp_dev *mdev;
198583be982SJeremy Kerr 	struct ifaddrmsg *ifm;
199583be982SJeremy Kerr 	unsigned long flags;
200583be982SJeremy Kerr 	u8 *tmp_addrs;
201583be982SJeremy Kerr 	int rc;
202583be982SJeremy Kerr 
203583be982SJeremy Kerr 	rc = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_mctp_policy,
204583be982SJeremy Kerr 			 extack);
205583be982SJeremy Kerr 	if (rc < 0)
206583be982SJeremy Kerr 		return rc;
207583be982SJeremy Kerr 
208583be982SJeremy Kerr 	ifm = nlmsg_data(nlh);
209583be982SJeremy Kerr 
210583be982SJeremy Kerr 	if (tb[IFA_LOCAL])
211583be982SJeremy Kerr 		addr = nla_data(tb[IFA_LOCAL]);
212583be982SJeremy Kerr 	else if (tb[IFA_ADDRESS])
213583be982SJeremy Kerr 		addr = nla_data(tb[IFA_ADDRESS]);
214583be982SJeremy Kerr 	else
215583be982SJeremy Kerr 		return -EINVAL;
216583be982SJeremy Kerr 
217583be982SJeremy Kerr 	/* find device */
218583be982SJeremy Kerr 	dev = __dev_get_by_index(net, ifm->ifa_index);
219583be982SJeremy Kerr 	if (!dev)
220583be982SJeremy Kerr 		return -ENODEV;
221583be982SJeremy Kerr 
222583be982SJeremy Kerr 	mdev = mctp_dev_get_rtnl(dev);
223583be982SJeremy Kerr 	if (!mdev)
224583be982SJeremy Kerr 		return -ENODEV;
225583be982SJeremy Kerr 
226cb196b72SJeremy Kerr 	if (!mctp_address_unicast(addr->s_addr))
227583be982SJeremy Kerr 		return -EINVAL;
228583be982SJeremy Kerr 
229583be982SJeremy Kerr 	/* Prevent duplicates. Under RTNL so don't need to lock for reading */
230583be982SJeremy Kerr 	if (memchr(mdev->addrs, addr->s_addr, mdev->num_addrs))
231583be982SJeremy Kerr 		return -EEXIST;
232583be982SJeremy Kerr 
233583be982SJeremy Kerr 	tmp_addrs = kmalloc(mdev->num_addrs + 1, GFP_KERNEL);
234583be982SJeremy Kerr 	if (!tmp_addrs)
235583be982SJeremy Kerr 		return -ENOMEM;
236583be982SJeremy Kerr 	memcpy(tmp_addrs, mdev->addrs, mdev->num_addrs);
237583be982SJeremy Kerr 	tmp_addrs[mdev->num_addrs] = addr->s_addr;
238583be982SJeremy Kerr 
239583be982SJeremy Kerr 	/* Lock to write */
240583be982SJeremy Kerr 	spin_lock_irqsave(&mdev->addrs_lock, flags);
241583be982SJeremy Kerr 	mdev->num_addrs++;
242583be982SJeremy Kerr 	swap(mdev->addrs, tmp_addrs);
243583be982SJeremy Kerr 	spin_unlock_irqrestore(&mdev->addrs_lock, flags);
244583be982SJeremy Kerr 
245583be982SJeremy Kerr 	kfree(tmp_addrs);
246583be982SJeremy Kerr 
247dbcefdebSMatt Johnston 	mctp_addr_notify(mdev, addr->s_addr, RTM_NEWADDR, skb, nlh);
248889b7da2SJeremy Kerr 	mctp_route_add_local(mdev, addr->s_addr);
249889b7da2SJeremy Kerr 
250583be982SJeremy Kerr 	return 0;
251583be982SJeremy Kerr }
252583be982SJeremy Kerr 
mctp_rtm_deladdr(struct sk_buff * skb,struct nlmsghdr * nlh,struct netlink_ext_ack * extack)253583be982SJeremy Kerr static int mctp_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh,
254583be982SJeremy Kerr 			    struct netlink_ext_ack *extack)
255583be982SJeremy Kerr {
256583be982SJeremy Kerr 	struct net *net = sock_net(skb->sk);
257583be982SJeremy Kerr 	struct nlattr *tb[IFA_MAX + 1];
258583be982SJeremy Kerr 	struct net_device *dev;
259583be982SJeremy Kerr 	struct mctp_addr *addr;
260583be982SJeremy Kerr 	struct mctp_dev *mdev;
261583be982SJeremy Kerr 	struct ifaddrmsg *ifm;
262583be982SJeremy Kerr 	unsigned long flags;
263583be982SJeremy Kerr 	u8 *pos;
264583be982SJeremy Kerr 	int rc;
265583be982SJeremy Kerr 
266583be982SJeremy Kerr 	rc = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_mctp_policy,
267583be982SJeremy Kerr 			 extack);
268583be982SJeremy Kerr 	if (rc < 0)
269583be982SJeremy Kerr 		return rc;
270583be982SJeremy Kerr 
271583be982SJeremy Kerr 	ifm = nlmsg_data(nlh);
272583be982SJeremy Kerr 
273583be982SJeremy Kerr 	if (tb[IFA_LOCAL])
274583be982SJeremy Kerr 		addr = nla_data(tb[IFA_LOCAL]);
275583be982SJeremy Kerr 	else if (tb[IFA_ADDRESS])
276583be982SJeremy Kerr 		addr = nla_data(tb[IFA_ADDRESS]);
277583be982SJeremy Kerr 	else
278583be982SJeremy Kerr 		return -EINVAL;
279583be982SJeremy Kerr 
280583be982SJeremy Kerr 	/* find device */
281583be982SJeremy Kerr 	dev = __dev_get_by_index(net, ifm->ifa_index);
282583be982SJeremy Kerr 	if (!dev)
283583be982SJeremy Kerr 		return -ENODEV;
284583be982SJeremy Kerr 
285583be982SJeremy Kerr 	mdev = mctp_dev_get_rtnl(dev);
286583be982SJeremy Kerr 	if (!mdev)
287583be982SJeremy Kerr 		return -ENODEV;
288583be982SJeremy Kerr 
289583be982SJeremy Kerr 	pos = memchr(mdev->addrs, addr->s_addr, mdev->num_addrs);
290583be982SJeremy Kerr 	if (!pos)
291583be982SJeremy Kerr 		return -ENOENT;
292583be982SJeremy Kerr 
293889b7da2SJeremy Kerr 	rc = mctp_route_remove_local(mdev, addr->s_addr);
294889b7da2SJeremy Kerr 	// we can ignore -ENOENT in the case a route was already removed
295889b7da2SJeremy Kerr 	if (rc < 0 && rc != -ENOENT)
296889b7da2SJeremy Kerr 		return rc;
297889b7da2SJeremy Kerr 
298583be982SJeremy Kerr 	spin_lock_irqsave(&mdev->addrs_lock, flags);
299583be982SJeremy Kerr 	memmove(pos, pos + 1, mdev->num_addrs - 1 - (pos - mdev->addrs));
300583be982SJeremy Kerr 	mdev->num_addrs--;
301583be982SJeremy Kerr 	spin_unlock_irqrestore(&mdev->addrs_lock, flags);
302583be982SJeremy Kerr 
303dbcefdebSMatt Johnston 	mctp_addr_notify(mdev, addr->s_addr, RTM_DELADDR, skb, nlh);
304dbcefdebSMatt Johnston 
305583be982SJeremy Kerr 	return 0;
306583be982SJeremy Kerr }
307583be982SJeremy Kerr 
mctp_dev_hold(struct mctp_dev * mdev)30843f55f23SJeremy Kerr void mctp_dev_hold(struct mctp_dev *mdev)
30943f55f23SJeremy Kerr {
31043f55f23SJeremy Kerr 	refcount_inc(&mdev->refs);
31143f55f23SJeremy Kerr }
31243f55f23SJeremy Kerr 
mctp_dev_put(struct mctp_dev * mdev)31343f55f23SJeremy Kerr void mctp_dev_put(struct mctp_dev *mdev)
31443f55f23SJeremy Kerr {
315dc121c00SMatt Johnston 	if (mdev && refcount_dec_and_test(&mdev->refs)) {
316b561275dSLin Ma 		kfree(mdev->addrs);
31743f55f23SJeremy Kerr 		dev_put(mdev->dev);
31843f55f23SJeremy Kerr 		kfree_rcu(mdev, rcu);
31943f55f23SJeremy Kerr 	}
32043f55f23SJeremy Kerr }
32143f55f23SJeremy Kerr 
mctp_dev_release_key(struct mctp_dev * dev,struct mctp_sk_key * key)32267737c45SJeremy Kerr void mctp_dev_release_key(struct mctp_dev *dev, struct mctp_sk_key *key)
32367737c45SJeremy Kerr 	__must_hold(&key->lock)
32467737c45SJeremy Kerr {
32567737c45SJeremy Kerr 	if (!dev)
32667737c45SJeremy Kerr 		return;
32767737c45SJeremy Kerr 	if (dev->ops && dev->ops->release_flow)
32867737c45SJeremy Kerr 		dev->ops->release_flow(dev, key);
32967737c45SJeremy Kerr 	key->dev = NULL;
33067737c45SJeremy Kerr 	mctp_dev_put(dev);
33167737c45SJeremy Kerr }
33267737c45SJeremy Kerr 
mctp_dev_set_key(struct mctp_dev * dev,struct mctp_sk_key * key)33367737c45SJeremy Kerr void mctp_dev_set_key(struct mctp_dev *dev, struct mctp_sk_key *key)
33467737c45SJeremy Kerr 	__must_hold(&key->lock)
33567737c45SJeremy Kerr {
33667737c45SJeremy Kerr 	mctp_dev_hold(dev);
33767737c45SJeremy Kerr 	key->dev = dev;
33867737c45SJeremy Kerr }
33967737c45SJeremy Kerr 
mctp_add_dev(struct net_device * dev)340583be982SJeremy Kerr static struct mctp_dev *mctp_add_dev(struct net_device *dev)
341583be982SJeremy Kerr {
342583be982SJeremy Kerr 	struct mctp_dev *mdev;
343583be982SJeremy Kerr 
344583be982SJeremy Kerr 	ASSERT_RTNL();
345583be982SJeremy Kerr 
346583be982SJeremy Kerr 	mdev = kzalloc(sizeof(*mdev), GFP_KERNEL);
347583be982SJeremy Kerr 	if (!mdev)
348583be982SJeremy Kerr 		return ERR_PTR(-ENOMEM);
349583be982SJeremy Kerr 
350583be982SJeremy Kerr 	spin_lock_init(&mdev->addrs_lock);
351583be982SJeremy Kerr 
35203f2bbc4SMatt Johnston 	mdev->net = mctp_default_net(dev_net(dev));
353583be982SJeremy Kerr 
354583be982SJeremy Kerr 	/* associate to net_device */
35543f55f23SJeremy Kerr 	refcount_set(&mdev->refs, 1);
356583be982SJeremy Kerr 	rcu_assign_pointer(dev->mctp_ptr, mdev);
35743f55f23SJeremy Kerr 
358583be982SJeremy Kerr 	dev_hold(dev);
359583be982SJeremy Kerr 	mdev->dev = dev;
360583be982SJeremy Kerr 
361583be982SJeremy Kerr 	return mdev;
362583be982SJeremy Kerr }
363583be982SJeremy Kerr 
mctp_fill_link_af(struct sk_buff * skb,const struct net_device * dev,u32 ext_filter_mask)364583be982SJeremy Kerr static int mctp_fill_link_af(struct sk_buff *skb,
365583be982SJeremy Kerr 			     const struct net_device *dev, u32 ext_filter_mask)
366583be982SJeremy Kerr {
367583be982SJeremy Kerr 	struct mctp_dev *mdev;
368583be982SJeremy Kerr 
369583be982SJeremy Kerr 	mdev = mctp_dev_get_rtnl(dev);
370583be982SJeremy Kerr 	if (!mdev)
371583be982SJeremy Kerr 		return -ENODATA;
372583be982SJeremy Kerr 	if (nla_put_u32(skb, IFLA_MCTP_NET, mdev->net))
373583be982SJeremy Kerr 		return -EMSGSIZE;
374583be982SJeremy Kerr 	return 0;
375583be982SJeremy Kerr }
376583be982SJeremy Kerr 
mctp_get_link_af_size(const struct net_device * dev,u32 ext_filter_mask)377583be982SJeremy Kerr static size_t mctp_get_link_af_size(const struct net_device *dev,
378583be982SJeremy Kerr 				    u32 ext_filter_mask)
379583be982SJeremy Kerr {
380583be982SJeremy Kerr 	struct mctp_dev *mdev;
381583be982SJeremy Kerr 	unsigned int ret;
382583be982SJeremy Kerr 
383583be982SJeremy Kerr 	/* caller holds RCU */
384583be982SJeremy Kerr 	mdev = __mctp_dev_get(dev);
385583be982SJeremy Kerr 	if (!mdev)
386583be982SJeremy Kerr 		return 0;
387583be982SJeremy Kerr 	ret = nla_total_size(4); /* IFLA_MCTP_NET */
388dc121c00SMatt Johnston 	mctp_dev_put(mdev);
389583be982SJeremy Kerr 	return ret;
390583be982SJeremy Kerr }
391583be982SJeremy Kerr 
392583be982SJeremy Kerr static const struct nla_policy ifla_af_mctp_policy[IFLA_MCTP_MAX + 1] = {
393583be982SJeremy Kerr 	[IFLA_MCTP_NET]		= { .type = NLA_U32 },
394583be982SJeremy Kerr };
395583be982SJeremy Kerr 
mctp_set_link_af(struct net_device * dev,const struct nlattr * attr,struct netlink_ext_ack * extack)396583be982SJeremy Kerr static int mctp_set_link_af(struct net_device *dev, const struct nlattr *attr,
397583be982SJeremy Kerr 			    struct netlink_ext_ack *extack)
398583be982SJeremy Kerr {
399583be982SJeremy Kerr 	struct nlattr *tb[IFLA_MCTP_MAX + 1];
400583be982SJeremy Kerr 	struct mctp_dev *mdev;
401583be982SJeremy Kerr 	int rc;
402583be982SJeremy Kerr 
403583be982SJeremy Kerr 	rc = nla_parse_nested(tb, IFLA_MCTP_MAX, attr, ifla_af_mctp_policy,
404583be982SJeremy Kerr 			      NULL);
405583be982SJeremy Kerr 	if (rc)
406583be982SJeremy Kerr 		return rc;
407583be982SJeremy Kerr 
408583be982SJeremy Kerr 	mdev = mctp_dev_get_rtnl(dev);
409583be982SJeremy Kerr 	if (!mdev)
410583be982SJeremy Kerr 		return 0;
411583be982SJeremy Kerr 
412583be982SJeremy Kerr 	if (tb[IFLA_MCTP_NET])
413583be982SJeremy Kerr 		WRITE_ONCE(mdev->net, nla_get_u32(tb[IFLA_MCTP_NET]));
414583be982SJeremy Kerr 
415583be982SJeremy Kerr 	return 0;
416583be982SJeremy Kerr }
417583be982SJeremy Kerr 
4187b1871afSMatt Johnston /* Matches netdev types that should have MCTP handling */
mctp_known(struct net_device * dev)4197b1871afSMatt Johnston static bool mctp_known(struct net_device *dev)
4207b1871afSMatt Johnston {
4217b1871afSMatt Johnston 	/* only register specific types (inc. NONE for TUN devices) */
4227b1871afSMatt Johnston 	return dev->type == ARPHRD_MCTP ||
4237b1871afSMatt Johnston 		   dev->type == ARPHRD_LOOPBACK ||
4247b1871afSMatt Johnston 		   dev->type == ARPHRD_NONE;
4257b1871afSMatt Johnston }
4267b1871afSMatt Johnston 
mctp_unregister(struct net_device * dev)427583be982SJeremy Kerr static void mctp_unregister(struct net_device *dev)
428583be982SJeremy Kerr {
429583be982SJeremy Kerr 	struct mctp_dev *mdev;
430583be982SJeremy Kerr 
431583be982SJeremy Kerr 	mdev = mctp_dev_get_rtnl(dev);
432583be982SJeremy Kerr 	if (!mdev)
433583be982SJeremy Kerr 		return;
434583be982SJeremy Kerr 
435583be982SJeremy Kerr 	RCU_INIT_POINTER(mdev->dev->mctp_ptr, NULL);
436583be982SJeremy Kerr 
437889b7da2SJeremy Kerr 	mctp_route_remove_dev(mdev);
4384d8b9319SMatt Johnston 	mctp_neigh_remove_dev(mdev);
439583be982SJeremy Kerr 
44043f55f23SJeremy Kerr 	mctp_dev_put(mdev);
441583be982SJeremy Kerr }
442583be982SJeremy Kerr 
mctp_register(struct net_device * dev)443583be982SJeremy Kerr static int mctp_register(struct net_device *dev)
444583be982SJeremy Kerr {
445583be982SJeremy Kerr 	struct mctp_dev *mdev;
446583be982SJeremy Kerr 
447583be982SJeremy Kerr 	/* Already registered? */
448b389a902SMatt Johnston 	if (rtnl_dereference(dev->mctp_ptr))
449583be982SJeremy Kerr 		return 0;
450583be982SJeremy Kerr 
4517b1871afSMatt Johnston 	/* only register specific types */
4527b1871afSMatt Johnston 	if (!mctp_known(dev))
4537b1871afSMatt Johnston 		return 0;
4547b1871afSMatt Johnston 
455583be982SJeremy Kerr 	mdev = mctp_add_dev(dev);
456583be982SJeremy Kerr 	if (IS_ERR(mdev))
457583be982SJeremy Kerr 		return PTR_ERR(mdev);
458583be982SJeremy Kerr 
459583be982SJeremy Kerr 	return 0;
460583be982SJeremy Kerr }
461583be982SJeremy Kerr 
mctp_dev_notify(struct notifier_block * this,unsigned long event,void * ptr)462583be982SJeremy Kerr static int mctp_dev_notify(struct notifier_block *this, unsigned long event,
463583be982SJeremy Kerr 			   void *ptr)
464583be982SJeremy Kerr {
465583be982SJeremy Kerr 	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
466583be982SJeremy Kerr 	int rc;
467583be982SJeremy Kerr 
468583be982SJeremy Kerr 	switch (event) {
469583be982SJeremy Kerr 	case NETDEV_REGISTER:
470583be982SJeremy Kerr 		rc = mctp_register(dev);
471583be982SJeremy Kerr 		if (rc)
472583be982SJeremy Kerr 			return notifier_from_errno(rc);
473583be982SJeremy Kerr 		break;
474583be982SJeremy Kerr 	case NETDEV_UNREGISTER:
475583be982SJeremy Kerr 		mctp_unregister(dev);
476583be982SJeremy Kerr 		break;
477583be982SJeremy Kerr 	}
478583be982SJeremy Kerr 
479583be982SJeremy Kerr 	return NOTIFY_OK;
480583be982SJeremy Kerr }
481583be982SJeremy Kerr 
mctp_register_netdevice(struct net_device * dev,const struct mctp_netdev_ops * ops)48267737c45SJeremy Kerr static int mctp_register_netdevice(struct net_device *dev,
48367737c45SJeremy Kerr 				   const struct mctp_netdev_ops *ops)
48467737c45SJeremy Kerr {
48567737c45SJeremy Kerr 	struct mctp_dev *mdev;
48667737c45SJeremy Kerr 
48767737c45SJeremy Kerr 	mdev = mctp_add_dev(dev);
48867737c45SJeremy Kerr 	if (IS_ERR(mdev))
48967737c45SJeremy Kerr 		return PTR_ERR(mdev);
49067737c45SJeremy Kerr 
49167737c45SJeremy Kerr 	mdev->ops = ops;
49267737c45SJeremy Kerr 
49367737c45SJeremy Kerr 	return register_netdevice(dev);
49467737c45SJeremy Kerr }
49567737c45SJeremy Kerr 
mctp_register_netdev(struct net_device * dev,const struct mctp_netdev_ops * ops)49667737c45SJeremy Kerr int mctp_register_netdev(struct net_device *dev,
49767737c45SJeremy Kerr 			 const struct mctp_netdev_ops *ops)
49867737c45SJeremy Kerr {
49967737c45SJeremy Kerr 	int rc;
50067737c45SJeremy Kerr 
50167737c45SJeremy Kerr 	rtnl_lock();
50267737c45SJeremy Kerr 	rc = mctp_register_netdevice(dev, ops);
50367737c45SJeremy Kerr 	rtnl_unlock();
50467737c45SJeremy Kerr 
50567737c45SJeremy Kerr 	return rc;
50667737c45SJeremy Kerr }
50767737c45SJeremy Kerr EXPORT_SYMBOL_GPL(mctp_register_netdev);
50867737c45SJeremy Kerr 
mctp_unregister_netdev(struct net_device * dev)50967737c45SJeremy Kerr void mctp_unregister_netdev(struct net_device *dev)
51067737c45SJeremy Kerr {
51167737c45SJeremy Kerr 	unregister_netdev(dev);
51267737c45SJeremy Kerr }
51367737c45SJeremy Kerr EXPORT_SYMBOL_GPL(mctp_unregister_netdev);
51467737c45SJeremy Kerr 
515583be982SJeremy Kerr static struct rtnl_af_ops mctp_af_ops = {
516583be982SJeremy Kerr 	.family = AF_MCTP,
517583be982SJeremy Kerr 	.fill_link_af = mctp_fill_link_af,
518583be982SJeremy Kerr 	.get_link_af_size = mctp_get_link_af_size,
519583be982SJeremy Kerr 	.set_link_af = mctp_set_link_af,
520583be982SJeremy Kerr };
521583be982SJeremy Kerr 
522583be982SJeremy Kerr static struct notifier_block mctp_dev_nb = {
523583be982SJeremy Kerr 	.notifier_call = mctp_dev_notify,
524583be982SJeremy Kerr 	.priority = ADDRCONF_NOTIFY_PRIORITY,
525583be982SJeremy Kerr };
526583be982SJeremy Kerr 
527*f4df31a0SKuniyuki Iwashima static const struct rtnl_msg_handler mctp_device_rtnl_msg_handlers[] = {
528*f4df31a0SKuniyuki Iwashima 	{THIS_MODULE, PF_MCTP, RTM_NEWADDR, mctp_rtm_newaddr, NULL, 0},
529*f4df31a0SKuniyuki Iwashima 	{THIS_MODULE, PF_MCTP, RTM_DELADDR, mctp_rtm_deladdr, NULL, 0},
530*f4df31a0SKuniyuki Iwashima 	{THIS_MODULE, PF_MCTP, RTM_GETADDR, NULL, mctp_dump_addrinfo, 0},
531*f4df31a0SKuniyuki Iwashima };
532583be982SJeremy Kerr 
mctp_device_init(void)533*f4df31a0SKuniyuki Iwashima int __init mctp_device_init(void)
534*f4df31a0SKuniyuki Iwashima {
535*f4df31a0SKuniyuki Iwashima 	int err;
536*f4df31a0SKuniyuki Iwashima 
537*f4df31a0SKuniyuki Iwashima 	register_netdevice_notifier(&mctp_dev_nb);
538583be982SJeremy Kerr 	rtnl_af_register(&mctp_af_ops);
539*f4df31a0SKuniyuki Iwashima 
540*f4df31a0SKuniyuki Iwashima 	err = rtnl_register_many(mctp_device_rtnl_msg_handlers);
541*f4df31a0SKuniyuki Iwashima 	if (err) {
542*f4df31a0SKuniyuki Iwashima 		rtnl_af_unregister(&mctp_af_ops);
543*f4df31a0SKuniyuki Iwashima 		unregister_netdevice_notifier(&mctp_dev_nb);
544*f4df31a0SKuniyuki Iwashima 	}
545*f4df31a0SKuniyuki Iwashima 
546*f4df31a0SKuniyuki Iwashima 	return err;
547583be982SJeremy Kerr }
548583be982SJeremy Kerr 
mctp_device_exit(void)549583be982SJeremy Kerr void __exit mctp_device_exit(void)
550583be982SJeremy Kerr {
551*f4df31a0SKuniyuki Iwashima 	rtnl_unregister_many(mctp_device_rtnl_msg_handlers);
552583be982SJeremy Kerr 	rtnl_af_unregister(&mctp_af_ops);
553583be982SJeremy Kerr 	unregister_netdevice_notifier(&mctp_dev_nb);
554583be982SJeremy Kerr }
555