1*1a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
284314fd4SJames Smart /*
384314fd4SJames Smart * scsi_netlink.c - SCSI Transport Netlink Interface
484314fd4SJames Smart *
584314fd4SJames Smart * Copyright (C) 2006 James Smart, Emulex Corporation
684314fd4SJames Smart */
784314fd4SJames Smart #include <linux/time.h>
884314fd4SJames Smart #include <linux/jiffies.h>
984314fd4SJames Smart #include <linux/security.h>
1022447be7SJames Smart #include <linux/delay.h>
115a0e3ad6STejun Heo #include <linux/slab.h>
1209703660SPaul Gortmaker #include <linux/export.h>
1384314fd4SJames Smart #include <net/sock.h>
1484314fd4SJames Smart #include <net/netlink.h>
1584314fd4SJames Smart
1684314fd4SJames Smart #include <scsi/scsi_netlink.h>
1784314fd4SJames Smart #include "scsi_priv.h"
1884314fd4SJames Smart
1984314fd4SJames Smart struct sock *scsi_nl_sock = NULL;
2084314fd4SJames Smart EXPORT_SYMBOL_GPL(scsi_nl_sock);
2184314fd4SJames Smart
2284314fd4SJames Smart /**
23eb44820cSRob Landley * scsi_nl_rcv_msg - Receive message handler.
24eb44820cSRob Landley * @skb: socket receive buffer
25eb44820cSRob Landley *
26eb44820cSRob Landley * Description: Extracts message from a receive buffer.
2784314fd4SJames Smart * Validates message header and calls appropriate transport message handler
2884314fd4SJames Smart *
2984314fd4SJames Smart *
3084314fd4SJames Smart **/
3184314fd4SJames Smart static void
scsi_nl_rcv_msg(struct sk_buff * skb)3284314fd4SJames Smart scsi_nl_rcv_msg(struct sk_buff *skb)
3384314fd4SJames Smart {
3484314fd4SJames Smart struct nlmsghdr *nlh;
3584314fd4SJames Smart struct scsi_nl_hdr *hdr;
3622447be7SJames Smart u32 rlen;
3722447be7SJames Smart int err, tport;
3884314fd4SJames Smart
39e07ebea0SHong zhi guo while (skb->len >= NLMSG_HDRLEN) {
4084314fd4SJames Smart err = 0;
4184314fd4SJames Smart
42b529ccf2SArnaldo Carvalho de Melo nlh = nlmsg_hdr(skb);
4384314fd4SJames Smart if ((nlh->nlmsg_len < (sizeof(*nlh) + sizeof(*hdr))) ||
4484314fd4SJames Smart (skb->len < nlh->nlmsg_len)) {
4584314fd4SJames Smart printk(KERN_WARNING "%s: discarding partial skb\n",
46cadbd4a5SHarvey Harrison __func__);
4784314fd4SJames Smart return;
4884314fd4SJames Smart }
4984314fd4SJames Smart
5084314fd4SJames Smart rlen = NLMSG_ALIGN(nlh->nlmsg_len);
5184314fd4SJames Smart if (rlen > skb->len)
5284314fd4SJames Smart rlen = skb->len;
5384314fd4SJames Smart
5484314fd4SJames Smart if (nlh->nlmsg_type != SCSI_TRANSPORT_MSG) {
5584314fd4SJames Smart err = -EBADMSG;
5622447be7SJames Smart goto next_msg;
5784314fd4SJames Smart }
5884314fd4SJames Smart
59e07ebea0SHong zhi guo hdr = nlmsg_data(nlh);
6084314fd4SJames Smart if ((hdr->version != SCSI_NL_VERSION) ||
6184314fd4SJames Smart (hdr->magic != SCSI_NL_MAGIC)) {
6284314fd4SJames Smart err = -EPROTOTYPE;
6384314fd4SJames Smart goto next_msg;
6484314fd4SJames Smart }
6584314fd4SJames Smart
6690f62cf3SEric W. Biederman if (!netlink_capable(skb, CAP_SYS_ADMIN)) {
6784314fd4SJames Smart err = -EPERM;
6884314fd4SJames Smart goto next_msg;
6984314fd4SJames Smart }
7084314fd4SJames Smart
7184314fd4SJames Smart if (nlh->nlmsg_len < (sizeof(*nlh) + hdr->msglen)) {
7284314fd4SJames Smart printk(KERN_WARNING "%s: discarding partial message\n",
73cadbd4a5SHarvey Harrison __func__);
7422447be7SJames Smart goto next_msg;
7584314fd4SJames Smart }
7684314fd4SJames Smart
7784314fd4SJames Smart /*
7822447be7SJames Smart * Deliver message to the appropriate transport
7984314fd4SJames Smart */
8022447be7SJames Smart tport = hdr->transport;
818289bab1SEric W. Biederman if (tport == SCSI_NL_TRANSPORT) {
828289bab1SEric W. Biederman switch (hdr->msgtype) {
838289bab1SEric W. Biederman case SCSI_NL_SHOST_VENDOR:
848289bab1SEric W. Biederman /* Locate the driver that corresponds to the message */
858289bab1SEric W. Biederman err = -ESRCH;
868289bab1SEric W. Biederman break;
878289bab1SEric W. Biederman default:
888289bab1SEric W. Biederman err = -EBADR;
898289bab1SEric W. Biederman break;
908289bab1SEric W. Biederman }
918289bab1SEric W. Biederman if (err)
928289bab1SEric W. Biederman printk(KERN_WARNING "%s: Msgtype %d failed - err %d\n",
938289bab1SEric W. Biederman __func__, hdr->msgtype, err);
948289bab1SEric W. Biederman }
958289bab1SEric W. Biederman else
9622447be7SJames Smart err = -ENOENT;
9722447be7SJames Smart
9884314fd4SJames Smart next_msg:
9984314fd4SJames Smart if ((err) || (nlh->nlmsg_flags & NLM_F_ACK))
1002d4bc933SJohannes Berg netlink_ack(skb, nlh, err, NULL);
10184314fd4SJames Smart
10284314fd4SJames Smart skb_pull(skb, rlen);
10384314fd4SJames Smart }
10484314fd4SJames Smart }
10584314fd4SJames Smart
10622447be7SJames Smart /**
107b595076aSUwe Kleine-König * scsi_netlink_init - Called by SCSI subsystem to initialize
10822447be7SJames Smart * the SCSI transport netlink interface
10984314fd4SJames Smart *
11084314fd4SJames Smart **/
11184314fd4SJames Smart void
scsi_netlink_init(void)11284314fd4SJames Smart scsi_netlink_init(void)
11384314fd4SJames Smart {
114a31f2d17SPablo Neira Ayuso struct netlink_kernel_cfg cfg = {
115a31f2d17SPablo Neira Ayuso .input = scsi_nl_rcv_msg,
116a31f2d17SPablo Neira Ayuso .groups = SCSI_NL_GRP_CNT,
117a31f2d17SPablo Neira Ayuso };
11884314fd4SJames Smart
119b4b51029SEric W. Biederman scsi_nl_sock = netlink_kernel_create(&init_net, NETLINK_SCSITRANSPORT,
1209f00d977SPablo Neira Ayuso &cfg);
12184314fd4SJames Smart if (!scsi_nl_sock) {
12225985edcSLucas De Marchi printk(KERN_ERR "%s: register of receive handler failed\n",
123cadbd4a5SHarvey Harrison __func__);
12422447be7SJames Smart return;
12584314fd4SJames Smart }
12684314fd4SJames Smart
12784314fd4SJames Smart return;
12884314fd4SJames Smart }
12984314fd4SJames Smart
13084314fd4SJames Smart
13184314fd4SJames Smart /**
132eb44820cSRob Landley * scsi_netlink_exit - Called by SCSI subsystem to disable the SCSI transport netlink interface
13384314fd4SJames Smart *
13484314fd4SJames Smart **/
13584314fd4SJames Smart void
scsi_netlink_exit(void)13684314fd4SJames Smart scsi_netlink_exit(void)
13784314fd4SJames Smart {
13884314fd4SJames Smart if (scsi_nl_sock) {
139b7c6ba6eSDenis V. Lunev netlink_kernel_release(scsi_nl_sock);
14084314fd4SJames Smart }
14184314fd4SJames Smart
14284314fd4SJames Smart return;
14384314fd4SJames Smart }
14484314fd4SJames Smart
145