184314fd4740ad73550c76dee4a9578979d84af48James Smart/*
284314fd4740ad73550c76dee4a9578979d84af48James Smart *  scsi_netlink.c  - SCSI Transport Netlink Interface
384314fd4740ad73550c76dee4a9578979d84af48James Smart *
484314fd4740ad73550c76dee4a9578979d84af48James Smart *  Copyright (C) 2006   James Smart, Emulex Corporation
584314fd4740ad73550c76dee4a9578979d84af48James Smart *
684314fd4740ad73550c76dee4a9578979d84af48James Smart *  This program is free software; you can redistribute it and/or modify
784314fd4740ad73550c76dee4a9578979d84af48James Smart *  it under the terms of the GNU General Public License as published by
884314fd4740ad73550c76dee4a9578979d84af48James Smart *  the Free Software Foundation; either version 2 of the License, or
984314fd4740ad73550c76dee4a9578979d84af48James Smart *  (at your option) any later version.
1084314fd4740ad73550c76dee4a9578979d84af48James Smart *
1184314fd4740ad73550c76dee4a9578979d84af48James Smart *  This program is distributed in the hope that it will be useful,
1284314fd4740ad73550c76dee4a9578979d84af48James Smart *  but WITHOUT ANY WARRANTY; without even the implied warranty of
1384314fd4740ad73550c76dee4a9578979d84af48James Smart *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1484314fd4740ad73550c76dee4a9578979d84af48James Smart *  GNU General Public License for more details.
1584314fd4740ad73550c76dee4a9578979d84af48James Smart *
1684314fd4740ad73550c76dee4a9578979d84af48James Smart *  You should have received a copy of the GNU General Public License
1784314fd4740ad73550c76dee4a9578979d84af48James Smart *  along with this program; if not, write to the Free Software
1884314fd4740ad73550c76dee4a9578979d84af48James Smart *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
1984314fd4740ad73550c76dee4a9578979d84af48James Smart *
2084314fd4740ad73550c76dee4a9578979d84af48James Smart */
2184314fd4740ad73550c76dee4a9578979d84af48James Smart#include <linux/time.h>
2284314fd4740ad73550c76dee4a9578979d84af48James Smart#include <linux/jiffies.h>
2384314fd4740ad73550c76dee4a9578979d84af48James Smart#include <linux/security.h>
2422447be7d15aefcfab84e9bec4859a28198b0c62James Smart#include <linux/delay.h>
255a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h>
2609703660edf83b8b6d175440bf745f30580d85abPaul Gortmaker#include <linux/export.h>
2784314fd4740ad73550c76dee4a9578979d84af48James Smart#include <net/sock.h>
2884314fd4740ad73550c76dee4a9578979d84af48James Smart#include <net/netlink.h>
2984314fd4740ad73550c76dee4a9578979d84af48James Smart
3084314fd4740ad73550c76dee4a9578979d84af48James Smart#include <scsi/scsi_netlink.h>
3184314fd4740ad73550c76dee4a9578979d84af48James Smart#include "scsi_priv.h"
3284314fd4740ad73550c76dee4a9578979d84af48James Smart
3384314fd4740ad73550c76dee4a9578979d84af48James Smartstruct sock *scsi_nl_sock = NULL;
3484314fd4740ad73550c76dee4a9578979d84af48James SmartEXPORT_SYMBOL_GPL(scsi_nl_sock);
3584314fd4740ad73550c76dee4a9578979d84af48James Smart
3684314fd4740ad73550c76dee4a9578979d84af48James Smart/**
37eb44820c28bc9a042e1157b41c677018a8fdfc74Rob Landley * scsi_nl_rcv_msg - Receive message handler.
38eb44820c28bc9a042e1157b41c677018a8fdfc74Rob Landley * @skb:		socket receive buffer
39eb44820c28bc9a042e1157b41c677018a8fdfc74Rob Landley *
40eb44820c28bc9a042e1157b41c677018a8fdfc74Rob Landley * Description: Extracts message from a receive buffer.
4184314fd4740ad73550c76dee4a9578979d84af48James Smart *    Validates message header and calls appropriate transport message handler
4284314fd4740ad73550c76dee4a9578979d84af48James Smart *
4384314fd4740ad73550c76dee4a9578979d84af48James Smart *
4484314fd4740ad73550c76dee4a9578979d84af48James Smart **/
4584314fd4740ad73550c76dee4a9578979d84af48James Smartstatic void
4684314fd4740ad73550c76dee4a9578979d84af48James Smartscsi_nl_rcv_msg(struct sk_buff *skb)
4784314fd4740ad73550c76dee4a9578979d84af48James Smart{
4884314fd4740ad73550c76dee4a9578979d84af48James Smart	struct nlmsghdr *nlh;
4984314fd4740ad73550c76dee4a9578979d84af48James Smart	struct scsi_nl_hdr *hdr;
5022447be7d15aefcfab84e9bec4859a28198b0c62James Smart	u32 rlen;
5122447be7d15aefcfab84e9bec4859a28198b0c62James Smart	int err, tport;
5284314fd4740ad73550c76dee4a9578979d84af48James Smart
53e07ebea0ccfaf627464460eb57d7f2fdbcccf8ecHong zhi guo	while (skb->len >= NLMSG_HDRLEN) {
5484314fd4740ad73550c76dee4a9578979d84af48James Smart		err = 0;
5584314fd4740ad73550c76dee4a9578979d84af48James Smart
56b529ccf2799c14346d1518e9bdf1f88f03643e99Arnaldo Carvalho de Melo		nlh = nlmsg_hdr(skb);
5784314fd4740ad73550c76dee4a9578979d84af48James Smart		if ((nlh->nlmsg_len < (sizeof(*nlh) + sizeof(*hdr))) ||
5884314fd4740ad73550c76dee4a9578979d84af48James Smart		    (skb->len < nlh->nlmsg_len)) {
5984314fd4740ad73550c76dee4a9578979d84af48James Smart			printk(KERN_WARNING "%s: discarding partial skb\n",
60cadbd4a5e36dde7e6c49b587b2c419103c0b7218Harvey Harrison				 __func__);
6184314fd4740ad73550c76dee4a9578979d84af48James Smart			return;
6284314fd4740ad73550c76dee4a9578979d84af48James Smart		}
6384314fd4740ad73550c76dee4a9578979d84af48James Smart
6484314fd4740ad73550c76dee4a9578979d84af48James Smart		rlen = NLMSG_ALIGN(nlh->nlmsg_len);
6584314fd4740ad73550c76dee4a9578979d84af48James Smart		if (rlen > skb->len)
6684314fd4740ad73550c76dee4a9578979d84af48James Smart			rlen = skb->len;
6784314fd4740ad73550c76dee4a9578979d84af48James Smart
6884314fd4740ad73550c76dee4a9578979d84af48James Smart		if (nlh->nlmsg_type != SCSI_TRANSPORT_MSG) {
6984314fd4740ad73550c76dee4a9578979d84af48James Smart			err = -EBADMSG;
7022447be7d15aefcfab84e9bec4859a28198b0c62James Smart			goto next_msg;
7184314fd4740ad73550c76dee4a9578979d84af48James Smart		}
7284314fd4740ad73550c76dee4a9578979d84af48James Smart
73e07ebea0ccfaf627464460eb57d7f2fdbcccf8ecHong zhi guo		hdr = nlmsg_data(nlh);
7484314fd4740ad73550c76dee4a9578979d84af48James Smart		if ((hdr->version != SCSI_NL_VERSION) ||
7584314fd4740ad73550c76dee4a9578979d84af48James Smart		    (hdr->magic != SCSI_NL_MAGIC)) {
7684314fd4740ad73550c76dee4a9578979d84af48James Smart			err = -EPROTOTYPE;
7784314fd4740ad73550c76dee4a9578979d84af48James Smart			goto next_msg;
7884314fd4740ad73550c76dee4a9578979d84af48James Smart		}
7984314fd4740ad73550c76dee4a9578979d84af48James Smart
8090f62cf30a78721641e08737bda787552428061eEric W. Biederman		if (!netlink_capable(skb, CAP_SYS_ADMIN)) {
8184314fd4740ad73550c76dee4a9578979d84af48James Smart			err = -EPERM;
8284314fd4740ad73550c76dee4a9578979d84af48James Smart			goto next_msg;
8384314fd4740ad73550c76dee4a9578979d84af48James Smart		}
8484314fd4740ad73550c76dee4a9578979d84af48James Smart
8584314fd4740ad73550c76dee4a9578979d84af48James Smart		if (nlh->nlmsg_len < (sizeof(*nlh) + hdr->msglen)) {
8684314fd4740ad73550c76dee4a9578979d84af48James Smart			printk(KERN_WARNING "%s: discarding partial message\n",
87cadbd4a5e36dde7e6c49b587b2c419103c0b7218Harvey Harrison				 __func__);
8822447be7d15aefcfab84e9bec4859a28198b0c62James Smart			goto next_msg;
8984314fd4740ad73550c76dee4a9578979d84af48James Smart		}
9084314fd4740ad73550c76dee4a9578979d84af48James Smart
9184314fd4740ad73550c76dee4a9578979d84af48James Smart		/*
9222447be7d15aefcfab84e9bec4859a28198b0c62James Smart		 * Deliver message to the appropriate transport
9384314fd4740ad73550c76dee4a9578979d84af48James Smart		 */
9422447be7d15aefcfab84e9bec4859a28198b0c62James Smart		tport = hdr->transport;
958289bab1daf9768c20114051a99c1bd5f48d4420Eric W. Biederman		if (tport == SCSI_NL_TRANSPORT) {
968289bab1daf9768c20114051a99c1bd5f48d4420Eric W. Biederman			switch (hdr->msgtype) {
978289bab1daf9768c20114051a99c1bd5f48d4420Eric W. Biederman			case SCSI_NL_SHOST_VENDOR:
988289bab1daf9768c20114051a99c1bd5f48d4420Eric W. Biederman				/* Locate the driver that corresponds to the message */
998289bab1daf9768c20114051a99c1bd5f48d4420Eric W. Biederman				err = -ESRCH;
1008289bab1daf9768c20114051a99c1bd5f48d4420Eric W. Biederman				break;
1018289bab1daf9768c20114051a99c1bd5f48d4420Eric W. Biederman			default:
1028289bab1daf9768c20114051a99c1bd5f48d4420Eric W. Biederman				err = -EBADR;
1038289bab1daf9768c20114051a99c1bd5f48d4420Eric W. Biederman				break;
1048289bab1daf9768c20114051a99c1bd5f48d4420Eric W. Biederman			}
1058289bab1daf9768c20114051a99c1bd5f48d4420Eric W. Biederman			if (err)
1068289bab1daf9768c20114051a99c1bd5f48d4420Eric W. Biederman				printk(KERN_WARNING "%s: Msgtype %d failed - err %d\n",
1078289bab1daf9768c20114051a99c1bd5f48d4420Eric W. Biederman				       __func__, hdr->msgtype, err);
1088289bab1daf9768c20114051a99c1bd5f48d4420Eric W. Biederman		}
1098289bab1daf9768c20114051a99c1bd5f48d4420Eric W. Biederman		else
11022447be7d15aefcfab84e9bec4859a28198b0c62James Smart			err = -ENOENT;
11122447be7d15aefcfab84e9bec4859a28198b0c62James Smart
11284314fd4740ad73550c76dee4a9578979d84af48James Smartnext_msg:
11384314fd4740ad73550c76dee4a9578979d84af48James Smart		if ((err) || (nlh->nlmsg_flags & NLM_F_ACK))
11484314fd4740ad73550c76dee4a9578979d84af48James Smart			netlink_ack(skb, nlh, err);
11584314fd4740ad73550c76dee4a9578979d84af48James Smart
11684314fd4740ad73550c76dee4a9578979d84af48James Smart		skb_pull(skb, rlen);
11784314fd4740ad73550c76dee4a9578979d84af48James Smart	}
11884314fd4740ad73550c76dee4a9578979d84af48James Smart}
11984314fd4740ad73550c76dee4a9578979d84af48James Smart
12022447be7d15aefcfab84e9bec4859a28198b0c62James Smart/**
121b595076a180a56d1bb170e6eceda6eb9d76f4cd3Uwe Kleine-König * scsi_netlink_init - Called by SCSI subsystem to initialize
12222447be7d15aefcfab84e9bec4859a28198b0c62James Smart * 	the SCSI transport netlink interface
12384314fd4740ad73550c76dee4a9578979d84af48James Smart *
12484314fd4740ad73550c76dee4a9578979d84af48James Smart **/
12584314fd4740ad73550c76dee4a9578979d84af48James Smartvoid
12684314fd4740ad73550c76dee4a9578979d84af48James Smartscsi_netlink_init(void)
12784314fd4740ad73550c76dee4a9578979d84af48James Smart{
128a31f2d17b331db970259e875b7223d3aba7e3821Pablo Neira Ayuso	struct netlink_kernel_cfg cfg = {
129a31f2d17b331db970259e875b7223d3aba7e3821Pablo Neira Ayuso		.input	= scsi_nl_rcv_msg,
130a31f2d17b331db970259e875b7223d3aba7e3821Pablo Neira Ayuso		.groups	= SCSI_NL_GRP_CNT,
131a31f2d17b331db970259e875b7223d3aba7e3821Pablo Neira Ayuso	};
13284314fd4740ad73550c76dee4a9578979d84af48James Smart
133b4b510290b056b86611757ce1175a230f1080f53Eric W. Biederman	scsi_nl_sock = netlink_kernel_create(&init_net, NETLINK_SCSITRANSPORT,
1349f00d9776bc5beb92e8bfc884a7e96ddc5589e2ePablo Neira Ayuso					     &cfg);
13584314fd4740ad73550c76dee4a9578979d84af48James Smart	if (!scsi_nl_sock) {
13625985edcedea6396277003854657b5f3cb31a628Lucas De Marchi		printk(KERN_ERR "%s: register of receive handler failed\n",
137cadbd4a5e36dde7e6c49b587b2c419103c0b7218Harvey Harrison				__func__);
13822447be7d15aefcfab84e9bec4859a28198b0c62James Smart		return;
13984314fd4740ad73550c76dee4a9578979d84af48James Smart	}
14084314fd4740ad73550c76dee4a9578979d84af48James Smart
14184314fd4740ad73550c76dee4a9578979d84af48James Smart	return;
14284314fd4740ad73550c76dee4a9578979d84af48James Smart}
14384314fd4740ad73550c76dee4a9578979d84af48James Smart
14484314fd4740ad73550c76dee4a9578979d84af48James Smart
14584314fd4740ad73550c76dee4a9578979d84af48James Smart/**
146eb44820c28bc9a042e1157b41c677018a8fdfc74Rob Landley * scsi_netlink_exit - Called by SCSI subsystem to disable the SCSI transport netlink interface
14784314fd4740ad73550c76dee4a9578979d84af48James Smart *
14884314fd4740ad73550c76dee4a9578979d84af48James Smart **/
14984314fd4740ad73550c76dee4a9578979d84af48James Smartvoid
15084314fd4740ad73550c76dee4a9578979d84af48James Smartscsi_netlink_exit(void)
15184314fd4740ad73550c76dee4a9578979d84af48James Smart{
15284314fd4740ad73550c76dee4a9578979d84af48James Smart	if (scsi_nl_sock) {
153b7c6ba6eb1234e35a74fb8ba8123232a7b1ba9e4Denis V. Lunev		netlink_kernel_release(scsi_nl_sock);
15484314fd4740ad73550c76dee4a9578979d84af48James Smart	}
15584314fd4740ad73550c76dee4a9578979d84af48James Smart
15684314fd4740ad73550c76dee4a9578979d84af48James Smart	return;
15784314fd4740ad73550c76dee4a9578979d84af48James Smart}
15884314fd4740ad73550c76dee4a9578979d84af48James Smart
159