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