0001
0002
0003
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;
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
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
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
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
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
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
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
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);