1/*
2 *  scsi_netlink.c  - SCSI Transport Netlink Interface
3 *
4 *  Copyright (C) 2006   James Smart, Emulex Corporation
5 *
6 *  This program is free software; you can redistribute it and/or modify
7 *  it under the terms of the GNU General Public License as published by
8 *  the Free Software Foundation; either version 2 of the License, or
9 *  (at your option) any later version.
10 *
11 *  This program is distributed in the hope that it will be useful,
12 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 *  GNU General Public License for more details.
15 *
16 *  You should have received a copy of the GNU General Public License
17 *  along with this program; if not, write to the Free Software
18 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 *
20 */
21#include <linux/time.h>
22#include <linux/jiffies.h>
23#include <linux/security.h>
24#include <net/sock.h>
25#include <net/netlink.h>
26
27#include <scsi/scsi_netlink.h>
28#include "scsi_priv.h"
29
30struct sock *scsi_nl_sock = NULL;
31EXPORT_SYMBOL_GPL(scsi_nl_sock);
32
33
34/**
35 * scsi_nl_rcv_msg -
36 *    Receive message handler. Extracts message from a receive buffer.
37 *    Validates message header and calls appropriate transport message handler
38 *
39 * @skb:		socket receive buffer
40 *
41 **/
42static void
43scsi_nl_rcv_msg(struct sk_buff *skb)
44{
45	struct nlmsghdr *nlh;
46	struct scsi_nl_hdr *hdr;
47	uint32_t rlen;
48	int err;
49
50	while (skb->len >= NLMSG_SPACE(0)) {
51		err = 0;
52
53		nlh = nlmsg_hdr(skb);
54		if ((nlh->nlmsg_len < (sizeof(*nlh) + sizeof(*hdr))) ||
55		    (skb->len < nlh->nlmsg_len)) {
56			printk(KERN_WARNING "%s: discarding partial skb\n",
57				 __FUNCTION__);
58			return;
59		}
60
61		rlen = NLMSG_ALIGN(nlh->nlmsg_len);
62		if (rlen > skb->len)
63			rlen = skb->len;
64
65		if (nlh->nlmsg_type != SCSI_TRANSPORT_MSG) {
66			err = -EBADMSG;
67			goto next_msg;
68		}
69
70		hdr = NLMSG_DATA(nlh);
71		if ((hdr->version != SCSI_NL_VERSION) ||
72		    (hdr->magic != SCSI_NL_MAGIC)) {
73			err = -EPROTOTYPE;
74			goto next_msg;
75		}
76
77		if (security_netlink_recv(skb, CAP_SYS_ADMIN)) {
78			err = -EPERM;
79			goto next_msg;
80		}
81
82		if (nlh->nlmsg_len < (sizeof(*nlh) + hdr->msglen)) {
83			printk(KERN_WARNING "%s: discarding partial message\n",
84				 __FUNCTION__);
85			return;
86		}
87
88		/*
89		 * We currently don't support anyone sending us a message
90		 */
91
92next_msg:
93		if ((err) || (nlh->nlmsg_flags & NLM_F_ACK))
94			netlink_ack(skb, nlh, err);
95
96		skb_pull(skb, rlen);
97	}
98}
99
100
101/**
102 * scsi_nl_rcv_msg -
103 *    Receive handler for a socket. Extracts a received message buffer from
104 *    the socket, and starts message processing.
105 *
106 * @sk:		socket
107 * @len:	unused
108 *
109 **/
110static void
111scsi_nl_rcv(struct sock *sk, int len)
112{
113	struct sk_buff *skb;
114
115	while ((skb = skb_dequeue(&sk->sk_receive_queue))) {
116		scsi_nl_rcv_msg(skb);
117		kfree_skb(skb);
118	}
119}
120
121
122/**
123 * scsi_nl_rcv_event -
124 *    Event handler for a netlink socket.
125 *
126 * @this:		event notifier block
127 * @event:		event type
128 * @ptr:		event payload
129 *
130 **/
131static int
132scsi_nl_rcv_event(struct notifier_block *this, unsigned long event, void *ptr)
133{
134	struct netlink_notify *n = ptr;
135
136	if (n->protocol != NETLINK_SCSITRANSPORT)
137		return NOTIFY_DONE;
138
139	/*
140	 * Currently, we are not tracking PID's, etc. There is nothing
141	 * to handle.
142	 */
143
144	return NOTIFY_DONE;
145}
146
147static struct notifier_block scsi_netlink_notifier = {
148	.notifier_call  = scsi_nl_rcv_event,
149};
150
151
152/**
153 * scsi_netlink_init -
154 *    Called by SCSI subsystem to intialize the SCSI transport netlink
155 *    interface
156 *
157 **/
158void
159scsi_netlink_init(void)
160{
161	int error;
162
163	error = netlink_register_notifier(&scsi_netlink_notifier);
164	if (error) {
165		printk(KERN_ERR "%s: register of event handler failed - %d\n",
166				__FUNCTION__, error);
167		return;
168	}
169
170	scsi_nl_sock = netlink_kernel_create(NETLINK_SCSITRANSPORT,
171				SCSI_NL_GRP_CNT, scsi_nl_rcv, NULL,
172				THIS_MODULE);
173	if (!scsi_nl_sock) {
174		printk(KERN_ERR "%s: register of recieve handler failed\n",
175				__FUNCTION__);
176		netlink_unregister_notifier(&scsi_netlink_notifier);
177	}
178
179	return;
180}
181
182
183/**
184 * scsi_netlink_exit -
185 *    Called by SCSI subsystem to disable the SCSI transport netlink
186 *    interface
187 *
188 **/
189void
190scsi_netlink_exit(void)
191{
192	if (scsi_nl_sock) {
193		sock_release(scsi_nl_sock->sk_socket);
194		netlink_unregister_notifier(&scsi_netlink_notifier);
195	}
196
197	return;
198}
199