xref: /illumos-gate/usr/src/cmd/isns/isnsd/pdu.c (revision 8b80e8cb6855118d46f605e91b5ed4ce83417395)
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
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
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
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
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
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
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
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
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 *
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 *
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 *
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