xref: /openbmc/linux/drivers/scsi/scsi_netlink.c (revision 1a59d1b8)
11a59d1b8SThomas 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