1 /*-
2  *   BSD LICENSE
3  *
4  *   Copyright(c) 2015-2016 Intel Corporation. All rights reserved.
5  *
6  *   Redistribution and use in source and binary forms, with or without
7  *   modification, are permitted provided that the following conditions
8  *   are met:
9  *
10  *	 * Redistributions of source code must retain the above copyright
11  *	   notice, this list of conditions and the following disclaimer.
12  *	 * Redistributions in binary form must reproduce the above copyright
13  *	   notice, this list of conditions and the following disclaimer in
14  *	   the documentation and/or other materials provided with the
15  *	   distribution.
16  *	 * Neither the name of Intel Corporation nor the names of its
17  *	   contributors may be used to endorse or promote products derived
18  *	   from this software without specific prior written permission.
19  *
20  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 #include <rte_common.h>
34 #include <rte_hexdump.h>
35 #include <rte_mbuf.h>
36 #include <rte_malloc.h>
37 #include <rte_memcpy.h>
38 
39 #include <rte_crypto.h>
40 #include <rte_cryptodev.h>
41 #include <rte_cryptodev_pmd.h>
42 
43 #include "test.h"
44 #include "test_cryptodev_blockcipher.h"
45 #include "test_cryptodev_aes_test_vectors.h"
46 #include "test_cryptodev_des_test_vectors.h"
47 #include "test_cryptodev_hash_test_vectors.h"
48 
49 static int
50 test_blockcipher_one_case(const struct blockcipher_test_case *t,
51 	struct rte_mempool *mbuf_pool,
52 	struct rte_mempool *op_mpool,
53 	uint8_t dev_id,
54 	enum rte_cryptodev_type cryptodev_type,
55 	char *test_msg)
56 {
57 	struct rte_mbuf *ibuf = NULL;
58 	struct rte_mbuf *obuf = NULL;
59 	struct rte_mbuf *iobuf;
60 	struct rte_crypto_sym_xform *cipher_xform = NULL;
61 	struct rte_crypto_sym_xform *auth_xform = NULL;
62 	struct rte_crypto_sym_xform *init_xform = NULL;
63 	struct rte_crypto_sym_op *sym_op = NULL;
64 	struct rte_crypto_op *op = NULL;
65 	struct rte_cryptodev_sym_session *sess = NULL;
66 
67 	int status = TEST_SUCCESS;
68 	const struct blockcipher_test_data *tdata = t->test_data;
69 	uint8_t cipher_key[tdata->cipher_key.len];
70 	uint8_t auth_key[tdata->auth_key.len];
71 	uint32_t buf_len = tdata->ciphertext.len;
72 	uint32_t digest_len = 0;
73 	char *buf_p = NULL;
74 
75 	if (tdata->cipher_key.len)
76 		memcpy(cipher_key, tdata->cipher_key.data,
77 			tdata->cipher_key.len);
78 	if (tdata->auth_key.len)
79 		memcpy(auth_key, tdata->auth_key.data,
80 			tdata->auth_key.len);
81 
82 	switch (cryptodev_type) {
83 	case RTE_CRYPTODEV_QAT_SYM_PMD:
84 	case RTE_CRYPTODEV_OPENSSL_PMD:
85 		digest_len = tdata->digest.len;
86 		break;
87 	case RTE_CRYPTODEV_AESNI_MB_PMD:
88 		digest_len = tdata->digest.truncated_len;
89 		break;
90 	default:
91 		snprintf(test_msg, BLOCKCIPHER_TEST_MSG_LEN,
92 			"line %u FAILED: %s",
93 			__LINE__, "Unsupported PMD type");
94 		status = TEST_FAILED;
95 		goto error_exit;
96 	}
97 
98 	/* preparing data */
99 	ibuf = rte_pktmbuf_alloc(mbuf_pool);
100 	if (!ibuf) {
101 		snprintf(test_msg, BLOCKCIPHER_TEST_MSG_LEN,
102 			"line %u FAILED: %s",
103 			__LINE__, "Allocation of rte_mbuf failed");
104 		status = TEST_FAILED;
105 		goto error_exit;
106 	}
107 
108 	if (t->op_mask & BLOCKCIPHER_TEST_OP_CIPHER)
109 		buf_len += tdata->iv.len;
110 	if (t->op_mask & BLOCKCIPHER_TEST_OP_AUTH)
111 		buf_len += digest_len;
112 
113 	buf_p = rte_pktmbuf_append(ibuf, buf_len);
114 	if (!buf_p) {
115 		snprintf(test_msg, BLOCKCIPHER_TEST_MSG_LEN,
116 			"line %u FAILED: %s",
117 			__LINE__, "No room to append mbuf");
118 		status = TEST_FAILED;
119 		goto error_exit;
120 	}
121 
122 	if (t->op_mask & BLOCKCIPHER_TEST_OP_CIPHER) {
123 		rte_memcpy(buf_p, tdata->iv.data, tdata->iv.len);
124 		buf_p += tdata->iv.len;
125 	}
126 
127 	/* only encryption requires plaintext.data input,
128 	 * decryption/(digest gen)/(digest verify) use ciphertext.data
129 	 * to be computed
130 	 */
131 	if (t->op_mask & BLOCKCIPHER_TEST_OP_ENCRYPT) {
132 		rte_memcpy(buf_p, tdata->plaintext.data,
133 			tdata->plaintext.len);
134 		buf_p += tdata->plaintext.len;
135 	} else {
136 		rte_memcpy(buf_p, tdata->ciphertext.data,
137 			tdata->ciphertext.len);
138 		buf_p += tdata->ciphertext.len;
139 	}
140 
141 	if (t->op_mask & BLOCKCIPHER_TEST_OP_AUTH_VERIFY)
142 		rte_memcpy(buf_p, tdata->digest.data, digest_len);
143 	else
144 		memset(buf_p, 0, digest_len);
145 
146 	if (t->feature_mask & BLOCKCIPHER_TEST_FEATURE_OOP) {
147 		obuf = rte_pktmbuf_alloc(mbuf_pool);
148 		if (!obuf) {
149 			snprintf(test_msg, BLOCKCIPHER_TEST_MSG_LEN, "line %u "
150 				"FAILED: %s", __LINE__,
151 				"Allocation of rte_mbuf failed");
152 			status = TEST_FAILED;
153 			goto error_exit;
154 		}
155 
156 		buf_p = rte_pktmbuf_append(obuf, buf_len);
157 		if (!buf_p) {
158 			snprintf(test_msg, BLOCKCIPHER_TEST_MSG_LEN, "line %u "
159 				"FAILED: %s", __LINE__,
160 				"No room to append mbuf");
161 			status = TEST_FAILED;
162 			goto error_exit;
163 		}
164 		memset(buf_p, 0, buf_len);
165 	}
166 
167 	/* Generate Crypto op data structure */
168 	op = rte_crypto_op_alloc(op_mpool, RTE_CRYPTO_OP_TYPE_SYMMETRIC);
169 	if (!op) {
170 		snprintf(test_msg, BLOCKCIPHER_TEST_MSG_LEN,
171 			"line %u FAILED: %s",
172 			__LINE__, "Failed to allocate symmetric crypto "
173 			"operation struct");
174 		status = TEST_FAILED;
175 		goto error_exit;
176 	}
177 
178 	sym_op = op->sym;
179 
180 	sym_op->m_src = ibuf;
181 
182 	if (t->feature_mask & BLOCKCIPHER_TEST_FEATURE_OOP) {
183 		sym_op->m_dst = obuf;
184 		iobuf = obuf;
185 	} else {
186 		sym_op->m_dst = NULL;
187 		iobuf = ibuf;
188 	}
189 
190 	/* sessionless op requires allocate xform using
191 	 * rte_crypto_op_sym_xforms_alloc(), otherwise rte_zmalloc()
192 	 * is used
193 	 */
194 	if (t->feature_mask & BLOCKCIPHER_TEST_FEATURE_SESSIONLESS) {
195 		uint32_t n_xforms = 0;
196 
197 		if (t->op_mask & BLOCKCIPHER_TEST_OP_CIPHER)
198 			n_xforms++;
199 		if (t->op_mask & BLOCKCIPHER_TEST_OP_AUTH)
200 			n_xforms++;
201 
202 		if (rte_crypto_op_sym_xforms_alloc(op, n_xforms)
203 			== NULL) {
204 			snprintf(test_msg, BLOCKCIPHER_TEST_MSG_LEN, "line %u "
205 				"FAILED: %s", __LINE__, "Failed to "
206 				"allocate space for crypto transforms");
207 			status = TEST_FAILED;
208 			goto error_exit;
209 		}
210 	} else {
211 		cipher_xform = rte_zmalloc(NULL,
212 			sizeof(struct rte_crypto_sym_xform), 0);
213 
214 		auth_xform = rte_zmalloc(NULL,
215 			sizeof(struct rte_crypto_sym_xform), 0);
216 
217 		if (!cipher_xform || !auth_xform) {
218 			snprintf(test_msg, BLOCKCIPHER_TEST_MSG_LEN, "line %u "
219 				"FAILED: %s", __LINE__, "Failed to "
220 				"allocate memory for crypto transforms");
221 			status = TEST_FAILED;
222 			goto error_exit;
223 		}
224 	}
225 
226 	/* preparing xform, for sessioned op, init_xform is initialized
227 	 * here and later as param in rte_cryptodev_sym_session_create() call
228 	 */
229 	if (t->op_mask == BLOCKCIPHER_TEST_OP_ENC_AUTH_GEN) {
230 		if (t->feature_mask & BLOCKCIPHER_TEST_FEATURE_SESSIONLESS) {
231 			cipher_xform = op->sym->xform;
232 			auth_xform = cipher_xform->next;
233 			auth_xform->next = NULL;
234 		} else {
235 			cipher_xform->next = auth_xform;
236 			auth_xform->next = NULL;
237 			init_xform = cipher_xform;
238 		}
239 	} else if (t->op_mask == BLOCKCIPHER_TEST_OP_AUTH_VERIFY_DEC) {
240 		if (t->feature_mask & BLOCKCIPHER_TEST_FEATURE_SESSIONLESS) {
241 			auth_xform = op->sym->xform;
242 			cipher_xform = auth_xform->next;
243 			cipher_xform->next = NULL;
244 		} else {
245 			auth_xform->next = cipher_xform;
246 			cipher_xform->next = NULL;
247 			init_xform = auth_xform;
248 		}
249 	} else if ((t->op_mask == BLOCKCIPHER_TEST_OP_ENCRYPT) ||
250 			(t->op_mask == BLOCKCIPHER_TEST_OP_DECRYPT)) {
251 		if (t->feature_mask & BLOCKCIPHER_TEST_FEATURE_SESSIONLESS)
252 			cipher_xform = op->sym->xform;
253 		else
254 			init_xform = cipher_xform;
255 		cipher_xform->next = NULL;
256 	} else if ((t->op_mask == BLOCKCIPHER_TEST_OP_AUTH_GEN) ||
257 			(t->op_mask == BLOCKCIPHER_TEST_OP_AUTH_VERIFY)) {
258 		if (t->feature_mask & BLOCKCIPHER_TEST_FEATURE_SESSIONLESS)
259 			auth_xform = op->sym->xform;
260 		else
261 			init_xform = auth_xform;
262 		auth_xform->next = NULL;
263 	} else {
264 		snprintf(test_msg, BLOCKCIPHER_TEST_MSG_LEN,
265 			"line %u FAILED: %s",
266 			__LINE__, "Unrecognized operation");
267 		status = TEST_FAILED;
268 		goto error_exit;
269 	}
270 
271 	/*configure xforms & sym_op cipher and auth data*/
272 	if (t->op_mask & BLOCKCIPHER_TEST_OP_CIPHER) {
273 		cipher_xform->type = RTE_CRYPTO_SYM_XFORM_CIPHER;
274 		cipher_xform->cipher.algo = tdata->crypto_algo;
275 		if (t->op_mask & BLOCKCIPHER_TEST_OP_ENCRYPT)
276 			cipher_xform->cipher.op =
277 				RTE_CRYPTO_CIPHER_OP_ENCRYPT;
278 		else
279 			cipher_xform->cipher.op =
280 				RTE_CRYPTO_CIPHER_OP_DECRYPT;
281 		cipher_xform->cipher.key.data = cipher_key;
282 		cipher_xform->cipher.key.length = tdata->cipher_key.len;
283 
284 		sym_op->cipher.data.offset = tdata->iv.len;
285 		sym_op->cipher.data.length = tdata->ciphertext.len;
286 		sym_op->cipher.iv.data = rte_pktmbuf_mtod(sym_op->m_src,
287 			uint8_t *);
288 		sym_op->cipher.iv.length = tdata->iv.len;
289 		sym_op->cipher.iv.phys_addr = rte_pktmbuf_mtophys(
290 			sym_op->m_src);
291 	}
292 
293 	if (t->op_mask & BLOCKCIPHER_TEST_OP_AUTH) {
294 		uint32_t auth_data_offset = 0;
295 		uint32_t digest_offset = tdata->ciphertext.len;
296 
297 		if (t->op_mask & BLOCKCIPHER_TEST_OP_CIPHER) {
298 			digest_offset += tdata->iv.len;
299 			auth_data_offset += tdata->iv.len;
300 		}
301 
302 		auth_xform->type = RTE_CRYPTO_SYM_XFORM_AUTH;
303 		auth_xform->auth.algo = tdata->auth_algo;
304 		auth_xform->auth.key.length = tdata->auth_key.len;
305 		auth_xform->auth.key.data = auth_key;
306 		auth_xform->auth.digest_length = digest_len;
307 
308 		if (t->op_mask & BLOCKCIPHER_TEST_OP_AUTH_GEN) {
309 			auth_xform->auth.op = RTE_CRYPTO_AUTH_OP_GENERATE;
310 			sym_op->auth.digest.data = rte_pktmbuf_mtod_offset
311 				(iobuf, uint8_t *, digest_offset);
312 			sym_op->auth.digest.phys_addr =
313 				rte_pktmbuf_mtophys_offset(iobuf,
314 					digest_offset);
315 		} else {
316 			auth_xform->auth.op = RTE_CRYPTO_AUTH_OP_VERIFY;
317 			sym_op->auth.digest.data = rte_pktmbuf_mtod_offset
318 				(sym_op->m_src, uint8_t *, digest_offset);
319 			sym_op->auth.digest.phys_addr =
320 				rte_pktmbuf_mtophys_offset(sym_op->m_src,
321 					digest_offset);
322 		}
323 
324 		sym_op->auth.data.offset = auth_data_offset;
325 		sym_op->auth.data.length = tdata->ciphertext.len;
326 		sym_op->auth.digest.length = digest_len;
327 	}
328 
329 	/* create session for sessioned op */
330 	if (!(t->feature_mask & BLOCKCIPHER_TEST_FEATURE_SESSIONLESS)) {
331 		sess = rte_cryptodev_sym_session_create(dev_id,
332 			init_xform);
333 		if (!sess) {
334 			snprintf(test_msg, BLOCKCIPHER_TEST_MSG_LEN, "line %u "
335 				"FAILED: %s", __LINE__,
336 				"Session creation failed");
337 			status = TEST_FAILED;
338 			goto error_exit;
339 		}
340 
341 		/* attach symmetric crypto session to crypto operations */
342 		rte_crypto_op_attach_sym_session(op, sess);
343 	}
344 
345 	/* Process crypto operation */
346 	if (rte_cryptodev_enqueue_burst(dev_id, 0, &op, 1) != 1) {
347 		snprintf(test_msg, BLOCKCIPHER_TEST_MSG_LEN,
348 			"line %u FAILED: %s",
349 			__LINE__, "Error sending packet for encryption");
350 		status = TEST_FAILED;
351 		goto error_exit;
352 	}
353 
354 	op = NULL;
355 
356 	while (rte_cryptodev_dequeue_burst(dev_id, 0, &op, 1) == 0)
357 		rte_pause();
358 
359 	if (!op) {
360 		snprintf(test_msg, BLOCKCIPHER_TEST_MSG_LEN,
361 			"line %u FAILED: %s",
362 			__LINE__, "Failed to process sym crypto op");
363 		status = TEST_FAILED;
364 		goto error_exit;
365 	}
366 
367 	TEST_HEXDUMP(stdout, "m_src:",
368 		rte_pktmbuf_mtod(sym_op->m_src, uint8_t *), buf_len);
369 	if (t->feature_mask & BLOCKCIPHER_TEST_FEATURE_OOP)
370 		TEST_HEXDUMP(stdout, "m_dst:",
371 			rte_pktmbuf_mtod(sym_op->m_dst, uint8_t *),
372 			buf_len);
373 
374 	/* Verify results */
375 	if (op->status != RTE_CRYPTO_OP_STATUS_SUCCESS) {
376 		if (t->op_mask & BLOCKCIPHER_TEST_OP_AUTH_VERIFY)
377 			snprintf(test_msg, BLOCKCIPHER_TEST_MSG_LEN, "line %u "
378 				"FAILED: Digest verification failed "
379 				"(0x%X)", __LINE__, op->status);
380 		else
381 			snprintf(test_msg, BLOCKCIPHER_TEST_MSG_LEN, "line %u "
382 				"FAILED: Digest verification failed "
383 				"(0x%X)", __LINE__, op->status);
384 		status = TEST_FAILED;
385 		goto error_exit;
386 	}
387 
388 	if (t->op_mask & BLOCKCIPHER_TEST_OP_CIPHER) {
389 		uint8_t *crypto_res;
390 		const uint8_t *compare_ref;
391 		uint32_t compare_len;
392 
393 		crypto_res = rte_pktmbuf_mtod_offset(iobuf, uint8_t *,
394 			tdata->iv.len);
395 
396 		if (t->op_mask & BLOCKCIPHER_TEST_OP_ENCRYPT) {
397 			compare_ref = tdata->ciphertext.data;
398 			compare_len = tdata->ciphertext.len;
399 		} else {
400 			compare_ref = tdata->plaintext.data;
401 			compare_len = tdata->plaintext.len;
402 		}
403 
404 		if (memcmp(crypto_res, compare_ref, compare_len)) {
405 			snprintf(test_msg, BLOCKCIPHER_TEST_MSG_LEN, "line %u "
406 				"FAILED: %s", __LINE__,
407 				"Crypto data not as expected");
408 			status = TEST_FAILED;
409 			goto error_exit;
410 		}
411 	}
412 
413 	if (t->op_mask & BLOCKCIPHER_TEST_OP_AUTH_GEN) {
414 		uint8_t *auth_res;
415 
416 		if (t->op_mask & BLOCKCIPHER_TEST_OP_CIPHER)
417 			auth_res = rte_pktmbuf_mtod_offset(iobuf,
418 				uint8_t *,
419 				tdata->iv.len + tdata->ciphertext.len);
420 		else
421 			auth_res = rte_pktmbuf_mtod_offset(iobuf,
422 				uint8_t *, tdata->ciphertext.len);
423 
424 		if (memcmp(auth_res, tdata->digest.data, digest_len)) {
425 			snprintf(test_msg, BLOCKCIPHER_TEST_MSG_LEN, "line %u "
426 				"FAILED: %s", __LINE__, "Generated "
427 				"digest data not as expected");
428 			status = TEST_FAILED;
429 			goto error_exit;
430 		}
431 	}
432 
433 	snprintf(test_msg, BLOCKCIPHER_TEST_MSG_LEN, "PASS");
434 
435 error_exit:
436 	if (!(t->feature_mask & BLOCKCIPHER_TEST_FEATURE_SESSIONLESS)) {
437 		if (sess)
438 			rte_cryptodev_sym_session_free(dev_id, sess);
439 		if (cipher_xform)
440 			rte_free(cipher_xform);
441 		if (auth_xform)
442 			rte_free(auth_xform);
443 	}
444 
445 	if (op)
446 		rte_crypto_op_free(op);
447 
448 	if (obuf)
449 		rte_pktmbuf_free(obuf);
450 
451 	if (ibuf)
452 		rte_pktmbuf_free(ibuf);
453 
454 	return status;
455 }
456 
457 int
458 test_blockcipher_all_tests(struct rte_mempool *mbuf_pool,
459 	struct rte_mempool *op_mpool,
460 	uint8_t dev_id,
461 	enum rte_cryptodev_type cryptodev_type,
462 	enum blockcipher_test_type test_type)
463 {
464 	int status, overall_status = TEST_SUCCESS;
465 	uint32_t i, test_index = 0;
466 	char test_msg[BLOCKCIPHER_TEST_MSG_LEN + 1];
467 	uint32_t n_test_cases = 0;
468 	uint32_t target_pmd_mask = 0;
469 	const struct blockcipher_test_case *tcs = NULL;
470 
471 	switch (test_type) {
472 	case BLKCIPHER_AES_CHAIN_TYPE:
473 		n_test_cases = sizeof(aes_chain_test_cases) /
474 		sizeof(aes_chain_test_cases[0]);
475 		tcs = aes_chain_test_cases;
476 		break;
477 	case BLKCIPHER_AES_CIPHERONLY_TYPE:
478 		n_test_cases = sizeof(aes_cipheronly_test_cases) /
479 		sizeof(aes_cipheronly_test_cases[0]);
480 		tcs = aes_cipheronly_test_cases;
481 		break;
482 	case BLKCIPHER_3DES_CHAIN_TYPE:
483 		n_test_cases = sizeof(triple_des_chain_test_cases) /
484 		sizeof(triple_des_chain_test_cases[0]);
485 		tcs = triple_des_chain_test_cases;
486 		break;
487 	case BLKCIPHER_3DES_CIPHERONLY_TYPE:
488 		n_test_cases = sizeof(triple_des_cipheronly_test_cases) /
489 		sizeof(triple_des_cipheronly_test_cases[0]);
490 		tcs = triple_des_cipheronly_test_cases;
491 		break;
492 	case BLKCIPHER_AUTHONLY_TYPE:
493 		n_test_cases = sizeof(hash_test_cases) /
494 		sizeof(hash_test_cases[0]);
495 		tcs = hash_test_cases;
496 		break;
497 	default:
498 		break;
499 	}
500 
501 	switch (cryptodev_type) {
502 	case RTE_CRYPTODEV_AESNI_MB_PMD:
503 		target_pmd_mask = BLOCKCIPHER_TEST_TARGET_PMD_MB;
504 		break;
505 	case RTE_CRYPTODEV_QAT_SYM_PMD:
506 		target_pmd_mask = BLOCKCIPHER_TEST_TARGET_PMD_QAT;
507 		break;
508 	case RTE_CRYPTODEV_OPENSSL_PMD:
509 		target_pmd_mask = BLOCKCIPHER_TEST_TARGET_PMD_OPENSSL;
510 		break;
511 	default:
512 		TEST_ASSERT(0, "Unrecognized cryptodev type");
513 		break;
514 	}
515 
516 	for (i = 0; i < n_test_cases; i++) {
517 		const struct blockcipher_test_case *tc = &tcs[i];
518 
519 		if (!(tc->pmd_mask & target_pmd_mask))
520 			continue;
521 
522 		status = test_blockcipher_one_case(tc, mbuf_pool, op_mpool,
523 			dev_id, cryptodev_type, test_msg);
524 
525 		printf("  %u) TestCase %s %s\n", test_index ++,
526 			tc->test_descr, test_msg);
527 
528 		if (status != TEST_SUCCESS) {
529 			if (overall_status == TEST_SUCCESS)
530 				overall_status = status;
531 
532 			if (tc->feature_mask & BLOCKCIPHER_TEST_FEATURE_STOPPER)
533 				break;
534 		}
535 	}
536 
537 	return overall_status;
538 }
539