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