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