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