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 19 struct sock *scsi_nl_sock = NULL; 20 EXPORT_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 **/ 31 static void 32 scsi_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 98 next_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 **/ 111 void 112 scsi_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 **/ 135 void 136 scsi_netlink_exit(void) 137 { 138 if (scsi_nl_sock) { 139 netlink_kernel_release(scsi_nl_sock); 140 } 141 142 return; 143 } 144 145