]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - usr.sbin/ctld/discovery.c
MFC r267606:
[FreeBSD/stable/10.git] / usr.sbin / ctld / discovery.c
1 /*-
2  * Copyright (c) 2012 The FreeBSD Foundation
3  * All rights reserved.
4  *
5  * This software was developed by Edward Tomasz Napierala under sponsorship
6  * from the FreeBSD Foundation.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * $FreeBSD$
30  */
31
32 #include <assert.h>
33 #include <stdint.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.h"
42 #include "iscsi_proto.h"
43
44 static struct pdu *
45 text_receive(struct connection *conn)
46 {
47         struct pdu *request;
48         struct iscsi_bhs_text_request *bhstr;
49
50         request = pdu_new(conn);
51         pdu_receive(request);
52         if ((request->pdu_bhs->bhs_opcode & ~ISCSI_BHS_OPCODE_IMMEDIATE) !=
53             ISCSI_BHS_OPCODE_TEXT_REQUEST)
54                 log_errx(1, "protocol error: received invalid opcode 0x%x",
55                     request->pdu_bhs->bhs_opcode);
56         bhstr = (struct iscsi_bhs_text_request *)request->pdu_bhs;
57 #if 0
58         if ((bhstr->bhstr_flags & ISCSI_BHSTR_FLAGS_FINAL) == 0)
59                 log_errx(1, "received Text PDU without the \"F\" flag");
60 #endif
61         /*
62          * XXX: Implement the C flag some day.
63          */
64         if ((bhstr->bhstr_flags & BHSTR_FLAGS_CONTINUE) != 0)
65                 log_errx(1, "received Text PDU with unsupported \"C\" flag");
66         if (ntohl(bhstr->bhstr_cmdsn) < conn->conn_cmdsn) {
67                 log_errx(1, "received Text PDU with decreasing CmdSN: "
68                     "was %d, is %d", conn->conn_cmdsn, ntohl(bhstr->bhstr_cmdsn));
69         }
70         if (ntohl(bhstr->bhstr_expstatsn) != conn->conn_statsn) {
71                 log_errx(1, "received Text PDU with wrong StatSN: "
72                     "is %d, should be %d", ntohl(bhstr->bhstr_expstatsn),
73                     conn->conn_statsn);
74         }
75         conn->conn_cmdsn = ntohl(bhstr->bhstr_cmdsn);
76
77         return (request);
78 }
79
80 static struct pdu *
81 text_new_response(struct pdu *request)
82 {
83         struct pdu *response;
84         struct connection *conn;
85         struct iscsi_bhs_text_request *bhstr;
86         struct iscsi_bhs_text_response *bhstr2;
87
88         bhstr = (struct iscsi_bhs_text_request *)request->pdu_bhs;
89         conn = request->pdu_connection;
90
91         response = pdu_new_response(request);
92         bhstr2 = (struct iscsi_bhs_text_response *)response->pdu_bhs;
93         bhstr2->bhstr_opcode = ISCSI_BHS_OPCODE_TEXT_RESPONSE;
94         bhstr2->bhstr_flags = BHSTR_FLAGS_FINAL;
95         bhstr2->bhstr_lun = bhstr->bhstr_lun;
96         bhstr2->bhstr_initiator_task_tag = bhstr->bhstr_initiator_task_tag;
97         bhstr2->bhstr_target_transfer_tag = bhstr->bhstr_target_transfer_tag;
98         bhstr2->bhstr_statsn = htonl(conn->conn_statsn++);
99         bhstr2->bhstr_expcmdsn = htonl(conn->conn_cmdsn);
100         bhstr2->bhstr_maxcmdsn = htonl(conn->conn_cmdsn);
101
102         return (response);
103 }
104
105 static struct pdu *
106 logout_receive(struct connection *conn)
107 {
108         struct pdu *request;
109         struct iscsi_bhs_logout_request *bhslr;
110
111         request = pdu_new(conn);
112         pdu_receive(request);
113         if ((request->pdu_bhs->bhs_opcode & ~ISCSI_BHS_OPCODE_IMMEDIATE) !=
114             ISCSI_BHS_OPCODE_LOGOUT_REQUEST)
115                 log_errx(1, "protocol error: received invalid opcode 0x%x",
116                     request->pdu_bhs->bhs_opcode);
117         bhslr = (struct iscsi_bhs_logout_request *)request->pdu_bhs;
118         if ((bhslr->bhslr_reason & 0x7f) != BHSLR_REASON_CLOSE_SESSION)
119                 log_debugx("received Logout PDU with invalid reason 0x%x; "
120                     "continuing anyway", bhslr->bhslr_reason & 0x7f);
121         if (ntohl(bhslr->bhslr_cmdsn) < conn->conn_cmdsn) {
122                 log_errx(1, "received Logout PDU with decreasing CmdSN: "
123                     "was %d, is %d", conn->conn_cmdsn,
124                     ntohl(bhslr->bhslr_cmdsn));
125         }
126         if (ntohl(bhslr->bhslr_expstatsn) != conn->conn_statsn) {
127                 log_errx(1, "received Logout PDU with wrong StatSN: "
128                     "is %d, should be %d", ntohl(bhslr->bhslr_expstatsn),
129                     conn->conn_statsn);
130         }
131         conn->conn_cmdsn = ntohl(bhslr->bhslr_cmdsn);
132
133         return (request);
134 }
135
136 static struct pdu *
137 logout_new_response(struct pdu *request)
138 {
139         struct pdu *response;
140         struct connection *conn;
141         struct iscsi_bhs_logout_request *bhslr;
142         struct iscsi_bhs_logout_response *bhslr2;
143
144         bhslr = (struct iscsi_bhs_logout_request *)request->pdu_bhs;
145         conn = request->pdu_connection;
146
147         response = pdu_new_response(request);
148         bhslr2 = (struct iscsi_bhs_logout_response *)response->pdu_bhs;
149         bhslr2->bhslr_opcode = ISCSI_BHS_OPCODE_LOGOUT_RESPONSE;
150         bhslr2->bhslr_flags = 0x80;
151         bhslr2->bhslr_response = BHSLR_RESPONSE_CLOSED_SUCCESSFULLY;
152         bhslr2->bhslr_initiator_task_tag = bhslr->bhslr_initiator_task_tag;
153         bhslr2->bhslr_statsn = htonl(conn->conn_statsn++);
154         bhslr2->bhslr_expcmdsn = htonl(conn->conn_cmdsn);
155         bhslr2->bhslr_maxcmdsn = htonl(conn->conn_cmdsn);
156
157         return (response);
158 }
159
160 static void
161 discovery_add_target(struct keys *response_keys, struct target *targ)
162 {
163         struct portal *portal;
164         char *buf;
165         char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
166         struct addrinfo *ai;
167         int ret;
168
169         keys_add(response_keys, "TargetName", targ->t_name);
170         TAILQ_FOREACH(portal, &targ->t_portal_group->pg_portals, p_next) {
171                 ai = portal->p_ai;
172                 ret = getnameinfo(ai->ai_addr, ai->ai_addrlen,
173                     hbuf, sizeof(hbuf), sbuf, sizeof(sbuf),
174                     NI_NUMERICHOST | NI_NUMERICSERV);
175                 if (ret != 0) {
176                         log_warnx("getnameinfo: %s", gai_strerror(ret));
177                         continue;
178                 }
179                 switch (ai->ai_addr->sa_family) {
180                 case AF_INET:
181                         if (strcmp(hbuf, "0.0.0.0") == 0)
182                                 continue;
183                         ret = asprintf(&buf, "%s:%s,%d", hbuf, sbuf,
184                             targ->t_portal_group->pg_tag);
185                         break;
186                 case AF_INET6:
187                         if (strcmp(hbuf, "::") == 0)
188                                 continue;
189                         ret = asprintf(&buf, "[%s]:%s,%d", hbuf, sbuf,
190                             targ->t_portal_group->pg_tag);
191                         break;
192                 default:
193                         continue;
194                 }
195                 if (ret <= 0)
196                     log_err(1, "asprintf");
197                 keys_add(response_keys, "TargetAddress", buf);
198                 free(buf);
199         }
200 }
201
202 void
203 discovery(struct connection *conn)
204 {
205         struct pdu *request, *response;
206         struct keys *request_keys, *response_keys;
207         struct target *targ;
208         const char *send_targets;
209
210         log_debugx("beginning discovery session; waiting for Text PDU");
211         request = text_receive(conn);
212         request_keys = keys_new();
213         keys_load(request_keys, request);
214
215         send_targets = keys_find(request_keys, "SendTargets");
216         if (send_targets == NULL)
217                 log_errx(1, "received Text PDU without SendTargets");
218
219         response = text_new_response(request);
220         response_keys = keys_new();
221
222         if (strcmp(send_targets, "All") == 0) {
223                 TAILQ_FOREACH(targ,
224                     &conn->conn_portal->p_portal_group->pg_conf->conf_targets,
225                     t_next) {
226                         if (targ->t_portal_group !=
227                             conn->conn_portal->p_portal_group) {
228                                 log_debugx("not returning target \"%s\"; "
229                                     "belongs to a different portal group",
230                                     targ->t_name);
231                                 continue;
232                         }
233                         discovery_add_target(response_keys, targ);
234                 }
235         } else {
236                 targ = target_find(conn->conn_portal->p_portal_group->pg_conf,
237                     send_targets);
238                 if (targ == NULL) {
239                         log_debugx("initiator requested information on unknown "
240                             "target \"%s\"; returning nothing", send_targets);
241                 } else
242                         discovery_add_target(response_keys, targ);
243         }
244         keys_save(response_keys, response);
245
246         pdu_send(response);
247         pdu_delete(response);
248         keys_delete(response_keys);
249         pdu_delete(request);
250         keys_delete(request_keys);
251
252         log_debugx("done sending targets; waiting for Logout PDU");
253         request = logout_receive(conn);
254         response = logout_new_response(request);
255
256         pdu_send(response);
257         pdu_delete(response);
258         pdu_delete(request);
259
260         log_debugx("discovery session done");
261 }