]>
Commit | Line | Data |
---|---|---|
8fcf1d61 YW |
1 | /* SPDX-License-Identifier: LGPL-2.1+ */ |
2 | ||
3 | #include "sd-dhcp-server.h" | |
4 | ||
dd1d3060 MAL |
5 | #include "fd-util.h" |
6 | #include "fileio.h" | |
8fcf1d61 YW |
7 | #include "networkd-dhcp-server.h" |
8 | #include "networkd-link.h" | |
9 | #include "networkd-manager.h" | |
10 | #include "networkd-network.h" | |
564ca984 | 11 | #include "parse-util.h" |
dd1d3060 | 12 | #include "socket-netlink.h" |
564ca984 SS |
13 | #include "string-table.h" |
14 | #include "string-util.h" | |
dd1d3060 | 15 | #include "strv.h" |
8fcf1d61 YW |
16 | |
17 | static Address* link_find_dhcp_server_address(Link *link) { | |
18 | Address *address; | |
19 | ||
20 | assert(link); | |
21 | assert(link->network); | |
22 | ||
23 | /* The first statically configured address if there is any */ | |
5dc31db7 ZJS |
24 | LIST_FOREACH(addresses, address, link->network->static_addresses) |
25 | if (address->family == AF_INET && | |
26 | !in_addr_is_null(address->family, &address->in_addr)) | |
27 | return address; | |
8fcf1d61 YW |
28 | |
29 | /* If that didn't work, find a suitable address we got from the pool */ | |
5dc31db7 ZJS |
30 | LIST_FOREACH(addresses, address, link->pool_addresses) |
31 | if (address->family == AF_INET) | |
32 | return address; | |
8fcf1d61 YW |
33 | |
34 | return NULL; | |
35 | } | |
36 | ||
37 | static int link_push_uplink_dns_to_dhcp_server(Link *link, sd_dhcp_server *s) { | |
38 | _cleanup_free_ struct in_addr *addresses = NULL; | |
39 | size_t n_addresses = 0, n_allocated = 0; | |
8fcf1d61 | 40 | |
5dc31db7 | 41 | for (unsigned i = 0; i < link->network->n_dns; i++) { |
8fcf1d61 YW |
42 | struct in_addr ia; |
43 | ||
44 | /* Only look for IPv4 addresses */ | |
45 | if (link->network->dns[i].family != AF_INET) | |
46 | continue; | |
47 | ||
48 | ia = link->network->dns[i].address.in; | |
49 | ||
50 | /* Never propagate obviously borked data */ | |
51 | if (in4_addr_is_null(&ia) || in4_addr_is_localhost(&ia)) | |
52 | continue; | |
53 | ||
54 | if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + 1)) | |
55 | return log_oom(); | |
56 | ||
57 | addresses[n_addresses++] = ia; | |
58 | } | |
59 | ||
60 | if (link->network->dhcp_use_dns && link->dhcp_lease) { | |
5dc31db7 | 61 | const struct in_addr *da; |
8fcf1d61 | 62 | |
5dc31db7 | 63 | int n = sd_dhcp_lease_get_dns(link->dhcp_lease, &da); |
8fcf1d61 | 64 | if (n > 0) { |
8fcf1d61 YW |
65 | if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + n)) |
66 | return log_oom(); | |
67 | ||
5dc31db7 | 68 | for (int j = 0; j < n; j++) |
8fcf1d61 YW |
69 | if (in4_addr_is_non_local(&da[j])) |
70 | addresses[n_addresses++] = da[j]; | |
71 | } | |
72 | } | |
73 | ||
74 | if (n_addresses <= 0) | |
75 | return 0; | |
76 | ||
77 | return sd_dhcp_server_set_dns(s, addresses, n_addresses); | |
78 | } | |
79 | ||
24e6f458 ZJS |
80 | static int link_push_uplink_to_dhcp_server( |
81 | Link *link, | |
82 | sd_dhcp_lease_info what, | |
83 | sd_dhcp_server *s) { | |
8fcf1d61 | 84 | |
284e8fd0 SS |
85 | _cleanup_free_ struct in_addr *addresses = NULL; |
86 | size_t n_addresses = 0, n_allocated = 0; | |
24e6f458 ZJS |
87 | bool lease_condition; |
88 | char **servers; | |
284e8fd0 SS |
89 | |
90 | if (!link->network) | |
91 | return 0; | |
92 | ||
24e6f458 | 93 | log_link_debug(link, "Copying %s from link", dhcp_lease_info_to_string(what)); |
284e8fd0 | 94 | |
24e6f458 ZJS |
95 | switch (what) { |
96 | case SD_DHCP_LEASE_DNS_SERVERS: | |
97 | /* DNS servers are stored as parsed data, so special handling is required. | |
98 | * TODO: check if DNS servers should be stored unparsed too. */ | |
99 | return link_push_uplink_dns_to_dhcp_server(link, s); | |
284e8fd0 | 100 | |
24e6f458 ZJS |
101 | case SD_DHCP_LEASE_NTP_SERVERS: |
102 | servers = link->network->ntp; | |
103 | lease_condition = link->network->dhcp_use_ntp; | |
104 | break; | |
284e8fd0 | 105 | |
24e6f458 ZJS |
106 | case SD_DHCP_LEASE_POP3_SERVERS: |
107 | servers = link->network->pop3; | |
108 | lease_condition = true; | |
109 | break; | |
284e8fd0 | 110 | |
24e6f458 ZJS |
111 | case SD_DHCP_LEASE_SMTP_SERVERS: |
112 | servers = link->network->smtp; | |
113 | lease_condition = true; | |
114 | break; | |
284e8fd0 | 115 | |
24e6f458 ZJS |
116 | case SD_DHCP_LEASE_SIP_SERVERS: |
117 | servers = link->network->sip; | |
118 | lease_condition = link->network->dhcp_use_sip; | |
119 | break; | |
284e8fd0 | 120 | |
d361b373 SS |
121 | case SD_DHCP_LEASE_LPR_SERVERS: |
122 | servers = link->network->lpr; | |
123 | lease_condition = true; | |
124 | break; | |
125 | ||
24e6f458 | 126 | default: |
86b52a39 | 127 | assert_not_reached("Unknown DHCP lease info item"); |
284e8fd0 SS |
128 | } |
129 | ||
f6269fe7 | 130 | char **a; |
24e6f458 | 131 | STRV_FOREACH(a, servers) { |
f6269fe7 SS |
132 | union in_addr_union ia; |
133 | ||
134 | /* Only look for IPv4 addresses */ | |
135 | if (in_addr_from_string(AF_INET, *a, &ia) <= 0) | |
136 | continue; | |
137 | ||
138 | /* Never propagate obviously borked data */ | |
139 | if (in4_addr_is_null(&ia.in) || in4_addr_is_localhost(&ia.in)) | |
140 | continue; | |
141 | ||
142 | if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + 1)) | |
143 | return log_oom(); | |
144 | ||
145 | addresses[n_addresses++] = ia.in; | |
146 | } | |
147 | ||
24e6f458 ZJS |
148 | if (lease_condition && link->dhcp_lease) { |
149 | const struct in_addr *da; | |
f6269fe7 | 150 | |
a2706075 | 151 | int n = sd_dhcp_lease_get_servers(link->dhcp_lease, what, &da); |
f6269fe7 | 152 | if (n > 0) { |
f6269fe7 SS |
153 | if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + n)) |
154 | return log_oom(); | |
155 | ||
a2706075 | 156 | for (int i = 0; i < n; i++) |
24e6f458 ZJS |
157 | if (in4_addr_is_non_local(&da[i])) |
158 | addresses[n_addresses++] = da[i]; | |
f6269fe7 SS |
159 | } |
160 | } | |
161 | ||
162 | if (n_addresses <= 0) | |
163 | return 0; | |
164 | ||
24e6f458 | 165 | return sd_dhcp_server_set_servers(s, what, addresses, n_addresses); |
299d578f SS |
166 | } |
167 | ||
dd1d3060 MAL |
168 | static int dhcp4_server_parse_dns_server_string_and_warn(Link *l, const char *string, struct in_addr **addresses, size_t *n_allocated, size_t *n_addresses) { |
169 | for (;;) { | |
170 | _cleanup_free_ char *word = NULL, *server_name = NULL; | |
171 | union in_addr_union address; | |
172 | int family, r, ifindex = 0; | |
173 | ||
174 | r = extract_first_word(&string, &word, NULL, 0); | |
175 | if (r < 0) | |
176 | return r; | |
177 | if (r == 0) | |
178 | break; | |
179 | ||
180 | r = in_addr_ifindex_name_from_string_auto(word, &family, &address, &ifindex, &server_name); | |
181 | if (r < 0) { | |
182 | log_warning_errno(r, "Failed to parse DNS server address '%s', ignoring: %m", word); | |
183 | continue; | |
184 | } | |
185 | ||
186 | /* Only look for IPv4 addresses */ | |
187 | if (family != AF_INET) | |
188 | continue; | |
189 | ||
190 | /* Never propagate obviously borked data */ | |
191 | if (in4_addr_is_null(&address.in) || in4_addr_is_localhost(&address.in)) | |
192 | continue; | |
193 | ||
194 | if (!GREEDY_REALLOC(*addresses, *n_allocated, *n_addresses + 1)) | |
195 | return log_oom(); | |
196 | ||
197 | (*addresses)[(*n_addresses)++] = address.in; | |
198 | } | |
199 | ||
200 | return 0; | |
201 | } | |
202 | ||
203 | static int dhcp4_server_set_dns_from_resolve_conf(Link *link) { | |
204 | _cleanup_free_ struct in_addr *addresses = NULL; | |
205 | size_t n_addresses = 0, n_allocated = 0; | |
206 | _cleanup_fclose_ FILE *f = NULL; | |
207 | int n = 0, r; | |
208 | ||
209 | f = fopen(PRIVATE_UPLINK_RESOLV_CONF, "re"); | |
210 | if (!f) { | |
211 | if (errno == ENOENT) | |
212 | return 0; | |
213 | ||
214 | return log_warning_errno(errno, "Failed to open " PRIVATE_UPLINK_RESOLV_CONF ": %m"); | |
215 | } | |
216 | ||
217 | for (;;) { | |
218 | _cleanup_free_ char *line = NULL; | |
219 | const char *a; | |
220 | char *l; | |
221 | ||
222 | r = read_line(f, LONG_LINE_MAX, &line); | |
223 | if (r < 0) | |
224 | return log_error_errno(r, "Failed to read " PRIVATE_UPLINK_RESOLV_CONF ": %m"); | |
225 | if (r == 0) | |
226 | break; | |
227 | ||
228 | n++; | |
229 | ||
230 | l = strstrip(line); | |
231 | if (IN_SET(*l, '#', ';', 0)) | |
232 | continue; | |
233 | ||
234 | a = first_word(l, "nameserver"); | |
235 | if (!a) | |
236 | continue; | |
237 | ||
238 | r = dhcp4_server_parse_dns_server_string_and_warn(link, a, &addresses, &n_allocated, &n_addresses); | |
239 | if (r < 0) | |
240 | log_warning_errno(r, "Failed to parse DNS server address '%s', ignoring.", a); | |
241 | } | |
242 | ||
243 | if (n_addresses <= 0) | |
244 | return 0; | |
245 | ||
246 | return sd_dhcp_server_set_dns(link->dhcp_server, addresses, n_addresses); | |
247 | } | |
248 | ||
8fcf1d61 | 249 | int dhcp4_server_configure(Link *link) { |
8fcf1d61 | 250 | bool acquired_uplink = false; |
461dbb2f | 251 | sd_dhcp_option *p; |
564ca984 SS |
252 | Link *uplink = NULL; |
253 | Address *address; | |
254 | Iterator i; | |
8fcf1d61 YW |
255 | int r; |
256 | ||
257 | address = link_find_dhcp_server_address(link); | |
258 | if (!address) | |
c00c3b64 YW |
259 | return log_link_error_errno(link, SYNTHETIC_ERRNO(EBUSY), |
260 | "Failed to find suitable address for DHCPv4 server instance."); | |
8fcf1d61 YW |
261 | |
262 | /* use the server address' subnet as the pool */ | |
263 | r = sd_dhcp_server_configure_pool(link->dhcp_server, &address->in_addr.in, address->prefixlen, | |
264 | link->network->dhcp_server_pool_offset, link->network->dhcp_server_pool_size); | |
265 | if (r < 0) | |
c00c3b64 | 266 | return log_link_error_errno(link, r, "Failed to configure address pool for DHCPv4 server instance: %m"); |
8fcf1d61 YW |
267 | |
268 | /* TODO: | |
269 | r = sd_dhcp_server_set_router(link->dhcp_server, &main_address->in_addr.in); | |
270 | if (r < 0) | |
271 | return r; | |
272 | */ | |
273 | ||
274 | if (link->network->dhcp_server_max_lease_time_usec > 0) { | |
275 | r = sd_dhcp_server_set_max_lease_time(link->dhcp_server, | |
276 | DIV_ROUND_UP(link->network->dhcp_server_max_lease_time_usec, USEC_PER_SEC)); | |
277 | if (r < 0) | |
c00c3b64 | 278 | return log_link_error_errno(link, r, "Failed to set maximum lease time for DHCPv4 server instance: %m"); |
8fcf1d61 YW |
279 | } |
280 | ||
281 | if (link->network->dhcp_server_default_lease_time_usec > 0) { | |
282 | r = sd_dhcp_server_set_default_lease_time(link->dhcp_server, | |
283 | DIV_ROUND_UP(link->network->dhcp_server_default_lease_time_usec, USEC_PER_SEC)); | |
284 | if (r < 0) | |
c00c3b64 | 285 | return log_link_error_errno(link, r, "Failed to set default lease time for DHCPv4 server instance: %m"); |
8fcf1d61 YW |
286 | } |
287 | ||
24e6f458 ZJS |
288 | const struct { |
289 | bool condition; | |
290 | const struct in_addr *servers; | |
291 | unsigned n_servers; | |
292 | } configs[] = { | |
293 | [SD_DHCP_LEASE_DNS_SERVERS] = { | |
294 | link->network->dhcp_server_emit_dns, | |
295 | link->network->dhcp_server_dns, | |
296 | link->network->n_dhcp_server_dns, | |
297 | }, | |
298 | [SD_DHCP_LEASE_NTP_SERVERS] = { | |
299 | link->network->dhcp_server_emit_ntp, | |
300 | link->network->dhcp_server_ntp, | |
301 | link->network->n_dhcp_server_ntp, | |
302 | }, | |
303 | [SD_DHCP_LEASE_SIP_SERVERS] = { | |
304 | link->network->dhcp_server_emit_sip, | |
305 | link->network->dhcp_server_sip, | |
306 | link->network->n_dhcp_server_sip, | |
307 | }, | |
308 | [SD_DHCP_LEASE_POP3_SERVERS] = { | |
309 | true, | |
310 | link->network->dhcp_server_pop3, | |
311 | link->network->n_dhcp_server_pop3, | |
312 | }, | |
313 | [SD_DHCP_LEASE_SMTP_SERVERS] = { | |
314 | true, | |
315 | link->network->dhcp_server_smtp, | |
316 | link->network->n_dhcp_server_smtp, | |
317 | }, | |
d361b373 SS |
318 | [SD_DHCP_LEASE_LPR_SERVERS] = { |
319 | true, | |
320 | link->network->dhcp_server_lpr, | |
321 | link->network->n_dhcp_server_lpr, | |
322 | }, | |
24e6f458 ZJS |
323 | }; |
324 | assert_cc(ELEMENTSOF(configs) == _SD_DHCP_LEASE_INFO_MAX); | |
325 | ||
326 | for (unsigned n = 0; n < ELEMENTSOF(configs); n++) | |
327 | if (configs[n].condition) { | |
328 | if (configs[n].n_servers > 0) | |
329 | r = sd_dhcp_server_set_servers(link->dhcp_server, n, | |
330 | configs[n].servers, configs[n].n_servers); | |
331 | else { | |
332 | if (!acquired_uplink) { | |
333 | uplink = manager_find_uplink(link->manager, link); | |
334 | acquired_uplink = true; | |
335 | } | |
336 | ||
337 | if (!uplink) { | |
338 | log_link_debug(link, | |
339 | "Not emitting %s on link, couldn't find suitable uplink.", | |
340 | dhcp_lease_info_to_string(n)); | |
341 | r = 0; | |
dd1d3060 | 342 | } else if (uplink->network) |
24e6f458 | 343 | r = link_push_uplink_to_dhcp_server(uplink, n, link->dhcp_server); |
dd1d3060 MAL |
344 | else if (n == SD_DHCP_LEASE_DNS_SERVERS) |
345 | r = dhcp4_server_set_dns_from_resolve_conf(link); | |
24e6f458 ZJS |
346 | } |
347 | if (r < 0) | |
348 | log_link_warning_errno(link, r, | |
349 | "Failed to set %s for DHCP server, ignoring: %m", | |
350 | dhcp_lease_info_to_string(n)); | |
299d578f | 351 | } |
284e8fd0 | 352 | |
8fcf1d61 YW |
353 | r = sd_dhcp_server_set_emit_router(link->dhcp_server, link->network->dhcp_server_emit_router); |
354 | if (r < 0) | |
a0fa3ef7 | 355 | return log_link_error_errno(link, r, "Failed to set router emission for DHCP server: %m"); |
8fcf1d61 YW |
356 | |
357 | if (link->network->dhcp_server_emit_timezone) { | |
358 | _cleanup_free_ char *buffer = NULL; | |
359 | const char *tz; | |
360 | ||
361 | if (link->network->dhcp_server_timezone) | |
362 | tz = link->network->dhcp_server_timezone; | |
363 | else { | |
364 | r = get_timezone(&buffer); | |
365 | if (r < 0) | |
98b02994 | 366 | return log_link_error_errno(link, r, "Failed to determine timezone: %m"); |
8fcf1d61 YW |
367 | |
368 | tz = buffer; | |
369 | } | |
370 | ||
371 | r = sd_dhcp_server_set_timezone(link->dhcp_server, tz); | |
372 | if (r < 0) | |
c00c3b64 | 373 | return log_link_error_errno(link, r, "Failed to set timezone for DHCP server: %m"); |
8fcf1d61 | 374 | } |
564ca984 | 375 | |
0e96961d | 376 | ORDERED_HASHMAP_FOREACH(p, link->network->dhcp_server_send_options, i) { |
461dbb2f | 377 | r = sd_dhcp_server_add_option(link->dhcp_server, p); |
564ca984 SS |
378 | if (r == -EEXIST) |
379 | continue; | |
380 | if (r < 0) | |
c00c3b64 | 381 | return log_link_error_errno(link, r, "Failed to set DHCPv4 option: %m"); |
564ca984 SS |
382 | } |
383 | ||
7354900d DW |
384 | ORDERED_HASHMAP_FOREACH(p, link->network->dhcp_server_send_vendor_options, i) { |
385 | r = sd_dhcp_server_add_vendor_option(link->dhcp_server, p); | |
386 | if (r == -EEXIST) | |
387 | continue; | |
388 | if (r < 0) | |
389 | return log_link_error_errno(link, r, "Failed to set DHCPv4 option: %m"); | |
390 | } | |
391 | ||
8fcf1d61 YW |
392 | if (!sd_dhcp_server_is_running(link->dhcp_server)) { |
393 | r = sd_dhcp_server_start(link->dhcp_server); | |
394 | if (r < 0) | |
a0fa3ef7 | 395 | return log_link_error_errno(link, r, "Could not start DHCPv4 server instance: %m"); |
8fcf1d61 YW |
396 | } |
397 | ||
398 | return 0; | |
399 | } | |
400 | ||
c1997a5b | 401 | static int config_parse_dhcp_lease_server_list( |
8fcf1d61 YW |
402 | const char *unit, |
403 | const char *filename, | |
404 | unsigned line, | |
8fcf1d61 | 405 | const char *lvalue, |
8fcf1d61 | 406 | const char *rvalue, |
c1997a5b ZJS |
407 | struct in_addr **addresses, |
408 | unsigned *n_addresses) { | |
8fcf1d61 YW |
409 | |
410 | assert(filename); | |
411 | assert(lvalue); | |
412 | assert(rvalue); | |
413 | ||
c1997a5b | 414 | for (const char *p = rvalue;;) { |
8fcf1d61 YW |
415 | _cleanup_free_ char *w = NULL; |
416 | union in_addr_union a; | |
c1997a5b | 417 | int r; |
8fcf1d61 YW |
418 | |
419 | r = extract_first_word(&p, &w, NULL, 0); | |
420 | if (r == -ENOMEM) | |
421 | return log_oom(); | |
422 | if (r < 0) { | |
423 | log_syntax(unit, LOG_ERR, filename, line, r, | |
424 | "Failed to extract word, ignoring: %s", rvalue); | |
425 | return 0; | |
426 | } | |
427 | if (r == 0) | |
c1997a5b | 428 | return 0; |
8fcf1d61 YW |
429 | |
430 | r = in_addr_from_string(AF_INET, w, &a); | |
431 | if (r < 0) { | |
432 | log_syntax(unit, LOG_ERR, filename, line, r, | |
c1997a5b | 433 | "Failed to parse %s= address '%s', ignoring: %m", lvalue, w); |
8fcf1d61 YW |
434 | continue; |
435 | } | |
436 | ||
c1997a5b | 437 | struct in_addr *m = reallocarray(*addresses, *n_addresses + 1, sizeof(struct in_addr)); |
8fcf1d61 YW |
438 | if (!m) |
439 | return log_oom(); | |
440 | ||
c1997a5b ZJS |
441 | m[(*n_addresses)++] = a.in; |
442 | *addresses = m; | |
8fcf1d61 | 443 | } |
8fcf1d61 YW |
444 | } |
445 | ||
c1997a5b | 446 | int config_parse_dhcp_server_dns( |
8fcf1d61 YW |
447 | const char *unit, |
448 | const char *filename, | |
449 | unsigned line, | |
450 | const char *section, | |
451 | unsigned section_line, | |
452 | const char *lvalue, | |
453 | int ltype, | |
454 | const char *rvalue, | |
455 | void *data, | |
456 | void *userdata) { | |
457 | ||
458 | Network *n = data; | |
8fcf1d61 | 459 | |
c1997a5b ZJS |
460 | return config_parse_dhcp_lease_server_list(unit, filename, line, |
461 | lvalue, rvalue, | |
462 | &n->dhcp_server_dns, &n->n_dhcp_server_dns); | |
463 | } | |
8fcf1d61 | 464 | |
c1997a5b ZJS |
465 | int config_parse_dhcp_server_ntp( |
466 | const char *unit, | |
467 | const char *filename, | |
468 | unsigned line, | |
469 | const char *section, | |
470 | unsigned section_line, | |
471 | const char *lvalue, | |
472 | int ltype, | |
473 | const char *rvalue, | |
474 | void *data, | |
475 | void *userdata) { | |
8fcf1d61 | 476 | |
c1997a5b | 477 | Network *n = data; |
8fcf1d61 | 478 | |
c1997a5b ZJS |
479 | return config_parse_dhcp_lease_server_list(unit, filename, line, |
480 | lvalue, rvalue, | |
481 | &n->dhcp_server_ntp, &n->n_dhcp_server_ntp); | |
8fcf1d61 | 482 | } |
299d578f SS |
483 | |
484 | int config_parse_dhcp_server_sip( | |
485 | const char *unit, | |
486 | const char *filename, | |
487 | unsigned line, | |
488 | const char *section, | |
489 | unsigned section_line, | |
490 | const char *lvalue, | |
491 | int ltype, | |
492 | const char *rvalue, | |
493 | void *data, | |
494 | void *userdata) { | |
495 | ||
496 | Network *n = data; | |
299d578f | 497 | |
c1997a5b ZJS |
498 | return config_parse_dhcp_lease_server_list(unit, filename, line, |
499 | lvalue, rvalue, | |
500 | &n->dhcp_server_sip, &n->n_dhcp_server_sip); | |
299d578f | 501 | } |
284e8fd0 SS |
502 | |
503 | int config_parse_dhcp_server_pop3_servers( | |
504 | const char *unit, | |
505 | const char *filename, | |
506 | unsigned line, | |
507 | const char *section, | |
508 | unsigned section_line, | |
509 | const char *lvalue, | |
510 | int ltype, | |
511 | const char *rvalue, | |
512 | void *data, | |
513 | void *userdata) { | |
514 | ||
515 | Network *n = data; | |
284e8fd0 | 516 | |
c1997a5b ZJS |
517 | return config_parse_dhcp_lease_server_list(unit, filename, line, |
518 | lvalue, rvalue, | |
519 | &n->dhcp_server_pop3, &n->n_dhcp_server_pop3); | |
f6269fe7 SS |
520 | } |
521 | ||
522 | int config_parse_dhcp_server_smtp_servers( | |
523 | const char *unit, | |
524 | const char *filename, | |
525 | unsigned line, | |
526 | const char *section, | |
527 | unsigned section_line, | |
528 | const char *lvalue, | |
529 | int ltype, | |
530 | const char *rvalue, | |
531 | void *data, | |
532 | void *userdata) { | |
533 | ||
534 | Network *n = data; | |
f6269fe7 | 535 | |
c1997a5b ZJS |
536 | return config_parse_dhcp_lease_server_list(unit, filename, line, |
537 | lvalue, rvalue, | |
538 | &n->dhcp_server_smtp, &n->n_dhcp_server_smtp); | |
f6269fe7 | 539 | |
284e8fd0 | 540 | } |
d361b373 SS |
541 | |
542 | int config_parse_dhcp_server_lpr_servers( | |
543 | const char *unit, | |
544 | const char *filename, | |
545 | unsigned line, | |
546 | const char *section, | |
547 | unsigned section_line, | |
548 | const char *lvalue, | |
549 | int ltype, | |
550 | const char *rvalue, | |
551 | void *data, | |
552 | void *userdata) { | |
553 | ||
554 | Network *n = data; | |
555 | ||
556 | return config_parse_dhcp_lease_server_list(unit, filename, line, | |
557 | lvalue, rvalue, | |
558 | &n->dhcp_server_lpr, &n->n_dhcp_server_lpr); | |
559 | ||
560 | } |