GNU Linux-libre 4.14.266-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) {
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);
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         rc = ncsi_write_package_info(skb, ndp, package_id);
188
189         if (rc) {
190                 nla_nest_cancel(skb, attr);
191                 goto err;
192         }
193
194         nla_nest_end(skb, attr);
195
196         genlmsg_end(skb, hdr);
197         return genlmsg_reply(skb, info);
198
199 err:
200         genlmsg_cancel(skb, hdr);
201         kfree(skb);
202         return rc;
203 }
204
205 static int ncsi_pkg_info_all_nl(struct sk_buff *skb,
206                                 struct netlink_callback *cb)
207 {
208         struct nlattr *attrs[NCSI_ATTR_MAX];
209         struct ncsi_package *np, *package;
210         struct ncsi_dev_priv *ndp;
211         unsigned int package_id;
212         struct nlattr *attr;
213         void *hdr;
214         int rc;
215
216         rc = genlmsg_parse(cb->nlh, &ncsi_genl_family, attrs, NCSI_ATTR_MAX,
217                            ncsi_genl_policy, NULL);
218         if (rc)
219                 return rc;
220
221         if (!attrs[NCSI_ATTR_IFINDEX])
222                 return -EINVAL;
223
224         ndp = ndp_from_ifindex(get_net(sock_net(skb->sk)),
225                                nla_get_u32(attrs[NCSI_ATTR_IFINDEX]));
226
227         if (!ndp)
228                 return -ENODEV;
229
230         package_id = cb->args[0];
231         package = NULL;
232         NCSI_FOR_EACH_PACKAGE(ndp, np)
233                 if (np->id == package_id)
234                         package = np;
235
236         if (!package)
237                 return 0; /* done */
238
239         hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
240                           &ncsi_genl_family, 0,  NCSI_CMD_PKG_INFO);
241         if (!hdr) {
242                 rc = -EMSGSIZE;
243                 goto err;
244         }
245
246         attr = nla_nest_start(skb, NCSI_ATTR_PACKAGE_LIST);
247         rc = ncsi_write_package_info(skb, ndp, package->id);
248         if (rc) {
249                 nla_nest_cancel(skb, attr);
250                 goto err;
251         }
252
253         nla_nest_end(skb, attr);
254         genlmsg_end(skb, hdr);
255
256         cb->args[0] = package_id + 1;
257
258         return skb->len;
259 err:
260         genlmsg_cancel(skb, hdr);
261         return rc;
262 }
263
264 static int ncsi_set_interface_nl(struct sk_buff *msg, struct genl_info *info)
265 {
266         struct ncsi_package *np, *package;
267         struct ncsi_channel *nc, *channel;
268         u32 package_id, channel_id;
269         struct ncsi_dev_priv *ndp;
270         unsigned long flags;
271
272         if (!info || !info->attrs)
273                 return -EINVAL;
274
275         if (!info->attrs[NCSI_ATTR_IFINDEX])
276                 return -EINVAL;
277
278         if (!info->attrs[NCSI_ATTR_PACKAGE_ID])
279                 return -EINVAL;
280
281         ndp = ndp_from_ifindex(get_net(sock_net(msg->sk)),
282                                nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX]));
283         if (!ndp)
284                 return -ENODEV;
285
286         package_id = nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_ID]);
287         package = NULL;
288
289         spin_lock_irqsave(&ndp->lock, flags);
290
291         NCSI_FOR_EACH_PACKAGE(ndp, np)
292                 if (np->id == package_id)
293                         package = np;
294         if (!package) {
295                 /* The user has set a package that does not exist */
296                 return -ERANGE;
297         }
298
299         channel = NULL;
300         if (!info->attrs[NCSI_ATTR_CHANNEL_ID]) {
301                 /* Allow any channel */
302                 channel_id = NCSI_RESERVED_CHANNEL;
303         } else {
304                 channel_id = nla_get_u32(info->attrs[NCSI_ATTR_CHANNEL_ID]);
305                 NCSI_FOR_EACH_CHANNEL(package, nc)
306                         if (nc->id == channel_id)
307                                 channel = nc;
308         }
309
310         if (channel_id != NCSI_RESERVED_CHANNEL && !channel) {
311                 /* The user has set a channel that does not exist on this
312                  * package
313                  */
314                 netdev_info(ndp->ndev.dev, "NCSI: Channel %u does not exist!\n",
315                             channel_id);
316                 return -ERANGE;
317         }
318
319         ndp->force_package = package;
320         ndp->force_channel = channel;
321         spin_unlock_irqrestore(&ndp->lock, flags);
322
323         netdev_info(ndp->ndev.dev, "Set package 0x%x, channel 0x%x%s as preferred\n",
324                     package_id, channel_id,
325                     channel_id == NCSI_RESERVED_CHANNEL ? " (any)" : "");
326
327         /* Bounce the NCSI channel to set changes */
328         ncsi_stop_dev(&ndp->ndev);
329         ncsi_start_dev(&ndp->ndev);
330
331         return 0;
332 }
333
334 static int ncsi_clear_interface_nl(struct sk_buff *msg, struct genl_info *info)
335 {
336         struct ncsi_dev_priv *ndp;
337         unsigned long flags;
338
339         if (!info || !info->attrs)
340                 return -EINVAL;
341
342         if (!info->attrs[NCSI_ATTR_IFINDEX])
343                 return -EINVAL;
344
345         ndp = ndp_from_ifindex(get_net(sock_net(msg->sk)),
346                                nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX]));
347         if (!ndp)
348                 return -ENODEV;
349
350         /* Clear any override */
351         spin_lock_irqsave(&ndp->lock, flags);
352         ndp->force_package = NULL;
353         ndp->force_channel = NULL;
354         spin_unlock_irqrestore(&ndp->lock, flags);
355         netdev_info(ndp->ndev.dev, "NCSI: Cleared preferred package/channel\n");
356
357         /* Bounce the NCSI channel to set changes */
358         ncsi_stop_dev(&ndp->ndev);
359         ncsi_start_dev(&ndp->ndev);
360
361         return 0;
362 }
363
364 static const struct genl_ops ncsi_ops[] = {
365         {
366                 .cmd = NCSI_CMD_PKG_INFO,
367                 .policy = ncsi_genl_policy,
368                 .doit = ncsi_pkg_info_nl,
369                 .dumpit = ncsi_pkg_info_all_nl,
370                 .flags = 0,
371         },
372         {
373                 .cmd = NCSI_CMD_SET_INTERFACE,
374                 .policy = ncsi_genl_policy,
375                 .doit = ncsi_set_interface_nl,
376                 .flags = GENL_ADMIN_PERM,
377         },
378         {
379                 .cmd = NCSI_CMD_CLEAR_INTERFACE,
380                 .policy = ncsi_genl_policy,
381                 .doit = ncsi_clear_interface_nl,
382                 .flags = GENL_ADMIN_PERM,
383         },
384 };
385
386 static struct genl_family ncsi_genl_family __ro_after_init = {
387         .name = "NCSI",
388         .version = 0,
389         .maxattr = NCSI_ATTR_MAX,
390         .module = THIS_MODULE,
391         .ops = ncsi_ops,
392         .n_ops = ARRAY_SIZE(ncsi_ops),
393 };
394
395 int ncsi_init_netlink(struct net_device *dev)
396 {
397         int rc;
398
399         rc = genl_register_family(&ncsi_genl_family);
400         if (rc)
401                 netdev_err(dev, "ncsi: failed to register netlink family\n");
402
403         return rc;
404 }
405
406 int ncsi_unregister_netlink(struct net_device *dev)
407 {
408         int rc;
409
410         rc = genl_unregister_family(&ncsi_genl_family);
411         if (rc)
412                 netdev_err(dev, "ncsi: failed to unregister netlink family\n");
413
414         return rc;
415 }