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 33 struct sock *scsi_nl_sock = NULL; 34 EXPORT_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 **/ 45 static void 46 scsi_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 (!capable(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 112 next_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 **/ 125 void 126 scsi_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 **/ 149 void 150 scsi_netlink_exit(void) 151 { 152 if (scsi_nl_sock) { 153 netlink_kernel_release(scsi_nl_sock); 154 } 155 156 return; 157 } 158 159