config.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561
  1. #include <resolv.h>
  2. #include <signal.h>
  3. #include <arpa/inet.h>
  4. #include <uci.h>
  5. #include <uci_blob.h>
  6. #include "odhcpd.h"
  7. static struct blob_buf b;
  8. struct list_head leases = LIST_HEAD_INIT(leases);
  9. struct list_head interfaces = LIST_HEAD_INIT(interfaces);
  10. struct config config = {false, NULL, NULL};
  11. enum {
  12. IFACE_ATTR_INTERFACE,
  13. IFACE_ATTR_IFNAME,
  14. IFACE_ATTR_DYNAMICDHCP,
  15. IFACE_ATTR_IGNORE,
  16. IFACE_ATTR_LEASETIME,
  17. IFACE_ATTR_LIMIT,
  18. IFACE_ATTR_START,
  19. IFACE_ATTR_MASTER,
  20. IFACE_ATTR_UPSTREAM,
  21. IFACE_ATTR_RA,
  22. IFACE_ATTR_DHCPV4,
  23. IFACE_ATTR_DHCPV6,
  24. IFACE_ATTR_NDPROXY,
  25. IFACE_ATTR_DNS,
  26. IFACE_ATTR_DOMAIN,
  27. IFACE_ATTR_ULA_COMPAT,
  28. IFACE_ATTR_RA_DEFAULT,
  29. IFACE_ATTR_RA_MANAGEMENT,
  30. IFACE_ATTR_RA_OFFLINK,
  31. IFACE_ATTR_RA_PREFERENCE,
  32. IFACE_ATTR_NDPROXY_ROUTING,
  33. IFACE_ATTR_NDPROXY_SLAVE,
  34. IFACE_ATTR_NDPROXY_STATIC,
  35. IFACE_ATTR_MAX
  36. };
  37. static const struct blobmsg_policy iface_attrs[IFACE_ATTR_MAX] = {
  38. [IFACE_ATTR_INTERFACE] = { .name = "interface", .type = BLOBMSG_TYPE_STRING },
  39. [IFACE_ATTR_IFNAME] = { .name = "ifname", .type = BLOBMSG_TYPE_STRING },
  40. [IFACE_ATTR_DYNAMICDHCP] = { .name = "dynamicdhcp", .type = BLOBMSG_TYPE_BOOL },
  41. [IFACE_ATTR_IGNORE] = { .name = "ignore", .type = BLOBMSG_TYPE_BOOL },
  42. [IFACE_ATTR_LEASETIME] = { .name = "leasetime", .type = BLOBMSG_TYPE_STRING },
  43. [IFACE_ATTR_START] = { .name = "start", .type = BLOBMSG_TYPE_INT32 },
  44. [IFACE_ATTR_LIMIT] = { .name = "limit", .type = BLOBMSG_TYPE_INT32 },
  45. [IFACE_ATTR_MASTER] = { .name = "master", .type = BLOBMSG_TYPE_BOOL },
  46. [IFACE_ATTR_UPSTREAM] = { .name = "upstream", .type = BLOBMSG_TYPE_ARRAY },
  47. [IFACE_ATTR_RA] = { .name = "ra", .type = BLOBMSG_TYPE_STRING },
  48. [IFACE_ATTR_DHCPV4] = { .name = "dhcpv4", .type = BLOBMSG_TYPE_STRING },
  49. [IFACE_ATTR_DHCPV6] = { .name = "dhcpv6", .type = BLOBMSG_TYPE_STRING },
  50. [IFACE_ATTR_NDPROXY] = { .name = "ndproxy", .type = BLOBMSG_TYPE_BOOL },
  51. [IFACE_ATTR_DNS] = { .name = "dns", .type = BLOBMSG_TYPE_ARRAY },
  52. [IFACE_ATTR_DOMAIN] = { .name = "domain", .type = BLOBMSG_TYPE_ARRAY },
  53. [IFACE_ATTR_ULA_COMPAT] = { .name = "ula_compat", .type = BLOBMSG_TYPE_BOOL },
  54. [IFACE_ATTR_RA_DEFAULT] = { .name = "ra_default", .type = BLOBMSG_TYPE_INT32 },
  55. [IFACE_ATTR_RA_MANAGEMENT] = { .name = "ra_management", .type = BLOBMSG_TYPE_INT32 },
  56. [IFACE_ATTR_RA_OFFLINK] = { .name = "ra_offlink", .type = BLOBMSG_TYPE_BOOL },
  57. [IFACE_ATTR_RA_PREFERENCE] = { .name = "ra_preference", .type = BLOBMSG_TYPE_STRING },
  58. [IFACE_ATTR_NDPROXY_ROUTING] = { .name = "ndproxy_routing", .type = BLOBMSG_TYPE_BOOL },
  59. [IFACE_ATTR_NDPROXY_SLAVE] = { .name = "ndproxy_slave", .type = BLOBMSG_TYPE_BOOL },
  60. [IFACE_ATTR_NDPROXY_STATIC] = { .name = "ndproxy_static", .type = BLOBMSG_TYPE_ARRAY },
  61. };
  62. static const struct uci_blob_param_info iface_attr_info[IFACE_ATTR_MAX] = {
  63. [IFACE_ATTR_UPSTREAM] = { .type = BLOBMSG_TYPE_STRING },
  64. [IFACE_ATTR_DNS] = { .type = BLOBMSG_TYPE_STRING },
  65. [IFACE_ATTR_DOMAIN] = { .type = BLOBMSG_TYPE_STRING },
  66. [IFACE_ATTR_NDPROXY_STATIC] = { .type = BLOBMSG_TYPE_STRING },
  67. };
  68. const struct uci_blob_param_list interface_attr_list = {
  69. .n_params = IFACE_ATTR_MAX,
  70. .params = iface_attrs,
  71. .info = iface_attr_info,
  72. };
  73. enum {
  74. LEASE_ATTR_IP,
  75. LEASE_ATTR_MAC,
  76. LEASE_ATTR_DUID,
  77. LEASE_ATTR_HOSTID,
  78. LEASE_ATTR_HOSTNAME,
  79. LEASE_ATTR_MAX
  80. };
  81. static const struct blobmsg_policy lease_attrs[LEASE_ATTR_MAX] = {
  82. [LEASE_ATTR_IP] = { .name = "ip", .type = BLOBMSG_TYPE_STRING },
  83. [LEASE_ATTR_MAC] = { .name = "mac", .type = BLOBMSG_TYPE_STRING },
  84. [LEASE_ATTR_DUID] = { .name = "duid", .type = BLOBMSG_TYPE_STRING },
  85. [LEASE_ATTR_HOSTID] = { .name = "hostid", .type = BLOBMSG_TYPE_STRING },
  86. [LEASE_ATTR_HOSTNAME] = { .name = "hostname", .type = BLOBMSG_TYPE_STRING },
  87. };
  88. const struct uci_blob_param_list lease_attr_list = {
  89. .n_params = LEASE_ATTR_MAX,
  90. .params = lease_attrs,
  91. };
  92. enum {
  93. ODHCPD_ATTR_LEGACY,
  94. ODHCPD_ATTR_LEASEFILE,
  95. ODHCPD_ATTR_LEASETRIGGER,
  96. ODHCPD_ATTR_MAX
  97. };
  98. static const struct blobmsg_policy odhcpd_attrs[LEASE_ATTR_MAX] = {
  99. [ODHCPD_ATTR_LEGACY] = { .name = "legacy", .type = BLOBMSG_TYPE_BOOL },
  100. [ODHCPD_ATTR_LEASEFILE] = { .name = "leasefile", .type = BLOBMSG_TYPE_STRING },
  101. [ODHCPD_ATTR_LEASETRIGGER] = { .name = "leasetrigger", .type = BLOBMSG_TYPE_STRING },
  102. };
  103. const struct uci_blob_param_list odhcpd_attr_list = {
  104. .n_params = ODHCPD_ATTR_MAX,
  105. .params = odhcpd_attrs,
  106. };
  107. static struct interface* get_interface(const char *name)
  108. {
  109. struct interface *c;
  110. list_for_each_entry(c, &interfaces, head)
  111. if (!strcmp(c->name, name))
  112. return c;
  113. return NULL;
  114. }
  115. static void clean_interface(struct interface *iface)
  116. {
  117. free(iface->dns);
  118. free(iface->search);
  119. free(iface->upstream);
  120. free(iface->static_ndp);
  121. free(iface->dhcpv4_dns);
  122. memset(&iface->ra, 0, sizeof(*iface) - offsetof(struct interface, ra));
  123. }
  124. static void close_interface(struct interface *iface)
  125. {
  126. if (iface->head.next)
  127. list_del(&iface->head);
  128. setup_router_interface(iface, false);
  129. setup_dhcpv6_interface(iface, false);
  130. setup_ndp_interface(iface, false);
  131. setup_dhcpv4_interface(iface, false);
  132. clean_interface(iface);
  133. free(iface);
  134. }
  135. static int parse_mode(const char *mode)
  136. {
  137. if (!strcmp(mode, "disabled")) {
  138. return RELAYD_DISABLED;
  139. } else if (!strcmp(mode, "server")) {
  140. return RELAYD_SERVER;
  141. } else if (!strcmp(mode, "relay")) {
  142. return RELAYD_RELAY;
  143. } else if (!strcmp(mode, "hybrid")) {
  144. return RELAYD_HYBRID;
  145. } else {
  146. return -1;
  147. }
  148. }
  149. static void set_config(struct uci_section *s)
  150. {
  151. struct blob_attr *tb[ODHCPD_ATTR_MAX], *c;
  152. blob_buf_init(&b, 0);
  153. uci_to_blob(&b, s, &lease_attr_list);
  154. blobmsg_parse(lease_attrs, ODHCPD_ATTR_MAX, tb, blob_data(b.head), blob_len(b.head));
  155. if ((c = tb[ODHCPD_ATTR_LEGACY]))
  156. config.legacy = blobmsg_get_bool(c);
  157. if ((c = tb[ODHCPD_ATTR_LEASEFILE])) {
  158. free(config.dhcp_statefile);
  159. config.dhcp_statefile = strdup(blobmsg_get_string(c));
  160. }
  161. if ((c = tb[ODHCPD_ATTR_LEASETRIGGER])) {
  162. free(config.dhcp_cb);
  163. config.dhcp_cb = strdup(blobmsg_get_string(c));
  164. }
  165. }
  166. static int set_lease(struct uci_section *s)
  167. {
  168. struct blob_attr *tb[LEASE_ATTR_MAX], *c;
  169. blob_buf_init(&b, 0);
  170. uci_to_blob(&b, s, &lease_attr_list);
  171. blobmsg_parse(lease_attrs, LEASE_ATTR_MAX, tb, blob_data(b.head), blob_len(b.head));
  172. size_t hostlen = 1;
  173. if ((c = tb[LEASE_ATTR_HOSTNAME]))
  174. hostlen = blobmsg_data_len(c);
  175. struct lease *lease = calloc(1, sizeof(*lease) + hostlen);
  176. if (hostlen > 1)
  177. memcpy(lease->hostname, blobmsg_get_string(c), hostlen);
  178. if ((c = tb[LEASE_ATTR_IP]))
  179. if (inet_pton(AF_INET, blobmsg_get_string(c), &lease->ipaddr) < 0)
  180. goto err;
  181. if ((c = tb[LEASE_ATTR_MAC]))
  182. if (!ether_aton_r(blobmsg_get_string(c), &lease->mac))
  183. goto err;
  184. if ((c = tb[LEASE_ATTR_DUID])) {
  185. size_t duidlen = (blobmsg_data_len(c) - 1) / 2;
  186. lease->duid = malloc(duidlen);
  187. ssize_t len = odhcpd_unhexlify(lease->duid,
  188. duidlen, blobmsg_get_string(c));
  189. if (len < 0)
  190. goto err;
  191. lease->duid_len = len;
  192. }
  193. if ((c = tb[LEASE_ATTR_HOSTID]))
  194. if (odhcpd_unhexlify((uint8_t*)&lease->hostid, sizeof(lease->hostid),
  195. blobmsg_get_string(c)) < 0)
  196. goto err;
  197. list_add(&lease->head, &leases);
  198. return 0;
  199. err:
  200. free(lease->duid);
  201. free(lease);
  202. return -1;
  203. }
  204. int config_parse_interface(struct blob_attr *b, const char *name)
  205. {
  206. bool overwrite = !!name;
  207. struct blob_attr *tb[IFACE_ATTR_MAX], *c;
  208. blobmsg_parse(iface_attrs, IFACE_ATTR_MAX, tb, blob_data(b), blob_len(b));
  209. if (tb[IFACE_ATTR_INTERFACE])
  210. name = blobmsg_data(tb[IFACE_ATTR_INTERFACE]);
  211. struct interface *iface = get_interface(name);
  212. if (!iface) {
  213. iface = calloc(1, sizeof(*iface));
  214. strncpy(iface->name, name, sizeof(iface->name) - 1);
  215. list_add(&iface->head, &interfaces);
  216. } else {
  217. clean_interface(iface);
  218. }
  219. const char *ifname = NULL;
  220. #ifdef WITH_UBUS
  221. if (overwrite)
  222. ifname = ubus_get_ifname(name);
  223. #endif
  224. if ((c = tb[IFACE_ATTR_IFNAME]))
  225. ifname = blobmsg_get_string(c);
  226. strncpy(iface->ifname, ifname, sizeof(iface->ifname) - 1);
  227. iface->inuse = true;
  228. if (overwrite)
  229. clean_interface(iface);
  230. if ((c = tb[IFACE_ATTR_DYNAMICDHCP]))
  231. iface->no_dynamic_dhcp = !blobmsg_get_bool(c);
  232. if ((c = tb[IFACE_ATTR_IGNORE]))
  233. iface->ignore = blobmsg_get_bool(c);
  234. if ((c = tb[IFACE_ATTR_LEASETIME])) {
  235. char *val = blobmsg_get_string(c), *endptr;
  236. double time = strtod(val, &endptr);
  237. if (time && endptr[0]) {
  238. if (endptr[0] == 's')
  239. time *= 1;
  240. else if (endptr[0] == 'm')
  241. time *= 60;
  242. else if (endptr[0] == 'h')
  243. time *= 3600;
  244. else if (endptr[0] == 'd')
  245. time *= 24 * 3600;
  246. else if (endptr[0] == 'w')
  247. time *= 7 * 24 * 3600;
  248. else
  249. goto err;
  250. }
  251. if (time >= 60)
  252. iface->dhcpv4_leasetime = time;
  253. }
  254. if ((c = tb[IFACE_ATTR_START])) {
  255. iface->dhcpv4_start.s_addr = htonl(blobmsg_get_u32(c));
  256. if (config.legacy)
  257. iface->dhcpv4 = RELAYD_SERVER;
  258. }
  259. if ((c = tb[IFACE_ATTR_LIMIT]))
  260. iface->dhcpv4_end.s_addr = htonl(
  261. ntohl(iface->dhcpv4_start.s_addr) + blobmsg_get_u32(c));
  262. if ((c = tb[IFACE_ATTR_MASTER]))
  263. iface->master = blobmsg_get_bool(c);
  264. if ((c = tb[IFACE_ATTR_UPSTREAM])) {
  265. struct blob_attr *cur;
  266. int rem;
  267. blobmsg_for_each_attr(cur, c, rem) {
  268. if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING || !blobmsg_check_attr(cur, NULL))
  269. continue;
  270. iface->upstream = realloc(iface->upstream,
  271. iface->upstream_len + blobmsg_data_len(cur));
  272. memcpy(iface->upstream + iface->upstream_len, blobmsg_get_string(cur), blobmsg_data_len(cur));
  273. iface->upstream_len += blobmsg_data_len(cur);
  274. }
  275. }
  276. if ((c = tb[IFACE_ATTR_RA]))
  277. if ((iface->ra = parse_mode(blobmsg_get_string(c))) < 0)
  278. goto err;
  279. if ((c = tb[IFACE_ATTR_DHCPV4]))
  280. if ((iface->dhcpv4 = parse_mode(blobmsg_get_string(c))) < 0)
  281. goto err;
  282. if ((c = tb[IFACE_ATTR_DHCPV6]))
  283. if ((iface->dhcpv6 = parse_mode(blobmsg_get_string(c))) < 0)
  284. goto err;
  285. if ((c = tb[IFACE_ATTR_NDPROXY]))
  286. iface->ndp = blobmsg_get_bool(c) ? RELAYD_RELAY : RELAYD_DISABLED;
  287. if ((c = tb[IFACE_ATTR_DNS])) {
  288. struct blob_attr *cur;
  289. int rem;
  290. blobmsg_for_each_attr(cur, c, rem) {
  291. if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING || !blobmsg_check_attr(cur, NULL))
  292. continue;
  293. struct in_addr addr4;
  294. struct in6_addr addr6;
  295. if (inet_pton(AF_INET, blobmsg_get_string(cur), &addr4) == 1) {
  296. iface->dhcpv4_dns = realloc(iface->dhcpv4_dns,
  297. (++iface->dhcpv4_dns_cnt) * sizeof(*iface->dhcpv4_dns));
  298. iface->dhcpv4_dns[iface->dhcpv4_dns_cnt - 1] = addr4;
  299. } else if (inet_pton(AF_INET6, blobmsg_get_string(cur), &addr6) == 1) {
  300. iface->dns = realloc(iface->dns,
  301. (++iface->dns_cnt) * sizeof(*iface->dns));
  302. iface->dns[iface->dns_cnt - 1] = addr6;
  303. } else {
  304. goto err;
  305. }
  306. }
  307. }
  308. if ((c = tb[IFACE_ATTR_DOMAIN])) {
  309. struct blob_attr *cur;
  310. int rem;
  311. blobmsg_for_each_attr(cur, c, rem) {
  312. if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING || !blobmsg_check_attr(cur, NULL))
  313. continue;
  314. uint8_t buf[256];
  315. int len = dn_comp(blobmsg_get_string(cur), buf, sizeof(buf), NULL, NULL);
  316. if (len <= 0)
  317. goto err;
  318. iface->search = realloc(iface->search, iface->search_len + len);
  319. memcpy(&iface->search[iface->search_len], buf, len);
  320. iface->search_len += len;
  321. }
  322. }
  323. if ((c = tb[IFACE_ATTR_ULA_COMPAT]))
  324. iface->deprecate_ula_if_public_avail = blobmsg_get_bool(c);
  325. if ((c = tb[IFACE_ATTR_RA_DEFAULT]))
  326. iface->default_router = blobmsg_get_u32(c);
  327. if ((c = tb[IFACE_ATTR_RA_MANAGEMENT]))
  328. iface->managed = blobmsg_get_u32(c);
  329. if ((c = tb[IFACE_ATTR_RA_OFFLINK]))
  330. iface->ra_not_onlink = blobmsg_get_bool(c);
  331. if ((c = tb[IFACE_ATTR_RA_PREFERENCE])) {
  332. const char *prio = blobmsg_get_string(c);
  333. if (!strcmp(prio, "high"))
  334. iface->route_preference = 1;
  335. else if (!strcmp(prio, "low"))
  336. iface->route_preference = -1;
  337. else if (!strcmp(prio, "medium") || !strcmp(prio, "default"))
  338. iface->route_preference = 0;
  339. else
  340. goto err;
  341. }
  342. if ((c = tb[IFACE_ATTR_NDPROXY_ROUTING]))
  343. iface->learn_routes = blobmsg_get_bool(c);
  344. if ((c = tb[IFACE_ATTR_NDPROXY_SLAVE]))
  345. iface->external = blobmsg_get_bool(c);
  346. if ((c = tb[IFACE_ATTR_NDPROXY_STATIC])) {
  347. struct blob_attr *cur;
  348. int rem;
  349. blobmsg_for_each_attr(cur, c, rem) {
  350. if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING || !blobmsg_check_attr(cur, NULL))
  351. continue;
  352. int len = blobmsg_data_len(cur);
  353. iface->static_ndp = realloc(iface->static_ndp, iface->static_ndp_len + len);
  354. memcpy(&iface->static_ndp[iface->static_ndp_len], blobmsg_get_string(cur), len);
  355. iface->static_ndp_len += len;
  356. }
  357. }
  358. iface->ignore = (iface->ifindex = if_nametoindex(iface->ifname)) < 0;
  359. return 0;
  360. err:
  361. close_interface(iface);
  362. return -1;
  363. }
  364. static int set_interface(struct uci_section *s)
  365. {
  366. blob_buf_init(&b, 0);
  367. uci_to_blob(&b, s, &interface_attr_list);
  368. return config_parse_interface(b.head, s->e.name);
  369. }
  370. static volatile bool do_reload = false;
  371. static void set_stop(int signal)
  372. {
  373. uloop_end();
  374. do_reload = (signal == SIGHUP);
  375. }
  376. void odhcpd_run(void)
  377. {
  378. struct uci_context *uci = uci_alloc_context();
  379. signal(SIGTERM, set_stop);
  380. signal(SIGHUP, set_stop);
  381. signal(SIGINT, set_stop);
  382. do {
  383. do_reload = false;
  384. struct lease *l;
  385. list_for_each_entry(l, &leases, head) {
  386. list_del(&l->head);
  387. free(l->duid);
  388. free(l);
  389. }
  390. struct uci_package *dhcp = NULL;
  391. if (!uci_load(uci, "dhcp", &dhcp)) {
  392. struct uci_element *e;
  393. uci_foreach_element(&dhcp->sections, e) {
  394. struct uci_section *s = uci_to_section(e);
  395. if (!strcmp(s->type, "lease"))
  396. set_lease(s);
  397. else if (!strcmp(s->type, "odhcpd"))
  398. set_config(s);
  399. }
  400. uci_foreach_element(&dhcp->sections, e) {
  401. struct uci_section *s = uci_to_section(e);
  402. if (!strcmp(s->type, "dhcp"))
  403. set_interface(s);
  404. }
  405. }
  406. #ifdef WITH_UBUS
  407. ubus_apply_network();
  408. #endif
  409. // Evaluate hybrid mode for master
  410. struct interface *master = NULL, *i;
  411. list_for_each_entry(i, &interfaces, head) {
  412. if (!i->master)
  413. continue;
  414. enum odhcpd_mode hybrid_mode = RELAYD_DISABLED;
  415. if (i->dhcpv6 == RELAYD_HYBRID)
  416. i->dhcpv6 = hybrid_mode;
  417. if (i->ra == RELAYD_HYBRID)
  418. i->ra = hybrid_mode;
  419. if (i->ndp == RELAYD_HYBRID)
  420. i->ndp = hybrid_mode;
  421. if (i->dhcpv6 == RELAYD_RELAY || i->ra == RELAYD_RELAY || i->ndp == RELAYD_RELAY)
  422. master = i;
  423. }
  424. list_for_each_entry(i, &interfaces, head) {
  425. if (i->inuse && !i->ignore) {
  426. // Resolve hybrid mode
  427. if (i->dhcpv6 == RELAYD_HYBRID)
  428. i->dhcpv6 = (master && master->dhcpv6 == RELAYD_RELAY) ?
  429. RELAYD_RELAY : RELAYD_SERVER;
  430. if (i->ra == RELAYD_HYBRID)
  431. i->ra = (master && master->ra == RELAYD_RELAY) ?
  432. RELAYD_RELAY : RELAYD_SERVER;
  433. if (i->ndp == RELAYD_HYBRID)
  434. i->ndp = (master && master->ndp == RELAYD_RELAY) ?
  435. RELAYD_RELAY : RELAYD_SERVER;
  436. setup_router_interface(i, true);
  437. setup_dhcpv6_interface(i, true);
  438. setup_ndp_interface(i, true);
  439. setup_dhcpv4_interface(i, true);
  440. } else {
  441. close_interface(i);
  442. }
  443. }
  444. uloop_run();
  445. if (dhcp)
  446. uci_unload(uci, dhcp);
  447. } while (do_reload);
  448. }