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 - Receive message handler. 36 * @skb: socket receive buffer 37 * 38 * Description: Extracts message from a receive buffer. 39 * Validates message header and calls appropriate transport message handler 40 * 41 * 42 **/ 43 static void 44 scsi_nl_rcv_msg(struct sk_buff *skb) 45 { 46 struct nlmsghdr *nlh; 47 struct scsi_nl_hdr *hdr; 48 uint32_t rlen; 49 int err; 50 51 while (skb->len >= NLMSG_SPACE(0)) { 52 err = 0; 53 54 nlh = nlmsg_hdr(skb); 55 if ((nlh->nlmsg_len < (sizeof(*nlh) + sizeof(*hdr))) || 56 (skb->len < nlh->nlmsg_len)) { 57 printk(KERN_WARNING "%s: discarding partial skb\n", 58 __FUNCTION__); 59 return; 60 } 61 62 rlen = NLMSG_ALIGN(nlh->nlmsg_len); 63 if (rlen > skb->len) 64 rlen = skb->len; 65 66 if (nlh->nlmsg_type != SCSI_TRANSPORT_MSG) { 67 err = -EBADMSG; 68 return; 69 } 70 71 hdr = NLMSG_DATA(nlh); 72 if ((hdr->version != SCSI_NL_VERSION) || 73 (hdr->magic != SCSI_NL_MAGIC)) { 74 err = -EPROTOTYPE; 75 goto next_msg; 76 } 77 78 if (security_netlink_recv(skb, CAP_SYS_ADMIN)) { 79 err = -EPERM; 80 goto next_msg; 81 } 82 83 if (nlh->nlmsg_len < (sizeof(*nlh) + hdr->msglen)) { 84 printk(KERN_WARNING "%s: discarding partial message\n", 85 __FUNCTION__); 86 return; 87 } 88 89 /* 90 * We currently don't support anyone sending us a message 91 */ 92 93 next_msg: 94 if ((err) || (nlh->nlmsg_flags & NLM_F_ACK)) 95 netlink_ack(skb, nlh, err); 96 97 skb_pull(skb, rlen); 98 } 99 } 100 101 102 /** 103 * scsi_nl_rcv_event - Event handler for a netlink socket. 104 * @this: event notifier block 105 * @event: event type 106 * @ptr: event payload 107 * 108 **/ 109 static int 110 scsi_nl_rcv_event(struct notifier_block *this, unsigned long event, void *ptr) 111 { 112 struct netlink_notify *n = ptr; 113 114 if (n->protocol != NETLINK_SCSITRANSPORT) 115 return NOTIFY_DONE; 116 117 /* 118 * Currently, we are not tracking PID's, etc. There is nothing 119 * to handle. 120 */ 121 122 return NOTIFY_DONE; 123 } 124 125 static struct notifier_block scsi_netlink_notifier = { 126 .notifier_call = scsi_nl_rcv_event, 127 }; 128 129 130 /** 131 * scsi_netlink_init - Called by SCSI subsystem to intialize the SCSI transport netlink interface 132 * 133 **/ 134 void 135 scsi_netlink_init(void) 136 { 137 int error; 138 139 error = netlink_register_notifier(&scsi_netlink_notifier); 140 if (error) { 141 printk(KERN_ERR "%s: register of event handler failed - %d\n", 142 __FUNCTION__, error); 143 return; 144 } 145 146 scsi_nl_sock = netlink_kernel_create(&init_net, NETLINK_SCSITRANSPORT, 147 SCSI_NL_GRP_CNT, scsi_nl_rcv_msg, NULL, 148 THIS_MODULE); 149 if (!scsi_nl_sock) { 150 printk(KERN_ERR "%s: register of recieve handler failed\n", 151 __FUNCTION__); 152 netlink_unregister_notifier(&scsi_netlink_notifier); 153 } 154 155 return; 156 } 157 158 159 /** 160 * scsi_netlink_exit - Called by SCSI subsystem to disable the SCSI transport netlink interface 161 * 162 **/ 163 void 164 scsi_netlink_exit(void) 165 { 166 if (scsi_nl_sock) { 167 netlink_kernel_release(scsi_nl_sock); 168 netlink_unregister_notifier(&scsi_netlink_notifier); 169 } 170 171 return; 172 } 173 174 175