1 1.6 andvar /* $NetBSD: iscsid_discover.c,v 1.6 2025/07/11 22:19:53 andvar Exp $ */ 2 1.1 agc 3 1.1 agc /*- 4 1.1 agc * Copyright (c) 2005,2006,2011 The NetBSD Foundation, Inc. 5 1.1 agc * All rights reserved. 6 1.1 agc * 7 1.1 agc * This code is derived from software contributed to The NetBSD Foundation 8 1.1 agc * by Wasabi Systems, Inc. 9 1.1 agc * 10 1.1 agc * Redistribution and use in source and binary forms, with or without 11 1.1 agc * modification, are permitted provided that the following conditions 12 1.1 agc * are met: 13 1.1 agc * 1. Redistributions of source code must retain the above copyright 14 1.1 agc * notice, this list of conditions and the following disclaimer. 15 1.1 agc * 2. Redistributions in binary form must reproduce the above copyright 16 1.1 agc * notice, this list of conditions and the following disclaimer in the 17 1.1 agc * documentation and/or other materials provided with the distribution. 18 1.1 agc * 19 1.1 agc * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 1.1 agc * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 1.1 agc * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 1.1 agc * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 1.1 agc * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 1.1 agc * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 1.1 agc * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 1.1 agc * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 1.1 agc * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 1.1 agc * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 1.1 agc * POSSIBILITY OF SUCH DAMAGE. 30 1.1 agc */ 31 1.1 agc 32 1.1 agc #ifndef ISCSI_MINIMAL 33 1.1 agc 34 1.1 agc #include "iscsid_globals.h" 35 1.1 agc #include "isns.h" 36 1.1 agc #include "isns_defs.h" 37 1.1 agc 38 1.1 agc #include <sys/socket.h> 39 1.1 agc #include <netinet/in.h> 40 1.1 agc #include <netinet/tcp.h> 41 1.1 agc #include <netdb.h> 42 1.1 agc 43 1.1 agc #define MY_FLAGS ISNS_FLAG_REPLACE_REG 44 1.1 agc 45 1.1 agc 46 1.1 agc /********************************************************************** 47 1.1 agc **********************************************************************/ 48 1.1 agc 49 1.1 agc uint32_t isns_id = 0; /* isns ID counter */ 50 1.1 agc 51 1.1 agc ISNS_HANDLE isns_handle = ISNS_INVALID_HANDLE; 52 1.1 agc 53 1.1 agc /********************************************************************** 54 1.1 agc **********************************************************************/ 55 1.1 agc 56 1.1 agc /* 57 1.1 agc * xlate_ip 58 1.1 agc * Support routine to translate binary IP into string form for storage in 59 1.1 agc * target object. Handles IPv6 and IPv4 formats. 60 1.1 agc * 61 1.1 agc * Parameters: 62 1.1 agc * dest the destination string 63 1.1 agc * data the source (from iSNS address field) 64 1.1 agc * 65 1.1 agc * Returns: status 66 1.1 agc */ 67 1.1 agc 68 1.3 agc static void 69 1.2 christos xlate_ip(uint8_t *dest, size_t size, void *data) 70 1.1 agc { 71 1.1 agc uint16_t *wdt = (uint16_t *) data; 72 1.1 agc size_t cc; 73 1.1 agc int i; 74 1.2 christos char *dst = (char *)dest; 75 1.2 christos char *dt = data; 76 1.1 agc 77 1.1 agc for (i = 0; i < 5 && !wdt[i]; i++) { 78 1.1 agc } 79 1.1 agc if (i == 5 && wdt[5] == 0xffff) { 80 1.2 christos snprintf(dst, size, "%d.%d.%d.%d", 81 1.2 christos dt[12], dt[13], dt[14], dt[15]); 82 1.1 agc } else { 83 1.1 agc for (cc = 0, i = 0; i < 7; i++) { 84 1.2 christos cc += snprintf(&dst[cc], size - cc, "%x:", wdt[i]); 85 1.1 agc } 86 1.2 christos snprintf(&dst[cc], size - cc, "%x", wdt[7]); 87 1.1 agc } 88 1.1 agc } 89 1.1 agc 90 1.1 agc 91 1.1 agc /* 92 1.1 agc * get_isns_target_info 93 1.1 agc * Support routine to query the server for the attributes of the given target. 94 1.1 agc * 95 1.1 agc * Parameters: 96 1.1 agc * TargetName The target name to query. 97 1.1 agc * 98 1.1 agc * Returns: status 99 1.1 agc */ 100 1.1 agc 101 1.3 agc static uint32_t 102 1.1 agc get_isns_target_info(isns_t * isns, uint8_t * TargetName) 103 1.1 agc { 104 1.1 agc int retval; 105 1.1 agc ISNS_TRANS t; 106 1.1 agc uint32_t tag; 107 1.2 christos uint32_t data_len; 108 1.1 agc void *data_p; 109 1.1 agc uint32_t u32; 110 1.1 agc struct timespec tout = { 5, 0 }; 111 1.1 agc uint32_t status; 112 1.1 agc target_t *targ; 113 1.1 agc char name[ISCSI_STRING_LENGTH]; 114 1.1 agc char alias[ISCSI_STRING_LENGTH]; 115 1.1 agc iscsi_portal_address_t addr; 116 1.1 agc 117 1.2 christos t = isns_new_trans(isns_handle, isnsp_DevAttrQry, MY_FLAGS); 118 1.2 christos if (ISNS_INVALID_TRANS == t) { 119 1.5 mlelstv DEB(10,("%s: get_targets iscsi_new_trans failed", __func__)); 120 1.1 agc return ISCSID_STATUS_NO_RESOURCES; 121 1.1 agc } 122 1.1 agc isns_add_string(t, isnst_iSCSIName, (char *)isns->reg_iscsi_name); 123 1.1 agc isns_add_string(t, isnst_iSCSIName, (char *)TargetName); 124 1.1 agc 125 1.1 agc isns_add_tlv(t, isnst_Delimiter, 0, NULL); 126 1.1 agc 127 1.1 agc isns_add_tlv(t, isnst_iSCSIName, 0, NULL); /* 32: name */ 128 1.1 agc isns_add_tlv(t, isnst_iSCSINodeType, 0, NULL); /* 33: node type */ 129 1.1 agc isns_add_tlv(t, isnst_iSCSIAlias, 0, NULL); /* 34: alias */ 130 1.1 agc /* ToDo: get security attributes... */ 131 1.1 agc /* isns_add_tlv (t, isnst_PortalSecBmap, 0, NULL); */ 132 1.1 agc /*tag=27: security bitmap */ 133 1.1 agc 134 1.1 agc retval = isns_send_trans(t, &tout, &status); 135 1.5 mlelstv DEB(9, ("isns_send_trans called, returns %d, status %d", retval, status)); 136 1.1 agc if (retval) { 137 1.5 mlelstv DEB(10,("iSNS Attribute Query failed, rc = %d", retval)); 138 1.1 agc isns_free_trans(t); 139 1.1 agc return ISCSID_STATUS_ISNS_SERVER_ERROR; 140 1.1 agc } 141 1.1 agc /* First is target name (the one we put in), ignore */ 142 1.1 agc if (isns_get_tlv(t, ISNS_TLV_FIRST, &tag, &data_len, &data_p)) { 143 1.5 mlelstv DEB(10,("iSNS Attribute Query returned nothing")); 144 1.1 agc isns_free_trans(t); 145 1.1 agc return ISCSID_STATUS_ISNS_SERVER_ERROR; 146 1.1 agc } 147 1.1 agc if (tag != isnst_iSCSIName) { 148 1.5 mlelstv DEB(10,("iSNS Query returned bad type (tag = %d, length = %d)", 149 1.1 agc tag, data_len)); 150 1.1 agc isns_free_trans(t); 151 1.1 agc return ISCSID_STATUS_ISNS_SERVER_ERROR; 152 1.1 agc } 153 1.1 agc 154 1.1 agc isns_get_tlv(t, ISNS_TLV_NEXT, &tag, &data_len, &data_p); 155 1.1 agc if (tag != isnst_Delimiter) { 156 1.5 mlelstv DEB(10,("Attr Query Missing Delimiter (tag = %d, length = %d)", 157 1.1 agc tag, data_len)); 158 1.1 agc isns_free_trans(t); 159 1.1 agc return ISCSID_STATUS_ISNS_SERVER_ERROR; 160 1.1 agc } 161 1.1 agc 162 1.1 agc isns_get_tlv(t, ISNS_TLV_NEXT, &tag, &data_len, &data_p); 163 1.1 agc if (tag != isnst_iSCSIName || !data_len || data_len >= ISCSI_STRING_LENGTH) { 164 1.5 mlelstv DEB(10,("iSNS Query returned no or invalid name (tag = %d, " 165 1.5 mlelstv "length = %d)", tag, data_len)); 166 1.1 agc isns_free_trans(t); 167 1.1 agc return ISCSID_STATUS_ISNS_SERVER_ERROR; 168 1.1 agc } 169 1.1 agc strlcpy(name, (char *) data_p, sizeof(name)); 170 1.1 agc 171 1.1 agc isns_get_tlv(t, ISNS_TLV_NEXT, &tag, &data_len, &data_p); 172 1.1 agc if (tag != isnst_iSCSINodeType || data_len != 4) { 173 1.5 mlelstv DEB(10,("iSNS Query returned no or invalid node type (tag = %d, " 174 1.5 mlelstv "length = %d)", tag, data_len)); 175 1.1 agc isns_free_trans(t); 176 1.1 agc return ISCSID_STATUS_ISNS_SERVER_ERROR; 177 1.1 agc } 178 1.1 agc u32 = ntohl(*((uint32_t *) data_p)); 179 1.1 agc if (!(u32 & 1)) { 180 1.5 mlelstv DEB(10,("iSNS Query returned bad type (type=%x, should be 1)", u32)); 181 1.1 agc isns_free_trans(t); 182 1.1 agc return ISCSID_STATUS_ISNS_SERVER_ERROR; 183 1.1 agc } 184 1.1 agc 185 1.1 agc isns_get_tlv(t, ISNS_TLV_NEXT, &tag, &data_len, &data_p); 186 1.1 agc if (tag == isnst_iSCSIAlias) { 187 1.1 agc if (data_len >= ISCSI_STRING_LENGTH) { 188 1.5 mlelstv DEB(10,("iSNS Query returned invalid alias (tag=%d, length=%d)", 189 1.1 agc tag, data_len)); 190 1.1 agc isns_free_trans(t); 191 1.1 agc return ISCSID_STATUS_ISNS_SERVER_ERROR; 192 1.1 agc } 193 1.1 agc strlcpy(alias, (char *) data_p, sizeof(alias)); 194 1.1 agc isns_get_tlv(t, ISNS_TLV_NEXT, &tag, &data_len, &data_p); 195 1.1 agc } else 196 1.1 agc alias[0] = 0; 197 1.1 agc 198 1.1 agc isns_free_trans(t); 199 1.1 agc 200 1.1 agc if (ISNS_INVALID_TRANS == 201 1.1 agc (t = isns_new_trans(isns_handle, isnsp_DevAttrQry, MY_FLAGS))) { 202 1.5 mlelstv DEB(10,("get_targets iscsi_new_trans failed")); 203 1.1 agc return ISCSID_STATUS_NO_RESOURCES; 204 1.1 agc } 205 1.1 agc isns_add_string(t, isnst_iSCSIName, (char *)isns->reg_iscsi_name); 206 1.1 agc isns_add_string(t, isnst_iSCSIName, (char *)TargetName); 207 1.1 agc 208 1.1 agc isns_add_tlv(t, isnst_Delimiter, 0, NULL); 209 1.1 agc 210 1.1 agc isns_add_tlv(t, isnst_PGiSCSIName, 0, NULL); /* 48: portal name */ 211 1.1 agc isns_add_tlv(t, isnst_PGPortIPAddr, 0, NULL); /* 49: portal IP */ 212 1.1 agc isns_add_tlv(t, isnst_PGPortIPPort, 0, NULL); /* 50: portal port */ 213 1.1 agc isns_add_tlv(t, isnst_PGTag, 0, NULL); /* 51: group tag */ 214 1.1 agc 215 1.1 agc retval = isns_send_trans(t, &tout, &status); 216 1.5 mlelstv DEB(9, ("isns_send_trans called, returns %d, status %d", retval, status)); 217 1.1 agc if (retval) { 218 1.5 mlelstv DEB(10,("iSNS Attribute Query failed, rc = %d", retval)); 219 1.1 agc isns_free_trans(t); 220 1.1 agc return ISCSID_STATUS_ISNS_SERVER_ERROR; 221 1.1 agc } 222 1.1 agc /* First is target name (the one we put in), ignore */ 223 1.1 agc if (isns_get_tlv(t, ISNS_TLV_FIRST, &tag, &data_len, &data_p)) { 224 1.5 mlelstv DEB(10,("iSNS Attribute Query returned nothing")); 225 1.1 agc isns_free_trans(t); 226 1.1 agc return ISCSID_STATUS_ISNS_SERVER_ERROR; 227 1.1 agc } 228 1.1 agc if (tag != isnst_iSCSIName) { 229 1.5 mlelstv DEB(10,("iSNS Query2 returned bad name (tag = %d, length = %d)", 230 1.1 agc tag, data_len)); 231 1.1 agc isns_free_trans(t); 232 1.1 agc return ISCSID_STATUS_ISNS_SERVER_ERROR; 233 1.1 agc } 234 1.1 agc 235 1.1 agc isns_get_tlv(t, ISNS_TLV_NEXT, &tag, &data_len, &data_p); 236 1.1 agc if (tag != isnst_Delimiter) { 237 1.5 mlelstv DEB(10,("Attr Query2 Missing Delimiter (tag = %d, length = %d)", 238 1.1 agc tag, data_len)); 239 1.1 agc isns_free_trans(t); 240 1.1 agc return ISCSID_STATUS_ISNS_SERVER_ERROR; 241 1.1 agc } 242 1.1 agc 243 1.1 agc while (!isns_get_tlv(t, ISNS_TLV_NEXT, &tag, &data_len, &data_p)) { 244 1.1 agc if (tag != isnst_PGiSCSIName || !data_len || 245 1.1 agc data_len >= ISCSI_STRING_LENGTH) { 246 1.5 mlelstv DEB(10,("iSNS Query2 returned no or invalid name (tag=%d, " 247 1.5 mlelstv "length=%d)", tag, data_len)); 248 1.1 agc isns_free_trans(t); 249 1.1 agc return ISCSID_STATUS_ISNS_SERVER_ERROR; 250 1.1 agc } 251 1.1 agc strlcpy(name, (char *) data_p, sizeof(name)); 252 1.1 agc 253 1.1 agc isns_get_tlv(t, ISNS_TLV_NEXT, &tag, &data_len, &data_p); 254 1.1 agc if (tag != isnst_PGPortIPAddr || data_len != 16) { 255 1.5 mlelstv DEB(10,("iSNS Query returned no or invalid address (tag=%d, " 256 1.5 mlelstv "length=%d)", tag, data_len)); 257 1.1 agc isns_free_trans(t); 258 1.1 agc return ISCSID_STATUS_ISNS_SERVER_ERROR; 259 1.1 agc } 260 1.1 agc xlate_ip(addr.address, sizeof(addr.address), (uint8_t *) data_p); 261 1.1 agc 262 1.1 agc /* Now comes the port */ 263 1.1 agc isns_get_tlv(t, ISNS_TLV_NEXT, &tag, &data_len, &data_p); 264 1.1 agc if (tag != isnst_PGPortIPPort || data_len != 4) { 265 1.5 mlelstv DEB(10,("iSNS Query returned no or invalid port (tag=%d, " 266 1.5 mlelstv "length=%d)", tag, data_len)); 267 1.1 agc isns_free_trans(t); 268 1.1 agc return ISCSID_STATUS_ISNS_SERVER_ERROR; 269 1.1 agc } 270 1.1 agc u32 = ntohl(*((uint32_t *) data_p)); 271 1.1 agc if (u32 & 0xffff0000) { 272 1.5 mlelstv DEB(10,("iSNS Query returned invalid port (flags=%x, " 273 1.5 mlelstv "should be 0)", u32 >> 16)); 274 1.1 agc isns_free_trans(t); 275 1.1 agc return ISCSID_STATUS_ISNS_SERVER_ERROR; 276 1.1 agc } 277 1.1 agc addr.port = (uint16_t) u32; 278 1.1 agc 279 1.1 agc /* And each target must have a group tag */ 280 1.1 agc isns_get_tlv(t, ISNS_TLV_NEXT, &tag, &data_len, &data_p); 281 1.1 agc if (tag != isnst_PGTag || (data_len && data_len != 4)) { 282 1.5 mlelstv DEB(10,("iSNS Query returned no or invalid group tag (tag=%d, " 283 1.5 mlelstv "length=%d)", tag, data_len)); 284 1.1 agc isns_free_trans(t); 285 1.1 agc return ISCSID_STATUS_ISNS_SERVER_ERROR; 286 1.1 agc } 287 1.1 agc if (data_len) { 288 1.1 agc u32 = ntohl(*((uint32_t *) data_p)); 289 1.1 agc addr.group_tag = (uint16_t) u32; 290 1.1 agc } else 291 1.1 agc addr.group_tag = 0; 292 1.1 agc 293 1.1 agc /* we have everything necessary to describe the target, add it. */ 294 1.1 agc 295 1.5 mlelstv DEB(2, ("Adding <%s>, IP <%s>, Port %d, Tag %d", 296 1.1 agc name, addr.address, addr.port, addr.group_tag)); 297 1.1 agc 298 1.1 agc if ((targ = add_discovered_target((unsigned char *)name, &addr, PORTAL_TYPE_ISNS, 299 1.1 agc isns->entry.sid.id)) == NULL) { 300 1.1 agc isns_free_trans(t); 301 1.1 agc return ISCSID_STATUS_NO_RESOURCES; 302 1.1 agc } 303 1.1 agc 304 1.1 agc if (alias[0]) { 305 1.1 agc strlcpy((char *)targ->TargetAlias, alias, 306 1.1 agc sizeof(targ->TargetAlias)); 307 1.1 agc } 308 1.1 agc } 309 1.1 agc isns_free_trans(t); 310 1.1 agc 311 1.1 agc return ISCSID_STATUS_SUCCESS; 312 1.1 agc } 313 1.1 agc 314 1.1 agc 315 1.1 agc /* 316 1.1 agc * deregister_isns_server 317 1.1 agc * Support routine to deregister the initiator from the iSNS server 318 1.1 agc * 319 1.1 agc * Parameters: The server descriptor 320 1.1 agc * 321 1.1 agc * Returns: status 322 1.1 agc */ 323 1.1 agc 324 1.1 agc static uint32_t 325 1.1 agc deregister_isns_server(isns_t * isns) 326 1.1 agc { 327 1.1 agc int retval; 328 1.1 agc ISNS_TRANS t; 329 1.1 agc struct timespec tout = { 5, 0 }; 330 1.1 agc uint32_t status; 331 1.1 agc 332 1.1 agc /* 333 1.1 agc * Create transaction for deregistering with iSNS server 334 1.1 agc */ 335 1.1 agc 336 1.1 agc if (ISNS_INVALID_TRANS == (t = isns_new_trans(isns_handle, isnsp_DevDereg, 337 1.1 agc MY_FLAGS))) { 338 1.5 mlelstv DEB(10,("dereg_isns_server iscsi_new_trans failed")); 339 1.1 agc return ISCSID_STATUS_NO_RESOURCES; 340 1.1 agc } 341 1.1 agc 342 1.1 agc isns_add_string(t, isnst_iSCSIName, (char *)isns->reg_iscsi_name); 343 1.1 agc isns_add_tlv(t, isnst_Delimiter, 0, NULL); 344 1.1 agc isns_add_string(t, isnst_EID, (char *)isns->reg_entity_id); 345 1.2 christos isns_add_tlv(t, isnst_PortalIPAddr, (uint32_t)sizeof(isns->reg_ip_addr), 346 1.1 agc isns->reg_ip_addr); 347 1.2 christos isns_add_tlv(t, isnst_PortalPort, (uint32_t)sizeof(isns->reg_ip_port), 348 1.1 agc &isns->reg_ip_port); 349 1.1 agc isns_add_string(t, isnst_iSCSIName, (char *)isns->reg_iscsi_name); 350 1.1 agc 351 1.1 agc retval = isns_send_trans(t, &tout, &status); 352 1.5 mlelstv DEB(9, ("DevAttrReg request returns %d, status %d", retval, status)); 353 1.1 agc 354 1.1 agc isns_free_trans(t); 355 1.1 agc return ISCSID_STATUS_SUCCESS; 356 1.1 agc } 357 1.1 agc 358 1.1 agc /* 359 1.1 agc * register_isns_server 360 1.1 agc * 361 1.1 agc * Parameters: The server descriptor 362 1.1 agc * 363 1.1 agc * Returns: status 364 1.1 agc */ 365 1.1 agc 366 1.1 agc 367 1.1 agc static uint32_t 368 1.1 agc register_isns_server(isns_t * isns) 369 1.1 agc { 370 1.1 agc int retval; 371 1.1 agc ISNS_TRANS t; 372 1.1 agc uint32_t u32; 373 1.1 agc struct timespec tout = { 5, 0 }; 374 1.1 agc uint32_t status; 375 1.1 agc 376 1.1 agc if (ISNS_INVALID_TRANS == (t = isns_new_trans(isns_handle, isnsp_DevAttrReg, 377 1.1 agc MY_FLAGS))) { 378 1.5 mlelstv DEB(10,("iscsi_new_trans failed")); 379 1.1 agc return ISCSID_STATUS_NO_RESOURCES; 380 1.1 agc } 381 1.1 agc 382 1.1 agc isns_add_string(t, isnst_iSCSIName, (char *)isns->reg_iscsi_name); /*tag=32 */ 383 1.1 agc isns_add_tlv(t, isnst_Delimiter, 0, NULL); 384 1.1 agc isns_add_string(t, isnst_EID, (char *)isns->reg_entity_id); 385 1.1 agc u32 = htonl(2); 386 1.2 christos isns_add_tlv(t, isnst_EntProtocol, (uint32_t)sizeof(u32), &u32); 387 1.2 christos isns_add_tlv(t, isnst_PortalIPAddr, (uint32_t)sizeof(isns->reg_ip_addr), 388 1.1 agc isns->reg_ip_addr); 389 1.2 christos isns_add_tlv(t, isnst_PortalPort, (uint32_t)sizeof(isns->reg_ip_port), 390 1.1 agc &isns->reg_ip_port); 391 1.1 agc isns_add_string(t, isnst_iSCSIName, (char *)isns->reg_iscsi_name); /*tag=32 */ 392 1.1 agc u32 = htonl(2); 393 1.2 christos isns_add_tlv(t, isnst_iSCSINodeType, (uint32_t)sizeof(u32), &u32); 394 1.6 andvar /*tag=33 (node type = initiator) */ 395 1.1 agc 396 1.1 agc retval = isns_send_trans(t, &tout, &status); 397 1.5 mlelstv DEB(9, ("DevAttrReg request returns %d, status %d", retval, status)); 398 1.1 agc isns_free_trans(t); 399 1.1 agc 400 1.1 agc if (retval || status) 401 1.1 agc return ISCSID_STATUS_ISNS_ERROR; 402 1.1 agc 403 1.1 agc return ISCSID_STATUS_SUCCESS; 404 1.1 agc } 405 1.1 agc 406 1.1 agc 407 1.1 agc /* 408 1.1 agc * get_registration_info 409 1.1 agc * 410 1.1 agc * Parameters: The server descriptor 411 1.1 agc * 412 1.1 agc * Returns: status 413 1.1 agc */ 414 1.1 agc 415 1.1 agc 416 1.1 agc static uint32_t 417 1.1 agc get_registration_info(isns_t * isns) 418 1.1 agc { 419 1.1 agc struct sockaddr_storage sa; 420 1.1 agc unsigned n; 421 1.1 agc 422 1.1 agc strlcpy((char *)isns->reg_iscsi_name, (char *)node_name.InitiatorName, 423 1.1 agc sizeof(isns->reg_iscsi_name)); 424 1.1 agc strlcpy((char *)isns->reg_entity_id, (char *)node_name.InitiatorAlias, 425 1.1 agc sizeof(isns->reg_entity_id)); 426 1.1 agc 427 1.1 agc /*Get our source IP and port numbers */ 428 1.1 agc n = sizeof(sa); 429 1.2 christos if (getsockname(isns->sock, (struct sockaddr *)(void *)&sa, &n)) { 430 1.5 mlelstv DEB(10,("Getsockname returned error %d", errno)); 431 1.1 agc return ISCSID_STATUS_GENERAL_ERROR; 432 1.1 agc } 433 1.1 agc switch (sa.ss_family) { 434 1.1 agc case AF_INET: 435 1.1 agc { 436 1.2 christos struct sockaddr_in *si = 437 1.2 christos (struct sockaddr_in *)(void *)&sa; 438 1.2 christos uint32_t *u32 = (uint32_t *)(void *)isns->reg_ip_addr; 439 1.1 agc 440 1.1 agc u32[0] = u32[1] = 0; 441 1.1 agc u32[2] = htonl(0xffff); 442 1.1 agc u32[3] = htonl(si->sin_addr.s_addr); 443 1.1 agc isns->reg_ip_port = htons(si->sin_port); 444 1.1 agc } 445 1.1 agc break; 446 1.1 agc 447 1.1 agc case AF_INET6: 448 1.1 agc { 449 1.2 christos struct sockaddr_in6 *si = 450 1.2 christos (struct sockaddr_in6 *)(void *) &sa; 451 1.1 agc 452 1.1 agc memcpy(isns->reg_ip_addr, &si->sin6_addr, 453 1.1 agc sizeof(isns->reg_ip_addr)); 454 1.1 agc isns->reg_ip_port = htons(si->sin6_port); 455 1.1 agc } 456 1.1 agc break; 457 1.1 agc 458 1.1 agc default: 459 1.5 mlelstv DEB(10,("Getsockname returned unknown address family: %d", 460 1.1 agc sa.ss_family)); 461 1.1 agc return ISCSID_STATUS_GENERAL_ERROR; 462 1.1 agc } 463 1.1 agc return ISCSID_STATUS_SUCCESS; 464 1.1 agc } 465 1.1 agc 466 1.1 agc 467 1.1 agc /* 468 1.1 agc * iscsi_isns_serverconn - given a set of server address, try connecting 469 1.1 agc * 470 1.1 agc * Parameters: The descriptor for the iSNS server to query 471 1.1 agc * 472 1.1 agc * Returns: status 473 1.1 agc */ 474 1.1 agc 475 1.1 agc static uint32_t 476 1.1 agc iscsi_isns_serverconn(isns_t * isns) 477 1.1 agc { 478 1.1 agc int sock = -1; 479 1.1 agc char port[16]; 480 1.1 agc struct addrinfo hints; 481 1.1 agc struct addrinfo *ai, *addr; 482 1.1 agc int retval; 483 1.1 agc 484 1.1 agc /* 485 1.1 agc * Initialize the iSNS library if it needs it 486 1.1 agc */ 487 1.1 agc 488 1.1 agc if (isns_handle == ISNS_INVALID_HANDLE) { 489 1.1 agc if ((retval = isns_init(&isns_handle, 0)) != 0) { 490 1.1 agc /*Couldn't initialize the iSNS library */ 491 1.5 mlelstv DEB(10,("isns_init failed with code %d", retval)); 492 1.1 agc isns_handle = ISNS_INVALID_HANDLE; 493 1.1 agc return ISCSID_STATUS_GENERAL_ERROR; 494 1.1 agc } 495 1.1 agc } 496 1.1 agc 497 1.1 agc /* 498 1.1 agc * Find the server address from the iSNS server list entry, 499 1.1 agc * and try to connect to the iSNS server 500 1.1 agc */ 501 1.1 agc 502 1.1 agc snprintf(port, sizeof(port), "%d", (isns->port) ? isns->port : ISCSI_DEFAULT_ISNS_PORT); 503 1.1 agc memset(&hints, 0, sizeof(hints)); 504 1.1 agc hints.ai_family = PF_UNSPEC; 505 1.1 agc hints.ai_socktype = SOCK_STREAM; 506 1.1 agc hints.ai_protocol = IPPROTO_TCP; 507 1.1 agc 508 1.1 agc retval = getaddrinfo((char *)isns->address, port, &hints, &ai); 509 1.1 agc if (retval) { 510 1.5 mlelstv DEB(10,("getaddrinfo failed with code %d (%s)", 511 1.1 agc retval, gai_strerror(retval))); 512 1.1 agc return ISCSID_STATUS_GENERAL_ERROR; 513 1.1 agc } 514 1.1 agc 515 1.1 agc for (addr = ai; addr != NULL; addr = addr->ai_next) { 516 1.2 christos sock = socket(addr->ai_family, addr->ai_socktype, 517 1.2 christos addr->ai_protocol); 518 1.1 agc 519 1.2 christos if (sock == -1) { 520 1.5 mlelstv DEB(10,("%s: socket call FAILED!", __func__)); 521 1.1 agc freeaddrinfo(ai); 522 1.2 christos return (uint32_t)-1; 523 1.1 agc } 524 1.1 agc 525 1.4 mlelstv if (connect(sock, addr->ai_addr, addr->ai_addrlen) != -1) 526 1.1 agc break; 527 1.1 agc 528 1.5 mlelstv DEB(1, ("%s: connect call FAILED!", __func__)); 529 1.1 agc close(sock); 530 1.1 agc sock = -1; 531 1.1 agc } 532 1.1 agc 533 1.1 agc if (addr == NULL) { 534 1.5 mlelstv DEB(10,("%s: couldn't connect!", __func__)); 535 1.1 agc freeaddrinfo(ai); 536 1.1 agc return ISCSID_STATUS_GENERAL_ERROR; 537 1.1 agc } 538 1.1 agc 539 1.1 agc if (isns_add_servercon(isns_handle, sock, addr)) { 540 1.5 mlelstv DEB(10,("%s: FAILED!", __func__)); 541 1.1 agc close(sock); 542 1.1 agc freeaddrinfo(ai); 543 1.1 agc return ISCSID_STATUS_GENERAL_ERROR; 544 1.1 agc } 545 1.1 agc 546 1.1 agc freeaddrinfo(ai); 547 1.1 agc isns->sock = sock; 548 1.1 agc 549 1.1 agc if ((retval = get_registration_info(isns)) != 0) { 550 1.1 agc return retval; 551 1.1 agc } 552 1.1 agc 553 1.1 agc deregister_isns_server(isns); 554 1.1 agc 555 1.1 agc return register_isns_server(isns); 556 1.1 agc } 557 1.1 agc 558 1.1 agc 559 1.1 agc /* 560 1.1 agc * update_isns_server_info 561 1.1 agc * Support routine to query the specified iSNS server for all targets 562 1.1 agc * Called from add_isns_server and refresh_isns_server 563 1.1 agc * 564 1.1 agc * Parameters: The descriptor for the iSNS server to query 565 1.1 agc * 566 1.1 agc * Returns: status 567 1.1 agc */ 568 1.1 agc 569 1.1 agc 570 1.1 agc static uint32_t 571 1.1 agc update_isns_server_info(isns_t * isns) 572 1.1 agc { 573 1.1 agc int retval; 574 1.1 agc ISNS_TRANS t; 575 1.1 agc uint32_t tag; 576 1.2 christos uint32_t data_len; 577 1.1 agc void *data_p; 578 1.1 agc uint32_t u32; 579 1.1 agc struct timespec tout = { 5, 0 }; 580 1.1 agc uint32_t status; 581 1.1 agc uint8_t TargetName[ISCSI_STRING_LENGTH]; 582 1.1 agc 583 1.1 agc 584 1.5 mlelstv DEB(9, ("update_isns_server_info for iSNS %s", isns->address)); 585 1.1 agc 586 1.1 agc if (isns->sock < 0) { 587 1.1 agc if ((status = iscsi_isns_serverconn(isns)) != 0) { 588 1.1 agc /*We couldn't connect to the iSNS server */ 589 1.5 mlelstv DEB(9, ("update_isns_server_info iscsi_isns_serverconn failed")); 590 1.1 agc return status; 591 1.1 agc } 592 1.1 agc } 593 1.1 agc 594 1.1 agc for (TargetName[0] = 0;;) { 595 1.1 agc if (ISNS_INVALID_TRANS == (t = isns_new_trans(isns_handle, 596 1.1 agc isnsp_DevGetNext, MY_FLAGS))) { 597 1.5 mlelstv DEB(10,("update_isns_server_info iscsi_new_trans failed")); 598 1.1 agc return ISCSID_STATUS_NO_RESOURCES; 599 1.1 agc } 600 1.1 agc 601 1.1 agc isns_add_string(t, isnst_iSCSIName, (char *)node_name.InitiatorName); 602 1.1 agc 603 1.1 agc if (TargetName[0]) 604 1.1 agc isns_add_string(t, isnst_iSCSIName, (char *)TargetName); 605 1.1 agc else 606 1.1 agc isns_add_tlv(t, isnst_iSCSIName, 0, NULL); 607 1.1 agc 608 1.1 agc isns_add_tlv(t, isnst_Delimiter, 0, NULL); 609 1.1 agc isns_add_tlv(t, isnst_iSCSINodeType, 0, NULL); 610 1.1 agc 611 1.1 agc if ((retval = isns_send_trans(t, &tout, &status)) != 0) { 612 1.5 mlelstv DEB(10,("isns_send_trans returns rc %d, status %d", 613 1.1 agc retval, status)); 614 1.1 agc isns_free_trans(t); 615 1.1 agc break; 616 1.1 agc } 617 1.1 agc if (status) { 618 1.5 mlelstv DEB(9, ("DevGetNext Status = %d", status)); 619 1.1 agc break; 620 1.1 agc } 621 1.1 agc 622 1.1 agc if (isns_get_tlv(t, ISNS_TLV_FIRST, &tag, &data_len, &data_p)) { 623 1.5 mlelstv DEB(10,("No TLV in DevGetNext response!")); 624 1.1 agc isns_free_trans(t); 625 1.1 agc break; 626 1.1 agc } 627 1.1 agc /* We need the name, or there's nothing left to do */ 628 1.1 agc 629 1.1 agc if (tag != isnst_iSCSIName || !data_len || 630 1.1 agc data_len >= ISCSI_STRING_LENGTH) { 631 1.5 mlelstv DEB(10,("iSNS GetNextDev returned no or invalid name (tag=%d, " 632 1.5 mlelstv "length=%d)", tag, data_len)); 633 1.1 agc isns_free_trans(t); 634 1.1 agc break; 635 1.1 agc } 636 1.1 agc strlcpy((char *)TargetName, (char *) data_p, sizeof(TargetName)); 637 1.1 agc 638 1.1 agc /* We must get at least the node type, and it must be a target */ 639 1.1 agc if (isns_get_tlv(t, ISNS_TLV_NEXT, &tag, &data_len, &data_p)) { 640 1.5 mlelstv DEB(10,("iSNS GetDevNext did not return node type")); 641 1.1 agc isns_free_trans(t); 642 1.1 agc break; 643 1.1 agc } 644 1.1 agc if (tag == isnst_Delimiter && isns_get_tlv(t, ISNS_TLV_NEXT, &tag, 645 1.1 agc &data_len, &data_p)) { 646 1.5 mlelstv DEB(10,("iSNS GetDevNext did not return node type (past delim)")); 647 1.1 agc isns_free_trans(t); 648 1.1 agc break; 649 1.1 agc } 650 1.1 agc if (tag != isnst_iSCSINodeType || data_len != 4) { 651 1.5 mlelstv DEB(10,("iSNS Query returned no or invalid node type (tag=%d, " 652 1.5 mlelstv "length=%d)", tag, data_len)); 653 1.1 agc isns_free_trans(t); 654 1.1 agc break; 655 1.1 agc } 656 1.1 agc u32 = ntohl(*((uint32_t *) data_p)); 657 1.1 agc isns_free_trans(t); 658 1.1 agc 659 1.1 agc if (u32 & 1) 660 1.1 agc get_isns_target_info(isns, TargetName); 661 1.1 agc } 662 1.1 agc 663 1.5 mlelstv DEB(9, ("update_isns_server_info returning SUCCESS!")); 664 1.1 agc return ISCSID_STATUS_SUCCESS; 665 1.1 agc } 666 1.1 agc 667 1.1 agc 668 1.1 agc /********************************************************************** 669 1.1 agc **********************************************************************/ 670 1.1 agc 671 1.1 agc /* 672 1.1 agc * create_isns: 673 1.1 agc * Create an isns structure and initialize it. 674 1.1 agc * 675 1.1 agc * Parameter: 676 1.1 agc * name The iSNS server name 677 1.1 agc * 678 1.1 agc * Returns: Pointer to isns structure, NULL if allocation failed. 679 1.1 agc */ 680 1.1 agc 681 1.3 agc static isns_t * 682 1.1 agc create_isns(iscsid_add_isns_server_req_t * req) 683 1.1 agc { 684 1.1 agc isns_t *isns; 685 1.1 agc 686 1.5 mlelstv DEB(9, ("Create iSNS %s", req->address)); 687 1.1 agc 688 1.1 agc if ((isns = calloc(1, sizeof(*isns))) == NULL) 689 1.1 agc return NULL; 690 1.1 agc 691 1.1 agc for (isns_id++; !isns_id || find_isns_id(isns_id) != NULL;) 692 1.1 agc isns_id++; 693 1.1 agc 694 1.1 agc isns->entry.sid.id = isns_id; 695 1.1 agc strlcpy((char *)isns->entry.sid.name, (char *)req->name, sizeof(isns->entry.sid.name)); 696 1.1 agc strlcpy((char *)isns->address, (char *)req->address, sizeof(isns->address)); 697 1.1 agc isns->port = req->port; 698 1.1 agc isns->sock = -1; 699 1.1 agc 700 1.1 agc return isns; 701 1.1 agc } 702 1.1 agc 703 1.1 agc 704 1.1 agc /* 705 1.1 agc * add_isns_server 706 1.1 agc * Adds an iSNS server to the server list. 707 1.1 agc * This command will add the address of an iSNS server to the list 708 1.1 agc * of iSNS servers that the discovery daemon queries to discover targets. 709 1.1 agc * The daemon will then register itself with the iSNS server, 710 1.1 agc * and query the iSNS server for the list of targets. 711 1.1 agc * The discovered targets will be added to the list of target portals. 712 1.1 agc * The response contains the ID of the iSNS server. 713 1.1 agc * 714 1.1 agc * Parameter: The parameter contains the address of the server. 715 1.1 agc * 716 1.1 agc * Returns: Nothing 717 1.1 agc * The response parameter is an iscsid_add_isns_server_rsp_t 718 1.1 agc * containing: 719 1.1 agc * server_id = Unique ID for the iSNS server 720 1.1 agc */ 721 1.1 agc 722 1.1 agc void 723 1.1 agc add_isns_server(iscsid_add_isns_server_req_t * req, iscsid_response_t ** prsp, 724 1.1 agc int *prsp_temp) 725 1.1 agc { 726 1.1 agc iscsid_response_t *rsp = *prsp; 727 1.1 agc iscsid_add_isns_server_rsp_t *res; 728 1.1 agc isns_t *isns; 729 1.1 agc 730 1.5 mlelstv DEB(9, ("IN add_isns_server")); 731 1.1 agc 732 1.1 agc /* 733 1.1 agc * Make a response 734 1.1 agc */ 735 1.1 agc 736 1.1 agc rsp = make_rsp(sizeof(iscsid_add_isns_server_rsp_t), prsp, prsp_temp); 737 1.1 agc if (rsp == NULL) { 738 1.5 mlelstv DEB(9, ("OUT add_isns_server: make_rsp FAILED")); 739 1.1 agc return; 740 1.1 agc } 741 1.1 agc 742 1.2 christos res = (iscsid_add_isns_server_rsp_t *)(void *)rsp->parameter; 743 1.1 agc 744 1.1 agc /* 745 1.1 agc * First, allocate the isns server structure to put on the list 746 1.1 agc */ 747 1.1 agc 748 1.1 agc isns = create_isns(req); 749 1.1 agc if (isns == NULL) { 750 1.1 agc rsp->status = ISCSID_STATUS_NO_RESOURCES; 751 1.5 mlelstv DEB(9, ("OUT add_isns_server: create_isns FAILED!")); 752 1.1 agc return; 753 1.1 agc } 754 1.1 agc 755 1.1 agc TAILQ_INSERT_TAIL(&list[ISNS_LIST].list, &isns->entry, link); 756 1.1 agc list[ISNS_LIST].num_entries++; 757 1.1 agc res->server_id = isns->entry.sid.id; 758 1.1 agc 759 1.5 mlelstv DEB(9, ("OUT add_isns_server: server_id = %d, name = %s", 760 1.1 agc isns->entry.sid.id, isns->address)); 761 1.1 agc 762 1.1 agc /* 763 1.1 agc * Now try to connect to the iSNS server... 764 1.1 agc */ 765 1.1 agc 766 1.1 agc update_isns_server_info(isns); 767 1.1 agc } 768 1.1 agc 769 1.1 agc 770 1.1 agc /* 771 1.1 agc * get_isns_server 772 1.1 agc * Returns the address of the iSNS server with the specified ID 773 1.1 agc * 774 1.1 agc * Parameters: The unique ID of the server 775 1.1 agc * 776 1.1 agc * Returns: The status returned by the driver 777 1.1 agc * The response parameter contains the iSNS server address as a 778 1.1 agc * zero-terminated UTF-8 string 779 1.1 agc */ 780 1.1 agc 781 1.1 agc void 782 1.1 agc get_isns_server(iscsid_sym_id_t * preq, iscsid_response_t ** prsp, 783 1.1 agc int *prsp_temp) 784 1.1 agc { 785 1.1 agc iscsid_response_t *rsp = *prsp; 786 1.1 agc iscsid_get_isns_server_rsp_t *res; 787 1.1 agc isns_t *isns; 788 1.1 agc 789 1.5 mlelstv DEB(9, ("IN get_isns_server")); 790 1.1 agc isns = find_isns(preq); 791 1.1 agc if (isns == NULL) { 792 1.1 agc rsp->status = ISCSID_STATUS_INVALID_ISNS_ID; 793 1.5 mlelstv DEB(9, ("OUT get_isns_server: find_isns FAILED!")); 794 1.1 agc return; 795 1.1 agc } 796 1.1 agc 797 1.1 agc rsp = make_rsp(sizeof(iscsid_get_isns_server_rsp_t), prsp, prsp_temp); 798 1.1 agc if (rsp == NULL) { 799 1.5 mlelstv DEB(9, ("OUT get_isns_server: make_rsp FAILED!")); 800 1.1 agc return; 801 1.1 agc } 802 1.2 christos res = (iscsid_get_isns_server_rsp_t *)(void *)rsp->parameter; 803 1.1 agc 804 1.2 christos strlcpy((char *)res->address, (char *)isns->address, 805 1.2 christos sizeof(res->address)); 806 1.1 agc res->port = isns->port; 807 1.1 agc res->server_id = isns->entry.sid; 808 1.5 mlelstv DEB(9, ("OUT get_isns_server: id = %d, address = %s", 809 1.1 agc res->server_id.id, res->address)); 810 1.1 agc } 811 1.1 agc 812 1.1 agc 813 1.1 agc /* 814 1.1 agc * slp_find_isns_servers 815 1.1 agc */ 816 1.1 agc 817 1.1 agc /* More Here Later... */ 818 1.1 agc 819 1.1 agc 820 1.1 agc /* 821 1.1 agc * refresh_isns_server 822 1.1 agc * Query the specified iSNS servers for the list of targets. 823 1.1 agc * 824 1.1 agc * Parameters: 825 1.1 agc * id Server ID 826 1.1 agc * 827 1.1 agc * Returns: Status 828 1.1 agc */ 829 1.1 agc 830 1.1 agc uint32_t 831 1.1 agc refresh_isns_server(uint32_t id) 832 1.1 agc { 833 1.1 agc uint32_t rc; 834 1.1 agc isns_t *isns; 835 1.1 agc generic_entry_t *curr; 836 1.1 agc generic_entry_t *next; 837 1.1 agc 838 1.1 agc isns = find_isns_id(id); 839 1.1 agc if (isns == NULL) 840 1.1 agc return ISCSID_STATUS_INVALID_ISNS_ID; 841 1.1 agc 842 1.1 agc TAILQ_FOREACH(curr, &list[PORTAL_LIST].list, link) { 843 1.2 christos portal_t *p = (portal_t *)(void *)curr; 844 1.2 christos if (p->portaltype == PORTAL_TYPE_ISNS && p->discoveryid == id) 845 1.2 christos p->portaltype = PORTAL_TYPE_REFRESHING; 846 1.1 agc } 847 1.1 agc 848 1.1 agc rc = update_isns_server_info(isns); 849 1.1 agc 850 1.1 agc /* 851 1.1 agc * Go through our list of portals and look for ones 852 1.1 agc * that are still marked for refreshing. 853 1.1 agc * These are ones that are no longer there and should be removed. 854 1.1 agc */ 855 1.1 agc 856 1.1 agc for (curr = TAILQ_FIRST(&list[PORTAL_LIST].list); curr != NULL; 857 1.1 agc curr = next) { 858 1.2 christos portal_t *p = (portal_t *)(void *)curr; 859 1.1 agc next = TAILQ_NEXT(curr, link); 860 1.2 christos if (p->portaltype == PORTAL_TYPE_REFRESHING) 861 1.2 christos delete_portal(p, TRUE); 862 1.1 agc } 863 1.1 agc 864 1.1 agc return rc; 865 1.1 agc } 866 1.1 agc 867 1.1 agc 868 1.1 agc /* 869 1.1 agc * remove_isns_server 870 1.1 agc * Removed an iSNS server. 871 1.1 agc * This does not remove the discovered targets from the list. 872 1.1 agc * 873 1.1 agc * Parameters: The iscid_remove_isns_req_t structure containing: 874 1.1 agc * server_id = unique ID of server to remove 875 1.1 agc * 876 1.1 agc * Returns: The status returned. 877 1.1 agc */ 878 1.1 agc 879 1.1 agc uint32_t 880 1.1 agc remove_isns_server(iscsid_sym_id_t * preq) 881 1.1 agc { 882 1.1 agc generic_entry_t *curr; 883 1.1 agc isns_t *isns; 884 1.1 agc uint32_t id; 885 1.1 agc 886 1.1 agc isns = find_isns(preq); 887 1.1 agc if (isns == NULL) 888 1.1 agc return ISCSID_STATUS_INVALID_ISNS_ID; 889 1.1 agc 890 1.1 agc /*Deregister with the iSNS server. */ 891 1.1 agc /*Ignore any errors during deregistration... */ 892 1.1 agc if (isns->sock >= 0) { 893 1.1 agc deregister_isns_server(isns); 894 1.1 agc close(isns->sock); 895 1.1 agc } 896 1.1 agc 897 1.1 agc TAILQ_REMOVE(&list[ISNS_LIST].list, &isns->entry, link); 898 1.1 agc list[ISNS_LIST].num_entries--; 899 1.1 agc 900 1.1 agc id = isns->entry.sid.id; 901 1.1 agc free(isns); 902 1.1 agc 903 1.1 agc TAILQ_FOREACH(curr, &list[PORTAL_LIST].list, link) { 904 1.2 christos portal_t *p = (portal_t *)(void *)curr; 905 1.2 christos if (p->portaltype == PORTAL_TYPE_ISNS && p->discoveryid == id) 906 1.2 christos p->discoveryid = 0; /* mark deleted */ 907 1.1 agc } 908 1.1 agc 909 1.1 agc return ISCSID_STATUS_SUCCESS; 910 1.1 agc } 911 1.1 agc 912 1.1 agc 913 1.1 agc /* 914 1.1 agc Deregister all isns servers on daemon termination 915 1.1 agc */ 916 1.1 agc 917 1.1 agc void 918 1.1 agc dereg_all_isns_servers(void) 919 1.1 agc { 920 1.1 agc generic_list_t *plist; 921 1.1 agc generic_entry_t *curr; 922 1.1 agc 923 1.1 agc plist = &list[ISNS_LIST].list; 924 1.1 agc TAILQ_FOREACH(curr, plist, link) 925 1.2 christos deregister_isns_server((isns_t *)(void *)curr); 926 1.1 agc } 927 1.1 agc 928 1.1 agc #endif 929 1.1 agc 930