📄 iscsi-discovery.c
字号:
/* * iSCSI connection daemon * Copyright (C) 2002 Cisco Systems, Inc. * maintained by linux-iscsi-devel@lists.sourceforge.net * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published * by the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * See the file COPYING included with this distribution for more details. * * $Id: iscsi-discovery.c,v 1.21 2005/01/21 13:10:21 krishmnc Exp $ * */#include <unistd.h>#include <fcntl.h>#include <signal.h>#include <errno.h>#include <netdb.h>#include <stdint.h>#include <sys/poll.h>#include <sys/time.h>#include "iscsi-sfnet.h"#include "iscsi-protocol.h"#include "iscsi-login.h"#include "iscsi-hooks.h"#include "string-buffer.h"#include "iscsi-session.h"#ifdef SLP_ENABLE#include "iscsi-slp-discovery.h"#endif#define DISCOVERY_NEED_RECONNECT 0xdead0001static int rediscover = 0;static int record_begin;static voidsighup_handler(int unused){ rediscover = 1;}static intsend_nop_reply(struct iscsi_session *session, struct iscsi_nop_in_hdr *nop, char *data, int timeout){ struct iscsi_nop_out_hdr out; memset(&out, 0, sizeof (out)); out.opcode = ISCSI_OP_NOOP_OUT | ISCSI_OP_IMMEDIATE; out.flags = ISCSI_FLAG_FINAL; memcpy(out.lun, nop->lun, sizeof (out.lun)); out.itt = nop->itt; out.ttt = nop->ttt; memcpy(out.dlength, nop->dlength, sizeof (out.dlength)); out.cmdsn = htonl(session->cmd_sn); /* don't increment after * immediate cmds */ out.expstatsn = htonl(session->exp_stat_sn); debugmsg(4, "sending nop reply for ttt %u, cmdsn %u, dlength %d", ntohl(out.ttt), ntohl(out.cmdsn), ntoh24(out.dlength)); return iscsi_send_pdu(session, (struct iscsi_hdr *) &out, ISCSI_DIGEST_NONE, data, ISCSI_DIGEST_NONE, timeout);}static intiscsi_make_text_pdu(struct iscsi_session *session, struct iscsi_hdr *pdu, char *data, int max_data_length){ struct iscsi_txt_hdr *text_pdu = (struct iscsi_txt_hdr *)pdu; /* initialize the PDU header */ memset(text_pdu, 0, sizeof (*text_pdu)); text_pdu->opcode = ISCSI_OP_TEXT_CMD; text_pdu->itt = htonl(session->itt); text_pdu->ttt = ISCSI_RSVD_TASK_TAG; text_pdu->cmdsn = htonl(session->cmd_sn++); text_pdu->expstatsn = htonl(session->exp_stat_sn); return 1;}static intrequest_targets(struct iscsi_session *session){ char data[64]; struct iscsi_txt_hdr text; struct iscsi_hdr *pdu = (struct iscsi_hdr *) &text; memset(&text, 0, sizeof (text)); memset(data, 0, sizeof (data)); /* make a text PDU with SendTargets=All */ if (!iscsi_make_text_pdu(session, pdu, data, sizeof (data))) { logmsg(AS_ERROR, "failed to make a SendTargets PDU"); return 0; } if (!iscsi_add_text (session, pdu, data, sizeof (data), "SendTargets", "All")) { logmsg(AS_ERROR, "failed to add SendTargets text key"); exit(1); } text.ttt = ISCSI_RSVD_TASK_TAG; text.flags = ISCSI_FLAG_FINAL; if (++session->itt == ISCSI_RSVD_TASK_TAG) session->itt = 1; if (!iscsi_send_pdu(session, pdu, ISCSI_DIGEST_NONE, data, ISCSI_DIGEST_NONE, session->active_timeout)) { logmsg(AS_ERROR, "failed to send SendTargets PDU"); return 0; } return 1;}static intiterate_targets(struct iscsi_session *session, uint32_t ttt){ char data[64]; struct iscsi_txt_hdr text; struct iscsi_hdr *pdu = (struct iscsi_hdr *) &text; memset(&text, 0, sizeof (text)); memset(data, 0, sizeof (data)); /* make an empty text PDU */ if (!iscsi_make_text_pdu(session, pdu, data, sizeof (data))) { logmsg(AS_ERROR, "failed to make an empty text PDU"); return 0; } text.ttt = ttt; text.flags = ISCSI_FLAG_FINAL; if (++session->itt == ISCSI_RSVD_TASK_TAG) session->itt = 1; if (!iscsi_send_pdu(session, pdu, ISCSI_DIGEST_NONE, data, ISCSI_DIGEST_NONE, session->active_timeout)) { logmsg(AS_ERROR, "failed to send empty text PDU"); return 0; } return 1;}/* try to reset the session's IP address and port, based on the TargetAddress * provided */intiscsi_update_address(struct iscsi_session *session, char *address){ char *port; char *tag; struct hostent *hostn = NULL; if ((tag = strrchr(address, ','))) { *tag = '\0'; tag++; } if ((port = strrchr(address, ':'))) { *port = '\0'; port++; } do { hostn = gethostbyname(address); if (hostn == NULL) { errormsg("cannot resolve host name %s", address); sleep(1); } if (iscsi_process_should_exit()) return 0; } while (!hostn); memcpy(session->ip_address, hostn->h_addr, MIN(sizeof (session->ip_address), hostn->h_length)); session->ip_length = hostn->h_length; if (port) { session->port = atoi(port); } else { session->port = ISCSI_LISTEN_PORT; } return 1;}intadd_portal(struct string_buffer *info, char *address, char *port, char *tag){ struct hostent *hostn = NULL; /* resolve the address, in case it was a DNS name */ do { hostn = gethostbyname(address); if (!hostn) { errormsg("cannot resolve %s", address); sleep(1); } if (iscsi_process_should_exit()) return 0; } while (!hostn); /* convert the resolved name to text */ if (hostn->h_length == 4) { struct in_addr addr; memcpy(&addr, hostn->h_addr, sizeof (addr)); if (tag && *tag) { if (!append_sprintf(info, "TT=%s\n", tag)) { logmsg(AS_ERROR, "couldn't add portal tag %s", tag); return 0; } } if (port && *port) { if (!append_sprintf(info, "TP=%s\n", port)) { logmsg(AS_ERROR, "couldn't add port %s", port); return 0; } } if (strcmp(inet_ntoa(addr), address)) { /* if the resolved name doesn't match the original, * send an RA line as well as a TA line */ return append_sprintf(info, "RA=%s\nTA=%s\n", inet_ntoa(addr), address); } else { /* don't need the RA line */ return append_sprintf(info, "TA=%s\n", address); } } else { /* FIXME: IPv6 */ logmsg(AS_ERROR, "can't handle network address %s", address); return 0; }}intadd_target_record(struct string_buffer *info, char *name, char *end, int lun_inventory_changed, char *default_address, char *default_port, int fd){ char *text = NULL; char *nul = name; size_t length; size_t original = data_length(info); /* address = IPv4 * address = [IPv6] * address = DNSname * address = IPv4:port * address = [IPv6]:port * address = DNSname:port * address = IPv4,tag * address = [IPv6],tag * address = DNSname,tag * address = IPv4:port,tag * address = [IPv6]:port,tag * address = DNSname:port,tag */ debugmsg(7, "adding target record %p, end %p", name, end); /* find the end of the name */ while ((nul < end) && (*nul != '\0')) nul++; length = nul - name; if (length > TARGET_NAME_MAXLEN) { logmsg(AS_ERROR, "TargetName %s too long, ignoring", name); return 0; } if (!record_begin) { if (!append_sprintf (info, lun_inventory_changed ? "DLC=%s\n" : "DTN=%s\n", name)) { logmsg(AS_ERROR, "couldn't report target %s", name); truncate_buffer(info, original); return 0; } record_begin = 1; } else { if (!append_sprintf (info, lun_inventory_changed ? "LC=%s\n" : "TN=%s\n", name)) { logmsg(AS_ERROR, "couldn't report target %s", name); truncate_buffer(info, original); return 0; } } text = name + length; /* skip NULs after the name */ while ((text < end) && (*text == '\0')) text++; /* if no address is provided, use the default */ if (text >= end) { if (default_address == NULL) { logmsg(AS_ERROR, "no default address known for target %s", name); truncate_buffer(info, original); return 0; } else if (!add_portal(info, default_address, default_port, NULL)) { logmsg(AS_ERROR, "failed to add default portal, ignoring " "target %s", name); truncate_buffer(info, original); return 0; } else if (!append_string(info, ";\n")) { logmsg(AS_ERROR, "failed to terminate target record, " "ignoring target %s", name); truncate_buffer(info, original); return 0; } /* finished adding the default */ return 1; } /* process TargetAddresses */ while (text < end) { char *next = text + strlen(text) + 1; debugmsg(7, "text %p, next %p, end %p, %s", text, next, end, text); if (strncmp(text, "TargetAddress=", 14) == 0) { char *port = NULL; char *tag = NULL; char *address = text + 14; /* FIXME: handle IPv6 */ if (address[0] == '[') { /* This is an IPv6 numeric address; skip it */ text = next; continue; } if ((tag = strrchr(text, ','))) { *tag = '\0'; tag++; } if ((port = strrchr(text, ':'))) { *port = '\0'; port++; } if (!add_portal(info, address, port, tag)) { logmsg(AS_ERROR, "failed to add default portal, " "ignoring target %s", name); truncate_buffer(info, original); return 0; } } else { logmsg(AS_ERROR, "unexpected SendTargets data: %s", text); } text = next; } /* indicate the end of the target record */ if (!append_string(info, ";\n")) { logmsg(AS_ERROR, "failed to terminate target record, ignoring target %s", name); truncate_buffer(info, original); return 0; } return 1;}static intprocess_sendtargets_response(struct string_buffer *sendtargets, struct string_buffer *info, int final, int lun_inventory_changed, char *default_address, char *default_port, int fd){ char *start = buffer_data(sendtargets); char *text = start; char *end = text + data_length(sendtargets); char *nul = end - 1; char *record = NULL; int num_targets = 0; size_t valid_info = 0; if (start == end) { /* no SendTargets data */ goto done; } /* scan backwards to find the last NUL in the data, to ensure we * don't walk off the end. Since key=value pairs can span PDU * boundaries, we're not guaranteed that the end of the data has a * NUL. */ while ((nul > start) && *nul) nul--; if (nul == start) { /* couldn't find anything we can process now, * it's one big partial string */ goto done; } /* find the boundaries between target records (TargetName or final PDU) */ for (;;) { /* skip NULs */ while ((text < nul) && (*text == '\0')) text++; if (text == nul) break; debugmsg(7, "processing sendtargets record %p, text %p, line %s", record, text, text); /* look for the start of a new target record */ if (strncmp(text, "TargetName=", 11) == 0) { if (record) { /* send the last record, which we just found * the end of. don't bother passing the * "TargetName=" prefix. */ if (!add_target_record (info, record + 11, text, lun_inventory_changed, default_address, default_port, fd)) { logmsg(AS_ERROR, "failed to add target record"); truncate_buffer(sendtargets, 0); truncate_buffer(info, valid_info); goto done; } num_targets++; valid_info = data_length(info); } record = text; } /* everything up til the next NUL must be part of the
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -