Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * Copyright Samuel Mendoza-Jonas, IBM Corporation 2018.
0004  */
0005 
0006 #include <linux/module.h>
0007 #include <linux/kernel.h>
0008 #include <linux/if_arp.h>
0009 #include <linux/rtnetlink.h>
0010 #include <linux/etherdevice.h>
0011 #include <net/genetlink.h>
0012 #include <net/ncsi.h>
0013 #include <linux/skbuff.h>
0014 #include <net/sock.h>
0015 #include <uapi/linux/ncsi.h>
0016 
0017 #include "internal.h"
0018 #include "ncsi-pkt.h"
0019 #include "ncsi-netlink.h"
0020 
0021 static struct genl_family ncsi_genl_family;
0022 
0023 static const struct nla_policy ncsi_genl_policy[NCSI_ATTR_MAX + 1] = {
0024     [NCSI_ATTR_IFINDEX] =       { .type = NLA_U32 },
0025     [NCSI_ATTR_PACKAGE_LIST] =  { .type = NLA_NESTED },
0026     [NCSI_ATTR_PACKAGE_ID] =    { .type = NLA_U32 },
0027     [NCSI_ATTR_CHANNEL_ID] =    { .type = NLA_U32 },
0028     [NCSI_ATTR_DATA] =      { .type = NLA_BINARY, .len = 2048 },
0029     [NCSI_ATTR_MULTI_FLAG] =    { .type = NLA_FLAG },
0030     [NCSI_ATTR_PACKAGE_MASK] =  { .type = NLA_U32 },
0031     [NCSI_ATTR_CHANNEL_MASK] =  { .type = NLA_U32 },
0032 };
0033 
0034 static struct ncsi_dev_priv *ndp_from_ifindex(struct net *net, u32 ifindex)
0035 {
0036     struct ncsi_dev_priv *ndp;
0037     struct net_device *dev;
0038     struct ncsi_dev *nd;
0039     struct ncsi_dev;
0040 
0041     if (!net)
0042         return NULL;
0043 
0044     dev = dev_get_by_index(net, ifindex);
0045     if (!dev) {
0046         pr_err("NCSI netlink: No device for ifindex %u\n", ifindex);
0047         return NULL;
0048     }
0049 
0050     nd = ncsi_find_dev(dev);
0051     ndp = nd ? TO_NCSI_DEV_PRIV(nd) : NULL;
0052 
0053     dev_put(dev);
0054     return ndp;
0055 }
0056 
0057 static int ncsi_write_channel_info(struct sk_buff *skb,
0058                    struct ncsi_dev_priv *ndp,
0059                    struct ncsi_channel *nc)
0060 {
0061     struct ncsi_channel_vlan_filter *ncf;
0062     struct ncsi_channel_mode *m;
0063     struct nlattr *vid_nest;
0064     int i;
0065 
0066     nla_put_u32(skb, NCSI_CHANNEL_ATTR_ID, nc->id);
0067     m = &nc->modes[NCSI_MODE_LINK];
0068     nla_put_u32(skb, NCSI_CHANNEL_ATTR_LINK_STATE, m->data[2]);
0069     if (nc->state == NCSI_CHANNEL_ACTIVE)
0070         nla_put_flag(skb, NCSI_CHANNEL_ATTR_ACTIVE);
0071     if (nc == nc->package->preferred_channel)
0072         nla_put_flag(skb, NCSI_CHANNEL_ATTR_FORCED);
0073 
0074     nla_put_u32(skb, NCSI_CHANNEL_ATTR_VERSION_MAJOR, nc->version.version);
0075     nla_put_u32(skb, NCSI_CHANNEL_ATTR_VERSION_MINOR, nc->version.alpha2);
0076     nla_put_string(skb, NCSI_CHANNEL_ATTR_VERSION_STR, nc->version.fw_name);
0077 
0078     vid_nest = nla_nest_start_noflag(skb, NCSI_CHANNEL_ATTR_VLAN_LIST);
0079     if (!vid_nest)
0080         return -ENOMEM;
0081     ncf = &nc->vlan_filter;
0082     i = -1;
0083     while ((i = find_next_bit((void *)&ncf->bitmap, ncf->n_vids,
0084                   i + 1)) < ncf->n_vids) {
0085         if (ncf->vids[i])
0086             nla_put_u16(skb, NCSI_CHANNEL_ATTR_VLAN_ID,
0087                     ncf->vids[i]);
0088     }
0089     nla_nest_end(skb, vid_nest);
0090 
0091     return 0;
0092 }
0093 
0094 static int ncsi_write_package_info(struct sk_buff *skb,
0095                    struct ncsi_dev_priv *ndp, unsigned int id)
0096 {
0097     struct nlattr *pnest, *cnest, *nest;
0098     struct ncsi_package *np;
0099     struct ncsi_channel *nc;
0100     bool found;
0101     int rc;
0102 
0103     if (id > ndp->package_num - 1) {
0104         netdev_info(ndp->ndev.dev, "NCSI: No package with id %u\n", id);
0105         return -ENODEV;
0106     }
0107 
0108     found = false;
0109     NCSI_FOR_EACH_PACKAGE(ndp, np) {
0110         if (np->id != id)
0111             continue;
0112         pnest = nla_nest_start_noflag(skb, NCSI_PKG_ATTR);
0113         if (!pnest)
0114             return -ENOMEM;
0115         rc = nla_put_u32(skb, NCSI_PKG_ATTR_ID, np->id);
0116         if (rc) {
0117             nla_nest_cancel(skb, pnest);
0118             return rc;
0119         }
0120         if ((0x1 << np->id) == ndp->package_whitelist)
0121             nla_put_flag(skb, NCSI_PKG_ATTR_FORCED);
0122         cnest = nla_nest_start_noflag(skb, NCSI_PKG_ATTR_CHANNEL_LIST);
0123         if (!cnest) {
0124             nla_nest_cancel(skb, pnest);
0125             return -ENOMEM;
0126         }
0127         NCSI_FOR_EACH_CHANNEL(np, nc) {
0128             nest = nla_nest_start_noflag(skb, NCSI_CHANNEL_ATTR);
0129             if (!nest) {
0130                 nla_nest_cancel(skb, cnest);
0131                 nla_nest_cancel(skb, pnest);
0132                 return -ENOMEM;
0133             }
0134             rc = ncsi_write_channel_info(skb, ndp, nc);
0135             if (rc) {
0136                 nla_nest_cancel(skb, nest);
0137                 nla_nest_cancel(skb, cnest);
0138                 nla_nest_cancel(skb, pnest);
0139                 return rc;
0140             }
0141             nla_nest_end(skb, nest);
0142         }
0143         nla_nest_end(skb, cnest);
0144         nla_nest_end(skb, pnest);
0145         found = true;
0146     }
0147 
0148     if (!found)
0149         return -ENODEV;
0150 
0151     return 0;
0152 }
0153 
0154 static int ncsi_pkg_info_nl(struct sk_buff *msg, struct genl_info *info)
0155 {
0156     struct ncsi_dev_priv *ndp;
0157     unsigned int package_id;
0158     struct sk_buff *skb;
0159     struct nlattr *attr;
0160     void *hdr;
0161     int rc;
0162 
0163     if (!info || !info->attrs)
0164         return -EINVAL;
0165 
0166     if (!info->attrs[NCSI_ATTR_IFINDEX])
0167         return -EINVAL;
0168 
0169     if (!info->attrs[NCSI_ATTR_PACKAGE_ID])
0170         return -EINVAL;
0171 
0172     ndp = ndp_from_ifindex(genl_info_net(info),
0173                    nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX]));
0174     if (!ndp)
0175         return -ENODEV;
0176 
0177     skb = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
0178     if (!skb)
0179         return -ENOMEM;
0180 
0181     hdr = genlmsg_put(skb, info->snd_portid, info->snd_seq,
0182               &ncsi_genl_family, 0, NCSI_CMD_PKG_INFO);
0183     if (!hdr) {
0184         kfree_skb(skb);
0185         return -EMSGSIZE;
0186     }
0187 
0188     package_id = nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_ID]);
0189 
0190     attr = nla_nest_start_noflag(skb, NCSI_ATTR_PACKAGE_LIST);
0191     if (!attr) {
0192         kfree_skb(skb);
0193         return -EMSGSIZE;
0194     }
0195     rc = ncsi_write_package_info(skb, ndp, package_id);
0196 
0197     if (rc) {
0198         nla_nest_cancel(skb, attr);
0199         goto err;
0200     }
0201 
0202     nla_nest_end(skb, attr);
0203 
0204     genlmsg_end(skb, hdr);
0205     return genlmsg_reply(skb, info);
0206 
0207 err:
0208     kfree_skb(skb);
0209     return rc;
0210 }
0211 
0212 static int ncsi_pkg_info_all_nl(struct sk_buff *skb,
0213                 struct netlink_callback *cb)
0214 {
0215     struct nlattr *attrs[NCSI_ATTR_MAX + 1];
0216     struct ncsi_package *np, *package;
0217     struct ncsi_dev_priv *ndp;
0218     unsigned int package_id;
0219     struct nlattr *attr;
0220     void *hdr;
0221     int rc;
0222 
0223     rc = genlmsg_parse_deprecated(cb->nlh, &ncsi_genl_family, attrs, NCSI_ATTR_MAX,
0224                       ncsi_genl_policy, NULL);
0225     if (rc)
0226         return rc;
0227 
0228     if (!attrs[NCSI_ATTR_IFINDEX])
0229         return -EINVAL;
0230 
0231     ndp = ndp_from_ifindex(get_net(sock_net(skb->sk)),
0232                    nla_get_u32(attrs[NCSI_ATTR_IFINDEX]));
0233 
0234     if (!ndp)
0235         return -ENODEV;
0236 
0237     package_id = cb->args[0];
0238     package = NULL;
0239     NCSI_FOR_EACH_PACKAGE(ndp, np)
0240         if (np->id == package_id)
0241             package = np;
0242 
0243     if (!package)
0244         return 0; /* done */
0245 
0246     hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
0247               &ncsi_genl_family, NLM_F_MULTI,  NCSI_CMD_PKG_INFO);
0248     if (!hdr) {
0249         rc = -EMSGSIZE;
0250         goto err;
0251     }
0252 
0253     attr = nla_nest_start_noflag(skb, NCSI_ATTR_PACKAGE_LIST);
0254     if (!attr) {
0255         rc = -EMSGSIZE;
0256         goto err;
0257     }
0258     rc = ncsi_write_package_info(skb, ndp, package->id);
0259     if (rc) {
0260         nla_nest_cancel(skb, attr);
0261         goto err;
0262     }
0263 
0264     nla_nest_end(skb, attr);
0265     genlmsg_end(skb, hdr);
0266 
0267     cb->args[0] = package_id + 1;
0268 
0269     return skb->len;
0270 err:
0271     genlmsg_cancel(skb, hdr);
0272     return rc;
0273 }
0274 
0275 static int ncsi_set_interface_nl(struct sk_buff *msg, struct genl_info *info)
0276 {
0277     struct ncsi_package *np, *package;
0278     struct ncsi_channel *nc, *channel;
0279     u32 package_id, channel_id;
0280     struct ncsi_dev_priv *ndp;
0281     unsigned long flags;
0282 
0283     if (!info || !info->attrs)
0284         return -EINVAL;
0285 
0286     if (!info->attrs[NCSI_ATTR_IFINDEX])
0287         return -EINVAL;
0288 
0289     if (!info->attrs[NCSI_ATTR_PACKAGE_ID])
0290         return -EINVAL;
0291 
0292     ndp = ndp_from_ifindex(get_net(sock_net(msg->sk)),
0293                    nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX]));
0294     if (!ndp)
0295         return -ENODEV;
0296 
0297     package_id = nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_ID]);
0298     package = NULL;
0299 
0300     NCSI_FOR_EACH_PACKAGE(ndp, np)
0301         if (np->id == package_id)
0302             package = np;
0303     if (!package) {
0304         /* The user has set a package that does not exist */
0305         return -ERANGE;
0306     }
0307 
0308     channel = NULL;
0309     if (info->attrs[NCSI_ATTR_CHANNEL_ID]) {
0310         channel_id = nla_get_u32(info->attrs[NCSI_ATTR_CHANNEL_ID]);
0311         NCSI_FOR_EACH_CHANNEL(package, nc)
0312             if (nc->id == channel_id) {
0313                 channel = nc;
0314                 break;
0315             }
0316         if (!channel) {
0317             netdev_info(ndp->ndev.dev,
0318                     "NCSI: Channel %u does not exist!\n",
0319                     channel_id);
0320             return -ERANGE;
0321         }
0322     }
0323 
0324     spin_lock_irqsave(&ndp->lock, flags);
0325     ndp->package_whitelist = 0x1 << package->id;
0326     ndp->multi_package = false;
0327     spin_unlock_irqrestore(&ndp->lock, flags);
0328 
0329     spin_lock_irqsave(&package->lock, flags);
0330     package->multi_channel = false;
0331     if (channel) {
0332         package->channel_whitelist = 0x1 << channel->id;
0333         package->preferred_channel = channel;
0334     } else {
0335         /* Allow any channel */
0336         package->channel_whitelist = UINT_MAX;
0337         package->preferred_channel = NULL;
0338     }
0339     spin_unlock_irqrestore(&package->lock, flags);
0340 
0341     if (channel)
0342         netdev_info(ndp->ndev.dev,
0343                 "Set package 0x%x, channel 0x%x as preferred\n",
0344                 package_id, channel_id);
0345     else
0346         netdev_info(ndp->ndev.dev, "Set package 0x%x as preferred\n",
0347                 package_id);
0348 
0349     /* Update channel configuration */
0350     if (!(ndp->flags & NCSI_DEV_RESET))
0351         ncsi_reset_dev(&ndp->ndev);
0352 
0353     return 0;
0354 }
0355 
0356 static int ncsi_clear_interface_nl(struct sk_buff *msg, struct genl_info *info)
0357 {
0358     struct ncsi_dev_priv *ndp;
0359     struct ncsi_package *np;
0360     unsigned long flags;
0361 
0362     if (!info || !info->attrs)
0363         return -EINVAL;
0364 
0365     if (!info->attrs[NCSI_ATTR_IFINDEX])
0366         return -EINVAL;
0367 
0368     ndp = ndp_from_ifindex(get_net(sock_net(msg->sk)),
0369                    nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX]));
0370     if (!ndp)
0371         return -ENODEV;
0372 
0373     /* Reset any whitelists and disable multi mode */
0374     spin_lock_irqsave(&ndp->lock, flags);
0375     ndp->package_whitelist = UINT_MAX;
0376     ndp->multi_package = false;
0377     spin_unlock_irqrestore(&ndp->lock, flags);
0378 
0379     NCSI_FOR_EACH_PACKAGE(ndp, np) {
0380         spin_lock_irqsave(&np->lock, flags);
0381         np->multi_channel = false;
0382         np->channel_whitelist = UINT_MAX;
0383         np->preferred_channel = NULL;
0384         spin_unlock_irqrestore(&np->lock, flags);
0385     }
0386     netdev_info(ndp->ndev.dev, "NCSI: Cleared preferred package/channel\n");
0387 
0388     /* Update channel configuration */
0389     if (!(ndp->flags & NCSI_DEV_RESET))
0390         ncsi_reset_dev(&ndp->ndev);
0391 
0392     return 0;
0393 }
0394 
0395 static int ncsi_send_cmd_nl(struct sk_buff *msg, struct genl_info *info)
0396 {
0397     struct ncsi_dev_priv *ndp;
0398     struct ncsi_pkt_hdr *hdr;
0399     struct ncsi_cmd_arg nca;
0400     unsigned char *data;
0401     u32 package_id;
0402     u32 channel_id;
0403     int len, ret;
0404 
0405     if (!info || !info->attrs) {
0406         ret = -EINVAL;
0407         goto out;
0408     }
0409 
0410     if (!info->attrs[NCSI_ATTR_IFINDEX]) {
0411         ret = -EINVAL;
0412         goto out;
0413     }
0414 
0415     if (!info->attrs[NCSI_ATTR_PACKAGE_ID]) {
0416         ret = -EINVAL;
0417         goto out;
0418     }
0419 
0420     if (!info->attrs[NCSI_ATTR_CHANNEL_ID]) {
0421         ret = -EINVAL;
0422         goto out;
0423     }
0424 
0425     if (!info->attrs[NCSI_ATTR_DATA]) {
0426         ret = -EINVAL;
0427         goto out;
0428     }
0429 
0430     ndp = ndp_from_ifindex(get_net(sock_net(msg->sk)),
0431                    nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX]));
0432     if (!ndp) {
0433         ret = -ENODEV;
0434         goto out;
0435     }
0436 
0437     package_id = nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_ID]);
0438     channel_id = nla_get_u32(info->attrs[NCSI_ATTR_CHANNEL_ID]);
0439 
0440     if (package_id >= NCSI_MAX_PACKAGE || channel_id >= NCSI_MAX_CHANNEL) {
0441         ret = -ERANGE;
0442         goto out_netlink;
0443     }
0444 
0445     len = nla_len(info->attrs[NCSI_ATTR_DATA]);
0446     if (len < sizeof(struct ncsi_pkt_hdr)) {
0447         netdev_info(ndp->ndev.dev, "NCSI: no command to send %u\n",
0448                 package_id);
0449         ret = -EINVAL;
0450         goto out_netlink;
0451     } else {
0452         data = (unsigned char *)nla_data(info->attrs[NCSI_ATTR_DATA]);
0453     }
0454 
0455     hdr = (struct ncsi_pkt_hdr *)data;
0456 
0457     nca.ndp = ndp;
0458     nca.package = (unsigned char)package_id;
0459     nca.channel = (unsigned char)channel_id;
0460     nca.type = hdr->type;
0461     nca.req_flags = NCSI_REQ_FLAG_NETLINK_DRIVEN;
0462     nca.info = info;
0463     nca.payload = ntohs(hdr->length);
0464     nca.data = data + sizeof(*hdr);
0465 
0466     ret = ncsi_xmit_cmd(&nca);
0467 out_netlink:
0468     if (ret != 0) {
0469         netdev_err(ndp->ndev.dev,
0470                "NCSI: Error %d sending command\n",
0471                ret);
0472         ncsi_send_netlink_err(ndp->ndev.dev,
0473                       info->snd_seq,
0474                       info->snd_portid,
0475                       info->nlhdr,
0476                       ret);
0477     }
0478 out:
0479     return ret;
0480 }
0481 
0482 int ncsi_send_netlink_rsp(struct ncsi_request *nr,
0483               struct ncsi_package *np,
0484               struct ncsi_channel *nc)
0485 {
0486     struct sk_buff *skb;
0487     struct net *net;
0488     void *hdr;
0489     int rc;
0490 
0491     net = dev_net(nr->rsp->dev);
0492 
0493     skb = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
0494     if (!skb)
0495         return -ENOMEM;
0496 
0497     hdr = genlmsg_put(skb, nr->snd_portid, nr->snd_seq,
0498               &ncsi_genl_family, 0, NCSI_CMD_SEND_CMD);
0499     if (!hdr) {
0500         kfree_skb(skb);
0501         return -EMSGSIZE;
0502     }
0503 
0504     nla_put_u32(skb, NCSI_ATTR_IFINDEX, nr->rsp->dev->ifindex);
0505     if (np)
0506         nla_put_u32(skb, NCSI_ATTR_PACKAGE_ID, np->id);
0507     if (nc)
0508         nla_put_u32(skb, NCSI_ATTR_CHANNEL_ID, nc->id);
0509     else
0510         nla_put_u32(skb, NCSI_ATTR_CHANNEL_ID, NCSI_RESERVED_CHANNEL);
0511 
0512     rc = nla_put(skb, NCSI_ATTR_DATA, nr->rsp->len, (void *)nr->rsp->data);
0513     if (rc)
0514         goto err;
0515 
0516     genlmsg_end(skb, hdr);
0517     return genlmsg_unicast(net, skb, nr->snd_portid);
0518 
0519 err:
0520     kfree_skb(skb);
0521     return rc;
0522 }
0523 
0524 int ncsi_send_netlink_timeout(struct ncsi_request *nr,
0525                   struct ncsi_package *np,
0526                   struct ncsi_channel *nc)
0527 {
0528     struct sk_buff *skb;
0529     struct net *net;
0530     void *hdr;
0531 
0532     skb = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
0533     if (!skb)
0534         return -ENOMEM;
0535 
0536     hdr = genlmsg_put(skb, nr->snd_portid, nr->snd_seq,
0537               &ncsi_genl_family, 0, NCSI_CMD_SEND_CMD);
0538     if (!hdr) {
0539         kfree_skb(skb);
0540         return -EMSGSIZE;
0541     }
0542 
0543     net = dev_net(nr->cmd->dev);
0544 
0545     nla_put_u32(skb, NCSI_ATTR_IFINDEX, nr->cmd->dev->ifindex);
0546 
0547     if (np)
0548         nla_put_u32(skb, NCSI_ATTR_PACKAGE_ID, np->id);
0549     else
0550         nla_put_u32(skb, NCSI_ATTR_PACKAGE_ID,
0551                 NCSI_PACKAGE_INDEX((((struct ncsi_pkt_hdr *)
0552                          nr->cmd->data)->channel)));
0553 
0554     if (nc)
0555         nla_put_u32(skb, NCSI_ATTR_CHANNEL_ID, nc->id);
0556     else
0557         nla_put_u32(skb, NCSI_ATTR_CHANNEL_ID, NCSI_RESERVED_CHANNEL);
0558 
0559     genlmsg_end(skb, hdr);
0560     return genlmsg_unicast(net, skb, nr->snd_portid);
0561 }
0562 
0563 int ncsi_send_netlink_err(struct net_device *dev,
0564               u32 snd_seq,
0565               u32 snd_portid,
0566               struct nlmsghdr *nlhdr,
0567               int err)
0568 {
0569     struct nlmsghdr *nlh;
0570     struct nlmsgerr *nle;
0571     struct sk_buff *skb;
0572     struct net *net;
0573 
0574     skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
0575     if (!skb)
0576         return -ENOMEM;
0577 
0578     net = dev_net(dev);
0579 
0580     nlh = nlmsg_put(skb, snd_portid, snd_seq,
0581             NLMSG_ERROR, sizeof(*nle), 0);
0582     nle = (struct nlmsgerr *)nlmsg_data(nlh);
0583     nle->error = err;
0584     memcpy(&nle->msg, nlhdr, sizeof(*nlh));
0585 
0586     nlmsg_end(skb, nlh);
0587 
0588     return nlmsg_unicast(net->genl_sock, skb, snd_portid);
0589 }
0590 
0591 static int ncsi_set_package_mask_nl(struct sk_buff *msg,
0592                     struct genl_info *info)
0593 {
0594     struct ncsi_dev_priv *ndp;
0595     unsigned long flags;
0596     int rc;
0597 
0598     if (!info || !info->attrs)
0599         return -EINVAL;
0600 
0601     if (!info->attrs[NCSI_ATTR_IFINDEX])
0602         return -EINVAL;
0603 
0604     if (!info->attrs[NCSI_ATTR_PACKAGE_MASK])
0605         return -EINVAL;
0606 
0607     ndp = ndp_from_ifindex(get_net(sock_net(msg->sk)),
0608                    nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX]));
0609     if (!ndp)
0610         return -ENODEV;
0611 
0612     spin_lock_irqsave(&ndp->lock, flags);
0613     if (nla_get_flag(info->attrs[NCSI_ATTR_MULTI_FLAG])) {
0614         if (ndp->flags & NCSI_DEV_HWA) {
0615             ndp->multi_package = true;
0616             rc = 0;
0617         } else {
0618             netdev_err(ndp->ndev.dev,
0619                    "NCSI: Can't use multiple packages without HWA\n");
0620             rc = -EPERM;
0621         }
0622     } else {
0623         ndp->multi_package = false;
0624         rc = 0;
0625     }
0626 
0627     if (!rc)
0628         ndp->package_whitelist =
0629             nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_MASK]);
0630     spin_unlock_irqrestore(&ndp->lock, flags);
0631 
0632     if (!rc) {
0633         /* Update channel configuration */
0634         if (!(ndp->flags & NCSI_DEV_RESET))
0635             ncsi_reset_dev(&ndp->ndev);
0636     }
0637 
0638     return rc;
0639 }
0640 
0641 static int ncsi_set_channel_mask_nl(struct sk_buff *msg,
0642                     struct genl_info *info)
0643 {
0644     struct ncsi_package *np, *package;
0645     struct ncsi_channel *nc, *channel;
0646     u32 package_id, channel_id;
0647     struct ncsi_dev_priv *ndp;
0648     unsigned long flags;
0649 
0650     if (!info || !info->attrs)
0651         return -EINVAL;
0652 
0653     if (!info->attrs[NCSI_ATTR_IFINDEX])
0654         return -EINVAL;
0655 
0656     if (!info->attrs[NCSI_ATTR_PACKAGE_ID])
0657         return -EINVAL;
0658 
0659     if (!info->attrs[NCSI_ATTR_CHANNEL_MASK])
0660         return -EINVAL;
0661 
0662     ndp = ndp_from_ifindex(get_net(sock_net(msg->sk)),
0663                    nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX]));
0664     if (!ndp)
0665         return -ENODEV;
0666 
0667     package_id = nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_ID]);
0668     package = NULL;
0669     NCSI_FOR_EACH_PACKAGE(ndp, np)
0670         if (np->id == package_id) {
0671             package = np;
0672             break;
0673         }
0674     if (!package)
0675         return -ERANGE;
0676 
0677     spin_lock_irqsave(&package->lock, flags);
0678 
0679     channel = NULL;
0680     if (info->attrs[NCSI_ATTR_CHANNEL_ID]) {
0681         channel_id = nla_get_u32(info->attrs[NCSI_ATTR_CHANNEL_ID]);
0682         NCSI_FOR_EACH_CHANNEL(np, nc)
0683             if (nc->id == channel_id) {
0684                 channel = nc;
0685                 break;
0686             }
0687         if (!channel) {
0688             spin_unlock_irqrestore(&package->lock, flags);
0689             return -ERANGE;
0690         }
0691         netdev_dbg(ndp->ndev.dev,
0692                "NCSI: Channel %u set as preferred channel\n",
0693                channel->id);
0694     }
0695 
0696     package->channel_whitelist =
0697         nla_get_u32(info->attrs[NCSI_ATTR_CHANNEL_MASK]);
0698     if (package->channel_whitelist == 0)
0699         netdev_dbg(ndp->ndev.dev,
0700                "NCSI: Package %u set to all channels disabled\n",
0701                package->id);
0702 
0703     package->preferred_channel = channel;
0704 
0705     if (nla_get_flag(info->attrs[NCSI_ATTR_MULTI_FLAG])) {
0706         package->multi_channel = true;
0707         netdev_info(ndp->ndev.dev,
0708                 "NCSI: Multi-channel enabled on package %u\n",
0709                 package_id);
0710     } else {
0711         package->multi_channel = false;
0712     }
0713 
0714     spin_unlock_irqrestore(&package->lock, flags);
0715 
0716     /* Update channel configuration */
0717     if (!(ndp->flags & NCSI_DEV_RESET))
0718         ncsi_reset_dev(&ndp->ndev);
0719 
0720     return 0;
0721 }
0722 
0723 static const struct genl_small_ops ncsi_ops[] = {
0724     {
0725         .cmd = NCSI_CMD_PKG_INFO,
0726         .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
0727         .doit = ncsi_pkg_info_nl,
0728         .dumpit = ncsi_pkg_info_all_nl,
0729         .flags = 0,
0730     },
0731     {
0732         .cmd = NCSI_CMD_SET_INTERFACE,
0733         .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
0734         .doit = ncsi_set_interface_nl,
0735         .flags = GENL_ADMIN_PERM,
0736     },
0737     {
0738         .cmd = NCSI_CMD_CLEAR_INTERFACE,
0739         .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
0740         .doit = ncsi_clear_interface_nl,
0741         .flags = GENL_ADMIN_PERM,
0742     },
0743     {
0744         .cmd = NCSI_CMD_SEND_CMD,
0745         .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
0746         .doit = ncsi_send_cmd_nl,
0747         .flags = GENL_ADMIN_PERM,
0748     },
0749     {
0750         .cmd = NCSI_CMD_SET_PACKAGE_MASK,
0751         .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
0752         .doit = ncsi_set_package_mask_nl,
0753         .flags = GENL_ADMIN_PERM,
0754     },
0755     {
0756         .cmd = NCSI_CMD_SET_CHANNEL_MASK,
0757         .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
0758         .doit = ncsi_set_channel_mask_nl,
0759         .flags = GENL_ADMIN_PERM,
0760     },
0761 };
0762 
0763 static struct genl_family ncsi_genl_family __ro_after_init = {
0764     .name = "NCSI",
0765     .version = 0,
0766     .maxattr = NCSI_ATTR_MAX,
0767     .policy = ncsi_genl_policy,
0768     .module = THIS_MODULE,
0769     .small_ops = ncsi_ops,
0770     .n_small_ops = ARRAY_SIZE(ncsi_ops),
0771 };
0772 
0773 static int __init ncsi_init_netlink(void)
0774 {
0775     return genl_register_family(&ncsi_genl_family);
0776 }
0777 subsys_initcall(ncsi_init_netlink);