2.6.27 kernel compat patch
[open-iscsi.git] / usr / discovery.c
blob381f825edeacd6d49c5b8f54d83bd264f9d89058
1 /*
2 * iSCSI Discovery
4 * Copyright (C) 2002 Cisco Systems, Inc.
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published
8 * by the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
16 * See the file COPYING included with this distribution for more details.
19 #include <stdio.h>
20 #include <unistd.h>
21 #include <fcntl.h>
22 #include <signal.h>
23 #include <errno.h>
24 #include <netdb.h>
25 #include <stdint.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <sys/poll.h>
29 #include <sys/time.h>
30 #include <sys/param.h>
31 #include <sys/socket.h>
32 #include <netinet/in.h>
33 #include <arpa/inet.h>
35 #include "strings.h"
36 #include "types.h"
37 #include "iscsi_proto.h"
38 #include "initiator.h"
39 #include "log.h"
40 #include "idbm.h"
41 #include "iscsi_settings.h"
42 #include "sysdeps.h"
43 #include "fw_context.h"
44 #include "iscsid_req.h"
45 #include "iscsi_util.h"
46 /* libisns includes */
47 #include "isns.h"
48 #include "paths.h"
49 #include "message.h"
51 #ifdef SLP_ENABLE
52 #include "iscsi-slp-discovery.h"
53 #endif
55 #define DISCOVERY_NEED_RECONNECT 0xdead0001
57 static int rediscover = 0;
59 static char initiator_name[TARGET_NAME_MAXLEN + 1];
60 static char initiator_alias[TARGET_NAME_MAXLEN + 1];
62 static int request_initiator_name(void)
64 int rc;
65 iscsiadm_req_t req;
66 iscsiadm_rsp_t rsp;
68 memset(initiator_name, 0, sizeof(initiator_name));
69 initiator_name[0] = '\0';
70 memset(initiator_alias, 0, sizeof(initiator_alias));
71 initiator_alias[0] = '\0';
73 memset(&req, 0, sizeof(req));
74 req.command = MGMT_IPC_CONFIG_INAME;
76 rc = iscsid_exec_req(&req, &rsp, 1);
77 if (rc)
78 return EIO;
80 if (rsp.u.config.var[0] != '\0')
81 strcpy(initiator_name, rsp.u.config.var);
83 memset(&req, 0, sizeof(req));
84 req.command = MGMT_IPC_CONFIG_IALIAS;
86 rc = iscsid_exec_req(&req, &rsp, 0);
87 if (rc)
88 /* alias is optional so return ok */
89 return 0;
91 if (rsp.u.config.var[0] != '\0')
92 strcpy(initiator_alias, rsp.u.config.var);
93 return 0;
96 void discovery_isns_free_servername(void)
98 if (isns_config.ic_server_name)
99 free(isns_config.ic_server_name);
100 isns_config.ic_server_name = NULL;
103 int discovery_isns_set_servername(char *address, int port)
105 char *server;
106 int len;
108 if (port > USHRT_MAX) {
109 log_error("Invalid port %d\n", port);
110 return EINVAL;
113 /* 5 for port and 1 for colon and 1 for null */
114 len = strlen(address) + 7;
115 server = calloc(1, len);
116 if (!server)
117 return ENOMEM;
119 snprintf(server, len, "%s:%d", address, port);
120 isns_assign_string(&isns_config.ic_server_name, server);
121 free(server);
122 return 0;
125 int discovery_isns_query(struct discovery_rec *drec, const char *iname,
126 const char *targetname, struct list_head *rec_list)
128 isns_attr_list_t key_attrs = ISNS_ATTR_LIST_INIT;
129 isns_object_list_t objects = ISNS_OBJECT_LIST_INIT;
130 isns_source_t *source;
131 isns_simple_t *qry;
132 isns_client_t *clnt;
133 uint32_t status;
134 int rc, i;
136 isns_config.ic_security = 0;
137 source = isns_source_create_iscsi(iname);
138 if (!source)
139 return ENOMEM;
141 clnt = isns_create_client(NULL, iname);
142 if (!clnt) {
143 rc = ENOMEM;
144 goto free_src;
147 /* do not retry forever */
148 isns_socket_set_disconnect_fatal(clnt->ic_socket);
150 if (targetname)
151 isns_attr_list_append_string(&key_attrs, ISNS_TAG_ISCSI_NAME,
152 targetname);
153 else
154 /* Query for all visible targets */
155 isns_attr_list_append_uint32(&key_attrs,
156 ISNS_TAG_ISCSI_NODE_TYPE,
157 ISNS_ISCSI_TARGET_MASK);
159 qry = isns_create_query2(clnt, &key_attrs, source);
160 if (!qry) {
161 rc = ENOMEM;
162 goto free_clnt;
165 isns_query_request_attr_tag(qry, ISNS_TAG_ISCSI_NAME);
166 isns_query_request_attr_tag(qry, ISNS_TAG_ISCSI_NODE_TYPE);
167 isns_query_request_attr_tag(qry, ISNS_TAG_PORTAL_IP_ADDRESS);
168 isns_query_request_attr_tag(qry, ISNS_TAG_PORTAL_TCP_UDP_PORT);
169 isns_query_request_attr_tag(qry, ISNS_TAG_PG_ISCSI_NAME);
170 isns_query_request_attr_tag(qry, ISNS_TAG_PG_PORTAL_IP_ADDR);
171 isns_query_request_attr_tag(qry, ISNS_TAG_PG_PORTAL_TCP_UDP_PORT);
172 isns_query_request_attr_tag(qry, ISNS_TAG_PG_TAG);
174 status = isns_client_call(clnt, &qry);
175 switch (status) {
176 case ISNS_SUCCESS:
177 break;
178 case ISNS_SOURCE_UNKNOWN:
179 /* server requires that we are registered but we are not */
180 rc = ENOENT;
181 goto free_query;
182 default:
183 log_error("iSNS discovery failed: %s", isns_strerror(status));
184 rc = EIO;
185 goto free_query;
188 status = isns_query_response_get_objects(qry, &objects);
189 if (status) {
190 log_error("Unable to extract object list from query "
191 "response: %s\n", isns_strerror(status));
192 rc = EIO;
193 goto free_query;
196 for (i = 0; i < objects.iol_count; ++i) {
197 isns_object_t *obj = objects.iol_data[i];
198 const char *pg_tgt = NULL;
199 struct in6_addr in_addr;
200 uint32_t pg_port = ISCSI_LISTEN_PORT;
201 uint32_t pg_tag = PORTAL_GROUP_TAG_UNKNOWN;
202 char pg_addr[INET6_ADDRSTRLEN + 1];
203 struct node_rec *rec;
205 if (!isns_object_is_pg(obj))
206 continue;
208 if (!isns_object_get_string(obj, ISNS_TAG_PG_ISCSI_NAME,
209 &pg_tgt)) {
210 log_debug(1, "Missing target name");
211 continue;
214 if (!isns_object_get_ipaddr(obj, ISNS_TAG_PG_PORTAL_IP_ADDR,
215 &in_addr)) {
216 log_debug(1, "Missing addr");
217 continue;
219 if (IN6_IS_ADDR_V4MAPPED(&in_addr) ||
220 IN6_IS_ADDR_V4COMPAT(&in_addr)) {
221 struct in_addr ipv4;
223 ipv4.s_addr = in_addr.s6_addr32[3];
224 inet_ntop(AF_INET, &ipv4, pg_addr, sizeof(pg_addr));
225 } else
226 inet_ntop(AF_INET6, &in_addr, pg_addr, sizeof(pg_addr));
228 if (!isns_object_get_uint32(obj,
229 ISNS_TAG_PG_PORTAL_TCP_UDP_PORT,
230 &pg_port)) {
231 log_debug(1, "Missing port");
232 continue;
235 if (!isns_object_get_uint32(obj, ISNS_TAG_PG_TAG, &pg_tag)) {
236 log_debug(1, "Missing tag");
237 continue;
240 rec = calloc(1, sizeof(*rec));
241 if (!rec) {
242 rc = ENOMEM;
243 goto destroy_list;
246 idbm_node_setup_from_conf(rec);
247 if (drec) {
248 rec->disc_type = drec->type;
249 rec->disc_port = drec->port;
250 strcpy(rec->disc_address, drec->address);
253 strlcpy(rec->name, pg_tgt, TARGET_NAME_MAXLEN);
254 rec->tpgt = pg_tag;
255 rec->conn[0].port = pg_port;
256 strlcpy(rec->conn[0].address, pg_addr, NI_MAXHOST);
257 list_add_tail(&rec->list, rec_list);
259 rc = 0;
261 isns_flush_events();
262 destroy_list:
263 isns_object_list_destroy(&objects);
264 free_query:
265 isns_simple_free(qry);
266 free_clnt:
267 isns_client_destroy(clnt);
268 free_src:
269 isns_source_release(source);
270 return rc;
274 * discovery_isns_reg_node - register/deregister node
275 * @iname: initiator name
276 * @reg: bool indicating if we are supposed to register or deregister node.
278 * We do a very simple registration just so we can query.
280 static int discovery_isns_reg_node(const char *iname, int op_reg)
282 isns_simple_t *reg;
283 isns_client_t *clnt;
284 isns_source_t *source;
285 int rc = 0, status;
287 isns_config.ic_security = 0;
289 log_debug(1, "trying to %s %s with iSNS server.",
290 op_reg ? "register" : "deregister", iname);
292 source = isns_source_create_iscsi(iname);
293 if (!source)
294 return ENOMEM;
296 clnt = isns_create_client(NULL, iname);
297 if (!clnt) {
298 rc = ENOMEM;
299 goto free_src;
302 reg = isns_simple_create(op_reg ? ISNS_DEVICE_ATTRIBUTE_REGISTER :
303 ISNS_DEVICE_DEREGISTER,
304 source, NULL);
305 if (!reg) {
306 rc = ENOMEM;
307 goto free_clnt;
310 isns_attr_list_append_string(&reg->is_operating_attrs,
311 ISNS_TAG_ISCSI_NAME, iname);
312 if (op_reg)
313 isns_attr_list_append_uint32(&reg->is_operating_attrs,
314 ISNS_TAG_ISCSI_NODE_TYPE,
315 ISNS_ISCSI_INITIATOR_MASK);
316 status = isns_client_call(clnt, &reg);
317 if (status != ISNS_SUCCESS) {
318 log_error("Could not %s %s with iSNS server: %s.",
319 reg ? "register" : "deregister", iname,
320 isns_strerror(status));
321 rc = EIO;
322 } else
323 log_debug(1, "%s %s with iSNS server successful.",
324 op_reg ? "register" : "deregister", iname);
325 free_clnt:
326 isns_client_destroy(clnt);
327 free_src:
328 isns_source_release(source);
329 return rc;
332 int discovery_isns(void *data, struct iface_rec *iface,
333 struct list_head *rec_list)
335 struct discovery_rec *drec = data;
336 char *iname;
337 int rc, registered = 0;
339 if (iface && strlen(iface->iname))
340 iname = iface->iname;
341 else {
342 if (request_initiator_name() || initiator_name[0] == '\0') {
343 log_error("Cannot perform discovery. Initiatorname "
344 "required.");
345 return EINVAL;
347 iname = initiator_name;
350 rc = discovery_isns_set_servername(drec->address, drec->port);
351 if (rc)
352 return rc;
353 retry:
354 rc = discovery_isns_query(drec, iname, NULL, rec_list);
355 if (!registered && rc == ENOENT) {
356 rc = discovery_isns_reg_node(iname, 1);
357 if (!rc) {
358 registered = 1;
359 goto retry;
363 if (registered)
364 discovery_isns_reg_node(iname, 0);
366 discovery_isns_free_servername();
367 return rc;
370 int discovery_fw(void *data, struct iface_rec *iface,
371 struct list_head *rec_list)
373 struct discovery_rec *drec = data;
374 struct boot_context *bcontext;
375 struct list_head targets;
376 struct node_rec *rec;
377 int rc;
379 INIT_LIST_HEAD(&targets);
380 rc = fw_get_targets(&targets);
381 if (rc) {
382 log_error("Could not get list of targets from firmware. "
383 "(err %d)\n", rc);
384 return rc;
386 if (list_empty(&targets))
387 return 0;
389 * TODO: Do we want to match the iface MAC/netdev with what is in
390 * the firmware or could the user want to bind based on what is
391 * in passed in or in the default ifaces?
394 list_for_each_entry(bcontext, &targets, list) {
395 rec = idbm_create_rec_from_boot_context(bcontext);
396 if (!rec) {
397 log_error("Could not convert firmware info to "
398 "node record.\n");
399 rc = ENOMEM;
400 goto free_targets;
402 rec->disc_type = drec->type;
404 list_add_tail(&rec->list, rec_list);
407 free_targets:
408 fw_free_targets(&targets);
409 return rc;
412 int discovery_offload_sendtargets(int host_no, int do_login,
413 discovery_rec_t *drec)
415 struct sockaddr_storage ss;
416 char default_port[NI_MAXSERV];
417 iscsiadm_req_t req;
418 iscsiadm_rsp_t rsp;
419 int rc;
421 log_debug(4, "offload st though host %d to %s", host_no,
422 drec->address);
424 memset(&req, 0, sizeof(req));
425 req.command = MGMT_IPC_SEND_TARGETS;
426 req.u.st.host_no = host_no;
427 req.u.st.do_login = do_login;
429 /* resolve the DiscoveryAddress to an IP address */
430 sprintf(default_port, "%d", drec->port);
431 if (resolve_address(drec->address, default_port, &ss)) {
432 log_error("Cannot resolve host name %s.", drec->address);
433 return EIO;
435 req.u.st.ss = ss;
438 * We only know how ask qla4xxx to do discovery and login
439 * to what it finds. We are not able to get what it finds or
440 * is able to log into so we just send the command and proceed.
442 * There is a way to just use the hw to send a sendtargets command
443 * and get back the results. We should do this since it would
444 * allows us to then process the results like software iscsi.
446 rc = iscsid_exec_req(&req, &rsp, 1);
447 if (rc) {
448 log_error("Could not offload sendtargets to %s.\n",
449 drec->address);
450 iscsid_handle_error(rc);
451 return EIO;
454 return 0;
457 static int
458 iscsi_make_text_pdu(iscsi_session_t *session, struct iscsi_hdr *hdr,
459 char *data, int max_data_length)
461 struct iscsi_text *text_pdu = (struct iscsi_text *)hdr;
463 /* initialize the PDU header */
464 memset(text_pdu, 0, sizeof (*text_pdu));
466 text_pdu->opcode = ISCSI_OP_TEXT;
467 text_pdu->itt = htonl(session->itt);
468 text_pdu->ttt = ISCSI_RESERVED_TAG;
469 text_pdu->cmdsn = htonl(session->cmdsn++);
470 text_pdu->exp_statsn = htonl(session->conn[0].exp_statsn);
472 return 1;
475 static int
476 request_targets(iscsi_session_t *session)
478 char data[64];
479 struct iscsi_text text;
480 struct iscsi_hdr *hdr = (struct iscsi_hdr *) &text;
482 memset(&text, 0, sizeof (text));
483 memset(data, 0, sizeof (data));
485 /* make a text PDU with SendTargets=All */
486 if (!iscsi_make_text_pdu(session, hdr, data, sizeof (data))) {
487 log_error("failed to make a SendTargets PDU");
488 return 0;
491 if (!iscsi_add_text(hdr, data, sizeof (data), "SendTargets", "All")) {
492 log_error("failed to add SendTargets text key");
493 exit(1);
496 text.ttt = ISCSI_RESERVED_TAG;
497 text.flags = ISCSI_FLAG_CMD_FINAL;
499 if (++session->itt == ISCSI_RESERVED_TAG)
500 session->itt = 1;
502 if (!iscsi_io_send_pdu(&session->conn[0], hdr, ISCSI_DIGEST_NONE, data,
503 ISCSI_DIGEST_NONE, session->conn[0].active_timeout)) {
504 log_error("failed to send SendTargets PDU");
505 return 0;
508 return 1;
511 static int
512 iterate_targets(iscsi_session_t *session, uint32_t ttt)
514 char data[64];
515 struct iscsi_text text;
516 struct iscsi_hdr *pdu = (struct iscsi_hdr *) &text;
518 memset(&text, 0, sizeof (text));
519 memset(data, 0, sizeof (data));
521 /* make an empty text PDU */
522 if (!iscsi_make_text_pdu(session, pdu, data, sizeof (data))) {
523 log_error("failed to make an empty text PDU");
524 return 0;
527 text.ttt = ttt;
528 text.flags = ISCSI_FLAG_CMD_FINAL;
530 if (++session->itt == ISCSI_RESERVED_TAG)
531 session->itt = 1;
533 if (!iscsi_io_send_pdu(&session->conn[0], pdu, ISCSI_DIGEST_NONE, data,
534 ISCSI_DIGEST_NONE, session->conn[0].active_timeout)) {
535 log_error("failed to send empty text PDU");
536 return 0;
539 return 1;
542 static int add_portal(struct list_head *rec_list, discovery_rec_t *drec,
543 char *targetname, char *address, char *port, char *tag)
545 struct sockaddr_storage ss;
546 char host[NI_MAXHOST];
547 struct node_rec *rec;
549 /* resolve the address, in case it was a DNS name */
550 if (resolve_address(address, port, &ss)) {
551 log_error("cannot resolve %s", address);
552 return 0;
555 /* convert the resolved name to text */
556 getnameinfo((struct sockaddr *) &ss, sizeof(ss),
557 host, sizeof(host), NULL, 0, NI_NUMERICHOST);
559 rec = calloc(1, sizeof(*rec));
560 if (!rec)
561 return 0;
563 idbm_node_setup_from_conf(rec);
564 rec->disc_type = drec->type;
565 rec->disc_port = drec->port;
566 strcpy(rec->disc_address, drec->address);
568 strlcpy(rec->name, targetname, TARGET_NAME_MAXLEN);
569 if (tag && *tag)
570 rec->tpgt = atoi(tag);
571 else
572 rec->tpgt = PORTAL_GROUP_TAG_UNKNOWN;
573 if (port && *port)
574 rec->conn[0].port = atoi(port);
575 else
576 rec->conn[0].port = ISCSI_LISTEN_PORT;
577 strlcpy(rec->conn[0].address, address, NI_MAXHOST);
579 list_add_tail(&rec->list, rec_list);
580 return 1;
583 static int
584 add_target_record(char *name, char *end, discovery_rec_t *drec,
585 struct list_head *rec_list, char *default_port)
587 char *text = NULL;
588 char *nul = name;
589 size_t length;
591 /* address = IPv4
592 * address = [IPv6]
593 * address = DNSname
594 * address = IPv4:port
595 * address = [IPv6]:port
596 * address = DNSname:port
597 * address = IPv4,tag
598 * address = [IPv6],tag
599 * address = DNSname,tag
600 * address = IPv4:port,tag
601 * address = [IPv6]:port,tag
602 * address = DNSname:port,tag
605 log_debug(7, "adding target record %p, end %p", name, end);
607 /* find the end of the name */
608 while ((nul < end) && (*nul != '\0'))
609 nul++;
611 length = nul - name;
612 if (length > TARGET_NAME_MAXLEN) {
613 log_error("TargetName %s too long, ignoring", name);
614 return 0;
616 text = name + length;
618 /* skip NULs after the name */
619 while ((text < end) && (*text == '\0'))
620 text++;
622 /* if no address is provided, use the default */
623 if (text >= end) {
624 if (drec->address == NULL) {
625 log_error("no default address known for target %s",
626 name);
627 return 0;
628 } else if (!add_portal(rec_list, drec, name, drec->address,
629 default_port, NULL)) {
630 log_error("failed to add default portal, ignoring "
631 "target %s", name);
632 return 0;
634 /* finished adding the default */
635 return 1;
638 /* process TargetAddresses */
639 while (text < end) {
640 char *next = text + strlen(text) + 1;
642 log_debug(7, "text %p, next %p, end %p, %s", text, next, end,
643 text);
645 if (strncmp(text, "TargetAddress=", 14) == 0) {
646 char *port = NULL;
647 char *tag = NULL;
648 char *address = text + 14;
649 char *temp;
651 if ((tag = strrchr(text, ','))) {
652 *tag = '\0';
653 tag++;
655 if ((port = strrchr(text, ':'))) {
656 *port = '\0';
657 port++;
660 if (*address == '[') {
661 address++;
662 if ((temp = strrchr(text, ']')))
663 *temp = '\0';
666 if (!add_portal(rec_list, drec, name, address, port,
667 tag)) {
668 log_error("failed to add default portal, "
669 "ignoring target %s", name);
670 return 0;
672 } else
673 log_error("unexpected SendTargets data: %s",
674 text);
675 text = next;
678 return 1;
681 static int
682 process_sendtargets_response(struct str_buffer *sendtargets,
683 int final, discovery_rec_t *drec,
684 struct list_head *rec_list,
685 char *default_port)
687 char *start = str_buffer_data(sendtargets);
688 char *text = start;
689 char *end = text + str_data_length(sendtargets);
690 char *nul = end - 1;
691 char *record = NULL;
692 int num_targets = 0;
694 if (start == end) {
695 /* no SendTargets data */
696 goto done;
699 /* scan backwards to find the last NUL in the data, to ensure we
700 * don't walk off the end. Since key=value pairs can span PDU
701 * boundaries, we're not guaranteed that the end of the data has a
702 * NUL.
704 while ((nul > start) && *nul)
705 nul--;
707 if (nul == start) {
708 /* couldn't find anything we can process now,
709 * it's one big partial string
711 goto done;
714 /* find the boundaries between target records (TargetName or final PDU)
716 for (;;) {
717 /* skip NULs */
718 while ((text < nul) && (*text == '\0'))
719 text++;
721 if (text == nul)
722 break;
724 log_debug(7,
725 "processing sendtargets record %p, text %p, line %s",
726 record, text, text);
728 /* look for the start of a new target record */
729 if (strncmp(text, "TargetName=", 11) == 0) {
730 if (record) {
731 /* send the last record, which we just found
732 * the end of. don't bother passing the
733 * "TargetName=" prefix.
735 if (!add_target_record(record + 11, text,
736 drec, rec_list,
737 default_port)) {
738 log_error(
739 "failed to add target record");
740 str_truncate_buffer(sendtargets, 0);
741 goto done;
743 num_targets++;
745 record = text;
748 /* everything up til the next NUL must be part of the
749 * current target record
751 while ((text < nul) && (*text != '\0'))
752 text++;
755 if (record) {
756 if (final) {
757 /* if this is the last PDU of the text sequence,
758 * it also ends a target record
760 log_debug(7,
761 "processing final sendtargets record %p, "
762 "line %s",
763 record, record);
764 if (add_target_record (record + 11, text,
765 drec, rec_list, default_port)) {
766 num_targets++;
767 record = NULL;
768 str_truncate_buffer(sendtargets, 0);
769 } else {
770 log_error("failed to add target record");
771 str_truncate_buffer(sendtargets, 0);
772 goto done;
774 } else {
775 /* remove the parts of the sendtargets buffer we've
776 * processed, and move the parts we haven't to the
777 * beginning of the buffer.
779 log_debug(7,
780 "processed %d bytes of sendtargets data, "
781 "%d remaining",
782 (int)(record - str_buffer_data(sendtargets)),
783 (int)(str_buffer_data(sendtargets) +
784 str_data_length(sendtargets) - record));
785 str_remove_initial(sendtargets,
786 record - str_buffer_data(sendtargets));
790 done:
792 return 1;
795 static void
796 clear_timer(struct timeval *timer)
798 memset(timer, 0, sizeof (*timer));
801 /* set timer to now + seconds */
802 static void
803 set_timer(struct timeval *timer, int seconds)
805 if (timer) {
806 memset(timer, 0, sizeof (*timer));
807 gettimeofday(timer, NULL);
809 timer->tv_sec += seconds;
813 static int
814 timer_expired(struct timeval *timer)
816 struct timeval now;
818 /* no timer, can't have expired */
819 if ((timer == NULL) || ((timer->tv_sec == 0) && (timer->tv_usec == 0)))
820 return 0;
822 memset(&now, 0, sizeof (now));
823 gettimeofday(&now, NULL);
825 if (now.tv_sec > timer->tv_sec)
826 return 1;
827 if ((now.tv_sec == timer->tv_sec) && (now.tv_usec >= timer->tv_usec))
828 return 1;
829 return 0;
832 static int
833 msecs_until(struct timeval *timer)
835 struct timeval now;
836 int msecs;
837 long partial;
839 /* no timer, can't have expired, infinite time til it expires */
840 if ((timer == NULL) || ((timer->tv_sec == 0) && (timer->tv_usec == 0)))
841 return -1;
843 memset(&now, 0, sizeof (now));
844 gettimeofday(&now, NULL);
846 /* already expired? */
847 if (now.tv_sec > timer->tv_sec)
848 return 0;
849 if ((now.tv_sec == timer->tv_sec) && (now.tv_usec >= timer->tv_usec))
850 return 0;
852 /* not expired yet, do the math */
853 partial = timer->tv_usec - now.tv_usec;
854 if (partial < 0) {
855 partial += 1000 * 1000;
856 msecs = (partial + 500) / 1000;
857 msecs += (timer->tv_sec - now.tv_sec - 1) * 1000;
858 } else {
859 msecs = (partial + 500) / 1000;
860 msecs += (timer->tv_sec - now.tv_sec) * 1000;
863 return msecs;
866 static iscsi_session_t *
867 init_new_session(struct iscsi_sendtargets_config *config,
868 struct iface_rec *iface)
870 iscsi_session_t *session;
872 session = calloc(1, sizeof (*session));
873 if (session == NULL)
874 goto done;
876 /* initialize the session's leading connection */
877 session->conn[0].socket_fd = -1;
878 session->conn[0].login_timeout = config->conn_timeo.login_timeout;
879 session->conn[0].auth_timeout = config->conn_timeo.auth_timeout;
880 session->conn[0].active_timeout = config->conn_timeo.active_timeout;
881 session->conn[0].hdrdgst_en = ISCSI_DIGEST_NONE;
882 session->conn[0].datadgst_en = ISCSI_DIGEST_NONE;
884 session->conn[0].max_recv_dlength =
885 config->iscsi.MaxRecvDataSegmentLength;
886 if (session->conn[0].max_recv_dlength < ISCSI_MIN_MAX_RECV_SEG_LEN ||
887 session->conn[0].max_recv_dlength > ISCSI_MAX_MAX_RECV_SEG_LEN) {
888 log_error("Invalid iscsi.MaxRecvDataSegmentLength. Must be "
889 "within %u and %u. Setting to %u.",
890 ISCSI_MIN_MAX_RECV_SEG_LEN,
891 ISCSI_MAX_MAX_RECV_SEG_LEN,
892 DEF_INI_DISC_MAX_RECV_SEG_LEN);
893 session->conn[0].max_recv_dlength =
894 DEF_INI_DISC_MAX_RECV_SEG_LEN;
896 session->conn[0].max_xmit_dlength = ISCSI_DEF_MAX_RECV_SEG_LEN;
898 session->reopen_cnt = config->reopen_max + 1;
900 /* OUI and uniqifying number */
901 session->isid[0] = DRIVER_ISID_0;
902 session->isid[1] = DRIVER_ISID_1;
903 session->isid[2] = DRIVER_ISID_2;
904 session->isid[3] = 0;
905 session->isid[4] = 0;
906 session->isid[5] = 0;
908 request_initiator_name();
910 if (iface && strlen(iface->iname)) {
911 strcpy(initiator_name, iface->iname);
912 /* MNC TODO add iface alias */
913 } else {
914 if (initiator_name[0] == '\0') {
915 log_error("Cannot perform discovery. Initiatorname "
916 "required.");
917 free(session);
918 return NULL;
922 session->initiator_name = initiator_name;
923 session->initiator_alias = initiator_alias;
924 session->portal_group_tag = PORTAL_GROUP_TAG_UNKNOWN;
925 session->type = ISCSI_SESSION_TYPE_DISCOVERY;
926 done:
927 return session;
931 static int
932 setup_authentication(iscsi_session_t *session,
933 discovery_rec_t *drec,
934 struct iscsi_sendtargets_config *config)
936 int rc;
938 rc = 1;
940 /* if we have any incoming credentials, we insist on authenticating
941 * the target or not logging in at all
943 if (config->auth.username_in[0]
944 || config->auth.password_in_length) {
945 session->bidirectional_auth = 1;
947 /* sanity check the config */
948 if (config->auth.password_length == 0) {
949 log_error(
950 "discovery process to %s:%d has incoming "
951 "authentication credentials but has no outgoing "
952 "credentials configured",
953 drec->address, drec->port);
954 log_error(
955 "discovery process to %s:%d exiting, bad "
956 "configuration",
957 drec->address, drec->port);
958 rc = 0;
959 goto done;
961 } else {
962 /* no or 1-way authentication */
963 session->bidirectional_auth = 0;
966 /* copy in whatever credentials we have */
967 strlcpy(session->username, config->auth.username,
968 sizeof (session->username));
969 session->username[sizeof (session->username) - 1] = '\0';
970 if ((session->password_length = config->auth.password_length))
971 memcpy(session->password, config->auth.password,
972 session->password_length);
974 strlcpy(session->username_in, config->auth.username_in,
975 sizeof (session->username_in));
976 session->username_in[sizeof (session->username_in) - 1] = '\0';
977 if ((session->password_in_length =
978 config->auth.password_in_length))
979 memcpy(session->password_in, config->auth.password_in,
980 session->password_in_length);
982 if (session->password_length || session->password_in_length) {
983 /* setup the auth buffers */
984 session->auth_buffers[0].address = &session->auth_client_block;
985 session->auth_buffers[0].length =
986 sizeof (session->auth_client_block);
987 session->auth_buffers[1].address =
988 &session->auth_recv_string_block;
989 session->auth_buffers[1].length =
990 sizeof (session->auth_recv_string_block);
992 session->auth_buffers[2].address =
993 &session->auth_send_string_block;
994 session->auth_buffers[2].length =
995 sizeof (session->auth_send_string_block);
997 session->auth_buffers[3].address =
998 &session->auth_recv_binary_block;
999 session->auth_buffers[3].length =
1000 sizeof (session->auth_recv_binary_block);
1002 session->auth_buffers[4].address =
1003 &session->auth_send_binary_block;
1004 session->auth_buffers[4].length =
1005 sizeof (session->auth_send_binary_block);
1007 session->num_auth_buffers = 5;
1008 } else {
1009 session->num_auth_buffers = 0;
1011 done:
1012 return(rc);
1015 static int
1016 process_recvd_pdu(struct iscsi_hdr *pdu,
1017 discovery_rec_t *drec,
1018 struct list_head *rec_list,
1019 iscsi_session_t *session,
1020 struct str_buffer *sendtargets,
1021 char *default_port,
1022 int *active,
1023 int *valid_text,
1024 char *data)
1026 int rc=0;
1028 switch (pdu->opcode) {
1029 case ISCSI_OP_TEXT_RSP:{
1030 struct iscsi_text_rsp *text_response =
1031 (struct iscsi_text_rsp *) pdu;
1032 int dlength = ntoh24(pdu->dlength);
1033 int final =
1034 (text_response->flags & ISCSI_FLAG_CMD_FINAL) ||
1035 (text_response-> ttt == ISCSI_RESERVED_TAG);
1036 size_t curr_data_length;
1038 log_debug(4, "discovery session to %s:%d received text"
1039 " response, %d data bytes, ttt 0x%x, "
1040 "final 0x%x",
1041 drec->address,
1042 drec->port,
1043 dlength,
1044 ntohl(text_response->ttt),
1045 text_response->flags & ISCSI_FLAG_CMD_FINAL);
1047 /* mark how much more data in the sendtargets
1048 * buffer is now valid
1050 curr_data_length = str_data_length(sendtargets);
1051 if (str_enlarge_data(sendtargets, dlength)) {
1052 log_error("Could not allocate memory to "
1053 "process SendTargets response.");
1054 rc = 0;
1055 goto done;
1058 memcpy(str_buffer_data(sendtargets) + curr_data_length,
1059 data, dlength);
1061 *valid_text = 1;
1062 /* process as much as we can right now */
1063 process_sendtargets_response(sendtargets,
1064 final,
1065 drec,
1066 rec_list,
1067 default_port);
1069 if (final) {
1070 /* SendTargets exchange is now complete
1072 *active = 0;
1073 /* from now on, after any reconnect,
1074 * assume LUNs may have changed
1076 } else {
1077 /* ask for more targets */
1078 if (!iterate_targets(session,
1079 text_response->ttt)) {
1080 rc = DISCOVERY_NEED_RECONNECT;
1081 goto done;
1084 break;
1086 default:
1087 log_warning(
1088 "discovery session to %s:%d received "
1089 "unexpected opcode 0x%x",
1090 drec->address, drec->port, pdu->opcode);
1091 rc = DISCOVERY_NEED_RECONNECT;
1092 goto done;
1094 done:
1095 return(rc);
1099 * Make a best effort to logout the session, then disconnect the
1100 * socket.
1102 static void
1103 iscsi_logout_and_disconnect(iscsi_session_t * session)
1105 struct iscsi_logout logout_req;
1106 struct iscsi_logout_rsp logout_resp;
1107 int rc;
1110 * Build logout request header
1112 memset(&logout_req, 0, sizeof (logout_req));
1113 logout_req.opcode = ISCSI_OP_LOGOUT | ISCSI_OP_IMMEDIATE;
1114 logout_req.flags = ISCSI_FLAG_CMD_FINAL |
1115 (ISCSI_LOGOUT_REASON_CLOSE_SESSION &
1116 ISCSI_FLAG_LOGOUT_REASON_MASK);
1117 logout_req.itt = htonl(session->itt);
1118 if (++session->itt == ISCSI_RESERVED_TAG)
1119 session->itt = 1;
1120 logout_req.cmdsn = htonl(session->cmdsn);
1121 logout_req.exp_statsn = htonl(++session->conn[0].exp_statsn);
1124 * Send the logout request
1126 rc = iscsi_io_send_pdu(&session->conn[0],(struct iscsi_hdr *)&logout_req,
1127 ISCSI_DIGEST_NONE, NULL, ISCSI_DIGEST_NONE, 3);
1128 if (!rc) {
1129 log_error(
1130 "iscsid: iscsi_logout - failed to send logout PDU.");
1131 goto done;
1135 * Read the logout response
1137 memset(&logout_resp, 0, sizeof(logout_resp));
1138 rc = iscsi_io_recv_pdu(&session->conn[0],
1139 (struct iscsi_hdr *)&logout_resp, ISCSI_DIGEST_NONE, NULL,
1140 0, ISCSI_DIGEST_NONE, 1);
1141 if (!rc) {
1142 log_error("iscsid: logout - failed to receive logout resp");
1143 goto done;
1145 if (logout_resp.response != ISCSI_LOGOUT_SUCCESS) {
1146 log_error("iscsid: logout failed - response = 0x%x",
1147 logout_resp.response);
1150 done:
1152 * Close the socket.
1154 iscsi_io_disconnect(&session->conn[0]);
1157 int discovery_sendtargets(void *fndata, struct iface_rec *iface,
1158 struct list_head *rec_list)
1160 discovery_rec_t *drec = fndata;
1161 iscsi_session_t *session;
1162 struct pollfd pfd;
1163 struct iscsi_hdr pdu_buffer;
1164 struct iscsi_hdr *pdu = &pdu_buffer;
1165 char *data = NULL;
1166 int active = 0, valid_text = 0;
1167 struct timeval connection_timer;
1168 int timeout;
1169 int rc;
1170 struct str_buffer sendtargets;
1171 uint8_t status_class = 0, status_detail = 0;
1172 unsigned int login_failures = 0, data_len;
1173 int login_delay = 0;
1174 struct sockaddr_storage ss;
1175 char host[NI_MAXHOST], serv[NI_MAXSERV], default_port[NI_MAXSERV];
1176 struct iscsi_sendtargets_config *config = &drec->u.sendtargets;
1178 /* initial setup */
1179 log_debug(1, "starting sendtargets discovery, address %s:%d, ",
1180 drec->address, drec->port);
1181 memset(&pdu_buffer, 0, sizeof (pdu_buffer));
1182 clear_timer(&connection_timer);
1184 /* allocate a new session, and initialize default values */
1185 session = init_new_session(config, iface);
1186 if (session == NULL) {
1187 log_error("Discovery process to %s:%d failed to "
1188 "create a discovery session.",
1189 drec->address, drec->port);
1190 return 1;
1193 log_debug(4, "sendtargets discovery to %s:%d using "
1194 "isid 0x%02x%02x%02x%02x%02x%02x",
1195 drec->address, drec->port, session->isid[0],
1196 session->isid[1], session->isid[2], session->isid[3],
1197 session->isid[4], session->isid[5]);
1199 /* allocate data buffers for SendTargets data */
1200 data = malloc(session->conn[0].max_recv_dlength);
1201 if (!data) {
1202 rc = 1;
1203 goto free_session;
1205 data_len = session->conn[0].max_recv_dlength;
1207 str_init_buffer(&sendtargets, 0);
1209 sprintf(default_port, "%d", drec->port);
1210 /* resolve the DiscoveryAddress to an IP address */
1211 if (resolve_address(drec->address, default_port, &ss)) {
1212 log_error("cannot resolve host name %s", drec->address);
1213 rc = 1;
1214 goto free_sendtargets;
1217 log_debug(4, "discovery timeouts: login %d, reopen_cnt %d, auth %d.",
1218 session->conn[0].login_timeout, session->reopen_cnt,
1219 session->conn[0].auth_timeout);
1221 /* setup authentication variables for the session*/
1222 rc = setup_authentication(session, drec, config);
1223 if (rc == 0) {
1224 rc = 1;
1225 goto free_sendtargets;
1228 set_address:
1230 * copy the saved address to the session,
1231 * undoing any temporary redirect
1233 session->conn[0].saddr = ss;
1235 reconnect:
1237 if (--session->reopen_cnt < 0) {
1238 log_error("connection login retries (reopen_max %d) exceeded",
1239 config->reopen_max);
1240 rc = 1;
1241 goto free_sendtargets;
1244 redirect_reconnect:
1246 iscsi_io_disconnect(&session->conn[0]);
1248 session->cmdsn = 1;
1249 session->itt = 1;
1250 session->portal_group_tag = PORTAL_GROUP_TAG_UNKNOWN;
1252 /* slowly back off the frequency of login attempts */
1253 if (login_failures == 0)
1254 login_delay = 0;
1255 else if (login_failures < 10)
1256 login_delay = 1; /* 10 seconds at 1 sec each */
1257 else if (login_failures < 20)
1258 login_delay = 2; /* 20 seconds at 2 sec each */
1259 else if (login_failures < 26)
1260 login_delay = 5; /* 30 seconds at 5 sec each */
1261 else if (login_failures < 34)
1262 login_delay = 15; /* 60 seconds at 15 sec each */
1263 else
1264 login_delay = 60; /* after 2 minutes, try once a minute */
1266 if (login_delay) {
1267 log_debug(4, "discovery session to %s:%d sleeping for %d "
1268 "seconds before next login attempt",
1269 drec->address, drec->port, login_delay);
1270 sleep(login_delay);
1273 getnameinfo((struct sockaddr *) &session->conn[0].saddr,
1274 sizeof(session->conn[0].saddr), host,
1275 sizeof(host), serv, sizeof(serv),
1276 NI_NUMERICHOST|NI_NUMERICSERV);
1278 if (!iscsi_io_connect(&session->conn[0])) {
1279 log_error("connection to discovery address %s "
1280 "failed", host);
1282 login_failures++;
1283 /* If a temporary redirect sent us to something unreachable,
1284 * we want to go back to the original IP address, so make sure
1285 * we reset the session's IP.
1287 goto set_address;
1290 log_debug(1, "connected to discovery address %s", host);
1292 log_debug(4, "discovery session to %s:%d starting iSCSI login on fd %d",
1293 drec->address, drec->port, session->conn[0].socket_fd);
1295 /* In case of discovery, we using socket's descriptor as ctrl. */
1296 session->ctrl_fd = session->conn[0].socket_fd;
1297 session->conn[0].session = session;
1299 status_class = 0;
1300 status_detail = 0;
1302 memset(data, 0, data_len);
1303 rc = iscsi_login(session, 0, data, data_len,
1304 &status_class, &status_detail);
1306 switch (rc) {
1307 case LOGIN_OK:
1308 case LOGIN_REDIRECT:
1309 break;
1311 case LOGIN_IO_ERROR:
1312 case LOGIN_REDIRECTION_FAILED:
1313 /* try again */
1314 log_warning("retrying discovery login to %s", host);
1315 iscsi_io_disconnect(&session->conn[0]);
1316 login_failures++;
1317 goto set_address;
1319 default:
1320 case LOGIN_FAILED:
1321 case LOGIN_NEGOTIATION_FAILED:
1322 case LOGIN_AUTHENTICATION_FAILED:
1323 case LOGIN_VERSION_MISMATCH:
1324 case LOGIN_INVALID_PDU:
1325 log_error("discovery login to %s failed, giving up", host);
1326 iscsi_io_disconnect(&session->conn[0]);
1327 rc = 1;
1328 goto free_sendtargets;
1331 /* check the login status */
1332 switch (status_class) {
1333 case ISCSI_STATUS_CLS_SUCCESS:
1334 log_debug(4, "discovery login success to %s", host);
1335 login_failures = 0;
1336 break;
1337 case ISCSI_STATUS_CLS_REDIRECT:
1338 switch (status_detail) {
1339 /* the session IP address was changed by the login
1340 * library, so just try again with this portal
1341 * config but the new address.
1343 case ISCSI_LOGIN_STATUS_TGT_MOVED_TEMP:
1344 log_warning(
1345 "discovery login temporarily redirected to "
1346 "%s port %s", host, serv);
1347 goto redirect_reconnect;
1348 case ISCSI_LOGIN_STATUS_TGT_MOVED_PERM:
1349 log_warning(
1350 "discovery login permanently redirected to "
1351 "%s port %s", host, serv);
1352 /* make the new address permanent */
1353 ss = session->conn[0].saddr;
1354 goto redirect_reconnect;
1355 default:
1356 log_error(
1357 "discovery login rejected: redirection type "
1358 "0x%x not supported",
1359 status_detail);
1360 goto set_address;
1362 break;
1363 case ISCSI_STATUS_CLS_INITIATOR_ERR:
1364 log_error(
1365 "discovery login to %s rejected: "
1366 "initiator error (%02x/%02x), non-retryable, giving up",
1367 host, status_class, status_detail);
1368 iscsi_io_disconnect(&session->conn[0]);
1369 rc = 1;
1370 goto free_sendtargets;
1371 case ISCSI_STATUS_CLS_TARGET_ERR:
1372 log_error(
1373 "discovery login to %s rejected: "
1374 "target error (%02x/%02x)",
1375 host, status_class, status_detail);
1376 iscsi_io_disconnect(&session->conn[0]);
1377 login_failures++;
1378 goto reconnect;
1379 default:
1380 log_error(
1381 "discovery login to %s failed, response "
1382 "with unknown status class 0x%x, detail 0x%x",
1383 host,
1384 status_class, status_detail);
1385 iscsi_io_disconnect(&session->conn[0]);
1386 login_failures++;
1387 goto reconnect;
1390 /* reinitialize */
1391 str_truncate_buffer(&sendtargets, 0);
1393 /* ask for targets */
1394 if (!request_targets(session)) {
1395 goto reconnect;
1397 active = 1;
1399 /* set timeouts */
1400 set_timer(&connection_timer, session->conn[0].active_timeout);
1402 /* prepare to poll */
1403 memset(&pfd, 0, sizeof (pfd));
1404 pfd.fd = session->conn[0].socket_fd;
1405 pfd.events = POLLIN | POLLPRI;
1407 repoll:
1408 timeout = msecs_until(&connection_timer);
1409 /* block until we receive a PDU, a TCP FIN, a TCP RST,
1410 * or a timeout
1412 log_debug(4,
1413 "discovery process %s:%d polling fd %d, "
1414 "timeout in %f seconds",
1415 drec->address, drec->port, pfd.fd,
1416 timeout / 1000.0);
1418 pfd.revents = 0;
1419 rc = poll(&pfd, 1, timeout);
1421 log_debug(7,
1422 "discovery process to %s:%d returned from poll, rc %d",
1423 drec->address, drec->port, rc);
1425 if (timer_expired(&connection_timer)) {
1426 log_warning("discovery session to %s:%d session "
1427 "logout, connection timer expired",
1428 drec->address, drec->port);
1429 iscsi_logout_and_disconnect(session);
1430 rc = 1;
1431 goto free_sendtargets;
1434 if (rc > 0) {
1435 if (pfd.revents & (POLLIN | POLLPRI)) {
1436 timeout = msecs_until(&connection_timer);
1438 memset(data, 0, data_len);
1439 if (!iscsi_io_recv_pdu(&session->conn[0],
1440 pdu, ISCSI_DIGEST_NONE, data,
1441 data_len, ISCSI_DIGEST_NONE,
1442 timeout)) {
1443 log_debug(1, "discovery session to "
1444 "%s:%d failed to recv a PDU "
1445 "response, terminating",
1446 drec->address,
1447 drec->port);
1448 iscsi_io_disconnect(&session->conn[0]);
1449 rc = 1;
1450 goto free_sendtargets;
1454 * process iSCSI PDU received
1456 rc = process_recvd_pdu(pdu, drec, rec_list,
1457 session, &sendtargets,
1458 default_port,
1459 &active, &valid_text, data);
1460 if (rc == DISCOVERY_NEED_RECONNECT)
1461 goto reconnect;
1463 /* reset timers after receiving a PDU */
1464 if (active) {
1465 set_timer(&connection_timer,
1466 session->conn[0].active_timeout);
1467 goto repoll;
1471 if (pfd.revents & POLLHUP) {
1472 log_warning("discovery session to %s:%d "
1473 "terminating after hangup",
1474 drec->address, drec->port);
1475 iscsi_io_disconnect(&session->conn[0]);
1476 rc = 1;
1477 goto free_sendtargets;
1480 if (pfd.revents & POLLNVAL) {
1481 log_warning("discovery POLLNVAL");
1482 sleep(1);
1483 goto reconnect;
1486 if (pfd.revents & POLLERR) {
1487 log_warning("discovery POLLERR");
1488 sleep(1);
1489 goto reconnect;
1491 } else if (rc < 0) {
1492 if (errno == EINTR) {
1493 /* if we got SIGHUP, reconnect and rediscover */
1494 if (rediscover) {
1495 rediscover = 0;
1496 log_debug(1, "rediscovery requested");
1497 goto reconnect;
1499 } else {
1500 log_error("poll error");
1501 rc = 1;
1502 goto free_sendtargets;
1506 log_debug(1, "discovery process to %s:%d exiting",
1507 drec->address, drec->port);
1508 rc = 0;
1510 free_sendtargets:
1511 str_free_buffer(&sendtargets);
1512 free(data);
1513 free_session:
1514 free(session);
1515 return rc;
1518 #ifdef SLP_ENABLE
1520 slp_discovery(struct iscsi_slp_config *config)
1522 struct sigaction action;
1523 char *pl;
1524 unsigned short flag = 0;
1526 memset(&action, 0, sizeof (struct sigaction));
1527 action.sa_sigaction = NULL;
1528 action.sa_flags = 0;
1529 action.sa_handler = SIG_DFL;
1530 sigaction(SIGTERM, &action, NULL);
1531 sigaction(SIGINT, &action, NULL);
1532 sigaction(SIGPIPE, &action, NULL);
1534 action.sa_handler = sighup_handler;
1535 sigaction(SIGHUP, &action, NULL);
1537 if (iscsi_process_should_exit()) {
1538 log_debug(1, "slp discovery process %p exiting", discovery);
1539 exit(0);
1542 discovery->pid = getpid();
1544 pl = generate_predicate_list(discovery, &flag);
1546 while (1) {
1547 if (flag == SLP_MULTICAST_ENABLED) {
1548 discovery->flag = SLP_MULTICAST_ENABLED;
1549 slp_multicast_srv_query(discovery, pl, GENERIC_QUERY);
1552 if (flag == SLP_UNICAST_ENABLED) {
1553 discovery->flag = SLP_UNICAST_ENABLED;
1554 slp_unicast_srv_query(discovery, pl, GENERIC_QUERY);
1557 sleep(config->poll_interval);
1560 exit(0);
1563 #endif