xref: /openbmc/linux/drivers/scsi/scsi_netlink.c (revision b7c6ba6e)
184314fd4SJames Smart /*
284314fd4SJames Smart  *  scsi_netlink.c  - SCSI Transport Netlink Interface
384314fd4SJames Smart  *
484314fd4SJames Smart  *  Copyright (C) 2006   James Smart, Emulex Corporation
584314fd4SJames Smart  *
684314fd4SJames Smart  *  This program is free software; you can redistribute it and/or modify
784314fd4SJames Smart  *  it under the terms of the GNU General Public License as published by
884314fd4SJames Smart  *  the Free Software Foundation; either version 2 of the License, or
984314fd4SJames Smart  *  (at your option) any later version.
1084314fd4SJames Smart  *
1184314fd4SJames Smart  *  This program is distributed in the hope that it will be useful,
1284314fd4SJames Smart  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
1384314fd4SJames Smart  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1484314fd4SJames Smart  *  GNU General Public License for more details.
1584314fd4SJames Smart  *
1684314fd4SJames Smart  *  You should have received a copy of the GNU General Public License
1784314fd4SJames Smart  *  along with this program; if not, write to the Free Software
1884314fd4SJames Smart  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
1984314fd4SJames Smart  *
2084314fd4SJames Smart  */
2184314fd4SJames Smart #include <linux/time.h>
2284314fd4SJames Smart #include <linux/jiffies.h>
2384314fd4SJames Smart #include <linux/security.h>
2484314fd4SJames Smart #include <net/sock.h>
2584314fd4SJames Smart #include <net/netlink.h>
2684314fd4SJames Smart 
2784314fd4SJames Smart #include <scsi/scsi_netlink.h>
2884314fd4SJames Smart #include "scsi_priv.h"
2984314fd4SJames Smart 
3084314fd4SJames Smart struct sock *scsi_nl_sock = NULL;
3184314fd4SJames Smart EXPORT_SYMBOL_GPL(scsi_nl_sock);
3284314fd4SJames Smart 
3384314fd4SJames Smart 
3484314fd4SJames Smart /**
35eb44820cSRob Landley  * scsi_nl_rcv_msg - Receive message handler.
36eb44820cSRob Landley  * @skb:		socket receive buffer
37eb44820cSRob Landley  *
38eb44820cSRob Landley  * Description: Extracts message from a receive buffer.
3984314fd4SJames Smart  *    Validates message header and calls appropriate transport message handler
4084314fd4SJames Smart  *
4184314fd4SJames Smart  *
4284314fd4SJames Smart  **/
4384314fd4SJames Smart static void
4484314fd4SJames Smart scsi_nl_rcv_msg(struct sk_buff *skb)
4584314fd4SJames Smart {
4684314fd4SJames Smart 	struct nlmsghdr *nlh;
4784314fd4SJames Smart 	struct scsi_nl_hdr *hdr;
4884314fd4SJames Smart 	uint32_t rlen;
4984314fd4SJames Smart 	int err;
5084314fd4SJames Smart 
5184314fd4SJames Smart 	while (skb->len >= NLMSG_SPACE(0)) {
5284314fd4SJames Smart 		err = 0;
5384314fd4SJames Smart 
54b529ccf2SArnaldo Carvalho de Melo 		nlh = nlmsg_hdr(skb);
5584314fd4SJames Smart 		if ((nlh->nlmsg_len < (sizeof(*nlh) + sizeof(*hdr))) ||
5684314fd4SJames Smart 		    (skb->len < nlh->nlmsg_len)) {
5784314fd4SJames Smart 			printk(KERN_WARNING "%s: discarding partial skb\n",
5884314fd4SJames Smart 				 __FUNCTION__);
5984314fd4SJames Smart 			return;
6084314fd4SJames Smart 		}
6184314fd4SJames Smart 
6284314fd4SJames Smart 		rlen = NLMSG_ALIGN(nlh->nlmsg_len);
6384314fd4SJames Smart 		if (rlen > skb->len)
6484314fd4SJames Smart 			rlen = skb->len;
6584314fd4SJames Smart 
6684314fd4SJames Smart 		if (nlh->nlmsg_type != SCSI_TRANSPORT_MSG) {
6784314fd4SJames Smart 			err = -EBADMSG;
68cd40b7d3SDenis V. Lunev 			return;
6984314fd4SJames Smart 		}
7084314fd4SJames Smart 
7184314fd4SJames Smart 		hdr = NLMSG_DATA(nlh);
7284314fd4SJames Smart 		if ((hdr->version != SCSI_NL_VERSION) ||
7384314fd4SJames Smart 		    (hdr->magic != SCSI_NL_MAGIC)) {
7484314fd4SJames Smart 			err = -EPROTOTYPE;
7584314fd4SJames Smart 			goto next_msg;
7684314fd4SJames Smart 		}
7784314fd4SJames Smart 
7884314fd4SJames Smart 		if (security_netlink_recv(skb, CAP_SYS_ADMIN)) {
7984314fd4SJames Smart 			err = -EPERM;
8084314fd4SJames Smart 			goto next_msg;
8184314fd4SJames Smart 		}
8284314fd4SJames Smart 
8384314fd4SJames Smart 		if (nlh->nlmsg_len < (sizeof(*nlh) + hdr->msglen)) {
8484314fd4SJames Smart 			printk(KERN_WARNING "%s: discarding partial message\n",
8584314fd4SJames Smart 				 __FUNCTION__);
8684314fd4SJames Smart 			return;
8784314fd4SJames Smart 		}
8884314fd4SJames Smart 
8984314fd4SJames Smart 		/*
9084314fd4SJames Smart 		 * We currently don't support anyone sending us a message
9184314fd4SJames Smart 		 */
9284314fd4SJames Smart 
9384314fd4SJames Smart next_msg:
9484314fd4SJames Smart 		if ((err) || (nlh->nlmsg_flags & NLM_F_ACK))
9584314fd4SJames Smart 			netlink_ack(skb, nlh, err);
9684314fd4SJames Smart 
9784314fd4SJames Smart 		skb_pull(skb, rlen);
9884314fd4SJames Smart 	}
9984314fd4SJames Smart }
10084314fd4SJames Smart 
10184314fd4SJames Smart 
10284314fd4SJames Smart /**
103eb44820cSRob Landley  * scsi_nl_rcv_event - Event handler for a netlink socket.
10484314fd4SJames Smart  * @this:		event notifier block
10584314fd4SJames Smart  * @event:		event type
10684314fd4SJames Smart  * @ptr:		event payload
10784314fd4SJames Smart  *
10884314fd4SJames Smart  **/
10984314fd4SJames Smart static int
11084314fd4SJames Smart scsi_nl_rcv_event(struct notifier_block *this, unsigned long event, void *ptr)
11184314fd4SJames Smart {
11284314fd4SJames Smart 	struct netlink_notify *n = ptr;
11384314fd4SJames Smart 
11484314fd4SJames Smart 	if (n->protocol != NETLINK_SCSITRANSPORT)
11584314fd4SJames Smart 		return NOTIFY_DONE;
11684314fd4SJames Smart 
11784314fd4SJames Smart 	/*
11884314fd4SJames Smart 	 * Currently, we are not tracking PID's, etc. There is nothing
11984314fd4SJames Smart 	 * to handle.
12084314fd4SJames Smart 	 */
12184314fd4SJames Smart 
12284314fd4SJames Smart 	return NOTIFY_DONE;
12384314fd4SJames Smart }
12484314fd4SJames Smart 
12584314fd4SJames Smart static struct notifier_block scsi_netlink_notifier = {
12684314fd4SJames Smart 	.notifier_call  = scsi_nl_rcv_event,
12784314fd4SJames Smart };
12884314fd4SJames Smart 
12984314fd4SJames Smart 
13084314fd4SJames Smart /**
131eb44820cSRob Landley  * scsi_netlink_init - Called by SCSI subsystem to intialize the SCSI transport netlink interface
13284314fd4SJames Smart  *
13384314fd4SJames Smart  **/
13484314fd4SJames Smart void
13584314fd4SJames Smart scsi_netlink_init(void)
13684314fd4SJames Smart {
13784314fd4SJames Smart 	int error;
13884314fd4SJames Smart 
13984314fd4SJames Smart 	error = netlink_register_notifier(&scsi_netlink_notifier);
14084314fd4SJames Smart 	if (error) {
14184314fd4SJames Smart 		printk(KERN_ERR "%s: register of event handler failed - %d\n",
14284314fd4SJames Smart 				__FUNCTION__, error);
14384314fd4SJames Smart 		return;
14484314fd4SJames Smart 	}
14584314fd4SJames Smart 
146b4b51029SEric W. Biederman 	scsi_nl_sock = netlink_kernel_create(&init_net, NETLINK_SCSITRANSPORT,
147cd40b7d3SDenis V. Lunev 				SCSI_NL_GRP_CNT, scsi_nl_rcv_msg, NULL,
148af65bdfcSPatrick McHardy 				THIS_MODULE);
14984314fd4SJames Smart 	if (!scsi_nl_sock) {
15084314fd4SJames Smart 		printk(KERN_ERR "%s: register of recieve handler failed\n",
15184314fd4SJames Smart 				__FUNCTION__);
15284314fd4SJames Smart 		netlink_unregister_notifier(&scsi_netlink_notifier);
15384314fd4SJames Smart 	}
15484314fd4SJames Smart 
15584314fd4SJames Smart 	return;
15684314fd4SJames Smart }
15784314fd4SJames Smart 
15884314fd4SJames Smart 
15984314fd4SJames Smart /**
160eb44820cSRob Landley  * scsi_netlink_exit - Called by SCSI subsystem to disable the SCSI transport netlink interface
16184314fd4SJames Smart  *
16284314fd4SJames Smart  **/
16384314fd4SJames Smart void
16484314fd4SJames Smart scsi_netlink_exit(void)
16584314fd4SJames Smart {
16684314fd4SJames Smart 	if (scsi_nl_sock) {
167b7c6ba6eSDenis V. Lunev 		netlink_kernel_release(scsi_nl_sock);
16884314fd4SJames Smart 		netlink_unregister_notifier(&scsi_netlink_notifier);
16984314fd4SJames Smart 	}
17084314fd4SJames Smart 
17184314fd4SJames Smart 	return;
17284314fd4SJames Smart }
17384314fd4SJames Smart 
17484314fd4SJames Smart 
175