linux/drivers/scsi/scsi_netlink.c
<<
>>
Prefs
   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 <linux/delay.h>
  25#include <linux/slab.h>
  26#include <linux/export.h>
  27#include <net/sock.h>
  28#include <net/netlink.h>
  29
  30#include <scsi/scsi_netlink.h>
  31#include "scsi_priv.h"
  32
  33struct sock *scsi_nl_sock = NULL;
  34EXPORT_SYMBOL_GPL(scsi_nl_sock);
  35
  36/**
  37 * scsi_nl_rcv_msg - Receive message handler.
  38 * @skb:                socket receive buffer
  39 *
  40 * Description: Extracts message from a receive buffer.
  41 *    Validates message header and calls appropriate transport message handler
  42 *
  43 *
  44 **/
  45static void
  46scsi_nl_rcv_msg(struct sk_buff *skb)
  47{
  48        struct nlmsghdr *nlh;
  49        struct scsi_nl_hdr *hdr;
  50        u32 rlen;
  51        int err, tport;
  52
  53        while (skb->len >= NLMSG_HDRLEN) {
  54                err = 0;
  55
  56                nlh = nlmsg_hdr(skb);
  57                if ((nlh->nlmsg_len < (sizeof(*nlh) + sizeof(*hdr))) ||
  58                    (skb->len < nlh->nlmsg_len)) {
  59                        printk(KERN_WARNING "%s: discarding partial skb\n",
  60                                 __func__);
  61                        return;
  62                }
  63
  64                rlen = NLMSG_ALIGN(nlh->nlmsg_len);
  65                if (rlen > skb->len)
  66                        rlen = skb->len;
  67
  68                if (nlh->nlmsg_type != SCSI_TRANSPORT_MSG) {
  69                        err = -EBADMSG;
  70                        goto next_msg;
  71                }
  72
  73                hdr = nlmsg_data(nlh);
  74                if ((hdr->version != SCSI_NL_VERSION) ||
  75                    (hdr->magic != SCSI_NL_MAGIC)) {
  76                        err = -EPROTOTYPE;
  77                        goto next_msg;
  78                }
  79
  80                if (!netlink_capable(skb, CAP_SYS_ADMIN)) {
  81                        err = -EPERM;
  82                        goto next_msg;
  83                }
  84
  85                if (nlh->nlmsg_len < (sizeof(*nlh) + hdr->msglen)) {
  86                        printk(KERN_WARNING "%s: discarding partial message\n",
  87                                 __func__);
  88                        goto next_msg;
  89                }
  90
  91                /*
  92                 * Deliver message to the appropriate transport
  93                 */
  94                tport = hdr->transport;
  95                if (tport == SCSI_NL_TRANSPORT) {
  96                        switch (hdr->msgtype) {
  97                        case SCSI_NL_SHOST_VENDOR:
  98                                /* Locate the driver that corresponds to the message */
  99                                err = -ESRCH;
 100                                break;
 101                        default:
 102                                err = -EBADR;
 103                                break;
 104                        }
 105                        if (err)
 106                                printk(KERN_WARNING "%s: Msgtype %d failed - err %d\n",
 107                                       __func__, hdr->msgtype, err);
 108                }
 109                else
 110                        err = -ENOENT;
 111
 112next_msg:
 113                if ((err) || (nlh->nlmsg_flags & NLM_F_ACK))
 114                        netlink_ack(skb, nlh, err);
 115
 116                skb_pull(skb, rlen);
 117        }
 118}
 119
 120/**
 121 * scsi_netlink_init - Called by SCSI subsystem to initialize
 122 *      the SCSI transport netlink interface
 123 *
 124 **/
 125void
 126scsi_netlink_init(void)
 127{
 128        struct netlink_kernel_cfg cfg = {
 129                .input  = scsi_nl_rcv_msg,
 130                .groups = SCSI_NL_GRP_CNT,
 131        };
 132
 133        scsi_nl_sock = netlink_kernel_create(&init_net, NETLINK_SCSITRANSPORT,
 134                                             &cfg);
 135        if (!scsi_nl_sock) {
 136                printk(KERN_ERR "%s: register of receive handler failed\n",
 137                                __func__);
 138                return;
 139        }
 140
 141        return;
 142}
 143
 144
 145/**
 146 * scsi_netlink_exit - Called by SCSI subsystem to disable the SCSI transport netlink interface
 147 *
 148 **/
 149void
 150scsi_netlink_exit(void)
 151{
 152        if (scsi_nl_sock) {
 153                netlink_kernel_release(scsi_nl_sock);
 154        }
 155
 156        return;
 157}
 158
 159