GNU Linux-libre 4.19.286-gnu1
[releases.git] / net / ncsi / ncsi-netlink.c
1 /*
2  * Copyright Samuel Mendoza-Jonas, IBM Corporation 2018.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  */
9
10 #include <linux/module.h>
11 #include <linux/kernel.h>
12 #include <linux/if_arp.h>
13 #include <linux/rtnetlink.h>
14 #include <linux/etherdevice.h>
15 #include <linux/module.h>
16 #include <net/genetlink.h>
17 #include <net/ncsi.h>
18 #include <linux/skbuff.h>
19 #include <net/sock.h>
20 #include <uapi/linux/ncsi.h>
21
22 #include "internal.h"
23 #include "ncsi-netlink.h"
24
25 static struct genl_family ncsi_genl_family;
26
27 static const struct nla_policy ncsi_genl_policy[NCSI_ATTR_MAX + 1] = {
28         [NCSI_ATTR_IFINDEX] =           { .type = NLA_U32 },
29         [NCSI_ATTR_PACKAGE_LIST] =      { .type = NLA_NESTED },
30         [NCSI_ATTR_PACKAGE_ID] =        { .type = NLA_U32 },
31         [NCSI_ATTR_CHANNEL_ID] =        { .type = NLA_U32 },
32 };
33
34 static struct ncsi_dev_priv *ndp_from_ifindex(struct net *net, u32 ifindex)
35 {
36         struct ncsi_dev_priv *ndp;
37         struct net_device *dev;
38         struct ncsi_dev *nd;
39         struct ncsi_dev;
40
41         if (!net)
42                 return NULL;
43
44         dev = dev_get_by_index(net, ifindex);
45         if (!dev) {
46                 pr_err("NCSI netlink: No device for ifindex %u\n", ifindex);
47                 return NULL;
48         }
49
50         nd = ncsi_find_dev(dev);
51         ndp = nd ? TO_NCSI_DEV_PRIV(nd) : NULL;
52
53         dev_put(dev);
54         return ndp;
55 }
56
57 static int ncsi_write_channel_info(struct sk_buff *skb,
58                                    struct ncsi_dev_priv *ndp,
59                                    struct ncsi_channel *nc)
60 {
61         struct ncsi_channel_vlan_filter *ncf;
62         struct ncsi_channel_mode *m;
63         struct nlattr *vid_nest;
64         int i;
65
66         nla_put_u32(skb, NCSI_CHANNEL_ATTR_ID, nc->id);
67         m = &nc->modes[NCSI_MODE_LINK];
68         nla_put_u32(skb, NCSI_CHANNEL_ATTR_LINK_STATE, m->data[2]);
69         if (nc->state == NCSI_CHANNEL_ACTIVE)
70                 nla_put_flag(skb, NCSI_CHANNEL_ATTR_ACTIVE);
71         if (ndp->force_channel == nc)
72                 nla_put_flag(skb, NCSI_CHANNEL_ATTR_FORCED);
73
74         nla_put_u32(skb, NCSI_CHANNEL_ATTR_VERSION_MAJOR, nc->version.version);
75         nla_put_u32(skb, NCSI_CHANNEL_ATTR_VERSION_MINOR, nc->version.alpha2);
76         nla_put_string(skb, NCSI_CHANNEL_ATTR_VERSION_STR, nc->version.fw_name);
77
78         vid_nest = nla_nest_start(skb, NCSI_CHANNEL_ATTR_VLAN_LIST);
79         if (!vid_nest)
80                 return -ENOMEM;
81         ncf = &nc->vlan_filter;
82         i = -1;
83         while ((i = find_next_bit((void *)&ncf->bitmap, ncf->n_vids,
84                                   i + 1)) < ncf->n_vids) {
85                 if (ncf->vids[i])
86                         nla_put_u16(skb, NCSI_CHANNEL_ATTR_VLAN_ID,
87                                     ncf->vids[i]);
88         }
89         nla_nest_end(skb, vid_nest);
90
91         return 0;
92 }
93
94 static int ncsi_write_package_info(struct sk_buff *skb,
95                                    struct ncsi_dev_priv *ndp, unsigned int id)
96 {
97         struct nlattr *pnest, *cnest, *nest;
98         struct ncsi_package *np;
99         struct ncsi_channel *nc;
100         bool found;
101         int rc;
102
103         if (id > ndp->package_num - 1) {
104                 netdev_info(ndp->ndev.dev, "NCSI: No package with id %u\n", id);
105                 return -ENODEV;
106         }
107
108         found = false;
109         NCSI_FOR_EACH_PACKAGE(ndp, np) {
110                 if (np->id != id)
111                         continue;
112                 pnest = nla_nest_start(skb, NCSI_PKG_ATTR);
113                 if (!pnest)
114                         return -ENOMEM;
115                 nla_put_u32(skb, NCSI_PKG_ATTR_ID, np->id);
116                 if (ndp->force_package == np)
117                         nla_put_flag(skb, NCSI_PKG_ATTR_FORCED);
118                 cnest = nla_nest_start(skb, NCSI_PKG_ATTR_CHANNEL_LIST);
119                 if (!cnest) {
120                         nla_nest_cancel(skb, pnest);
121                         return -ENOMEM;
122                 }
123                 NCSI_FOR_EACH_CHANNEL(np, nc) {
124                         nest = nla_nest_start(skb, NCSI_CHANNEL_ATTR);
125                         if (!nest) {
126                                 nla_nest_cancel(skb, cnest);
127                                 nla_nest_cancel(skb, pnest);
128                                 return -ENOMEM;
129                         }
130                         rc = ncsi_write_channel_info(skb, ndp, nc);
131                         if (rc) {
132                                 nla_nest_cancel(skb, nest);
133                                 nla_nest_cancel(skb, cnest);
134                                 nla_nest_cancel(skb, pnest);
135                                 return rc;
136                         }
137                         nla_nest_end(skb, nest);
138                 }
139                 nla_nest_end(skb, cnest);
140                 nla_nest_end(skb, pnest);
141                 found = true;
142         }
143
144         if (!found)
145                 return -ENODEV;
146
147         return 0;
148 }
149
150 static int ncsi_pkg_info_nl(struct sk_buff *msg, struct genl_info *info)
151 {
152         struct ncsi_dev_priv *ndp;
153         unsigned int package_id;
154         struct sk_buff *skb;
155         struct nlattr *attr;
156         void *hdr;
157         int rc;
158
159         if (!info || !info->attrs)
160                 return -EINVAL;
161
162         if (!info->attrs[NCSI_ATTR_IFINDEX])
163                 return -EINVAL;
164
165         if (!info->attrs[NCSI_ATTR_PACKAGE_ID])
166                 return -EINVAL;
167
168         ndp = ndp_from_ifindex(genl_info_net(info),
169                                nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX]));
170         if (!ndp)
171                 return -ENODEV;
172
173         skb = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
174         if (!skb)
175                 return -ENOMEM;
176
177         hdr = genlmsg_put(skb, info->snd_portid, info->snd_seq,
178                           &ncsi_genl_family, 0, NCSI_CMD_PKG_INFO);
179         if (!hdr) {
180                 kfree_skb(skb);
181                 return -EMSGSIZE;
182         }
183
184         package_id = nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_ID]);
185
186         attr = nla_nest_start(skb, NCSI_ATTR_PACKAGE_LIST);
187         if (!attr) {
188                 kfree_skb(skb);
189                 return -EMSGSIZE;
190         }
191         rc = ncsi_write_package_info(skb, ndp, package_id);
192
193         if (rc) {
194                 nla_nest_cancel(skb, attr);
195                 goto err;
196         }
197
198         nla_nest_end(skb, attr);
199
200         genlmsg_end(skb, hdr);
201         return genlmsg_reply(skb, info);
202
203 err:
204         kfree_skb(skb);
205         return rc;
206 }
207
208 static int ncsi_pkg_info_all_nl(struct sk_buff *skb,
209                                 struct netlink_callback *cb)
210 {
211         struct nlattr *attrs[NCSI_ATTR_MAX + 1];
212         struct ncsi_package *np, *package;
213         struct ncsi_dev_priv *ndp;
214         unsigned int package_id;
215         struct nlattr *attr;
216         void *hdr;
217         int rc;
218
219         rc = genlmsg_parse(cb->nlh, &ncsi_genl_family, attrs, NCSI_ATTR_MAX,
220                            ncsi_genl_policy, NULL);
221         if (rc)
222                 return rc;
223
224         if (!attrs[NCSI_ATTR_IFINDEX])
225                 return -EINVAL;
226
227         ndp = ndp_from_ifindex(get_net(sock_net(skb->sk)),
228                                nla_get_u32(attrs[NCSI_ATTR_IFINDEX]));
229
230         if (!ndp)
231                 return -ENODEV;
232
233         package_id = cb->args[0];
234         package = NULL;
235         NCSI_FOR_EACH_PACKAGE(ndp, np)
236                 if (np->id == package_id)
237                         package = np;
238
239         if (!package)
240                 return 0; /* done */
241
242         hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
243                           &ncsi_genl_family, NLM_F_MULTI,  NCSI_CMD_PKG_INFO);
244         if (!hdr) {
245                 rc = -EMSGSIZE;
246                 goto err;
247         }
248
249         attr = nla_nest_start(skb, NCSI_ATTR_PACKAGE_LIST);
250         rc = ncsi_write_package_info(skb, ndp, package->id);
251         if (rc) {
252                 nla_nest_cancel(skb, attr);
253                 goto err;
254         }
255
256         nla_nest_end(skb, attr);
257         genlmsg_end(skb, hdr);
258
259         cb->args[0] = package_id + 1;
260
261         return skb->len;
262 err:
263         genlmsg_cancel(skb, hdr);
264         return rc;
265 }
266
267 static int ncsi_set_interface_nl(struct sk_buff *msg, struct genl_info *info)
268 {
269         struct ncsi_package *np, *package;
270         struct ncsi_channel *nc, *channel;
271         u32 package_id, channel_id;
272         struct ncsi_dev_priv *ndp;
273         unsigned long flags;
274
275         if (!info || !info->attrs)
276                 return -EINVAL;
277
278         if (!info->attrs[NCSI_ATTR_IFINDEX])
279                 return -EINVAL;
280
281         if (!info->attrs[NCSI_ATTR_PACKAGE_ID])
282                 return -EINVAL;
283
284         ndp = ndp_from_ifindex(get_net(sock_net(msg->sk)),
285                                nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX]));
286         if (!ndp)
287                 return -ENODEV;
288
289         package_id = nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_ID]);
290         package = NULL;
291
292         spin_lock_irqsave(&ndp->lock, flags);
293
294         NCSI_FOR_EACH_PACKAGE(ndp, np)
295                 if (np->id == package_id)
296                         package = np;
297         if (!package) {
298                 /* The user has set a package that does not exist */
299                 spin_unlock_irqrestore(&ndp->lock, flags);
300                 return -ERANGE;
301         }
302
303         channel = NULL;
304         if (!info->attrs[NCSI_ATTR_CHANNEL_ID]) {
305                 /* Allow any channel */
306                 channel_id = NCSI_RESERVED_CHANNEL;
307         } else {
308                 channel_id = nla_get_u32(info->attrs[NCSI_ATTR_CHANNEL_ID]);
309                 NCSI_FOR_EACH_CHANNEL(package, nc)
310                         if (nc->id == channel_id)
311                                 channel = nc;
312         }
313
314         if (channel_id != NCSI_RESERVED_CHANNEL && !channel) {
315                 /* The user has set a channel that does not exist on this
316                  * package
317                  */
318                 spin_unlock_irqrestore(&ndp->lock, flags);
319                 netdev_info(ndp->ndev.dev, "NCSI: Channel %u does not exist!\n",
320                             channel_id);
321                 return -ERANGE;
322         }
323
324         ndp->force_package = package;
325         ndp->force_channel = channel;
326         spin_unlock_irqrestore(&ndp->lock, flags);
327
328         netdev_info(ndp->ndev.dev, "Set package 0x%x, channel 0x%x%s as preferred\n",
329                     package_id, channel_id,
330                     channel_id == NCSI_RESERVED_CHANNEL ? " (any)" : "");
331
332         /* Bounce the NCSI channel to set changes */
333         ncsi_stop_dev(&ndp->ndev);
334         ncsi_start_dev(&ndp->ndev);
335
336         return 0;
337 }
338
339 static int ncsi_clear_interface_nl(struct sk_buff *msg, struct genl_info *info)
340 {
341         struct ncsi_dev_priv *ndp;
342         unsigned long flags;
343
344         if (!info || !info->attrs)
345                 return -EINVAL;
346
347         if (!info->attrs[NCSI_ATTR_IFINDEX])
348                 return -EINVAL;
349
350         ndp = ndp_from_ifindex(get_net(sock_net(msg->sk)),
351                                nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX]));
352         if (!ndp)
353                 return -ENODEV;
354
355         /* Clear any override */
356         spin_lock_irqsave(&ndp->lock, flags);
357         ndp->force_package = NULL;
358         ndp->force_channel = NULL;
359         spin_unlock_irqrestore(&ndp->lock, flags);
360         netdev_info(ndp->ndev.dev, "NCSI: Cleared preferred package/channel\n");
361
362         /* Bounce the NCSI channel to set changes */
363         ncsi_stop_dev(&ndp->ndev);
364         ncsi_start_dev(&ndp->ndev);
365
366         return 0;
367 }
368
369 static const struct genl_ops ncsi_ops[] = {
370         {
371                 .cmd = NCSI_CMD_PKG_INFO,
372                 .policy = ncsi_genl_policy,
373                 .doit = ncsi_pkg_info_nl,
374                 .dumpit = ncsi_pkg_info_all_nl,
375                 .flags = 0,
376         },
377         {
378                 .cmd = NCSI_CMD_SET_INTERFACE,
379                 .policy = ncsi_genl_policy,
380                 .doit = ncsi_set_interface_nl,
381                 .flags = GENL_ADMIN_PERM,
382         },
383         {
384                 .cmd = NCSI_CMD_CLEAR_INTERFACE,
385                 .policy = ncsi_genl_policy,
386                 .doit = ncsi_clear_interface_nl,
387                 .flags = GENL_ADMIN_PERM,
388         },
389 };
390
391 static struct genl_family ncsi_genl_family __ro_after_init = {
392         .name = "NCSI",
393         .version = 0,
394         .maxattr = NCSI_ATTR_MAX,
395         .module = THIS_MODULE,
396         .ops = ncsi_ops,
397         .n_ops = ARRAY_SIZE(ncsi_ops),
398 };
399
400 static int __init ncsi_init_netlink(void)
401 {
402         return genl_register_family(&ncsi_genl_family);
403 }
404 subsys_initcall(ncsi_init_netlink);