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