diff options
Diffstat (limited to 'drivers/scsi/scsi_netlink.c')
-rw-r--r-- | drivers/scsi/scsi_netlink.c | 199 |
1 files changed, 199 insertions, 0 deletions
diff --git a/drivers/scsi/scsi_netlink.c b/drivers/scsi/scsi_netlink.c new file mode 100644 index 000000000000..1b59b27e887f --- /dev/null +++ b/drivers/scsi/scsi_netlink.c | |||
@@ -0,0 +1,199 @@ | |||
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 | |||
30 | struct sock *scsi_nl_sock = NULL; | ||
31 | EXPORT_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 | **/ | ||
42 | static void | ||
43 | scsi_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 = (struct nlmsghdr *) skb->data; | ||
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 | |||
92 | next_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 | **/ | ||
110 | static void | ||
111 | scsi_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 | **/ | ||
131 | static int | ||
132 | scsi_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 | |||
147 | static 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 | **/ | ||
158 | void | ||
159 | scsi_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, THIS_MODULE); | ||
172 | if (!scsi_nl_sock) { | ||
173 | printk(KERN_ERR "%s: register of recieve handler failed\n", | ||
174 | __FUNCTION__); | ||
175 | netlink_unregister_notifier(&scsi_netlink_notifier); | ||
176 | } | ||
177 | |||
178 | return; | ||
179 | } | ||
180 | |||
181 | |||
182 | /** | ||
183 | * scsi_netlink_exit - | ||
184 | * Called by SCSI subsystem to disable the SCSI transport netlink | ||
185 | * interface | ||
186 | * | ||
187 | **/ | ||
188 | void | ||
189 | scsi_netlink_exit(void) | ||
190 | { | ||
191 | if (scsi_nl_sock) { | ||
192 | sock_release(scsi_nl_sock->sk_socket); | ||
193 | netlink_unregister_notifier(&scsi_netlink_notifier); | ||
194 | } | ||
195 | |||
196 | return; | ||
197 | } | ||
198 | |||
199 | |||