xref: /freebsd/usr.sbin/ctld/uclparse.cc (revision ab328f4e33f8ba7adc7c285fd6aa74254c2e7971)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2015 iXsystems Inc.
5  * All rights reserved.
6  *
7  * This software was developed by Jakub Klama <jceel@FreeBSD.org>
8  * under sponsorship from iXsystems Inc.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #include <sys/types.h>
33 #include <sys/nv.h>
34 #include <sys/queue.h>
35 #include <assert.h>
36 #include <stdio.h>
37 #include <stdint.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <ucl++.h>
41 #include <netinet/in.h>
42 #include <netinet/ip.h>
43 
44 #include <libutil++.hh>
45 #include <memory>
46 
47 #include "conf.h"
48 #include "ctld.hh"
49 
50 struct scope_exit {
51 	using callback = void();
scope_exitscope_exit52 	scope_exit(callback *fn) : fn(fn) {}
53 
~scope_exitscope_exit54 	~scope_exit() { fn(); }
55 
56 private:
57 	callback *fn;
58 };
59 
60 static bool uclparse_toplevel(const ucl::Ucl &);
61 static bool uclparse_chap(const char *, const ucl::Ucl &);
62 static bool uclparse_chap_mutual(const char *, const ucl::Ucl &);
63 static bool uclparse_lun(const char *, const ucl::Ucl &);
64 static bool uclparse_lun_entries(const char *, const ucl::Ucl &);
65 static bool uclparse_auth_group(const char *, const ucl::Ucl &);
66 static bool uclparse_portal_group(const char *, const ucl::Ucl &);
67 static bool uclparse_transport_group(const char *, const ucl::Ucl &);
68 static bool uclparse_controller(const char *, const ucl::Ucl &);
69 static bool uclparse_controller_transport_group(const char *, const ucl::Ucl &);
70 static bool uclparse_controller_namespace(const char *, const ucl::Ucl &);
71 static bool uclparse_target(const char *, const ucl::Ucl &);
72 static bool uclparse_target_portal_group(const char *, const ucl::Ucl &);
73 static bool uclparse_target_lun(const char *, const ucl::Ucl &);
74 
75 static bool
uclparse_chap(const char * ag_name,const ucl::Ucl & obj)76 uclparse_chap(const char *ag_name, const ucl::Ucl &obj)
77 {
78 	auto user = obj["user"];
79 	if (!user || user.type() != UCL_STRING) {
80 		log_warnx("chap section in auth-group \"%s\" is missing "
81 		    "\"user\" string key", ag_name);
82 		return (false);
83 	}
84 
85 	auto secret = obj["secret"];
86 	if (!secret || secret.type() != UCL_STRING) {
87 		log_warnx("chap section in auth-group \"%s\" is missing "
88 		    "\"secret\" string key", ag_name);
89 		return (false);
90 	}
91 
92 	return (auth_group_add_chap(
93 	    user.string_value().c_str(),
94 	    secret.string_value().c_str()));
95 }
96 
97 static bool
uclparse_chap_mutual(const char * ag_name,const ucl::Ucl & obj)98 uclparse_chap_mutual(const char *ag_name, const ucl::Ucl &obj)
99 {
100 	auto user = obj["user"];
101 	if (!user || user.type() != UCL_STRING) {
102 		log_warnx("chap-mutual section in auth-group \"%s\" is missing "
103 		    "\"user\" string key", ag_name);
104 		return (false);
105 	}
106 
107 	auto secret = obj["secret"];
108 	if (!secret || secret.type() != UCL_STRING) {
109 		log_warnx("chap-mutual section in auth-group \"%s\" is missing "
110 		    "\"secret\" string key", ag_name);
111 		return (false);
112 	}
113 
114 	auto mutual_user = obj["mutual-user"];
115 	if (!mutual_user || mutual_user.type() != UCL_STRING) {
116 		log_warnx("chap-mutual section in auth-group \"%s\" is missing "
117 		    "\"mutual-user\" string key", ag_name);
118 		return (false);
119 	}
120 
121 	auto mutual_secret = obj["mutual-secret"];
122 	if (!mutual_secret || mutual_secret.type() != UCL_STRING) {
123 		log_warnx("chap-mutual section in auth-group \"%s\" is missing "
124 		    "\"mutual-secret\" string key", ag_name);
125 		return (false);
126 	}
127 
128 	return (auth_group_add_chap_mutual(
129 	    user.string_value().c_str(),
130 	    secret.string_value().c_str(),
131 	    mutual_user.string_value().c_str(),
132 	    mutual_secret.string_value().c_str()));
133 }
134 
135 static bool
uclparse_target_chap(const char * t_name,const ucl::Ucl & obj)136 uclparse_target_chap(const char *t_name, const ucl::Ucl &obj)
137 {
138 	auto user = obj["user"];
139 	if (!user || user.type() != UCL_STRING) {
140 		log_warnx("chap section in target \"%s\" is missing "
141 		    "\"user\" string key", t_name);
142 		return (false);
143 	}
144 
145 	auto secret = obj["secret"];
146 	if (!secret || secret.type() != UCL_STRING) {
147 		log_warnx("chap section in target \"%s\" is missing "
148 		    "\"secret\" string key", t_name);
149 		return (false);
150 	}
151 
152 	return (target_add_chap(
153 	    user.string_value().c_str(),
154 	    secret.string_value().c_str()));
155 }
156 
157 static bool
uclparse_target_chap_mutual(const char * t_name,const ucl::Ucl & obj)158 uclparse_target_chap_mutual(const char *t_name, const ucl::Ucl &obj)
159 {
160 	auto user = obj["user"];
161 	if (!user || user.type() != UCL_STRING) {
162 		log_warnx("chap-mutual section in target \"%s\" is missing "
163 		    "\"user\" string key", t_name);
164 		return (false);
165 	}
166 
167 	auto secret = obj["secret"];
168 	if (!secret || secret.type() != UCL_STRING) {
169 		log_warnx("chap-mutual section in target \"%s\" is missing "
170 		    "\"secret\" string key", t_name);
171 		return (false);
172 	}
173 
174 	auto mutual_user = obj["mutual-user"];
175 	if (!mutual_user || mutual_user.type() != UCL_STRING) {
176 		log_warnx("chap-mutual section in target \"%s\" is missing "
177 		    "\"mutual-user\" string key", t_name);
178 		return (false);
179 	}
180 
181 	auto mutual_secret = obj["mutual-secret"];
182 	if (!mutual_secret || mutual_secret.type() != UCL_STRING) {
183 		log_warnx("chap-mutual section in target \"%s\" is missing "
184 		    "\"mutual-secret\" string key", t_name);
185 		return (false);
186 	}
187 
188 	return (target_add_chap_mutual(
189 	    user.string_value().c_str(),
190 	    secret.string_value().c_str(),
191 	    mutual_user.string_value().c_str(),
192 	    mutual_secret.string_value().c_str()));
193 }
194 
195 static bool
uclparse_target_portal_group(const char * t_name,const ucl::Ucl & obj)196 uclparse_target_portal_group(const char *t_name, const ucl::Ucl &obj)
197 {
198 	/*
199 	 * If the value is a single string, assume it is a
200 	 * portal-group name.
201 	 */
202 	if (obj.type() == UCL_STRING)
203 		return (target_add_portal_group(obj.string_value().c_str(),
204 		    NULL));
205 
206 	if (obj.type() != UCL_OBJECT) {
207 		log_warnx("portal-group section in target \"%s\" must be "
208 		    "an object or string", t_name);
209 		return (false);
210 	}
211 
212 	auto portal_group = obj["name"];
213 	if (!portal_group || portal_group.type() != UCL_STRING) {
214 		log_warnx("portal-group section in target \"%s\" is missing "
215 		    "\"name\" string key", t_name);
216 		return (false);
217 	}
218 
219 	auto auth_group = obj["auth-group-name"];
220 	if (auth_group) {
221 		if (auth_group.type() != UCL_STRING) {
222 			log_warnx("\"auth-group-name\" property in "
223 			    "portal-group section for target \"%s\" is not "
224 			    "a string", t_name);
225 			return (false);
226 		}
227 		return (target_add_portal_group(
228 		    portal_group.string_value().c_str(),
229 		    auth_group.string_value().c_str()));
230 	}
231 
232 	return (target_add_portal_group(portal_group.string_value().c_str(),
233 	    NULL));
234 }
235 
236 static bool
uclparse_controller_transport_group(const char * t_name,const ucl::Ucl & obj)237 uclparse_controller_transport_group(const char *t_name, const ucl::Ucl &obj)
238 {
239 	/*
240 	 * If the value is a single string, assume it is a
241 	 * transport-group name.
242 	 */
243 	if (obj.type() == UCL_STRING)
244 		return target_add_portal_group(obj.string_value().c_str(),
245 		    nullptr);
246 
247 	if (obj.type() != UCL_OBJECT) {
248 		log_warnx("transport-group section in controller \"%s\" must "
249 		    "be an object or string", t_name);
250 		return false;
251 	}
252 
253 	auto portal_group = obj["name"];
254 	if (!portal_group || portal_group.type() != UCL_STRING) {
255 		log_warnx("transport-group section in controller \"%s\" is "
256 		    "missing \"name\" string key", t_name);
257 		return false;
258 	}
259 
260 	auto auth_group = obj["auth-group-name"];
261 	if (auth_group) {
262 		if (auth_group.type() != UCL_STRING) {
263 			log_warnx("\"auth-group-name\" property in "
264 			    "transport-group section for controller \"%s\" is "
265 			    "not a string", t_name);
266 			return false;
267 		}
268 		return target_add_portal_group(
269 		    portal_group.string_value().c_str(),
270 		    auth_group.string_value().c_str());
271 	}
272 
273 	return target_add_portal_group(portal_group.string_value().c_str(),
274 	    nullptr);
275 }
276 
277 static bool
uclparse_target_lun(const char * t_name,const ucl::Ucl & obj)278 uclparse_target_lun(const char *t_name, const ucl::Ucl &obj)
279 {
280 	char *end;
281 	u_int id;
282 
283 	std::string key = obj.key();
284 	if (!key.empty()) {
285 		id = strtoul(key.c_str(), &end, 0);
286 		if (*end != '\0') {
287 			log_warnx("lun key \"%s\" in target \"%s\" is invalid",
288 			    key.c_str(), t_name);
289 			return (false);
290 		}
291 
292 		if (obj.type() == UCL_STRING)
293 			return (target_add_lun(id, obj.string_value().c_str()));
294 	}
295 
296 	if (obj.type() != UCL_OBJECT) {
297 		log_warnx("lun section entries in target \"%s\" must be objects",
298 		    t_name);
299 		return (false);
300 	}
301 
302 	if (key.empty()) {
303 		auto num = obj["number"];
304 		if (!num || num.type() != UCL_INT) {
305 			log_warnx("lun section in target \"%s\" is missing "
306 			    "\"number\" integer property", t_name);
307 			return (false);
308 		}
309 		id = num.int_value();
310 	}
311 
312 	auto name = obj["name"];
313 	if (!name) {
314 		if (!target_start_lun(id))
315 			return (false);
316 
317 		scope_exit finisher(lun_finish);
318 		std::string lun_name =
319 		    freebsd::stringf("lun %u for target \"%s\"", id, t_name);
320 		return (uclparse_lun_entries(lun_name.c_str(), obj));
321 	}
322 
323 	if (name.type() != UCL_STRING) {
324 		log_warnx("\"name\" property for lun %u for target "
325 		    "\"%s\" is not a string", id, t_name);
326 		return (false);
327 	}
328 
329 	return (target_add_lun(id, name.string_value().c_str()));
330 }
331 
332 static bool
uclparse_controller_namespace(const char * t_name,const ucl::Ucl & obj)333 uclparse_controller_namespace(const char *t_name, const ucl::Ucl &obj)
334 {
335 	char *end;
336 	u_int id;
337 
338 	std::string key = obj.key();
339 	if (!key.empty()) {
340 		id = strtoul(key.c_str(), &end, 0);
341 		if (*end != '\0') {
342 			log_warnx("namespace key \"%s\" in controller \"%s\""
343 			    " is invalid", key.c_str(), t_name);
344 			return false;
345 		}
346 
347 		if (obj.type() == UCL_STRING)
348 			return controller_add_namespace(id,
349 			    obj.string_value().c_str());
350 	}
351 
352 	if (obj.type() != UCL_OBJECT) {
353 		log_warnx("namespace section entries in controller \"%s\""
354 		    " must be objects", t_name);
355 		return false;
356 	}
357 
358 	if (key.empty()) {
359 		auto num = obj["number"];
360 		if (!num || num.type() != UCL_INT) {
361 			log_warnx("namespace section in controller \"%s\" is "
362 			    "missing \"id\" integer property", t_name);
363 			return (false);
364 		}
365 		id = num.int_value();
366 	}
367 
368 	auto name = obj["name"];
369 	if (!name) {
370 		if (!controller_start_namespace(id))
371 			return false;
372 
373 		std::string lun_name =
374 		    freebsd::stringf("namespace %u for controller \"%s\"", id,
375 			t_name);
376 		return uclparse_lun_entries(lun_name.c_str(), obj);
377 	}
378 
379 	if (name.type() != UCL_STRING) {
380 		log_warnx("\"name\" property for namespace %u for "
381 		    "controller \"%s\" is not a string", id, t_name);
382 		return (false);
383 	}
384 
385 	return controller_add_namespace(id, name.string_value().c_str());
386 }
387 
388 static bool
uclparse_toplevel(const ucl::Ucl & top)389 uclparse_toplevel(const ucl::Ucl &top)
390 {
391 	/* Pass 1 - everything except targets */
392 	for (const auto &obj : top) {
393 		std::string key = obj.key();
394 
395 		if (key == "debug") {
396 			if (obj.type() == UCL_INT)
397 				conf_set_debug(obj.int_value());
398 			else {
399 				log_warnx("\"debug\" property value is not integer");
400 				return (false);
401 			}
402 		}
403 
404 		if (key == "timeout") {
405 			if (obj.type() == UCL_INT)
406 				conf_set_timeout(obj.int_value());
407 			else {
408 				log_warnx("\"timeout\" property value is not integer");
409 				return (false);
410 			}
411 		}
412 
413 		if (key == "maxproc") {
414 			if (obj.type() == UCL_INT)
415 				conf_set_maxproc(obj.int_value());
416 			else {
417 				log_warnx("\"maxproc\" property value is not integer");
418 				return (false);
419 			}
420 		}
421 
422 		if (key == "pidfile") {
423 			if (obj.type() == UCL_STRING) {
424 				if (!conf_set_pidfile_path(
425 				    obj.string_value().c_str()))
426 					return (false);
427 			} else {
428 				log_warnx("\"pidfile\" property value is not string");
429 				return (false);
430 			}
431 		}
432 
433 		if (key == "isns-server") {
434 			if (obj.type() == UCL_ARRAY) {
435 				for (const auto &child : obj) {
436 					if (child.type() != UCL_STRING)
437 						return (false);
438 
439 					if (!isns_add_server(
440 					    child.string_value().c_str()))
441 						return (false);
442 				}
443 			} else {
444 				log_warnx("\"isns-server\" property value is "
445 				    "not an array");
446 				return (false);
447 			}
448 		}
449 
450 		if (key == "isns-period") {
451 			if (obj.type() == UCL_INT)
452 				conf_set_isns_period(obj.int_value());
453 			else {
454 				log_warnx("\"isns-period\" property value is not integer");
455 				return (false);
456 			}
457 		}
458 
459 		if (key == "isns-timeout") {
460 			if (obj.type() == UCL_INT)
461 				conf_set_isns_timeout(obj.int_value());
462 			else {
463 				log_warnx("\"isns-timeout\" property value is not integer");
464 				return (false);
465 			}
466 		}
467 
468 		if (key == "auth-group") {
469 			if (obj.type() == UCL_OBJECT) {
470 				for (const auto &child : obj) {
471 					if (!uclparse_auth_group(
472 					    child.key().c_str(), child))
473 						return (false);
474 				}
475 			} else {
476 				log_warnx("\"auth-group\" section is not an object");
477 				return (false);
478 			}
479 		}
480 
481 		if (key == "portal-group") {
482 			if (obj.type() == UCL_OBJECT) {
483 				for (const auto &child : obj) {
484 					if (!uclparse_portal_group(
485 					    child.key().c_str(), child))
486 						return (false);
487 				}
488 			} else {
489 				log_warnx("\"portal-group\" section is not an object");
490 				return (false);
491 			}
492 		}
493 
494 		if (key == "transport-group") {
495 			if (obj.type() == UCL_OBJECT) {
496 				for (const auto &child : obj) {
497 					if (!uclparse_transport_group(
498 					    child.key().c_str(), child))
499 						return false;
500 				}
501 			} else {
502 				log_warnx("\"transport-group\" section is not an object");
503 				return false;
504 			}
505 		}
506 
507 		if (key == "lun") {
508 			if (obj.type() == UCL_OBJECT) {
509 				for (const auto &child : obj) {
510 					if (!uclparse_lun(child.key().c_str(),
511 					    child))
512 						return (false);
513 				}
514 			} else {
515 				log_warnx("\"lun\" section is not an object");
516 				return (false);
517 			}
518 		}
519 	}
520 
521 	/* Pass 2 - targets */
522 	for (const auto &obj : top) {
523 		std::string key = obj.key();
524 
525 		if (key == "controller") {
526 			if (obj.type() == UCL_OBJECT) {
527 				for (const auto &child : obj) {
528 					if (!uclparse_controller(
529 					    child.key().c_str(), child))
530 						return false;
531 				}
532 			} else {
533 				log_warnx("\"controller\" section is not an object");
534 				return false;
535 			}
536 		}
537 
538 		if (key == "target") {
539 			if (obj.type() == UCL_OBJECT) {
540 				for (const auto &child : obj) {
541 					if (!uclparse_target(
542 					    child.key().c_str(), child))
543 						return (false);
544 				}
545 			} else {
546 				log_warnx("\"target\" section is not an object");
547 				return (false);
548 			}
549 		}
550 	}
551 
552 	return (true);
553 }
554 
555 static bool
uclparse_auth_group(const char * name,const ucl::Ucl & top)556 uclparse_auth_group(const char *name, const ucl::Ucl &top)
557 {
558 	if (!auth_group_start(name))
559 		return (false);
560 
561 	scope_exit finisher(auth_group_finish);
562 	for (const auto &obj : top) {
563 		std::string key = obj.key();
564 
565 		if (key == "auth-type") {
566 			if (!auth_group_set_type(obj.string_value().c_str()))
567 				return false;
568 		}
569 
570 		if (key == "chap") {
571 			if (obj.type() == UCL_OBJECT) {
572 				if (!uclparse_chap(name, obj))
573 					return false;
574 			} else if (obj.type() == UCL_ARRAY) {
575 				for (const auto &tmp : obj) {
576 					if (!uclparse_chap(name, tmp))
577 						return false;
578 				}
579 			} else {
580 				log_warnx("\"chap\" property of auth-group "
581 				    "\"%s\" is not an array or object",
582 				    name);
583 				return false;
584 			}
585 		}
586 
587 		if (key == "chap-mutual") {
588 			if (obj.type() == UCL_OBJECT) {
589 				if (!uclparse_chap_mutual(name, obj))
590 					return false;
591 			} else if (obj.type() == UCL_ARRAY) {
592 				for (const auto &tmp : obj) {
593 					if (!uclparse_chap_mutual(name, tmp))
594 						return false;
595 				}
596 			} else {
597 				log_warnx("\"chap-mutual\" property of "
598 				    "auth-group \"%s\" is not an array or object",
599 				    name);
600 				return false;
601 			}
602 		}
603 
604 		if (key == "host-address") {
605 			if (obj.type() == UCL_STRING) {
606 				if (!auth_group_add_host_address(
607 				    obj.string_value().c_str()))
608 					return false;
609 			} else if (obj.type() == UCL_ARRAY) {
610 				for (const auto &tmp : obj) {
611 					if (!auth_group_add_host_address(
612 					    tmp.string_value().c_str()))
613 						return false;
614 				}
615 			} else {
616 				log_warnx("\"host-address\" property of "
617 				    "auth-group \"%s\" is not an array or string",
618 				    name);
619 				return false;
620 			}
621 		}
622 
623 		if (key == "host-nqn") {
624 			if (obj.type() == UCL_STRING) {
625 				if (!auth_group_add_host_nqn(
626 				    obj.string_value().c_str()))
627 					return false;
628 			} else if (obj.type() == UCL_ARRAY) {
629 				for (const auto &tmp : obj) {
630 					if (!auth_group_add_host_nqn(
631 					    tmp.string_value().c_str()))
632 						return false;
633 				}
634 			} else {
635 				log_warnx("\"host-nqn\" property of "
636 				    "auth-group \"%s\" is not an array or string",
637 				    name);
638 				return false;
639 			}
640 		}
641 
642 		if (key == "initiator-name") {
643 			if (obj.type() == UCL_STRING) {
644 				if (!auth_group_add_initiator_name(
645 				    obj.string_value().c_str()))
646 					return false;
647 			} else if (obj.type() == UCL_ARRAY) {
648 				for (const auto &tmp : obj) {
649 					if (!auth_group_add_initiator_name(
650 					    tmp.string_value().c_str()))
651 						return false;
652 				}
653 			} else {
654 				log_warnx("\"initiator-name\" property of "
655 				    "auth-group \"%s\" is not an array or string",
656 				    name);
657 				return false;
658 			}
659 		}
660 
661 		if (key == "initiator-portal") {
662 			if (obj.type() == UCL_STRING) {
663 				if (!auth_group_add_initiator_portal(
664 				    obj.string_value().c_str()))
665 					return false;
666 			} else if (obj.type() == UCL_ARRAY) {
667 				for (const auto &tmp : obj) {
668 					if (!auth_group_add_initiator_portal(
669 					    tmp.string_value().c_str()))
670 						return false;
671 				}
672 			} else {
673 				log_warnx("\"initiator-portal\" property of "
674 				    "auth-group \"%s\" is not an array or string",
675 				    name);
676 				return false;
677 			}
678 		}
679 	}
680 
681 	return (true);
682 }
683 
684 static bool
uclparse_dscp(const char * group_type,const char * pg_name,const ucl::Ucl & obj)685 uclparse_dscp(const char *group_type, const char *pg_name,
686     const ucl::Ucl &obj)
687 {
688 	if ((obj.type() != UCL_STRING) && (obj.type() != UCL_INT)) {
689 		log_warnx("\"dscp\" property of %s group \"%s\" is not a "
690 		    "string or integer", group_type, pg_name);
691 		return (false);
692 	}
693 	if (obj.type() == UCL_INT)
694 		return (portal_group_set_dscp(obj.int_value()));
695 
696 	std::string key = obj.key();
697 	if (key == "be" || key == "cs0")
698 		portal_group_set_dscp(IPTOS_DSCP_CS0 >> 2);
699 	else if (key == "ef")
700 		portal_group_set_dscp(IPTOS_DSCP_EF >> 2);
701 	else if (key == "cs0")
702 		portal_group_set_dscp(IPTOS_DSCP_CS0 >> 2);
703 	else if (key == "cs1")
704 		portal_group_set_dscp(IPTOS_DSCP_CS1 >> 2);
705 	else if (key == "cs2")
706 		portal_group_set_dscp(IPTOS_DSCP_CS2 >> 2);
707 	else if (key == "cs3")
708 		portal_group_set_dscp(IPTOS_DSCP_CS3 >> 2);
709 	else if (key == "cs4")
710 		portal_group_set_dscp(IPTOS_DSCP_CS4 >> 2);
711 	else if (key == "cs5")
712 		portal_group_set_dscp(IPTOS_DSCP_CS5 >> 2);
713 	else if (key == "cs6")
714 		portal_group_set_dscp(IPTOS_DSCP_CS6 >> 2);
715 	else if (key == "cs7")
716 		portal_group_set_dscp(IPTOS_DSCP_CS7 >> 2);
717 	else if (key == "af11")
718 		portal_group_set_dscp(IPTOS_DSCP_AF11 >> 2);
719 	else if (key == "af12")
720 		portal_group_set_dscp(IPTOS_DSCP_AF12 >> 2);
721 	else if (key == "af13")
722 		portal_group_set_dscp(IPTOS_DSCP_AF13 >> 2);
723 	else if (key == "af21")
724 		portal_group_set_dscp(IPTOS_DSCP_AF21 >> 2);
725 	else if (key == "af22")
726 		portal_group_set_dscp(IPTOS_DSCP_AF22 >> 2);
727 	else if (key == "af23")
728 		portal_group_set_dscp(IPTOS_DSCP_AF23 >> 2);
729 	else if (key == "af31")
730 		portal_group_set_dscp(IPTOS_DSCP_AF31 >> 2);
731 	else if (key == "af32")
732 		portal_group_set_dscp(IPTOS_DSCP_AF32 >> 2);
733 	else if (key == "af33")
734 		portal_group_set_dscp(IPTOS_DSCP_AF33 >> 2);
735 	else if (key == "af41")
736 		portal_group_set_dscp(IPTOS_DSCP_AF41 >> 2);
737 	else if (key == "af42")
738 		portal_group_set_dscp(IPTOS_DSCP_AF42 >> 2);
739 	else if (key == "af43")
740 		portal_group_set_dscp(IPTOS_DSCP_AF43 >> 2);
741 	else {
742 		log_warnx("\"dscp\" property value is not a supported textual value");
743 		return (false);
744 	}
745 	return (true);
746 }
747 
748 static bool
uclparse_pcp(const char * group_type,const char * pg_name,const ucl::Ucl & obj)749 uclparse_pcp(const char *group_type, const char *pg_name,
750     const ucl::Ucl &obj)
751 {
752 	if (obj.type() != UCL_INT) {
753 		log_warnx("\"pcp\" property of %s group \"%s\" is not an "
754 		    "integer", group_type, pg_name);
755 		return (false);
756 	}
757 	return (portal_group_set_pcp(obj.int_value()));
758 }
759 
760 static bool
uclparse_portal_group(const char * name,const ucl::Ucl & top)761 uclparse_portal_group(const char *name, const ucl::Ucl &top)
762 {
763 	if (!portal_group_start(name))
764 		return (false);
765 
766 	scope_exit finisher(portal_group_finish);
767 	for (const auto &obj : top) {
768 		std::string key = obj.key();
769 
770 		if (key == "discovery-auth-group") {
771 			if (obj.type() != UCL_STRING) {
772 				log_warnx("\"discovery-auth-group\" property "
773 				    "of portal-group \"%s\" is not a string",
774 				    name);
775 				return false;
776 			}
777 
778 			if (!portal_group_set_discovery_auth_group(
779 			    obj.string_value().c_str()))
780 				return false;
781 		}
782 
783 		if (key == "discovery-filter") {
784 			if (obj.type() != UCL_STRING) {
785 				log_warnx("\"discovery-filter\" property of "
786 				    "portal-group \"%s\" is not a string",
787 				    name);
788 				return false;
789 			}
790 
791 			if (!portal_group_set_filter(
792 			    obj.string_value().c_str()))
793 				return false;
794 		}
795 
796 		if (key == "foreign") {
797 			portal_group_set_foreign();
798 		}
799 
800 		if (key == "listen") {
801 			if (obj.type() == UCL_STRING) {
802 				if (!portal_group_add_listen(
803 				    obj.string_value().c_str(), false))
804 					return false;
805 			} else if (obj.type() == UCL_ARRAY) {
806 				for (const auto &tmp : obj) {
807 					if (!portal_group_add_listen(
808 					    tmp.string_value().c_str(),
809 					    false))
810 						return false;
811 				}
812 			} else {
813 				log_warnx("\"listen\" property of "
814 				    "portal-group \"%s\" is not a string",
815 				    name);
816 				return false;
817 			}
818 		}
819 
820 		if (key == "listen-iser") {
821 			if (obj.type() == UCL_STRING) {
822 				if (!portal_group_add_listen(
823 				    obj.string_value().c_str(), true))
824 					return false;
825 			} else if (obj.type() == UCL_ARRAY) {
826 				for (const auto &tmp : obj) {
827 					if (!portal_group_add_listen(
828 					    tmp.string_value().c_str(),
829 					    true))
830 						return false;
831 				}
832 			} else {
833 				log_warnx("\"listen\" property of "
834 				    "portal-group \"%s\" is not a string",
835 				    name);
836 				return false;
837 			}
838 		}
839 
840 		if (key == "offload") {
841 			if (obj.type() != UCL_STRING) {
842 				log_warnx("\"offload\" property of "
843 				    "portal-group \"%s\" is not a string",
844 				    name);
845 				return false;
846 			}
847 
848 			if (!portal_group_set_offload(
849 			    obj.string_value().c_str()))
850 				return false;
851 		}
852 
853 		if (key == "redirect") {
854 			if (obj.type() != UCL_STRING) {
855 				log_warnx("\"listen\" property of "
856 				    "portal-group \"%s\" is not a string",
857 				    name);
858 				return false;
859 			}
860 
861 			if (!portal_group_set_redirection(
862 			    obj.string_value().c_str()))
863 				return false;
864 		}
865 
866 		if (key == "options") {
867 			if (obj.type() != UCL_OBJECT) {
868 				log_warnx("\"options\" property of portal group "
869 				    "\"%s\" is not an object", name);
870 				return false;
871 			}
872 
873 			for (const auto &tmp : obj) {
874 				if (!portal_group_add_option(
875 				    tmp.key().c_str(),
876 				    tmp.forced_string_value().c_str()))
877 					return false;
878 			}
879 		}
880 
881 		if (key == "tag") {
882 			if (obj.type() != UCL_INT) {
883 				log_warnx("\"tag\" property of portal group "
884 				    "\"%s\" is not an integer",
885 				    name);
886 				return false;
887 			}
888 
889 			portal_group_set_tag(obj.int_value());
890 		}
891 
892 		if (key == "dscp") {
893 			if (!uclparse_dscp("portal", name, obj))
894 				return false;
895 		}
896 
897 		if (key == "pcp") {
898 			if (!uclparse_pcp("portal", name, obj))
899 				return false;
900 		}
901 	}
902 
903 	return (true);
904 }
905 
906 static bool
uclparse_transport_listen_obj(const char * pg_name,const ucl::Ucl & top)907 uclparse_transport_listen_obj(const char *pg_name, const ucl::Ucl &top)
908 {
909 	for (const auto &obj : top) {
910 		std::string key = obj.key();
911 
912 		if (key.empty()) {
913 			log_warnx("missing protocol for \"listen\" "
914 			    "property of transport-group \"%s\"", pg_name);
915 			return false;
916 		}
917 
918 		if (key == "tcp") {
919 			if (obj.type() == UCL_STRING) {
920 				if (!transport_group_add_listen_tcp(
921 				    obj.string_value().c_str()))
922 					return false;
923 			} else if (obj.type() == UCL_ARRAY) {
924 				for (const auto &tmp : obj) {
925 					if (!transport_group_add_listen_tcp(
926 					    tmp.string_value().c_str()))
927 						return false;
928 				}
929 			}
930 		} else if (key == "discovery-tcp") {
931 			if (obj.type() == UCL_STRING) {
932 				if (!transport_group_add_listen_discovery_tcp(
933 				    obj.string_value().c_str()))
934 					return false;
935 			} else if (obj.type() == UCL_ARRAY) {
936 				for (const auto &tmp : obj) {
937 					if (!transport_group_add_listen_discovery_tcp(
938 					    tmp.string_value().c_str()))
939 						return false;
940 				}
941 			}
942 		} else {
943 			log_warnx("invalid listen protocol \"%s\" for "
944 			    "transport-group \"%s\"", key.c_str(), pg_name);
945 			return false;
946 		}
947 	}
948 	return true;
949 }
950 
951 static bool
uclparse_transport_group(const char * name,const ucl::Ucl & top)952 uclparse_transport_group(const char *name, const ucl::Ucl &top)
953 {
954 	if (!transport_group_start(name))
955 		return false;
956 
957 	scope_exit finisher(portal_group_finish);
958 	for (const auto &obj : top) {
959 		std::string key = obj.key();
960 
961 		if (key == "discovery-auth-group") {
962 			if (obj.type() != UCL_STRING) {
963 				log_warnx("\"discovery-auth-group\" property "
964 				    "of transport-group \"%s\" is not a string",
965 				    name);
966 				return false;
967 			}
968 
969 			if (!portal_group_set_discovery_auth_group(
970 			    obj.string_value().c_str()))
971 				return false;
972 		}
973 
974 		if (key == "discovery-filter") {
975 			if (obj.type() != UCL_STRING) {
976 				log_warnx("\"discovery-filter\" property of "
977 				    "transport-group \"%s\" is not a string",
978 				    name);
979 				return false;
980 			}
981 
982 			if (!portal_group_set_filter(
983 			    obj.string_value().c_str()))
984 				return false;
985 		}
986 
987 		if (key == "listen") {
988 			if (obj.type() != UCL_OBJECT) {
989 				log_warnx("\"listen\" property of "
990 				    "transport-group \"%s\" is not an object",
991 				    name);
992 				return false;
993 			}
994 			if (!uclparse_transport_listen_obj(name, obj))
995 				return false;
996 		}
997 
998 		if (key == "options") {
999 			if (obj.type() != UCL_OBJECT) {
1000 				log_warnx("\"options\" property of transport group "
1001 				    "\"%s\" is not an object", name);
1002 				return false;
1003 			}
1004 
1005 			for (const auto &tmp : obj) {
1006 				if (!portal_group_add_option(
1007 				    tmp.key().c_str(),
1008 				    tmp.forced_string_value().c_str()))
1009 					return false;
1010 			}
1011 		}
1012 
1013 		if (key == "dscp") {
1014 			if (!uclparse_dscp("transport", name, obj))
1015 				return false;
1016 		}
1017 
1018 		if (key == "pcp") {
1019 			if (!uclparse_pcp("transport", name, obj))
1020 				return false;
1021 		}
1022 	}
1023 
1024 	return true;
1025 }
1026 
1027 static bool
uclparse_controller(const char * name,const ucl::Ucl & top)1028 uclparse_controller(const char *name, const ucl::Ucl &top)
1029 {
1030 	if (!controller_start(name))
1031 		return false;
1032 
1033 	scope_exit finisher(target_finish);
1034 	for (const auto &obj : top) {
1035 		std::string key = obj.key();
1036 
1037 		if (key == "auth-group") {
1038 			if (obj.type() != UCL_STRING) {
1039 				log_warnx("\"auth-group\" property of "
1040 				    "controller \"%s\" is not a string", name);
1041 				return false;
1042 			}
1043 
1044 			if (!target_set_auth_group(obj.string_value().c_str()))
1045 				return false;
1046 		}
1047 
1048 		if (key == "auth-type") {
1049 			if (obj.type() != UCL_STRING) {
1050 				log_warnx("\"auth-type\" property of "
1051 				    "controller \"%s\" is not a string", name);
1052 				return false;
1053 			}
1054 
1055 			if (!target_set_auth_type(obj.string_value().c_str()))
1056 				return false;
1057 		}
1058 
1059 		if (key == "host-address") {
1060 			if (obj.type() == UCL_STRING) {
1061 				if (!controller_add_host_address(
1062 				    obj.string_value().c_str()))
1063 					return false;
1064 			} else if (obj.type() == UCL_ARRAY) {
1065 				for (const auto &tmp : obj) {
1066 					if (!controller_add_host_address(
1067 					    tmp.string_value().c_str()))
1068 						return false;
1069 				}
1070 			} else {
1071 				log_warnx("\"host-address\" property of "
1072 				    "controller \"%s\" is not an array or "
1073 				    "string", name);
1074 				return false;
1075 			}
1076 		}
1077 
1078 		if (key == "host-nqn") {
1079 			if (obj.type() == UCL_STRING) {
1080 				if (!controller_add_host_nqn(
1081 				    obj.string_value().c_str()))
1082 					return false;
1083 			} else if (obj.type() == UCL_ARRAY) {
1084 				for (const auto &tmp : obj) {
1085 					if (!controller_add_host_nqn(
1086 					    tmp.string_value().c_str()))
1087 						return false;
1088 				}
1089 			} else {
1090 				log_warnx("\"host-nqn\" property of "
1091 				    "controller \"%s\" is not an array or "
1092 				    "string", name);
1093 				return false;
1094 			}
1095 		}
1096 
1097 		if (key == "transport-group") {
1098 			if (obj.type() == UCL_ARRAY) {
1099 				for (const auto &tmp : obj) {
1100 					if (!uclparse_controller_transport_group(name,
1101 					    tmp))
1102 						return false;
1103 				}
1104 			} else {
1105 				if (!uclparse_controller_transport_group(name,
1106 				    obj))
1107 					return false;
1108 			}
1109 		}
1110 
1111 		if (key == "namespace") {
1112 			for (const auto &tmp : obj) {
1113 				if (!uclparse_controller_namespace(name, tmp))
1114 					return false;
1115 			}
1116 		}
1117 	}
1118 
1119 	return true;
1120 }
1121 
1122 static bool
uclparse_target(const char * name,const ucl::Ucl & top)1123 uclparse_target(const char *name, const ucl::Ucl &top)
1124 {
1125 	if (!target_start(name))
1126 		return (false);
1127 
1128 	scope_exit finisher(target_finish);
1129 	for (const auto &obj : top) {
1130 		std::string key = obj.key();
1131 
1132 		if (key == "alias") {
1133 			if (obj.type() != UCL_STRING) {
1134 				log_warnx("\"alias\" property of target "
1135 				    "\"%s\" is not a string", name);
1136 				return false;
1137 			}
1138 
1139 			if (!target_set_alias(obj.string_value().c_str()))
1140 				return false;
1141 		}
1142 
1143 		if (key == "auth-group") {
1144 			if (obj.type() != UCL_STRING) {
1145 				log_warnx("\"auth-group\" property of target "
1146 				    "\"%s\" is not a string", name);
1147 				return false;
1148 			}
1149 
1150 			if (!target_set_auth_group(obj.string_value().c_str()))
1151 				return false;
1152 		}
1153 
1154 		if (key == "auth-type") {
1155 			if (obj.type() != UCL_STRING) {
1156 				log_warnx("\"auth-type\" property of target "
1157 				    "\"%s\" is not a string", name);
1158 				return false;
1159 			}
1160 
1161 			if (!target_set_auth_type(obj.string_value().c_str()))
1162 				return false;
1163 		}
1164 
1165 		if (key == "chap") {
1166 			if (obj.type() == UCL_OBJECT) {
1167 				if (!uclparse_target_chap(name, obj))
1168 					return false;
1169 			} else if (obj.type() == UCL_ARRAY) {
1170 				for (const auto &tmp : obj) {
1171 					if (!uclparse_target_chap(name, tmp))
1172 						return false;
1173 				}
1174 			} else {
1175 				log_warnx("\"chap\" property of target "
1176 				    "\"%s\" is not an array or object",
1177 				    name);
1178 				return false;
1179 			}
1180 		}
1181 
1182 		if (key == "chap-mutual") {
1183 			if (obj.type() == UCL_OBJECT) {
1184 				if (!uclparse_target_chap_mutual(name, obj))
1185 					return false;
1186 			} else if (obj.type() == UCL_ARRAY) {
1187 				for (const auto &tmp : obj) {
1188 					if (!uclparse_target_chap_mutual(name,
1189 					    tmp))
1190 						return false;
1191 				}
1192 			} else {
1193 				log_warnx("\"chap-mutual\" property of target "
1194 				    "\"%s\" is not an array or object",
1195 				    name);
1196 				return false;
1197 			}
1198 		}
1199 
1200 		if (key == "initiator-name") {
1201 			if (obj.type() == UCL_STRING) {
1202 				if (!target_add_initiator_name(
1203 				    obj.string_value().c_str()))
1204 					return false;
1205 			} else if (obj.type() == UCL_ARRAY) {
1206 				for (const auto &tmp : obj) {
1207 					if (!target_add_initiator_name(
1208 					    tmp.string_value().c_str()))
1209 						return false;
1210 				}
1211 			} else {
1212 				log_warnx("\"initiator-name\" property of "
1213 				    "target \"%s\" is not an array or string",
1214 				    name);
1215 				return false;
1216 			}
1217 		}
1218 
1219 		if (key == "initiator-portal") {
1220 			if (obj.type() == UCL_STRING) {
1221 				if (!target_add_initiator_portal(
1222 				    obj.string_value().c_str()))
1223 					return false;
1224 			} else if (obj.type() == UCL_ARRAY) {
1225 				for (const auto &tmp : obj) {
1226 					if (!target_add_initiator_portal(
1227 					    tmp.string_value().c_str()))
1228 						return false;
1229 				}
1230 			} else {
1231 				log_warnx("\"initiator-portal\" property of "
1232 				    "target \"%s\" is not an array or string",
1233 				    name);
1234 				return false;
1235 			}
1236 		}
1237 
1238 		if (key == "portal-group") {
1239 			if (obj.type() == UCL_ARRAY) {
1240 				for (const auto &tmp : obj) {
1241 					if (!uclparse_target_portal_group(name,
1242 					    tmp))
1243 						return false;
1244 				}
1245 			} else {
1246 				if (!uclparse_target_portal_group(name, obj))
1247 					return false;
1248 			}
1249 		}
1250 
1251 		if (key == "port") {
1252 			if (obj.type() != UCL_STRING) {
1253 				log_warnx("\"port\" property of target "
1254 				    "\"%s\" is not a string", name);
1255 				return false;
1256 			}
1257 
1258 			if (!target_set_physical_port(obj.string_value().c_str()))
1259 				return false;
1260 		}
1261 
1262 		if (key == "redirect") {
1263 			if (obj.type() != UCL_STRING) {
1264 				log_warnx("\"redirect\" property of target "
1265 				    "\"%s\" is not a string", name);
1266 				return false;
1267 			}
1268 
1269 			if (!target_set_redirection(obj.string_value().c_str()))
1270 				return false;
1271 		}
1272 
1273 		if (key == "lun") {
1274 			for (const auto &tmp : obj) {
1275 				if (!uclparse_target_lun(name, tmp))
1276 					return false;
1277 			}
1278 		}
1279 	}
1280 
1281 	return (true);
1282 }
1283 
1284 static bool
uclparse_lun(const char * name,const ucl::Ucl & top)1285 uclparse_lun(const char *name, const ucl::Ucl &top)
1286 {
1287 	if (!lun_start(name))
1288 		return (false);
1289 
1290 	scope_exit finisher(lun_finish);
1291 	std::string lun_name = freebsd::stringf("lun \"%s\"", name);
1292 	return (uclparse_lun_entries(lun_name.c_str(), top));
1293 }
1294 
1295 static bool
uclparse_lun_entries(const char * name,const ucl::Ucl & top)1296 uclparse_lun_entries(const char *name, const ucl::Ucl &top)
1297 {
1298 	for (const auto &obj : top) {
1299 		std::string key = obj.key();
1300 
1301 		if (key == "backend") {
1302 			if (obj.type() != UCL_STRING) {
1303 				log_warnx("\"backend\" property of %s "
1304 				    "is not a string", name);
1305 				return false;
1306 			}
1307 
1308 			if (!lun_set_backend(obj.string_value().c_str()))
1309 				return false;
1310 		}
1311 
1312 		if (key == "blocksize") {
1313 			if (obj.type() != UCL_INT) {
1314 				log_warnx("\"blocksize\" property of %s "
1315 				    "is not an integer", name);
1316 				return false;
1317 			}
1318 
1319 			if (!lun_set_blocksize(obj.int_value()))
1320 				return false;
1321 		}
1322 
1323 		if (key == "device-id") {
1324 			if (obj.type() != UCL_STRING) {
1325 				log_warnx("\"device-id\" property of %s "
1326 				    "is not an integer", name);
1327 				return false;
1328 			}
1329 
1330 			if (!lun_set_device_id(obj.string_value().c_str()))
1331 				return false;
1332 		}
1333 
1334 		if (key == "device-type") {
1335 			if (obj.type() != UCL_STRING) {
1336 				log_warnx("\"device-type\" property of %s "
1337 				    "is not an integer", name);
1338 				return false;
1339 			}
1340 
1341 			if (!lun_set_device_type(obj.string_value().c_str()))
1342 				return false;
1343 		}
1344 
1345 		if (key == "ctl-lun") {
1346 			if (obj.type() != UCL_INT) {
1347 				log_warnx("\"ctl-lun\" property of %s "
1348 				    "is not an integer", name);
1349 				return false;
1350 			}
1351 
1352 			if (!lun_set_ctl_lun(obj.int_value()))
1353 				return false;
1354 		}
1355 
1356 		if (key == "options") {
1357 			if (obj.type() != UCL_OBJECT) {
1358 				log_warnx("\"options\" property of %s "
1359 				    "is not an object", name);
1360 				return false;
1361 			}
1362 
1363 			for (const auto &child : obj) {
1364 				if (!lun_add_option(child.key().c_str(),
1365 				    child.forced_string_value().c_str()))
1366 					return false;
1367 			}
1368 		}
1369 
1370 		if (key == "path") {
1371 			if (obj.type() != UCL_STRING) {
1372 				log_warnx("\"path\" property of %s "
1373 				    "is not a string", name);
1374 				return false;
1375 			}
1376 
1377 			if (!lun_set_path(obj.string_value().c_str()))
1378 				return false;
1379 		}
1380 
1381 		if (key == "serial") {
1382 			if (obj.type() != UCL_STRING) {
1383 				log_warnx("\"serial\" property of %s "
1384 				    "is not a string", name);
1385 				return false;
1386 			}
1387 
1388 			if (!lun_set_serial(obj.string_value().c_str()))
1389 				return false;
1390 		}
1391 
1392 		if (key == "size") {
1393 			if (obj.type() != UCL_INT) {
1394 				log_warnx("\"size\" property of %s "
1395 				    "is not an integer", name);
1396 				return false;
1397 			}
1398 
1399 			if (!lun_set_size(obj.int_value()))
1400 				return false;
1401 		}
1402 	}
1403 
1404 	return (true);
1405 }
1406 
1407 bool
uclparse_conf(const char * path)1408 uclparse_conf(const char *path)
1409 {
1410 	std::string err;
1411 	ucl::Ucl top = ucl::Ucl::parse_from_file(path, err);
1412 	if (!top) {
1413 		log_warnx("unable to parse configuration file %s: %s", path,
1414 		    err.c_str());
1415 		return (false);
1416 	}
1417 
1418 	bool parsed;
1419 	try {
1420 		parsed = uclparse_toplevel(top);
1421 	} catch (std::bad_alloc &) {
1422 		log_warnx("failed to allocate memory parsing %s", path);
1423 		parsed = false;
1424 	} catch (...) {
1425 		log_warnx("unknown exception parsing %s", path);
1426 		parsed = false;
1427 	}
1428 
1429 	return (parsed);
1430 }
1431