Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/usr.sbin/ctld/discovery.cc
103478 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
3
*
4
* Copyright (c) 2012 The FreeBSD Foundation
5
*
6
* This software was developed by Edward Tomasz Napierala under sponsorship
7
* from the FreeBSD Foundation.
8
*
9
* Redistribution and use in source and binary forms, with or without
10
* modification, are permitted provided that the following conditions
11
* are met:
12
* 1. Redistributions of source code must retain the above copyright
13
* notice, this list of conditions and the following disclaimer.
14
* 2. Redistributions in binary form must reproduce the above copyright
15
* notice, this list of conditions and the following disclaimer in the
16
* documentation and/or other materials provided with the distribution.
17
*
18
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28
* SUCH DAMAGE.
29
*
30
*/
31
32
#include <sys/cdefs.h>
33
#include <assert.h>
34
#include <stdio.h>
35
#include <stdlib.h>
36
#include <string.h>
37
#include <netinet/in.h>
38
#include <netdb.h>
39
#include <sys/socket.h>
40
41
#include "ctld.hh"
42
#include "iscsi.hh"
43
#include "iscsi_proto.h"
44
45
static struct pdu *
46
logout_receive(struct connection *conn)
47
{
48
struct pdu *request;
49
struct iscsi_bhs_logout_request *bhslr;
50
51
request = pdu_new(conn);
52
pdu_receive(request);
53
if ((request->pdu_bhs->bhs_opcode & ~ISCSI_BHS_OPCODE_IMMEDIATE) !=
54
ISCSI_BHS_OPCODE_LOGOUT_REQUEST)
55
log_errx(1, "protocol error: received invalid opcode 0x%x",
56
request->pdu_bhs->bhs_opcode);
57
bhslr = (struct iscsi_bhs_logout_request *)request->pdu_bhs;
58
if ((bhslr->bhslr_reason & 0x7f) != BHSLR_REASON_CLOSE_SESSION)
59
log_debugx("received Logout PDU with invalid reason 0x%x; "
60
"continuing anyway", bhslr->bhslr_reason & 0x7f);
61
if (ISCSI_SNLT(ntohl(bhslr->bhslr_cmdsn), conn->conn_cmdsn)) {
62
log_errx(1, "received Logout PDU with decreasing CmdSN: "
63
"was %u, is %u", conn->conn_cmdsn,
64
ntohl(bhslr->bhslr_cmdsn));
65
}
66
if (ntohl(bhslr->bhslr_expstatsn) != conn->conn_statsn) {
67
log_errx(1, "received Logout PDU with wrong ExpStatSN: "
68
"is %u, should be %u", ntohl(bhslr->bhslr_expstatsn),
69
conn->conn_statsn);
70
}
71
conn->conn_cmdsn = ntohl(bhslr->bhslr_cmdsn);
72
if ((bhslr->bhslr_opcode & ISCSI_BHS_OPCODE_IMMEDIATE) == 0)
73
conn->conn_cmdsn++;
74
75
return (request);
76
}
77
78
static struct pdu *
79
logout_new_response(struct pdu *request)
80
{
81
struct pdu *response;
82
struct connection *conn;
83
struct iscsi_bhs_logout_request *bhslr;
84
struct iscsi_bhs_logout_response *bhslr2;
85
86
bhslr = (struct iscsi_bhs_logout_request *)request->pdu_bhs;
87
conn = request->pdu_connection;
88
89
response = pdu_new_response(request);
90
bhslr2 = (struct iscsi_bhs_logout_response *)response->pdu_bhs;
91
bhslr2->bhslr_opcode = ISCSI_BHS_OPCODE_LOGOUT_RESPONSE;
92
bhslr2->bhslr_flags = 0x80;
93
bhslr2->bhslr_response = BHSLR_RESPONSE_CLOSED_SUCCESSFULLY;
94
bhslr2->bhslr_initiator_task_tag = bhslr->bhslr_initiator_task_tag;
95
bhslr2->bhslr_statsn = htonl(conn->conn_statsn++);
96
bhslr2->bhslr_expcmdsn = htonl(conn->conn_cmdsn);
97
bhslr2->bhslr_maxcmdsn = htonl(conn->conn_cmdsn);
98
99
return (response);
100
}
101
102
static void
103
discovery_add_target(struct keys *response_keys, const struct target *targ)
104
{
105
char *buf;
106
char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
107
const struct addrinfo *ai;
108
int ret;
109
110
keys_add(response_keys, "TargetName", targ->name());
111
for (const port *port : targ->ports()) {
112
const struct portal_group *pg = port->portal_group();
113
if (pg == nullptr)
114
continue;
115
for (const portal_up &portal : pg->portals()) {
116
if (portal->protocol() != portal_protocol::ISCSI &&
117
portal->protocol() != portal_protocol::ISER)
118
continue;
119
ai = portal->ai();
120
ret = getnameinfo(ai->ai_addr, ai->ai_addrlen,
121
hbuf, sizeof(hbuf), sbuf, sizeof(sbuf),
122
NI_NUMERICHOST | NI_NUMERICSERV);
123
if (ret != 0) {
124
log_warnx("getnameinfo: %s", gai_strerror(ret));
125
continue;
126
}
127
switch (ai->ai_addr->sa_family) {
128
case AF_INET:
129
if (strcmp(hbuf, "0.0.0.0") == 0)
130
continue;
131
ret = asprintf(&buf, "%s:%s,%d", hbuf, sbuf,
132
pg->tag());
133
break;
134
case AF_INET6:
135
if (strcmp(hbuf, "::") == 0)
136
continue;
137
ret = asprintf(&buf, "[%s]:%s,%d", hbuf, sbuf,
138
pg->tag());
139
break;
140
default:
141
continue;
142
}
143
if (ret <= 0)
144
log_err(1, "asprintf");
145
keys_add(response_keys, "TargetAddress", buf);
146
free(buf);
147
}
148
}
149
}
150
151
bool
152
iscsi_connection::discovery_target_filtered_out(const struct port *port) const
153
{
154
const struct auth_group *ag;
155
const struct portal_group *pg;
156
const struct target *targ;
157
const struct auth *auth;
158
int error;
159
160
targ = port->target();
161
ag = port->auth_group();
162
if (ag == nullptr)
163
ag = targ->auth_group();
164
pg = conn_portal->portal_group();
165
166
assert(pg->discovery_filter() != discovery_filter::UNKNOWN);
167
168
if (pg->discovery_filter() >= discovery_filter::PORTAL &&
169
!ag->initiator_permitted(conn_initiator_sa)) {
170
log_debugx("initiator does not match initiator portals "
171
"allowed for target \"%s\"; skipping", targ->name());
172
return (true);
173
}
174
175
if (pg->discovery_filter() >= discovery_filter::PORTAL_NAME &&
176
!ag->initiator_permitted(conn_initiator_name)) {
177
log_debugx("initiator does not match initiator names "
178
"allowed for target \"%s\"; skipping", targ->name());
179
return (true);
180
}
181
182
if (pg->discovery_filter() >= discovery_filter::PORTAL_NAME_AUTH &&
183
ag->type() != auth_type::NO_AUTHENTICATION) {
184
if (conn_chap == nullptr) {
185
assert(pg->discovery_auth_group()->type() ==
186
auth_type::NO_AUTHENTICATION);
187
188
log_debugx("initiator didn't authenticate, but target "
189
"\"%s\" requires CHAP; skipping", targ->name());
190
return (true);
191
}
192
193
assert(!conn_user.empty());
194
auth = ag->find_auth(conn_user);
195
if (auth == NULL) {
196
log_debugx("CHAP user \"%s\" doesn't match target "
197
"\"%s\"; skipping", conn_user.c_str(),
198
targ->name());
199
return (true);
200
}
201
202
error = chap_authenticate(conn_chap, auth->secret());
203
if (error != 0) {
204
log_debugx("password for CHAP user \"%s\" doesn't "
205
"match target \"%s\"; skipping",
206
conn_user.c_str(), targ->name());
207
return (true);
208
}
209
}
210
211
return (false);
212
}
213
214
void
215
iscsi_connection::discovery()
216
{
217
struct pdu *request, *response;
218
struct keys *request_keys, *response_keys;
219
const struct port *port;
220
const struct portal_group *pg;
221
const char *send_targets;
222
223
pg = conn_portal->portal_group();
224
225
log_debugx("beginning discovery session; waiting for TextRequest PDU");
226
request_keys = text_read_request(&conn, &request);
227
228
send_targets = keys_find(request_keys, "SendTargets");
229
if (send_targets == NULL)
230
log_errx(1, "received TextRequest PDU without SendTargets");
231
232
response_keys = keys_new();
233
234
if (strcmp(send_targets, "All") == 0) {
235
for (const auto &kv : pg->ports()) {
236
port = kv.second;
237
if (discovery_target_filtered_out(port)) {
238
/* Ignore this target. */
239
continue;
240
}
241
discovery_add_target(response_keys, port->target());
242
}
243
} else {
244
port = pg->find_port(send_targets);
245
if (port == NULL) {
246
log_debugx("initiator requested information on unknown "
247
"target \"%s\"; returning nothing", send_targets);
248
} else {
249
if (discovery_target_filtered_out(port)) {
250
/* Ignore this target. */
251
} else {
252
discovery_add_target(response_keys,
253
port->target());
254
}
255
}
256
}
257
258
text_send_response(request, response_keys);
259
keys_delete(response_keys);
260
pdu_delete(request);
261
keys_delete(request_keys);
262
263
log_debugx("done sending targets; waiting for Logout PDU");
264
request = logout_receive(&conn);
265
response = logout_new_response(request);
266
267
pdu_send(response);
268
pdu_delete(response);
269
pdu_delete(request);
270
271
log_debugx("discovery session done");
272
}
273
274