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(in_skb: skb, nlh, err, NULL); |
101 | |
102 | skb_pull(skb, len: 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(net: &init_net, NETLINK_SCSITRANSPORT, |
120 | cfg: &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(sk: scsi_nl_sock); |
140 | } |
141 | |
142 | return; |
143 | } |
144 | |
145 | |