| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * 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 | 4 | */ |
|---|
| 9 | 5 | |
|---|
| 10 | 6 | #include <linux/module.h> |
|---|
| .. | .. |
|---|
| 12 | 8 | #include <linux/if_arp.h> |
|---|
| 13 | 9 | #include <linux/rtnetlink.h> |
|---|
| 14 | 10 | #include <linux/etherdevice.h> |
|---|
| 15 | | -#include <linux/module.h> |
|---|
| 16 | 11 | #include <net/genetlink.h> |
|---|
| 17 | 12 | #include <net/ncsi.h> |
|---|
| 18 | 13 | #include <linux/skbuff.h> |
|---|
| .. | .. |
|---|
| 20 | 15 | #include <uapi/linux/ncsi.h> |
|---|
| 21 | 16 | |
|---|
| 22 | 17 | #include "internal.h" |
|---|
| 18 | +#include "ncsi-pkt.h" |
|---|
| 23 | 19 | #include "ncsi-netlink.h" |
|---|
| 24 | 20 | |
|---|
| 25 | 21 | static struct genl_family ncsi_genl_family; |
|---|
| .. | .. |
|---|
| 29 | 25 | [NCSI_ATTR_PACKAGE_LIST] = { .type = NLA_NESTED }, |
|---|
| 30 | 26 | [NCSI_ATTR_PACKAGE_ID] = { .type = NLA_U32 }, |
|---|
| 31 | 27 | [NCSI_ATTR_CHANNEL_ID] = { .type = NLA_U32 }, |
|---|
| 28 | + [NCSI_ATTR_DATA] = { .type = NLA_BINARY, .len = 2048 }, |
|---|
| 29 | + [NCSI_ATTR_MULTI_FLAG] = { .type = NLA_FLAG }, |
|---|
| 30 | + [NCSI_ATTR_PACKAGE_MASK] = { .type = NLA_U32 }, |
|---|
| 31 | + [NCSI_ATTR_CHANNEL_MASK] = { .type = NLA_U32 }, |
|---|
| 32 | 32 | }; |
|---|
| 33 | 33 | |
|---|
| 34 | 34 | static struct ncsi_dev_priv *ndp_from_ifindex(struct net *net, u32 ifindex) |
|---|
| .. | .. |
|---|
| 68 | 68 | nla_put_u32(skb, NCSI_CHANNEL_ATTR_LINK_STATE, m->data[2]); |
|---|
| 69 | 69 | if (nc->state == NCSI_CHANNEL_ACTIVE) |
|---|
| 70 | 70 | nla_put_flag(skb, NCSI_CHANNEL_ATTR_ACTIVE); |
|---|
| 71 | | - if (ndp->force_channel == nc) |
|---|
| 71 | + if (nc == nc->package->preferred_channel) |
|---|
| 72 | 72 | nla_put_flag(skb, NCSI_CHANNEL_ATTR_FORCED); |
|---|
| 73 | 73 | |
|---|
| 74 | 74 | nla_put_u32(skb, NCSI_CHANNEL_ATTR_VERSION_MAJOR, nc->version.version); |
|---|
| 75 | 75 | nla_put_u32(skb, NCSI_CHANNEL_ATTR_VERSION_MINOR, nc->version.alpha2); |
|---|
| 76 | 76 | nla_put_string(skb, NCSI_CHANNEL_ATTR_VERSION_STR, nc->version.fw_name); |
|---|
| 77 | 77 | |
|---|
| 78 | | - vid_nest = nla_nest_start(skb, NCSI_CHANNEL_ATTR_VLAN_LIST); |
|---|
| 78 | + vid_nest = nla_nest_start_noflag(skb, NCSI_CHANNEL_ATTR_VLAN_LIST); |
|---|
| 79 | 79 | if (!vid_nest) |
|---|
| 80 | 80 | return -ENOMEM; |
|---|
| 81 | 81 | ncf = &nc->vlan_filter; |
|---|
| .. | .. |
|---|
| 109 | 109 | NCSI_FOR_EACH_PACKAGE(ndp, np) { |
|---|
| 110 | 110 | if (np->id != id) |
|---|
| 111 | 111 | continue; |
|---|
| 112 | | - pnest = nla_nest_start(skb, NCSI_PKG_ATTR); |
|---|
| 112 | + pnest = nla_nest_start_noflag(skb, NCSI_PKG_ATTR); |
|---|
| 113 | 113 | if (!pnest) |
|---|
| 114 | 114 | return -ENOMEM; |
|---|
| 115 | | - nla_put_u32(skb, NCSI_PKG_ATTR_ID, np->id); |
|---|
| 116 | | - if (ndp->force_package == np) |
|---|
| 115 | + rc = nla_put_u32(skb, NCSI_PKG_ATTR_ID, np->id); |
|---|
| 116 | + if (rc) { |
|---|
| 117 | + nla_nest_cancel(skb, pnest); |
|---|
| 118 | + return rc; |
|---|
| 119 | + } |
|---|
| 120 | + if ((0x1 << np->id) == ndp->package_whitelist) |
|---|
| 117 | 121 | nla_put_flag(skb, NCSI_PKG_ATTR_FORCED); |
|---|
| 118 | | - cnest = nla_nest_start(skb, NCSI_PKG_ATTR_CHANNEL_LIST); |
|---|
| 122 | + cnest = nla_nest_start_noflag(skb, NCSI_PKG_ATTR_CHANNEL_LIST); |
|---|
| 119 | 123 | if (!cnest) { |
|---|
| 120 | 124 | nla_nest_cancel(skb, pnest); |
|---|
| 121 | 125 | return -ENOMEM; |
|---|
| 122 | 126 | } |
|---|
| 123 | 127 | NCSI_FOR_EACH_CHANNEL(np, nc) { |
|---|
| 124 | | - nest = nla_nest_start(skb, NCSI_CHANNEL_ATTR); |
|---|
| 128 | + nest = nla_nest_start_noflag(skb, NCSI_CHANNEL_ATTR); |
|---|
| 125 | 129 | if (!nest) { |
|---|
| 126 | 130 | nla_nest_cancel(skb, cnest); |
|---|
| 127 | 131 | nla_nest_cancel(skb, pnest); |
|---|
| .. | .. |
|---|
| 183 | 187 | |
|---|
| 184 | 188 | package_id = nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_ID]); |
|---|
| 185 | 189 | |
|---|
| 186 | | - attr = nla_nest_start(skb, NCSI_ATTR_PACKAGE_LIST); |
|---|
| 190 | + attr = nla_nest_start_noflag(skb, NCSI_ATTR_PACKAGE_LIST); |
|---|
| 187 | 191 | if (!attr) { |
|---|
| 188 | 192 | kfree_skb(skb); |
|---|
| 189 | 193 | return -EMSGSIZE; |
|---|
| .. | .. |
|---|
| 216 | 220 | void *hdr; |
|---|
| 217 | 221 | int rc; |
|---|
| 218 | 222 | |
|---|
| 219 | | - rc = genlmsg_parse(cb->nlh, &ncsi_genl_family, attrs, NCSI_ATTR_MAX, |
|---|
| 220 | | - ncsi_genl_policy, NULL); |
|---|
| 223 | + rc = genlmsg_parse_deprecated(cb->nlh, &ncsi_genl_family, attrs, NCSI_ATTR_MAX, |
|---|
| 224 | + ncsi_genl_policy, NULL); |
|---|
| 221 | 225 | if (rc) |
|---|
| 222 | 226 | return rc; |
|---|
| 223 | 227 | |
|---|
| .. | .. |
|---|
| 246 | 250 | goto err; |
|---|
| 247 | 251 | } |
|---|
| 248 | 252 | |
|---|
| 249 | | - attr = nla_nest_start(skb, NCSI_ATTR_PACKAGE_LIST); |
|---|
| 253 | + attr = nla_nest_start_noflag(skb, NCSI_ATTR_PACKAGE_LIST); |
|---|
| 254 | + if (!attr) { |
|---|
| 255 | + rc = -EMSGSIZE; |
|---|
| 256 | + goto err; |
|---|
| 257 | + } |
|---|
| 250 | 258 | rc = ncsi_write_package_info(skb, ndp, package->id); |
|---|
| 251 | 259 | if (rc) { |
|---|
| 252 | 260 | nla_nest_cancel(skb, attr); |
|---|
| .. | .. |
|---|
| 289 | 297 | package_id = nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_ID]); |
|---|
| 290 | 298 | package = NULL; |
|---|
| 291 | 299 | |
|---|
| 292 | | - spin_lock_irqsave(&ndp->lock, flags); |
|---|
| 293 | | - |
|---|
| 294 | 300 | NCSI_FOR_EACH_PACKAGE(ndp, np) |
|---|
| 295 | 301 | if (np->id == package_id) |
|---|
| 296 | 302 | package = np; |
|---|
| 297 | 303 | if (!package) { |
|---|
| 298 | 304 | /* The user has set a package that does not exist */ |
|---|
| 299 | | - spin_unlock_irqrestore(&ndp->lock, flags); |
|---|
| 300 | 305 | return -ERANGE; |
|---|
| 301 | 306 | } |
|---|
| 302 | 307 | |
|---|
| 303 | 308 | channel = NULL; |
|---|
| 304 | | - if (!info->attrs[NCSI_ATTR_CHANNEL_ID]) { |
|---|
| 305 | | - /* Allow any channel */ |
|---|
| 306 | | - channel_id = NCSI_RESERVED_CHANNEL; |
|---|
| 307 | | - } else { |
|---|
| 309 | + if (info->attrs[NCSI_ATTR_CHANNEL_ID]) { |
|---|
| 308 | 310 | channel_id = nla_get_u32(info->attrs[NCSI_ATTR_CHANNEL_ID]); |
|---|
| 309 | 311 | NCSI_FOR_EACH_CHANNEL(package, nc) |
|---|
| 310 | | - if (nc->id == channel_id) |
|---|
| 312 | + if (nc->id == channel_id) { |
|---|
| 311 | 313 | channel = nc; |
|---|
| 314 | + break; |
|---|
| 315 | + } |
|---|
| 316 | + if (!channel) { |
|---|
| 317 | + netdev_info(ndp->ndev.dev, |
|---|
| 318 | + "NCSI: Channel %u does not exist!\n", |
|---|
| 319 | + channel_id); |
|---|
| 320 | + return -ERANGE; |
|---|
| 321 | + } |
|---|
| 312 | 322 | } |
|---|
| 313 | 323 | |
|---|
| 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; |
|---|
| 324 | + spin_lock_irqsave(&ndp->lock, flags); |
|---|
| 325 | + ndp->package_whitelist = 0x1 << package->id; |
|---|
| 326 | + ndp->multi_package = false; |
|---|
| 326 | 327 | spin_unlock_irqrestore(&ndp->lock, flags); |
|---|
| 327 | 328 | |
|---|
| 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)" : ""); |
|---|
| 329 | + spin_lock_irqsave(&package->lock, flags); |
|---|
| 330 | + package->multi_channel = false; |
|---|
| 331 | + if (channel) { |
|---|
| 332 | + package->channel_whitelist = 0x1 << channel->id; |
|---|
| 333 | + package->preferred_channel = channel; |
|---|
| 334 | + } else { |
|---|
| 335 | + /* Allow any channel */ |
|---|
| 336 | + package->channel_whitelist = UINT_MAX; |
|---|
| 337 | + package->preferred_channel = NULL; |
|---|
| 338 | + } |
|---|
| 339 | + spin_unlock_irqrestore(&package->lock, flags); |
|---|
| 331 | 340 | |
|---|
| 332 | | - /* Bounce the NCSI channel to set changes */ |
|---|
| 333 | | - ncsi_stop_dev(&ndp->ndev); |
|---|
| 334 | | - ncsi_start_dev(&ndp->ndev); |
|---|
| 341 | + if (channel) |
|---|
| 342 | + netdev_info(ndp->ndev.dev, |
|---|
| 343 | + "Set package 0x%x, channel 0x%x as preferred\n", |
|---|
| 344 | + package_id, channel_id); |
|---|
| 345 | + else |
|---|
| 346 | + netdev_info(ndp->ndev.dev, "Set package 0x%x as preferred\n", |
|---|
| 347 | + package_id); |
|---|
| 348 | + |
|---|
| 349 | + /* Update channel configuration */ |
|---|
| 350 | + if (!(ndp->flags & NCSI_DEV_RESET)) |
|---|
| 351 | + ncsi_reset_dev(&ndp->ndev); |
|---|
| 335 | 352 | |
|---|
| 336 | 353 | return 0; |
|---|
| 337 | 354 | } |
|---|
| .. | .. |
|---|
| 339 | 356 | static int ncsi_clear_interface_nl(struct sk_buff *msg, struct genl_info *info) |
|---|
| 340 | 357 | { |
|---|
| 341 | 358 | struct ncsi_dev_priv *ndp; |
|---|
| 359 | + struct ncsi_package *np; |
|---|
| 342 | 360 | unsigned long flags; |
|---|
| 343 | 361 | |
|---|
| 344 | 362 | if (!info || !info->attrs) |
|---|
| .. | .. |
|---|
| 352 | 370 | if (!ndp) |
|---|
| 353 | 371 | return -ENODEV; |
|---|
| 354 | 372 | |
|---|
| 355 | | - /* Clear any override */ |
|---|
| 373 | + /* Reset any whitelists and disable multi mode */ |
|---|
| 356 | 374 | spin_lock_irqsave(&ndp->lock, flags); |
|---|
| 357 | | - ndp->force_package = NULL; |
|---|
| 358 | | - ndp->force_channel = NULL; |
|---|
| 375 | + ndp->package_whitelist = UINT_MAX; |
|---|
| 376 | + ndp->multi_package = false; |
|---|
| 359 | 377 | spin_unlock_irqrestore(&ndp->lock, flags); |
|---|
| 378 | + |
|---|
| 379 | + NCSI_FOR_EACH_PACKAGE(ndp, np) { |
|---|
| 380 | + spin_lock_irqsave(&np->lock, flags); |
|---|
| 381 | + np->multi_channel = false; |
|---|
| 382 | + np->channel_whitelist = UINT_MAX; |
|---|
| 383 | + np->preferred_channel = NULL; |
|---|
| 384 | + spin_unlock_irqrestore(&np->lock, flags); |
|---|
| 385 | + } |
|---|
| 360 | 386 | netdev_info(ndp->ndev.dev, "NCSI: Cleared preferred package/channel\n"); |
|---|
| 361 | 387 | |
|---|
| 362 | | - /* Bounce the NCSI channel to set changes */ |
|---|
| 363 | | - ncsi_stop_dev(&ndp->ndev); |
|---|
| 364 | | - ncsi_start_dev(&ndp->ndev); |
|---|
| 388 | + /* Update channel configuration */ |
|---|
| 389 | + if (!(ndp->flags & NCSI_DEV_RESET)) |
|---|
| 390 | + ncsi_reset_dev(&ndp->ndev); |
|---|
| 365 | 391 | |
|---|
| 366 | 392 | return 0; |
|---|
| 367 | 393 | } |
|---|
| 368 | 394 | |
|---|
| 369 | | -static const struct genl_ops ncsi_ops[] = { |
|---|
| 395 | +static int ncsi_send_cmd_nl(struct sk_buff *msg, struct genl_info *info) |
|---|
| 396 | +{ |
|---|
| 397 | + struct ncsi_dev_priv *ndp; |
|---|
| 398 | + struct ncsi_pkt_hdr *hdr; |
|---|
| 399 | + struct ncsi_cmd_arg nca; |
|---|
| 400 | + unsigned char *data; |
|---|
| 401 | + u32 package_id; |
|---|
| 402 | + u32 channel_id; |
|---|
| 403 | + int len, ret; |
|---|
| 404 | + |
|---|
| 405 | + if (!info || !info->attrs) { |
|---|
| 406 | + ret = -EINVAL; |
|---|
| 407 | + goto out; |
|---|
| 408 | + } |
|---|
| 409 | + |
|---|
| 410 | + if (!info->attrs[NCSI_ATTR_IFINDEX]) { |
|---|
| 411 | + ret = -EINVAL; |
|---|
| 412 | + goto out; |
|---|
| 413 | + } |
|---|
| 414 | + |
|---|
| 415 | + if (!info->attrs[NCSI_ATTR_PACKAGE_ID]) { |
|---|
| 416 | + ret = -EINVAL; |
|---|
| 417 | + goto out; |
|---|
| 418 | + } |
|---|
| 419 | + |
|---|
| 420 | + if (!info->attrs[NCSI_ATTR_CHANNEL_ID]) { |
|---|
| 421 | + ret = -EINVAL; |
|---|
| 422 | + goto out; |
|---|
| 423 | + } |
|---|
| 424 | + |
|---|
| 425 | + if (!info->attrs[NCSI_ATTR_DATA]) { |
|---|
| 426 | + ret = -EINVAL; |
|---|
| 427 | + goto out; |
|---|
| 428 | + } |
|---|
| 429 | + |
|---|
| 430 | + ndp = ndp_from_ifindex(get_net(sock_net(msg->sk)), |
|---|
| 431 | + nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX])); |
|---|
| 432 | + if (!ndp) { |
|---|
| 433 | + ret = -ENODEV; |
|---|
| 434 | + goto out; |
|---|
| 435 | + } |
|---|
| 436 | + |
|---|
| 437 | + package_id = nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_ID]); |
|---|
| 438 | + channel_id = nla_get_u32(info->attrs[NCSI_ATTR_CHANNEL_ID]); |
|---|
| 439 | + |
|---|
| 440 | + if (package_id >= NCSI_MAX_PACKAGE || channel_id >= NCSI_MAX_CHANNEL) { |
|---|
| 441 | + ret = -ERANGE; |
|---|
| 442 | + goto out_netlink; |
|---|
| 443 | + } |
|---|
| 444 | + |
|---|
| 445 | + len = nla_len(info->attrs[NCSI_ATTR_DATA]); |
|---|
| 446 | + if (len < sizeof(struct ncsi_pkt_hdr)) { |
|---|
| 447 | + netdev_info(ndp->ndev.dev, "NCSI: no command to send %u\n", |
|---|
| 448 | + package_id); |
|---|
| 449 | + ret = -EINVAL; |
|---|
| 450 | + goto out_netlink; |
|---|
| 451 | + } else { |
|---|
| 452 | + data = (unsigned char *)nla_data(info->attrs[NCSI_ATTR_DATA]); |
|---|
| 453 | + } |
|---|
| 454 | + |
|---|
| 455 | + hdr = (struct ncsi_pkt_hdr *)data; |
|---|
| 456 | + |
|---|
| 457 | + nca.ndp = ndp; |
|---|
| 458 | + nca.package = (unsigned char)package_id; |
|---|
| 459 | + nca.channel = (unsigned char)channel_id; |
|---|
| 460 | + nca.type = hdr->type; |
|---|
| 461 | + nca.req_flags = NCSI_REQ_FLAG_NETLINK_DRIVEN; |
|---|
| 462 | + nca.info = info; |
|---|
| 463 | + nca.payload = ntohs(hdr->length); |
|---|
| 464 | + nca.data = data + sizeof(*hdr); |
|---|
| 465 | + |
|---|
| 466 | + ret = ncsi_xmit_cmd(&nca); |
|---|
| 467 | +out_netlink: |
|---|
| 468 | + if (ret != 0) { |
|---|
| 469 | + netdev_err(ndp->ndev.dev, |
|---|
| 470 | + "NCSI: Error %d sending command\n", |
|---|
| 471 | + ret); |
|---|
| 472 | + ncsi_send_netlink_err(ndp->ndev.dev, |
|---|
| 473 | + info->snd_seq, |
|---|
| 474 | + info->snd_portid, |
|---|
| 475 | + info->nlhdr, |
|---|
| 476 | + ret); |
|---|
| 477 | + } |
|---|
| 478 | +out: |
|---|
| 479 | + return ret; |
|---|
| 480 | +} |
|---|
| 481 | + |
|---|
| 482 | +int ncsi_send_netlink_rsp(struct ncsi_request *nr, |
|---|
| 483 | + struct ncsi_package *np, |
|---|
| 484 | + struct ncsi_channel *nc) |
|---|
| 485 | +{ |
|---|
| 486 | + struct sk_buff *skb; |
|---|
| 487 | + struct net *net; |
|---|
| 488 | + void *hdr; |
|---|
| 489 | + int rc; |
|---|
| 490 | + |
|---|
| 491 | + net = dev_net(nr->rsp->dev); |
|---|
| 492 | + |
|---|
| 493 | + skb = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); |
|---|
| 494 | + if (!skb) |
|---|
| 495 | + return -ENOMEM; |
|---|
| 496 | + |
|---|
| 497 | + hdr = genlmsg_put(skb, nr->snd_portid, nr->snd_seq, |
|---|
| 498 | + &ncsi_genl_family, 0, NCSI_CMD_SEND_CMD); |
|---|
| 499 | + if (!hdr) { |
|---|
| 500 | + kfree_skb(skb); |
|---|
| 501 | + return -EMSGSIZE; |
|---|
| 502 | + } |
|---|
| 503 | + |
|---|
| 504 | + nla_put_u32(skb, NCSI_ATTR_IFINDEX, nr->rsp->dev->ifindex); |
|---|
| 505 | + if (np) |
|---|
| 506 | + nla_put_u32(skb, NCSI_ATTR_PACKAGE_ID, np->id); |
|---|
| 507 | + if (nc) |
|---|
| 508 | + nla_put_u32(skb, NCSI_ATTR_CHANNEL_ID, nc->id); |
|---|
| 509 | + else |
|---|
| 510 | + nla_put_u32(skb, NCSI_ATTR_CHANNEL_ID, NCSI_RESERVED_CHANNEL); |
|---|
| 511 | + |
|---|
| 512 | + rc = nla_put(skb, NCSI_ATTR_DATA, nr->rsp->len, (void *)nr->rsp->data); |
|---|
| 513 | + if (rc) |
|---|
| 514 | + goto err; |
|---|
| 515 | + |
|---|
| 516 | + genlmsg_end(skb, hdr); |
|---|
| 517 | + return genlmsg_unicast(net, skb, nr->snd_portid); |
|---|
| 518 | + |
|---|
| 519 | +err: |
|---|
| 520 | + kfree_skb(skb); |
|---|
| 521 | + return rc; |
|---|
| 522 | +} |
|---|
| 523 | + |
|---|
| 524 | +int ncsi_send_netlink_timeout(struct ncsi_request *nr, |
|---|
| 525 | + struct ncsi_package *np, |
|---|
| 526 | + struct ncsi_channel *nc) |
|---|
| 527 | +{ |
|---|
| 528 | + struct sk_buff *skb; |
|---|
| 529 | + struct net *net; |
|---|
| 530 | + void *hdr; |
|---|
| 531 | + |
|---|
| 532 | + skb = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); |
|---|
| 533 | + if (!skb) |
|---|
| 534 | + return -ENOMEM; |
|---|
| 535 | + |
|---|
| 536 | + hdr = genlmsg_put(skb, nr->snd_portid, nr->snd_seq, |
|---|
| 537 | + &ncsi_genl_family, 0, NCSI_CMD_SEND_CMD); |
|---|
| 538 | + if (!hdr) { |
|---|
| 539 | + kfree_skb(skb); |
|---|
| 540 | + return -EMSGSIZE; |
|---|
| 541 | + } |
|---|
| 542 | + |
|---|
| 543 | + net = dev_net(nr->cmd->dev); |
|---|
| 544 | + |
|---|
| 545 | + nla_put_u32(skb, NCSI_ATTR_IFINDEX, nr->cmd->dev->ifindex); |
|---|
| 546 | + |
|---|
| 547 | + if (np) |
|---|
| 548 | + nla_put_u32(skb, NCSI_ATTR_PACKAGE_ID, np->id); |
|---|
| 549 | + else |
|---|
| 550 | + nla_put_u32(skb, NCSI_ATTR_PACKAGE_ID, |
|---|
| 551 | + NCSI_PACKAGE_INDEX((((struct ncsi_pkt_hdr *) |
|---|
| 552 | + nr->cmd->data)->channel))); |
|---|
| 553 | + |
|---|
| 554 | + if (nc) |
|---|
| 555 | + nla_put_u32(skb, NCSI_ATTR_CHANNEL_ID, nc->id); |
|---|
| 556 | + else |
|---|
| 557 | + nla_put_u32(skb, NCSI_ATTR_CHANNEL_ID, NCSI_RESERVED_CHANNEL); |
|---|
| 558 | + |
|---|
| 559 | + genlmsg_end(skb, hdr); |
|---|
| 560 | + return genlmsg_unicast(net, skb, nr->snd_portid); |
|---|
| 561 | +} |
|---|
| 562 | + |
|---|
| 563 | +int ncsi_send_netlink_err(struct net_device *dev, |
|---|
| 564 | + u32 snd_seq, |
|---|
| 565 | + u32 snd_portid, |
|---|
| 566 | + struct nlmsghdr *nlhdr, |
|---|
| 567 | + int err) |
|---|
| 568 | +{ |
|---|
| 569 | + struct nlmsghdr *nlh; |
|---|
| 570 | + struct nlmsgerr *nle; |
|---|
| 571 | + struct sk_buff *skb; |
|---|
| 572 | + struct net *net; |
|---|
| 573 | + |
|---|
| 574 | + skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); |
|---|
| 575 | + if (!skb) |
|---|
| 576 | + return -ENOMEM; |
|---|
| 577 | + |
|---|
| 578 | + net = dev_net(dev); |
|---|
| 579 | + |
|---|
| 580 | + nlh = nlmsg_put(skb, snd_portid, snd_seq, |
|---|
| 581 | + NLMSG_ERROR, sizeof(*nle), 0); |
|---|
| 582 | + nle = (struct nlmsgerr *)nlmsg_data(nlh); |
|---|
| 583 | + nle->error = err; |
|---|
| 584 | + memcpy(&nle->msg, nlhdr, sizeof(*nlh)); |
|---|
| 585 | + |
|---|
| 586 | + nlmsg_end(skb, nlh); |
|---|
| 587 | + |
|---|
| 588 | + return nlmsg_unicast(net->genl_sock, skb, snd_portid); |
|---|
| 589 | +} |
|---|
| 590 | + |
|---|
| 591 | +static int ncsi_set_package_mask_nl(struct sk_buff *msg, |
|---|
| 592 | + struct genl_info *info) |
|---|
| 593 | +{ |
|---|
| 594 | + struct ncsi_dev_priv *ndp; |
|---|
| 595 | + unsigned long flags; |
|---|
| 596 | + int rc; |
|---|
| 597 | + |
|---|
| 598 | + if (!info || !info->attrs) |
|---|
| 599 | + return -EINVAL; |
|---|
| 600 | + |
|---|
| 601 | + if (!info->attrs[NCSI_ATTR_IFINDEX]) |
|---|
| 602 | + return -EINVAL; |
|---|
| 603 | + |
|---|
| 604 | + if (!info->attrs[NCSI_ATTR_PACKAGE_MASK]) |
|---|
| 605 | + return -EINVAL; |
|---|
| 606 | + |
|---|
| 607 | + ndp = ndp_from_ifindex(get_net(sock_net(msg->sk)), |
|---|
| 608 | + nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX])); |
|---|
| 609 | + if (!ndp) |
|---|
| 610 | + return -ENODEV; |
|---|
| 611 | + |
|---|
| 612 | + spin_lock_irqsave(&ndp->lock, flags); |
|---|
| 613 | + if (nla_get_flag(info->attrs[NCSI_ATTR_MULTI_FLAG])) { |
|---|
| 614 | + if (ndp->flags & NCSI_DEV_HWA) { |
|---|
| 615 | + ndp->multi_package = true; |
|---|
| 616 | + rc = 0; |
|---|
| 617 | + } else { |
|---|
| 618 | + netdev_err(ndp->ndev.dev, |
|---|
| 619 | + "NCSI: Can't use multiple packages without HWA\n"); |
|---|
| 620 | + rc = -EPERM; |
|---|
| 621 | + } |
|---|
| 622 | + } else { |
|---|
| 623 | + ndp->multi_package = false; |
|---|
| 624 | + rc = 0; |
|---|
| 625 | + } |
|---|
| 626 | + |
|---|
| 627 | + if (!rc) |
|---|
| 628 | + ndp->package_whitelist = |
|---|
| 629 | + nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_MASK]); |
|---|
| 630 | + spin_unlock_irqrestore(&ndp->lock, flags); |
|---|
| 631 | + |
|---|
| 632 | + if (!rc) { |
|---|
| 633 | + /* Update channel configuration */ |
|---|
| 634 | + if (!(ndp->flags & NCSI_DEV_RESET)) |
|---|
| 635 | + ncsi_reset_dev(&ndp->ndev); |
|---|
| 636 | + } |
|---|
| 637 | + |
|---|
| 638 | + return rc; |
|---|
| 639 | +} |
|---|
| 640 | + |
|---|
| 641 | +static int ncsi_set_channel_mask_nl(struct sk_buff *msg, |
|---|
| 642 | + struct genl_info *info) |
|---|
| 643 | +{ |
|---|
| 644 | + struct ncsi_package *np, *package; |
|---|
| 645 | + struct ncsi_channel *nc, *channel; |
|---|
| 646 | + u32 package_id, channel_id; |
|---|
| 647 | + struct ncsi_dev_priv *ndp; |
|---|
| 648 | + unsigned long flags; |
|---|
| 649 | + |
|---|
| 650 | + if (!info || !info->attrs) |
|---|
| 651 | + return -EINVAL; |
|---|
| 652 | + |
|---|
| 653 | + if (!info->attrs[NCSI_ATTR_IFINDEX]) |
|---|
| 654 | + return -EINVAL; |
|---|
| 655 | + |
|---|
| 656 | + if (!info->attrs[NCSI_ATTR_PACKAGE_ID]) |
|---|
| 657 | + return -EINVAL; |
|---|
| 658 | + |
|---|
| 659 | + if (!info->attrs[NCSI_ATTR_CHANNEL_MASK]) |
|---|
| 660 | + return -EINVAL; |
|---|
| 661 | + |
|---|
| 662 | + ndp = ndp_from_ifindex(get_net(sock_net(msg->sk)), |
|---|
| 663 | + nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX])); |
|---|
| 664 | + if (!ndp) |
|---|
| 665 | + return -ENODEV; |
|---|
| 666 | + |
|---|
| 667 | + package_id = nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_ID]); |
|---|
| 668 | + package = NULL; |
|---|
| 669 | + NCSI_FOR_EACH_PACKAGE(ndp, np) |
|---|
| 670 | + if (np->id == package_id) { |
|---|
| 671 | + package = np; |
|---|
| 672 | + break; |
|---|
| 673 | + } |
|---|
| 674 | + if (!package) |
|---|
| 675 | + return -ERANGE; |
|---|
| 676 | + |
|---|
| 677 | + spin_lock_irqsave(&package->lock, flags); |
|---|
| 678 | + |
|---|
| 679 | + channel = NULL; |
|---|
| 680 | + if (info->attrs[NCSI_ATTR_CHANNEL_ID]) { |
|---|
| 681 | + channel_id = nla_get_u32(info->attrs[NCSI_ATTR_CHANNEL_ID]); |
|---|
| 682 | + NCSI_FOR_EACH_CHANNEL(np, nc) |
|---|
| 683 | + if (nc->id == channel_id) { |
|---|
| 684 | + channel = nc; |
|---|
| 685 | + break; |
|---|
| 686 | + } |
|---|
| 687 | + if (!channel) { |
|---|
| 688 | + spin_unlock_irqrestore(&package->lock, flags); |
|---|
| 689 | + return -ERANGE; |
|---|
| 690 | + } |
|---|
| 691 | + netdev_dbg(ndp->ndev.dev, |
|---|
| 692 | + "NCSI: Channel %u set as preferred channel\n", |
|---|
| 693 | + channel->id); |
|---|
| 694 | + } |
|---|
| 695 | + |
|---|
| 696 | + package->channel_whitelist = |
|---|
| 697 | + nla_get_u32(info->attrs[NCSI_ATTR_CHANNEL_MASK]); |
|---|
| 698 | + if (package->channel_whitelist == 0) |
|---|
| 699 | + netdev_dbg(ndp->ndev.dev, |
|---|
| 700 | + "NCSI: Package %u set to all channels disabled\n", |
|---|
| 701 | + package->id); |
|---|
| 702 | + |
|---|
| 703 | + package->preferred_channel = channel; |
|---|
| 704 | + |
|---|
| 705 | + if (nla_get_flag(info->attrs[NCSI_ATTR_MULTI_FLAG])) { |
|---|
| 706 | + package->multi_channel = true; |
|---|
| 707 | + netdev_info(ndp->ndev.dev, |
|---|
| 708 | + "NCSI: Multi-channel enabled on package %u\n", |
|---|
| 709 | + package_id); |
|---|
| 710 | + } else { |
|---|
| 711 | + package->multi_channel = false; |
|---|
| 712 | + } |
|---|
| 713 | + |
|---|
| 714 | + spin_unlock_irqrestore(&package->lock, flags); |
|---|
| 715 | + |
|---|
| 716 | + /* Update channel configuration */ |
|---|
| 717 | + if (!(ndp->flags & NCSI_DEV_RESET)) |
|---|
| 718 | + ncsi_reset_dev(&ndp->ndev); |
|---|
| 719 | + |
|---|
| 720 | + return 0; |
|---|
| 721 | +} |
|---|
| 722 | + |
|---|
| 723 | +static const struct genl_small_ops ncsi_ops[] = { |
|---|
| 370 | 724 | { |
|---|
| 371 | 725 | .cmd = NCSI_CMD_PKG_INFO, |
|---|
| 372 | | - .policy = ncsi_genl_policy, |
|---|
| 726 | + .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
|---|
| 373 | 727 | .doit = ncsi_pkg_info_nl, |
|---|
| 374 | 728 | .dumpit = ncsi_pkg_info_all_nl, |
|---|
| 375 | 729 | .flags = 0, |
|---|
| 376 | 730 | }, |
|---|
| 377 | 731 | { |
|---|
| 378 | 732 | .cmd = NCSI_CMD_SET_INTERFACE, |
|---|
| 379 | | - .policy = ncsi_genl_policy, |
|---|
| 733 | + .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
|---|
| 380 | 734 | .doit = ncsi_set_interface_nl, |
|---|
| 381 | 735 | .flags = GENL_ADMIN_PERM, |
|---|
| 382 | 736 | }, |
|---|
| 383 | 737 | { |
|---|
| 384 | 738 | .cmd = NCSI_CMD_CLEAR_INTERFACE, |
|---|
| 385 | | - .policy = ncsi_genl_policy, |
|---|
| 739 | + .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
|---|
| 386 | 740 | .doit = ncsi_clear_interface_nl, |
|---|
| 741 | + .flags = GENL_ADMIN_PERM, |
|---|
| 742 | + }, |
|---|
| 743 | + { |
|---|
| 744 | + .cmd = NCSI_CMD_SEND_CMD, |
|---|
| 745 | + .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
|---|
| 746 | + .doit = ncsi_send_cmd_nl, |
|---|
| 747 | + .flags = GENL_ADMIN_PERM, |
|---|
| 748 | + }, |
|---|
| 749 | + { |
|---|
| 750 | + .cmd = NCSI_CMD_SET_PACKAGE_MASK, |
|---|
| 751 | + .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
|---|
| 752 | + .doit = ncsi_set_package_mask_nl, |
|---|
| 753 | + .flags = GENL_ADMIN_PERM, |
|---|
| 754 | + }, |
|---|
| 755 | + { |
|---|
| 756 | + .cmd = NCSI_CMD_SET_CHANNEL_MASK, |
|---|
| 757 | + .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
|---|
| 758 | + .doit = ncsi_set_channel_mask_nl, |
|---|
| 387 | 759 | .flags = GENL_ADMIN_PERM, |
|---|
| 388 | 760 | }, |
|---|
| 389 | 761 | }; |
|---|
| .. | .. |
|---|
| 392 | 764 | .name = "NCSI", |
|---|
| 393 | 765 | .version = 0, |
|---|
| 394 | 766 | .maxattr = NCSI_ATTR_MAX, |
|---|
| 767 | + .policy = ncsi_genl_policy, |
|---|
| 395 | 768 | .module = THIS_MODULE, |
|---|
| 396 | | - .ops = ncsi_ops, |
|---|
| 397 | | - .n_ops = ARRAY_SIZE(ncsi_ops), |
|---|
| 769 | + .small_ops = ncsi_ops, |
|---|
| 770 | + .n_small_ops = ARRAY_SIZE(ncsi_ops), |
|---|
| 398 | 771 | }; |
|---|
| 399 | 772 | |
|---|
| 400 | 773 | static int __init ncsi_init_netlink(void) |
|---|