1
linux/drivers/scsi/scsi_netlink.c
Eric W. Biederman b4b510290b [NET]: Support multiple network namespaces with netlink
Each netlink socket will live in exactly one network namespace,
this includes the controlling kernel sockets.

This patch updates all of the existing netlink protocols
to only support the initial network namespace.  Request
by clients in other namespaces will get -ECONREFUSED.
As they would if the kernel did not have the support for
that netlink protocol compiled in.

As each netlink protocol is updated to be multiple network
namespace safe it can register multiple kernel sockets
to acquire a presence in the rest of the network namespaces.

The implementation in af_netlink is a simple filter implementation
at hash table insertion and hash table look up time.

Signed-off-by: Eric W. Biederman <ebiederm@xmission.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2007-10-10 16:49:09 -07:00

201 lines
4.2 KiB
C

/*
* scsi_netlink.c - SCSI Transport Netlink Interface
*
* Copyright (C) 2006 James Smart, Emulex Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <linux/time.h>
#include <linux/jiffies.h>
#include <linux/security.h>
#include <net/sock.h>
#include <net/netlink.h>
#include <scsi/scsi_netlink.h>
#include "scsi_priv.h"
struct sock *scsi_nl_sock = NULL;
EXPORT_SYMBOL_GPL(scsi_nl_sock);
/**
* scsi_nl_rcv_msg -
* Receive message handler. Extracts message from a receive buffer.
* Validates message header and calls appropriate transport message handler
*
* @skb: socket receive buffer
*
**/
static void
scsi_nl_rcv_msg(struct sk_buff *skb)
{
struct nlmsghdr *nlh;
struct scsi_nl_hdr *hdr;
uint32_t rlen;
int err;
while (skb->len >= NLMSG_SPACE(0)) {
err = 0;
nlh = nlmsg_hdr(skb);
if ((nlh->nlmsg_len < (sizeof(*nlh) + sizeof(*hdr))) ||
(skb->len < nlh->nlmsg_len)) {
printk(KERN_WARNING "%s: discarding partial skb\n",
__FUNCTION__);
return;
}
rlen = NLMSG_ALIGN(nlh->nlmsg_len);
if (rlen > skb->len)
rlen = skb->len;
if (nlh->nlmsg_type != SCSI_TRANSPORT_MSG) {
err = -EBADMSG;
goto next_msg;
}
hdr = NLMSG_DATA(nlh);
if ((hdr->version != SCSI_NL_VERSION) ||
(hdr->magic != SCSI_NL_MAGIC)) {
err = -EPROTOTYPE;
goto next_msg;
}
if (security_netlink_recv(skb, CAP_SYS_ADMIN)) {
err = -EPERM;
goto next_msg;
}
if (nlh->nlmsg_len < (sizeof(*nlh) + hdr->msglen)) {
printk(KERN_WARNING "%s: discarding partial message\n",
__FUNCTION__);
return;
}
/*
* We currently don't support anyone sending us a message
*/
next_msg:
if ((err) || (nlh->nlmsg_flags & NLM_F_ACK))
netlink_ack(skb, nlh, err);
skb_pull(skb, rlen);
}
}
/**
* scsi_nl_rcv_msg -
* Receive handler for a socket. Extracts a received message buffer from
* the socket, and starts message processing.
*
* @sk: socket
* @len: unused
*
**/
static void
scsi_nl_rcv(struct sock *sk, int len)
{
struct sk_buff *skb;
while ((skb = skb_dequeue(&sk->sk_receive_queue))) {
scsi_nl_rcv_msg(skb);
kfree_skb(skb);
}
}
/**
* scsi_nl_rcv_event -
* Event handler for a netlink socket.
*
* @this: event notifier block
* @event: event type
* @ptr: event payload
*
**/
static int
scsi_nl_rcv_event(struct notifier_block *this, unsigned long event, void *ptr)
{
struct netlink_notify *n = ptr;
if (n->protocol != NETLINK_SCSITRANSPORT)
return NOTIFY_DONE;
/*
* Currently, we are not tracking PID's, etc. There is nothing
* to handle.
*/
return NOTIFY_DONE;
}
static struct notifier_block scsi_netlink_notifier = {
.notifier_call = scsi_nl_rcv_event,
};
/**
* scsi_netlink_init -
* Called by SCSI subsystem to intialize the SCSI transport netlink
* interface
*
**/
void
scsi_netlink_init(void)
{
int error;
error = netlink_register_notifier(&scsi_netlink_notifier);
if (error) {
printk(KERN_ERR "%s: register of event handler failed - %d\n",
__FUNCTION__, error);
return;
}
scsi_nl_sock = netlink_kernel_create(&init_net, NETLINK_SCSITRANSPORT,
SCSI_NL_GRP_CNT, scsi_nl_rcv, NULL,
THIS_MODULE);
if (!scsi_nl_sock) {
printk(KERN_ERR "%s: register of recieve handler failed\n",
__FUNCTION__);
netlink_unregister_notifier(&scsi_netlink_notifier);
}
return;
}
/**
* scsi_netlink_exit -
* Called by SCSI subsystem to disable the SCSI transport netlink
* interface
*
**/
void
scsi_netlink_exit(void)
{
if (scsi_nl_sock) {
sock_release(scsi_nl_sock->sk_socket);
netlink_unregister_notifier(&scsi_netlink_notifier);
}
return;
}