1 /*
2 * CDDL HEADER START
3 *
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.
7 *
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.
12 *
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]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
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>
38
39 #include "isns_server.h"
40 #include "isns_log.h"
41 #include "isns_pdu.h"
42
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
48
49 /* externs */
50 #ifdef DEBUG
51 extern void dump_pdu2(isns_pdu_t *);
52 #endif
53
54 /*
55 * local functions.
56 */
57
58 size_t
isns_rcv_pdu(int fd,isns_pdu_t ** pdu,size_t * pdu_size,int rcv_timeout)59 isns_rcv_pdu(
60 int fd,
61 isns_pdu_t **pdu,
62 size_t *pdu_size,
63 int rcv_timeout
64 )
65 {
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;
73
74 uint16_t payload_len = 0;
75
76 /* initialize to zero */
77 *pdu = NULL;
78 *pdu_size = 0;
79
80 fds.fd = fd;
81 fds.events = (POLLIN | POLLRDNORM);
82 fds.revents = 0;
83
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);
88 }
89 (void) memset((void *)&tmp_pdu_hdr[0], 0, ISNSP_HEADER_SIZE);
90 (void) memset((void *)&iovec[0], 0, sizeof (iovec_t));
91 iovec[0].iov_base = (void *)tmp_pdu_hdr;
92 iovec[0].iov_len = ISNSP_HEADER_SIZE;
93
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;
99
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;
109 }
110 } while (poll_cnt < ISNS_RCV_RETRY_MAX);
111
112 if (poll_cnt >= ISNS_RCV_RETRY_MAX) {
113 free(tmp_pdu_hdr);
114 return (0);
115 }
116
117 if (bytes_received <= 0) {
118 free(tmp_pdu_hdr);
119 return (0);
120 }
121
122 total_bytes_received += bytes_received;
123
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);
129 }
130
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);
136 }
137 (void) memset((void *)&iovec[0], 0, sizeof (iovec_t));
138 iovec[0].iov_base = (void *)tmp_pdu_data;
139 iovec[0].iov_len = payload_len;
140
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;
146
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;
156 }
157 } while (poll_cnt < ISNS_RCV_RETRY_MAX);
158
159 if (poll_cnt >= ISNS_RCV_RETRY_MAX) {
160 free(tmp_pdu_data);
161 free(tmp_pdu_hdr);
162 return (0);
163 }
164
165 if (bytes_received <= 0) {
166 free(tmp_pdu_data);
167 free(tmp_pdu_hdr);
168 return (0);
169 }
170
171 total_bytes_received += bytes_received;
172
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);
180 }
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);
188
189 free(tmp_pdu_data);
190 tmp_pdu_data = NULL;
191 free(tmp_pdu_hdr);
192 tmp_pdu_hdr = NULL;
193
194 return (total_bytes_received);
195 }
196
197 int
isns_send_pdu(int fd,isns_pdu_t * pdu,size_t pl)198 isns_send_pdu(
199 int fd,
200 isns_pdu_t *pdu,
201 size_t pl
202 )
203 {
204 uint8_t *payload;
205 uint16_t flags;
206 uint16_t seq;
207 iovec_t iovec[ISNS_MAX_IOVEC];
208 struct msghdr msg = { 0 };
209
210 size_t send_len;
211 ssize_t bytes_sent;
212
213
214 /* Initialization of the message header. */
215 msg.msg_iov = &iovec[0];
216 /* msg.msg_flags = MSG_WAITALL, */
217 msg.msg_iovlen = 2;
218
219 /*
220 * Initialize the pdu flags.
221 */
222 flags = ISNS_FLAG_SERVER;
223 flags |= ISNS_FLAG_FIRST_PDU;
224
225 /*
226 * Initialize the pdu sequence id.
227 */
228 seq = 0;
229
230 iovec[0].iov_base = (void *)pdu;
231 iovec[0].iov_len = (ISNSP_HEADER_SIZE);
232
233 payload = pdu->payload;
234
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
241
242 do {
243 /* set the payload for sending */
244 iovec[1].iov_base = (void *)payload;
245
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;
252 }
253 iovec[1].iov_len = send_len;
254 pdu->payload_len = htons(send_len);
255
256 /* set the pdu flags */
257 pdu->flags = htons(flags);
258 /* set the pdu sequence id */
259 pdu->seq = htons(seq);
260
261 /* send the packet */
262 bytes_sent = sendmsg(fd, &msg, 0);
263
264 /* get rid of the first pdu flag */
265 flags &= ~(ISNS_FLAG_FIRST_PDU);
266
267 /* next part of payload */
268 payload += send_len;
269 pl -= send_len;
270
271 /* add the length of header for verification */
272 send_len += ISNSP_HEADER_SIZE;
273
274 /* increase the sequence id by one */
275 seq ++;
276 } while (bytes_sent == send_len && pl > 0);
277
278 if (bytes_sent == send_len) {
279 return (0);
280 } else {
281 isnslog(LOG_DEBUG, "isns_send_pdu", "sending pdu failed.");
282 return (-1);
283 }
284 }
285
286 #define RSP_PDU_FRAG_SZ (ISNSP_MAX_PDU_SIZE / 10)
287 static int
pdu_reset(isns_pdu_t ** rsp,size_t * sz)288 pdu_reset(
289 isns_pdu_t **rsp,
290 size_t *sz
291 )
292 {
293 int ec = 0;
294
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;
301 }
302 }
303
304 return (ec);
305 }
306
307 int
pdu_reset_rsp(isns_pdu_t ** rsp,size_t * pl,size_t * sz)308 pdu_reset_rsp(
309 isns_pdu_t **rsp,
310 size_t *pl,
311 size_t *sz
312 )
313 {
314 int ec = pdu_reset(rsp, sz);
315
316 if (ec == 0) {
317 /* leave space for status code */
318 *pl = 4;
319 }
320
321 return (ec);
322 }
323
324 int
pdu_reset_scn(isns_pdu_t ** pdu,size_t * pl,size_t * sz)325 pdu_reset_scn(
326 isns_pdu_t **pdu,
327 size_t *pl,
328 size_t *sz
329 )
330 {
331 int ec = pdu_reset(pdu, sz);
332
333 if (ec == 0) {
334 *pl = 0;
335 }
336
337 return (ec);
338 }
339
340 int
pdu_reset_esi(isns_pdu_t ** pdu,size_t * pl,size_t * sz)341 pdu_reset_esi(
342 isns_pdu_t **pdu,
343 size_t *pl,
344 size_t *sz
345 )
346 {
347 return (pdu_reset_scn(pdu, pl, sz));
348 }
349
350 int
pdu_update_code(isns_pdu_t * pdu,size_t * pl,int code)351 pdu_update_code(
352 isns_pdu_t *pdu,
353 size_t *pl,
354 int code
355 )
356 {
357 isns_resp_t *resp;
358
359 resp = (isns_resp_t *)pdu->payload;
360
361 /* reset the payload length */
362 if (code != ISNS_RSP_SUCCESSFUL || *pl == 0) {
363 *pl = 4;
364 }
365
366 resp->status = htonl(code);
367
368 return (0);
369 }
370
371 int
pdu_add_tlv(isns_pdu_t ** pdu,size_t * pl,size_t * sz,uint32_t attr_id,uint32_t attr_len,void * attr_data,int pflag)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
380 )
381 {
382 int ec = 0;
383
384 isns_pdu_t *new_pdu;
385 size_t new_sz;
386
387 isns_tlv_t *attr_tlv;
388 uint8_t *payload_ptr;
389 uint32_t normalized_attr_len;
390 uint64_t attr_tlv_len;
391
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);
408 }
409 }
410
411 attr_tlv = (isns_tlv_t *)malloc(attr_tlv_len);
412 (void) memset((void *)attr_tlv, 0, attr_tlv_len);
413
414 attr_tlv->attr_id = htonl(attr_id);
415
416 switch (attr_id) {
417 case ISNS_DELIMITER_ATTR_ID:
418 break;
419
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;
427
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;
435
436 default:
437 if (attr_len == 8) {
438 if (pflag == 0) {
439 /*
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.
449 */
450 ec = ISNS_RSP_INTERNAL_ERROR;
451 return (ec);
452 } else {
453 *(uint64_t *)attr_tlv->attr_value =
454 *(uint64_t *)attr_data;
455 }
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;
463 }
464 }
465 break;
466 }
467
468 attr_tlv->attr_len = htonl(normalized_attr_len);
469 /*
470 * Convert the network byte ordered payload length to host byte
471 * ordered for local address calculation.
472 */
473 payload_ptr = (*pdu)->payload + *pl;
474 (void) memcpy(payload_ptr, attr_tlv, attr_tlv_len);
475 *pl += attr_tlv_len;
476
477 /*
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.
481 */
482
483 free(attr_tlv);
484 attr_tlv = NULL;
485
486 return (ec);
487 }
488
489 isns_tlv_t *
pdu_get_source(isns_pdu_t * pdu)490 pdu_get_source(
491 isns_pdu_t *pdu
492 )
493 {
494 uint8_t *payload = &pdu->payload[0];
495 uint16_t payload_len = pdu->payload_len;
496 isns_tlv_t *tlv = NULL;
497
498 /* response code */
499 if (pdu->func_id & ISNS_RSP_MASK) {
500 if (payload_len < 4) {
501 return (NULL);
502 }
503 payload += 4;
504 payload_len -= 4;
505 }
506
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);
511 }
512
513 return (tlv);
514 }
515
516 isns_tlv_t *
pdu_get_key(isns_pdu_t * pdu,size_t * key_len)517 pdu_get_key(
518 isns_pdu_t *pdu,
519 size_t *key_len
520 )
521 {
522 uint8_t *payload = &pdu->payload[0];
523 uint16_t payload_len = pdu->payload_len;
524 isns_tlv_t *tlv, *key;
525
526 /* reset */
527 *key_len = 0;
528
529 /* response code */
530 if (pdu->func_id & ISNS_RSP_MASK) {
531 if (payload_len <= 4) {
532 return (NULL);
533 }
534 payload += 4;
535 payload_len -= 4;
536 }
537
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;
550 }
551 *key_len += (8 + tlv->attr_len);
552 payload += (8 + tlv->attr_len);
553 payload_len -= (8 + tlv->attr_len);
554 }
555 }
556
557 if (*key_len >= 8) {
558 return (key);
559 }
560
561 return (NULL);
562 }
563
564 isns_tlv_t *
pdu_get_operand(isns_pdu_t * pdu,size_t * op_len)565 pdu_get_operand(
566 isns_pdu_t *pdu,
567 size_t *op_len
568 )
569 {
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;
574
575 /* reset */
576 *op_len = 0;
577
578 /* response code */
579 if (pdu->func_id & ISNS_RSP_MASK) {
580 if (payload_len < 4) {
581 return (NULL);
582 }
583 payload += 4;
584 payload_len -= 4;
585 }
586
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;
603 }
604 }
605 }
606
607 if (*op_len >= 8) {
608 return (op);
609 }
610
611 return (NULL);
612 }
613