diff options
author | Justin.Lee1@Dell.com <Justin.Lee1@Dell.com> | 2018-10-11 14:07:37 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2018-10-16 01:00:59 -0400 |
commit | 9771b8ccdfa6dcb1ac5128ca7fe8649f3092d392 (patch) | |
tree | c685a032ce049cb71428c0019925aeb7c5f2cda3 /net/ncsi/ncsi-netlink.c | |
parent | 6384e483239fd07a2d4393f888027118fecd4c6e (diff) |
net/ncsi: Extend NC-SI Netlink interface to allow user space to send NC-SI command
The new command (NCSI_CMD_SEND_CMD) is added to allow user space application
to send NC-SI command to the network card.
Also, add a new attribute (NCSI_ATTR_DATA) for transferring request and response.
The work flow is as below.
Request:
User space application
-> Netlink interface (msg)
-> new Netlink handler - ncsi_send_cmd_nl()
-> ncsi_xmit_cmd()
Response:
Response received - ncsi_rcv_rsp()
-> internal response handler - ncsi_rsp_handler_xxx()
-> ncsi_rsp_handler_netlink()
-> ncsi_send_netlink_rsp ()
-> Netlink interface (msg)
-> user space application
Command timeout - ncsi_request_timeout()
-> ncsi_send_netlink_timeout ()
-> Netlink interface (msg with zero data length)
-> user space application
Error:
Error detected
-> ncsi_send_netlink_err ()
-> Netlink interface (err msg)
-> user space application
Signed-off-by: Justin Lee <justin.lee1@dell.com>
Reviewed-by: Samuel Mendoza-Jonas <sam@mendozajonas.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ncsi/ncsi-netlink.c')
-rw-r--r-- | net/ncsi/ncsi-netlink.c | 204 |
1 files changed, 204 insertions, 0 deletions
diff --git a/net/ncsi/ncsi-netlink.c b/net/ncsi/ncsi-netlink.c index 32cb7751d216..33314381b4f5 100644 --- a/net/ncsi/ncsi-netlink.c +++ b/net/ncsi/ncsi-netlink.c | |||
@@ -19,6 +19,7 @@ | |||
19 | #include <uapi/linux/ncsi.h> | 19 | #include <uapi/linux/ncsi.h> |
20 | 20 | ||
21 | #include "internal.h" | 21 | #include "internal.h" |
22 | #include "ncsi-pkt.h" | ||
22 | #include "ncsi-netlink.h" | 23 | #include "ncsi-netlink.h" |
23 | 24 | ||
24 | static struct genl_family ncsi_genl_family; | 25 | static struct genl_family ncsi_genl_family; |
@@ -28,6 +29,7 @@ static const struct nla_policy ncsi_genl_policy[NCSI_ATTR_MAX + 1] = { | |||
28 | [NCSI_ATTR_PACKAGE_LIST] = { .type = NLA_NESTED }, | 29 | [NCSI_ATTR_PACKAGE_LIST] = { .type = NLA_NESTED }, |
29 | [NCSI_ATTR_PACKAGE_ID] = { .type = NLA_U32 }, | 30 | [NCSI_ATTR_PACKAGE_ID] = { .type = NLA_U32 }, |
30 | [NCSI_ATTR_CHANNEL_ID] = { .type = NLA_U32 }, | 31 | [NCSI_ATTR_CHANNEL_ID] = { .type = NLA_U32 }, |
32 | [NCSI_ATTR_DATA] = { .type = NLA_BINARY, .len = 2048 }, | ||
31 | }; | 33 | }; |
32 | 34 | ||
33 | static struct ncsi_dev_priv *ndp_from_ifindex(struct net *net, u32 ifindex) | 35 | static struct ncsi_dev_priv *ndp_from_ifindex(struct net *net, u32 ifindex) |
@@ -365,6 +367,202 @@ static int ncsi_clear_interface_nl(struct sk_buff *msg, struct genl_info *info) | |||
365 | return 0; | 367 | return 0; |
366 | } | 368 | } |
367 | 369 | ||
370 | static int ncsi_send_cmd_nl(struct sk_buff *msg, struct genl_info *info) | ||
371 | { | ||
372 | struct ncsi_dev_priv *ndp; | ||
373 | struct ncsi_pkt_hdr *hdr; | ||
374 | struct ncsi_cmd_arg nca; | ||
375 | unsigned char *data; | ||
376 | u32 package_id; | ||
377 | u32 channel_id; | ||
378 | int len, ret; | ||
379 | |||
380 | if (!info || !info->attrs) { | ||
381 | ret = -EINVAL; | ||
382 | goto out; | ||
383 | } | ||
384 | |||
385 | if (!info->attrs[NCSI_ATTR_IFINDEX]) { | ||
386 | ret = -EINVAL; | ||
387 | goto out; | ||
388 | } | ||
389 | |||
390 | if (!info->attrs[NCSI_ATTR_PACKAGE_ID]) { | ||
391 | ret = -EINVAL; | ||
392 | goto out; | ||
393 | } | ||
394 | |||
395 | if (!info->attrs[NCSI_ATTR_CHANNEL_ID]) { | ||
396 | ret = -EINVAL; | ||
397 | goto out; | ||
398 | } | ||
399 | |||
400 | if (!info->attrs[NCSI_ATTR_DATA]) { | ||
401 | ret = -EINVAL; | ||
402 | goto out; | ||
403 | } | ||
404 | |||
405 | ndp = ndp_from_ifindex(get_net(sock_net(msg->sk)), | ||
406 | nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX])); | ||
407 | if (!ndp) { | ||
408 | ret = -ENODEV; | ||
409 | goto out; | ||
410 | } | ||
411 | |||
412 | package_id = nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_ID]); | ||
413 | channel_id = nla_get_u32(info->attrs[NCSI_ATTR_CHANNEL_ID]); | ||
414 | |||
415 | if (package_id >= NCSI_MAX_PACKAGE || channel_id >= NCSI_MAX_CHANNEL) { | ||
416 | ret = -ERANGE; | ||
417 | goto out_netlink; | ||
418 | } | ||
419 | |||
420 | len = nla_len(info->attrs[NCSI_ATTR_DATA]); | ||
421 | if (len < sizeof(struct ncsi_pkt_hdr)) { | ||
422 | netdev_info(ndp->ndev.dev, "NCSI: no command to send %u\n", | ||
423 | package_id); | ||
424 | ret = -EINVAL; | ||
425 | goto out_netlink; | ||
426 | } else { | ||
427 | data = (unsigned char *)nla_data(info->attrs[NCSI_ATTR_DATA]); | ||
428 | } | ||
429 | |||
430 | hdr = (struct ncsi_pkt_hdr *)data; | ||
431 | |||
432 | nca.ndp = ndp; | ||
433 | nca.package = (unsigned char)package_id; | ||
434 | nca.channel = (unsigned char)channel_id; | ||
435 | nca.type = hdr->type; | ||
436 | nca.req_flags = NCSI_REQ_FLAG_NETLINK_DRIVEN; | ||
437 | nca.info = info; | ||
438 | nca.payload = ntohs(hdr->length); | ||
439 | nca.data = data + sizeof(*hdr); | ||
440 | |||
441 | ret = ncsi_xmit_cmd(&nca); | ||
442 | out_netlink: | ||
443 | if (ret != 0) { | ||
444 | netdev_err(ndp->ndev.dev, | ||
445 | "NCSI: Error %d sending command\n", | ||
446 | ret); | ||
447 | ncsi_send_netlink_err(ndp->ndev.dev, | ||
448 | info->snd_seq, | ||
449 | info->snd_portid, | ||
450 | info->nlhdr, | ||
451 | ret); | ||
452 | } | ||
453 | out: | ||
454 | return ret; | ||
455 | } | ||
456 | |||
457 | int ncsi_send_netlink_rsp(struct ncsi_request *nr, | ||
458 | struct ncsi_package *np, | ||
459 | struct ncsi_channel *nc) | ||
460 | { | ||
461 | struct sk_buff *skb; | ||
462 | struct net *net; | ||
463 | void *hdr; | ||
464 | int rc; | ||
465 | |||
466 | net = dev_net(nr->rsp->dev); | ||
467 | |||
468 | skb = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); | ||
469 | if (!skb) | ||
470 | return -ENOMEM; | ||
471 | |||
472 | hdr = genlmsg_put(skb, nr->snd_portid, nr->snd_seq, | ||
473 | &ncsi_genl_family, 0, NCSI_CMD_SEND_CMD); | ||
474 | if (!hdr) { | ||
475 | kfree_skb(skb); | ||
476 | return -EMSGSIZE; | ||
477 | } | ||
478 | |||
479 | nla_put_u32(skb, NCSI_ATTR_IFINDEX, nr->rsp->dev->ifindex); | ||
480 | if (np) | ||
481 | nla_put_u32(skb, NCSI_ATTR_PACKAGE_ID, np->id); | ||
482 | if (nc) | ||
483 | nla_put_u32(skb, NCSI_ATTR_CHANNEL_ID, nc->id); | ||
484 | else | ||
485 | nla_put_u32(skb, NCSI_ATTR_CHANNEL_ID, NCSI_RESERVED_CHANNEL); | ||
486 | |||
487 | rc = nla_put(skb, NCSI_ATTR_DATA, nr->rsp->len, (void *)nr->rsp->data); | ||
488 | if (rc) | ||
489 | goto err; | ||
490 | |||
491 | genlmsg_end(skb, hdr); | ||
492 | return genlmsg_unicast(net, skb, nr->snd_portid); | ||
493 | |||
494 | err: | ||
495 | kfree_skb(skb); | ||
496 | return rc; | ||
497 | } | ||
498 | |||
499 | int ncsi_send_netlink_timeout(struct ncsi_request *nr, | ||
500 | struct ncsi_package *np, | ||
501 | struct ncsi_channel *nc) | ||
502 | { | ||
503 | struct sk_buff *skb; | ||
504 | struct net *net; | ||
505 | void *hdr; | ||
506 | |||
507 | skb = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); | ||
508 | if (!skb) | ||
509 | return -ENOMEM; | ||
510 | |||
511 | hdr = genlmsg_put(skb, nr->snd_portid, nr->snd_seq, | ||
512 | &ncsi_genl_family, 0, NCSI_CMD_SEND_CMD); | ||
513 | if (!hdr) { | ||
514 | kfree_skb(skb); | ||
515 | return -EMSGSIZE; | ||
516 | } | ||
517 | |||
518 | net = dev_net(nr->cmd->dev); | ||
519 | |||
520 | nla_put_u32(skb, NCSI_ATTR_IFINDEX, nr->cmd->dev->ifindex); | ||
521 | |||
522 | if (np) | ||
523 | nla_put_u32(skb, NCSI_ATTR_PACKAGE_ID, np->id); | ||
524 | else | ||
525 | nla_put_u32(skb, NCSI_ATTR_PACKAGE_ID, | ||
526 | NCSI_PACKAGE_INDEX((((struct ncsi_pkt_hdr *) | ||
527 | nr->cmd->data)->channel))); | ||
528 | |||
529 | if (nc) | ||
530 | nla_put_u32(skb, NCSI_ATTR_CHANNEL_ID, nc->id); | ||
531 | else | ||
532 | nla_put_u32(skb, NCSI_ATTR_CHANNEL_ID, NCSI_RESERVED_CHANNEL); | ||
533 | |||
534 | genlmsg_end(skb, hdr); | ||
535 | return genlmsg_unicast(net, skb, nr->snd_portid); | ||
536 | } | ||
537 | |||
538 | int ncsi_send_netlink_err(struct net_device *dev, | ||
539 | u32 snd_seq, | ||
540 | u32 snd_portid, | ||
541 | struct nlmsghdr *nlhdr, | ||
542 | int err) | ||
543 | { | ||
544 | struct nlmsghdr *nlh; | ||
545 | struct nlmsgerr *nle; | ||
546 | struct sk_buff *skb; | ||
547 | struct net *net; | ||
548 | |||
549 | skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); | ||
550 | if (!skb) | ||
551 | return -ENOMEM; | ||
552 | |||
553 | net = dev_net(dev); | ||
554 | |||
555 | nlh = nlmsg_put(skb, snd_portid, snd_seq, | ||
556 | NLMSG_ERROR, sizeof(*nle), 0); | ||
557 | nle = (struct nlmsgerr *)nlmsg_data(nlh); | ||
558 | nle->error = err; | ||
559 | memcpy(&nle->msg, nlhdr, sizeof(*nlh)); | ||
560 | |||
561 | nlmsg_end(skb, nlh); | ||
562 | |||
563 | return nlmsg_unicast(net->genl_sock, skb, snd_portid); | ||
564 | } | ||
565 | |||
368 | static const struct genl_ops ncsi_ops[] = { | 566 | static const struct genl_ops ncsi_ops[] = { |
369 | { | 567 | { |
370 | .cmd = NCSI_CMD_PKG_INFO, | 568 | .cmd = NCSI_CMD_PKG_INFO, |
@@ -385,6 +583,12 @@ static const struct genl_ops ncsi_ops[] = { | |||
385 | .doit = ncsi_clear_interface_nl, | 583 | .doit = ncsi_clear_interface_nl, |
386 | .flags = GENL_ADMIN_PERM, | 584 | .flags = GENL_ADMIN_PERM, |
387 | }, | 585 | }, |
586 | { | ||
587 | .cmd = NCSI_CMD_SEND_CMD, | ||
588 | .policy = ncsi_genl_policy, | ||
589 | .doit = ncsi_send_cmd_nl, | ||
590 | .flags = GENL_ADMIN_PERM, | ||
591 | }, | ||
388 | }; | 592 | }; |
389 | 593 | ||
390 | static struct genl_family ncsi_genl_family __ro_after_init = { | 594 | static struct genl_family ncsi_genl_family __ro_after_init = { |