dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / cmd / isns / isnsd / pdu.c
blob4a52f5ae80017da0ad2466b9324c4c83653a0929
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
19 * CDDL HEADER END
23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <strings.h>
31 #include <sys/types.h>
32 #include <sys/socket.h>
33 #include <netinet/in.h>
34 #include <arpa/inet.h>
35 #include <unistd.h>
36 #include <poll.h>
37 #include <errno.h>
39 #include "isns_server.h"
40 #include "isns_log.h"
41 #include "isns_pdu.h"
43 #define ISNS_MAX_IOVEC 5
44 #define MAX_XID (2^16)
45 #define MAX_RCV_RSP_COUNT 10 /* Maximum number of unmatched xid */
46 #define ISNS_RCV_RETRY_MAX 2
47 #define IPV4_RSVD_BYTES 10
49 /* externs */
50 #ifdef DEBUG
51 extern void dump_pdu2(isns_pdu_t *);
52 #endif
55 * local functions.
58 size_t
59 isns_rcv_pdu(
60 int fd,
61 isns_pdu_t **pdu,
62 size_t *pdu_size,
63 int rcv_timeout
66 int poll_cnt;
67 struct pollfd fds;
68 iovec_t iovec[ISNS_MAX_IOVEC];
69 isns_pdu_t *tmp_pdu_hdr;
70 ssize_t bytes_received, total_bytes_received = 0;
71 struct msghdr msg;
72 uint8_t *tmp_pdu_data;
74 uint16_t payload_len = 0;
76 /* initialize to zero */
77 *pdu = NULL;
78 *pdu_size = 0;
80 fds.fd = fd;
81 fds.events = (POLLIN | POLLRDNORM);
82 fds.revents = 0;
84 /* Receive the header first */
85 tmp_pdu_hdr = (isns_pdu_t *)malloc(ISNSP_HEADER_SIZE);
86 if (tmp_pdu_hdr == NULL) {
87 return (0);
89 (void) memset(&tmp_pdu_hdr[0], 0, ISNSP_HEADER_SIZE);
90 (void) memset(&iovec[0], 0, sizeof (iovec_t));
91 iovec[0].iov_base = (void *)tmp_pdu_hdr;
92 iovec[0].iov_len = ISNSP_HEADER_SIZE;
94 /* Initialization of the message header. */
95 bzero(&msg, sizeof (msg));
96 msg.msg_iov = &iovec[0];
97 /* msg.msg_flags = MSG_WAITALL, */
98 msg.msg_iovlen = 1;
100 /* Poll and receive the pdu header */
101 poll_cnt = 0;
102 do {
103 int err = poll(&fds, 1, rcv_timeout * 1000);
104 if (err <= 0) {
105 poll_cnt ++;
106 } else {
107 bytes_received = recvmsg(fd, &msg, MSG_WAITALL);
108 break;
110 } while (poll_cnt < ISNS_RCV_RETRY_MAX);
112 if (poll_cnt >= ISNS_RCV_RETRY_MAX) {
113 free(tmp_pdu_hdr);
114 return (0);
117 if (bytes_received <= 0) {
118 free(tmp_pdu_hdr);
119 return (0);
122 total_bytes_received += bytes_received;
124 payload_len = ntohs(tmp_pdu_hdr->payload_len);
125 /* Verify the received payload len is within limit */
126 if (payload_len > ISNSP_MAX_PAYLOAD_SIZE) {
127 free(tmp_pdu_hdr);
128 return (0);
131 /* Proceed to receive additional data. */
132 tmp_pdu_data = malloc(payload_len);
133 if (tmp_pdu_data == NULL) {
134 free(tmp_pdu_hdr);
135 return (0);
137 (void) memset(&iovec[0], 0, sizeof (iovec_t));
138 iovec[0].iov_base = (void *)tmp_pdu_data;
139 iovec[0].iov_len = payload_len;
141 /* Initialization of the message header. */
142 bzero(&msg, sizeof (msg));
143 msg.msg_iov = &iovec[0];
144 /* msg.msg_flags = MSG_WAITALL, */
145 msg.msg_iovlen = 1;
147 /* poll and receive the pdu payload */
148 poll_cnt = 0;
149 do {
150 int err = poll(&fds, 1, rcv_timeout * 1000);
151 if (err <= 0) {
152 poll_cnt ++;
153 } else {
154 bytes_received = recvmsg(fd, &msg, MSG_WAITALL);
155 break;
157 } while (poll_cnt < ISNS_RCV_RETRY_MAX);
159 if (poll_cnt >= ISNS_RCV_RETRY_MAX) {
160 free(tmp_pdu_data);
161 free(tmp_pdu_hdr);
162 return (0);
165 if (bytes_received <= 0) {
166 free(tmp_pdu_data);
167 free(tmp_pdu_hdr);
168 return (0);
171 total_bytes_received += bytes_received;
173 *pdu_size = ISNSP_HEADER_SIZE + payload_len;
174 (*pdu) = (isns_pdu_t *)malloc(*pdu_size);
175 if (*pdu == NULL) {
176 *pdu_size = 0;
177 free(tmp_pdu_data);
178 free(tmp_pdu_hdr);
179 return (0);
181 (*pdu)->version = ntohs(tmp_pdu_hdr->version);
182 (*pdu)->func_id = ntohs(tmp_pdu_hdr->func_id);
183 (*pdu)->payload_len = payload_len;
184 (*pdu)->flags = ntohs(tmp_pdu_hdr->flags);
185 (*pdu)->xid = ntohs(tmp_pdu_hdr->xid);
186 (*pdu)->seq = ntohs(tmp_pdu_hdr->seq);
187 (void) memcpy(&((*pdu)->payload), tmp_pdu_data, payload_len);
189 free(tmp_pdu_data);
190 tmp_pdu_data = NULL;
191 free(tmp_pdu_hdr);
192 tmp_pdu_hdr = NULL;
194 return (total_bytes_received);
198 isns_send_pdu(
199 int fd,
200 isns_pdu_t *pdu,
201 size_t pl
204 uint8_t *payload;
205 uint16_t flags;
206 uint16_t seq;
207 iovec_t iovec[ISNS_MAX_IOVEC];
208 struct msghdr msg = { 0 };
210 size_t send_len;
211 ssize_t bytes_sent;
214 /* Initialization of the message header. */
215 msg.msg_iov = &iovec[0];
216 /* msg.msg_flags = MSG_WAITALL, */
217 msg.msg_iovlen = 2;
220 * Initialize the pdu flags.
222 flags = ISNS_FLAG_SERVER;
223 flags |= ISNS_FLAG_FIRST_PDU;
226 * Initialize the pdu sequence id.
228 seq = 0;
230 iovec[0].iov_base = (void *)pdu;
231 iovec[0].iov_len = (ISNSP_HEADER_SIZE);
233 payload = pdu->payload;
235 #ifdef DEBUG
236 pdu->flags = htons(flags);
237 pdu->seq = htons(0);
238 pdu->payload_len = htons(pl);
239 dump_pdu2(pdu);
240 #endif
242 do {
243 /* set the payload for sending */
244 iovec[1].iov_base = (void *)payload;
246 if (pl > ISNSP_MAX_PAYLOAD_SIZE) {
247 send_len = ISNSP_MAX_PAYLOAD_SIZE;
248 } else {
249 send_len = pl;
250 /* set the last pdu flag */
251 flags |= ISNS_FLAG_LAST_PDU;
253 iovec[1].iov_len = send_len;
254 pdu->payload_len = htons(send_len);
256 /* set the pdu flags */
257 pdu->flags = htons(flags);
258 /* set the pdu sequence id */
259 pdu->seq = htons(seq);
261 /* send the packet */
262 bytes_sent = sendmsg(fd, &msg, 0);
264 /* get rid of the first pdu flag */
265 flags &= ~(ISNS_FLAG_FIRST_PDU);
267 /* next part of payload */
268 payload += send_len;
269 pl -= send_len;
271 /* add the length of header for verification */
272 send_len += ISNSP_HEADER_SIZE;
274 /* increase the sequence id by one */
275 seq ++;
276 } while (bytes_sent == send_len && pl > 0);
278 if (bytes_sent == send_len) {
279 return (0);
280 } else {
281 isnslog(LOG_DEBUG, "isns_send_pdu", "sending pdu failed.");
282 return (-1);
286 #define RSP_PDU_FRAG_SZ (ISNSP_MAX_PDU_SIZE / 10)
287 static int
288 pdu_reset(
289 isns_pdu_t **rsp,
290 size_t *sz
293 int ec = 0;
295 if (*rsp == NULL) {
296 *rsp = (isns_pdu_t *)malloc(RSP_PDU_FRAG_SZ);
297 if (*rsp != NULL) {
298 *sz = RSP_PDU_FRAG_SZ;
299 } else {
300 ec = ISNS_RSP_INTERNAL_ERROR;
304 return (ec);
308 pdu_reset_rsp(
309 isns_pdu_t **rsp,
310 size_t *pl,
311 size_t *sz
314 int ec = pdu_reset(rsp, sz);
316 if (ec == 0) {
317 /* leave space for status code */
318 *pl = 4;
321 return (ec);
325 pdu_reset_scn(
326 isns_pdu_t **pdu,
327 size_t *pl,
328 size_t *sz
331 int ec = pdu_reset(pdu, sz);
333 if (ec == 0) {
334 *pl = 0;
337 return (ec);
341 pdu_reset_esi(
342 isns_pdu_t **pdu,
343 size_t *pl,
344 size_t *sz
347 return (pdu_reset_scn(pdu, pl, sz));
351 pdu_update_code(
352 isns_pdu_t *pdu,
353 size_t *pl,
354 int code
357 isns_resp_t *resp;
359 resp = (isns_resp_t *)pdu->payload;
361 /* reset the payload length */
362 if (code != ISNS_RSP_SUCCESSFUL || *pl == 0) {
363 *pl = 4;
366 resp->status = htonl(code);
368 return (0);
372 pdu_add_tlv(
373 isns_pdu_t **pdu,
374 size_t *pl,
375 size_t *sz,
376 uint32_t attr_id,
377 uint32_t attr_len,
378 void *attr_data,
379 int pflag
382 int ec = 0;
384 isns_pdu_t *new_pdu;
385 size_t new_sz;
387 isns_tlv_t *attr_tlv;
388 uint8_t *payload_ptr;
389 uint32_t normalized_attr_len;
390 uint64_t attr_tlv_len;
392 /* The attribute length must be 4-byte aligned. Section 5.1.3. */
393 normalized_attr_len = (attr_len % 4) == 0 ? (attr_len) :
394 (attr_len + (4 - (attr_len % 4)));
395 attr_tlv_len = ISNS_TLV_ATTR_ID_LEN
396 + ISNS_TLV_ATTR_LEN_LEN
397 + normalized_attr_len;
398 /* Check if we are going to exceed the maximum PDU length. */
399 if ((ISNSP_HEADER_SIZE + *pl + attr_tlv_len) > *sz) {
400 new_sz = *sz + RSP_PDU_FRAG_SZ;
401 new_pdu = (isns_pdu_t *)realloc(*pdu, new_sz);
402 if (new_pdu != NULL) {
403 *sz = new_sz;
404 *pdu = new_pdu;
405 } else {
406 ec = ISNS_RSP_INTERNAL_ERROR;
407 return (ec);
411 attr_tlv = (isns_tlv_t *)malloc(attr_tlv_len);
412 (void) memset(attr_tlv, 0, attr_tlv_len);
414 attr_tlv->attr_id = htonl(attr_id);
416 switch (attr_id) {
417 case ISNS_DELIMITER_ATTR_ID:
418 break;
420 case ISNS_PORTAL_IP_ADDR_ATTR_ID:
421 case ISNS_PG_PORTAL_IP_ADDR_ATTR_ID:
422 /* IPv6 */
423 ASSERT(attr_len == sizeof (in6_addr_t));
424 (void) memcpy(attr_tlv->attr_value, attr_data,
425 sizeof (in6_addr_t));
426 break;
428 case ISNS_EID_ATTR_ID:
429 case ISNS_ISCSI_NAME_ATTR_ID:
430 case ISNS_ISCSI_ALIAS_ATTR_ID:
431 case ISNS_PG_ISCSI_NAME_ATTR_ID:
432 (void) memcpy(attr_tlv->attr_value, (char *)attr_data,
433 attr_len);
434 break;
436 default:
437 if (attr_len == 8) {
438 if (pflag == 0) {
440 * In the iSNS protocol, there is only one
441 * attribute ISNS_TIMESTAMP_ATTR_ID which has
442 * 8 bytes length integer value and when the
443 * function "pdu_add_tlv" is called for adding
444 * the timestamp attribute, the value of
445 * the attribute is always passed in as its
446 * address, i.e. the pflag sets to 1.
447 * So it is an error when we get to this code
448 * path.
450 ec = ISNS_RSP_INTERNAL_ERROR;
451 return (ec);
452 } else {
453 *(uint64_t *)attr_tlv->attr_value =
454 *(uint64_t *)attr_data;
456 } else if (attr_len == 4) {
457 if (pflag == 0) {
458 *(uint32_t *)attr_tlv->attr_value =
459 htonl((uint32_t)attr_data);
460 } else {
461 *(uint32_t *)attr_tlv->attr_value =
462 *(uint32_t *)attr_data;
465 break;
468 attr_tlv->attr_len = htonl(normalized_attr_len);
470 * Convert the network byte ordered payload length to host byte
471 * ordered for local address calculation.
473 payload_ptr = (*pdu)->payload + *pl;
474 (void) memcpy(payload_ptr, attr_tlv, attr_tlv_len);
475 *pl += attr_tlv_len;
478 * The payload length might exceed the maximum length of a
479 * payload that isnsp allows, we will split the payload and
480 * set the size of each payload before they are sent.
483 free(attr_tlv);
484 attr_tlv = NULL;
486 return (ec);
489 isns_tlv_t *
490 pdu_get_source(
491 isns_pdu_t *pdu
494 uint8_t *payload = &pdu->payload[0];
495 uint16_t payload_len = pdu->payload_len;
496 isns_tlv_t *tlv = NULL;
498 /* response code */
499 if (pdu->func_id & ISNS_RSP_MASK) {
500 if (payload_len < 4) {
501 return (NULL);
503 payload += 4;
504 payload_len -= 4;
507 if (payload_len > 8) {
508 tlv = (isns_tlv_t *)payload;
509 tlv->attr_id = ntohl(tlv->attr_id);
510 tlv->attr_len = ntohl(tlv->attr_len);
513 return (tlv);
516 isns_tlv_t *
517 pdu_get_key(
518 isns_pdu_t *pdu,
519 size_t *key_len
522 uint8_t *payload = &pdu->payload[0];
523 uint16_t payload_len = pdu->payload_len;
524 isns_tlv_t *tlv, *key;
526 /* reset */
527 *key_len = 0;
529 /* response code */
530 if (pdu->func_id & ISNS_RSP_MASK) {
531 if (payload_len <= 4) {
532 return (NULL);
534 payload += 4;
535 payload_len -= 4;
538 /* skip the soure */
539 if (payload_len >= 8) {
540 tlv = (isns_tlv_t *)payload;
541 payload += (8 + tlv->attr_len);
542 payload_len -= (8 + tlv->attr_len);
543 key = (isns_tlv_t *)payload;
544 while (payload_len >= 8) {
545 tlv = (isns_tlv_t *)payload;
546 tlv->attr_id = ntohl(tlv->attr_id);
547 tlv->attr_len = ntohl(tlv->attr_len);
548 if (tlv->attr_id == ISNS_DELIMITER_ATTR_ID) {
549 break;
551 *key_len += (8 + tlv->attr_len);
552 payload += (8 + tlv->attr_len);
553 payload_len -= (8 + tlv->attr_len);
557 if (*key_len >= 8) {
558 return (key);
561 return (NULL);
564 isns_tlv_t *
565 pdu_get_operand(
566 isns_pdu_t *pdu,
567 size_t *op_len
570 uint8_t *payload = &pdu->payload[0];
571 uint16_t payload_len = pdu->payload_len;
572 isns_tlv_t *tlv, *op = NULL;
573 int found_op = 0;
575 /* reset */
576 *op_len = 0;
578 /* response code */
579 if (pdu->func_id & ISNS_RSP_MASK) {
580 if (payload_len < 4) {
581 return (NULL);
583 payload += 4;
584 payload_len -= 4;
587 /* tlvs */
588 while (payload_len >= 8) {
589 tlv = (isns_tlv_t *)payload;
590 if (found_op != 0) {
591 tlv->attr_id = ntohl(tlv->attr_id);
592 tlv->attr_len = ntohl(tlv->attr_len);
593 payload += (8 + tlv->attr_len);
594 payload_len -= (8 + tlv->attr_len);
595 } else {
596 payload += (8 + tlv->attr_len);
597 payload_len -= (8 + tlv->attr_len);
598 if (tlv->attr_id == ISNS_DELIMITER_ATTR_ID) {
599 /* found it */
600 op = (isns_tlv_t *)payload;
601 *op_len = payload_len;
602 found_op = 1;
607 if (*op_len >= 8) {
608 return (op);
611 return (NULL);