Bug Summary

File:build-scan/../src/libsystemd-network/sd-dhcp-server.c
Warning:line 195, column 25
Potential leak of memory pointed to by 'server'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-unknown-linux-gnu -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name sd-dhcp-server.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=all -relaxed-aliasing -menable-no-infs -menable-no-nans -menable-unsafe-fp-math -fno-signed-zeros -mreassociate -freciprocal-math -fdenormal-fp-math=preserve-sign,preserve-sign -ffp-contract=fast -fno-rounding-math -ffast-math -ffinite-math-only -mconstructor-aliases -munwind-tables -target-cpu x86-64 -tune-cpu generic -fno-split-dwarf-inlining -debugger-tuning=gdb -resource-dir /usr/lib64/clang/12.0.0 -include config.h -I src/libsystemd-network/libsystemd-network.a.p -I src/libsystemd-network -I ../src/libsystemd-network -I src/basic -I ../src/basic -I src/shared -I ../src/shared -I src/systemd -I ../src/systemd -I src/journal -I ../src/journal -I src/journal-remote -I ../src/journal-remote -I src/nspawn -I ../src/nspawn -I src/resolve -I ../src/resolve -I src/timesync -I ../src/timesync -I ../src/time-wait-sync -I src/login -I ../src/login -I src/udev -I ../src/udev -I src/libudev -I ../src/libudev -I src/core -I ../src/core -I ../src/libsystemd/sd-bus -I ../src/libsystemd/sd-device -I ../src/libsystemd/sd-hwdb -I ../src/libsystemd/sd-id128 -I ../src/libsystemd/sd-netlink -I ../src/libsystemd/sd-network -I . -I .. -D _FILE_OFFSET_BITS=64 -internal-isystem /usr/local/include -internal-isystem /usr/lib64/clang/12.0.0/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -Wwrite-strings -Wno-unused-parameter -Wno-missing-field-initializers -Wno-unused-result -Wno-format-signedness -Wno-error=nonnull -std=gnu99 -fconst-strings -fdebug-compilation-dir /home/mrc0mmand/repos/@redhat-plumbers/systemd-rhel8/build-scan -ferror-limit 19 -fvisibility hidden -stack-protector 2 -fgnuc-version=4.2.1 -fcolor-diagnostics -analyzer-output=html -faddrsig -o /tmp/scan-build-2021-07-16-221226-1465241-1 -x c ../src/libsystemd-network/sd-dhcp-server.c
1/* SPDX-License-Identifier: LGPL-2.1+ */
2/***
3 Copyright © 2013 Intel Corporation. All rights reserved.
4***/
5
6#include <sys/ioctl.h>
7
8#include "sd-dhcp-server.h"
9
10#include "alloc-util.h"
11#include "dhcp-internal.h"
12#include "dhcp-server-internal.h"
13#include "fd-util.h"
14#include "in-addr-util.h"
15#include "sd-id128.h"
16#include "siphash24.h"
17#include "string-util.h"
18#include "unaligned.h"
19
20#define DHCP_DEFAULT_LEASE_TIME_USEC((usec_t) (60ULL*((usec_t) (60ULL*((usec_t) 1000000ULL))))) USEC_PER_HOUR((usec_t) (60ULL*((usec_t) (60ULL*((usec_t) 1000000ULL)))))
21#define DHCP_MAX_LEASE_TIME_USEC(((usec_t) (60ULL*((usec_t) (60ULL*((usec_t) 1000000ULL)))))*
12)
(USEC_PER_HOUR((usec_t) (60ULL*((usec_t) (60ULL*((usec_t) 1000000ULL)))))*12)
22
23static void dhcp_lease_free(DHCPLease *lease) {
24 if (!lease)
25 return;
26
27 free(lease->client_id.data);
28 free(lease);
29}
30
31/* configures the server's address and subnet, and optionally the pool's size and offset into the subnet
32 * the whole pool must fit into the subnet, and may not contain the first (any) nor last (broadcast) address
33 * moreover, the server's own address may be in the pool, and is in that case reserved in order not to
34 * accidentally hand it out */
35int sd_dhcp_server_configure_pool(sd_dhcp_server *server, struct in_addr *address, unsigned char prefixlen, uint32_t offset, uint32_t size) {
36 struct in_addr netmask_addr;
37 be32_t netmask;
38 uint32_t server_off, broadcast_off, size_max;
39
40 assert_return(server, -EINVAL)do { if (!(((__builtin_expect(!!(server),1))) ? (1) : (log_assert_failed_return_realm
(LOG_REALM_SYSTEMD, ("server"), "../src/libsystemd-network/sd-dhcp-server.c"
, 40, __PRETTY_FUNCTION__), 0))) return (-22); } while (0)
;
41 assert_return(address, -EINVAL)do { if (!(((__builtin_expect(!!(address),1))) ? (1) : (log_assert_failed_return_realm
(LOG_REALM_SYSTEMD, ("address"), "../src/libsystemd-network/sd-dhcp-server.c"
, 41, __PRETTY_FUNCTION__), 0))) return (-22); } while (0)
;
42 assert_return(address->s_addr != INADDR_ANY, -EINVAL)do { if (!(((__builtin_expect(!!(address->s_addr != ((in_addr_t
) 0x00000000)),1))) ? (1) : (log_assert_failed_return_realm(LOG_REALM_SYSTEMD
, ("address->s_addr != INADDR_ANY"), "../src/libsystemd-network/sd-dhcp-server.c"
, 42, __PRETTY_FUNCTION__), 0))) return (-22); } while (0)
;
43 assert_return(prefixlen <= 32, -ERANGE)do { if (!(((__builtin_expect(!!(prefixlen <= 32),1))) ? (
1) : (log_assert_failed_return_realm(LOG_REALM_SYSTEMD, ("prefixlen <= 32"
), "../src/libsystemd-network/sd-dhcp-server.c", 43, __PRETTY_FUNCTION__
), 0))) return (-34); } while (0)
;
44
45 assert_se(in4_addr_prefixlen_to_netmask(&netmask_addr, prefixlen))do { if ((__builtin_expect(!!(!(in4_addr_prefixlen_to_netmask
(&netmask_addr, prefixlen))),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("in4_addr_prefixlen_to_netmask(&netmask_addr, prefixlen)"
), "../src/libsystemd-network/sd-dhcp-server.c", 45, __PRETTY_FUNCTION__
); } while (0)
;
46 netmask = netmask_addr.s_addr;
47
48 server_off = be32toh(address->s_addr & ~netmask);
49 broadcast_off = be32toh(~netmask);
50
51 /* the server address cannot be the subnet address */
52 assert_return(server_off != 0, -ERANGE)do { if (!(((__builtin_expect(!!(server_off != 0),1))) ? (1) :
(log_assert_failed_return_realm(LOG_REALM_SYSTEMD, ("server_off != 0"
), "../src/libsystemd-network/sd-dhcp-server.c", 52, __PRETTY_FUNCTION__
), 0))) return (-34); } while (0)
;
53
54 /* nor the broadcast address */
55 assert_return(server_off != broadcast_off, -ERANGE)do { if (!(((__builtin_expect(!!(server_off != broadcast_off)
,1))) ? (1) : (log_assert_failed_return_realm(LOG_REALM_SYSTEMD
, ("server_off != broadcast_off"), "../src/libsystemd-network/sd-dhcp-server.c"
, 55, __PRETTY_FUNCTION__), 0))) return (-34); } while (0)
;
56
57 /* 0 offset means we should set a default, we skip the first (subnet) address
58 and take the next one */
59 if (offset == 0)
60 offset = 1;
61
62 size_max = (broadcast_off + 1) /* the number of addresses in the subnet */
63 - offset /* exclude the addresses before the offset */
64 - 1; /* exclude the last (broadcast) address */
65
66 /* The pool must contain at least one address */
67 assert_return(size_max >= 1, -ERANGE)do { if (!(((__builtin_expect(!!(size_max >= 1),1))) ? (1)
: (log_assert_failed_return_realm(LOG_REALM_SYSTEMD, ("size_max >= 1"
), "../src/libsystemd-network/sd-dhcp-server.c", 67, __PRETTY_FUNCTION__
), 0))) return (-34); } while (0)
;
68
69 if (size != 0)
70 assert_return(size <= size_max, -ERANGE)do { if (!(((__builtin_expect(!!(size <= size_max),1))) ? (
1) : (log_assert_failed_return_realm(LOG_REALM_SYSTEMD, ("size <= size_max"
), "../src/libsystemd-network/sd-dhcp-server.c", 70, __PRETTY_FUNCTION__
), 0))) return (-34); } while (0)
;
71 else
72 size = size_max;
73
74 if (server->address != address->s_addr || server->netmask != netmask || server->pool_size != size || server->pool_offset != offset) {
75
76 free(server->bound_leases);
77 server->bound_leases = new0(DHCPLease*, size)((DHCPLease**) calloc((size), sizeof(DHCPLease*)));
78 if (!server->bound_leases)
79 return -ENOMEM12;
80
81 server->pool_offset = offset;
82 server->pool_size = size;
83
84 server->address = address->s_addr;
85 server->netmask = netmask;
86 server->subnet = address->s_addr & netmask;
87
88 if (server_off >= offset && server_off - offset < size)
89 server->bound_leases[server_off - offset] = &server->invalid_lease;
90
91 /* Drop any leases associated with the old address range */
92 hashmap_clear_with_destructor(server->leases_by_client_id, dhcp_lease_free)({ void *_item; while ((_item = hashmap_steal_first(server->
leases_by_client_id))) dhcp_lease_free(_item); })
;
93 }
94
95 return 0;
96}
97
98int sd_dhcp_server_is_running(sd_dhcp_server *server) {
99 assert_return(server, false)do { if (!(((__builtin_expect(!!(server),1))) ? (1) : (log_assert_failed_return_realm
(LOG_REALM_SYSTEMD, ("server"), "../src/libsystemd-network/sd-dhcp-server.c"
, 99, __PRETTY_FUNCTION__), 0))) return (0); } while (0)
;
100
101 return !!server->receive_message;
102}
103
104sd_dhcp_server *sd_dhcp_server_ref(sd_dhcp_server *server) {
105
106 if (!server)
107 return NULL((void*)0);
108
109 assert(server->n_ref >= 1)do { if ((__builtin_expect(!!(!(server->n_ref >= 1)),0)
)) log_assert_failed_realm(LOG_REALM_SYSTEMD, ("server->n_ref >= 1"
), "../src/libsystemd-network/sd-dhcp-server.c", 109, __PRETTY_FUNCTION__
); } while (0)
;
110 server->n_ref++;
111
112 return server;
113}
114
115void client_id_hash_func(const void *p, struct siphash *state) {
116 const DHCPClientId *id = p;
117
118 assert(id)do { if ((__builtin_expect(!!(!(id)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("id"), "../src/libsystemd-network/sd-dhcp-server.c"
, 118, __PRETTY_FUNCTION__); } while (0)
;
119 assert(id->length)do { if ((__builtin_expect(!!(!(id->length)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("id->length"), "../src/libsystemd-network/sd-dhcp-server.c"
, 119, __PRETTY_FUNCTION__); } while (0)
;
120 assert(id->data)do { if ((__builtin_expect(!!(!(id->data)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("id->data"), "../src/libsystemd-network/sd-dhcp-server.c"
, 120, __PRETTY_FUNCTION__); } while (0)
;
121
122 siphash24_compress(&id->length, sizeof(id->length), state);
123 siphash24_compress(id->data, id->length, state);
124}
125
126int client_id_compare_func(const void *_a, const void *_b) {
127 const DHCPClientId *a, *b;
128
129 a = _a;
130 b = _b;
131
132 assert(!a->length || a->data)do { if ((__builtin_expect(!!(!(!a->length || a->data))
,0))) log_assert_failed_realm(LOG_REALM_SYSTEMD, ("!a->length || a->data"
), "../src/libsystemd-network/sd-dhcp-server.c", 132, __PRETTY_FUNCTION__
); } while (0)
;
133 assert(!b->length || b->data)do { if ((__builtin_expect(!!(!(!b->length || b->data))
,0))) log_assert_failed_realm(LOG_REALM_SYSTEMD, ("!b->length || b->data"
), "../src/libsystemd-network/sd-dhcp-server.c", 133, __PRETTY_FUNCTION__
); } while (0)
;
134
135 if (a->length != b->length)
136 return a->length < b->length ? -1 : 1;
137
138 return memcmp(a->data, b->data, a->length);
139}
140
141static const struct hash_ops client_id_hash_ops = {
142 .hash = client_id_hash_func,
143 .compare = client_id_compare_func
144};
145
146sd_dhcp_server *sd_dhcp_server_unref(sd_dhcp_server *server) {
147 DHCPLease *lease;
148
149 if (!server)
150 return NULL((void*)0);
151
152 assert(server->n_ref >= 1)do { if ((__builtin_expect(!!(!(server->n_ref >= 1)),0)
)) log_assert_failed_realm(LOG_REALM_SYSTEMD, ("server->n_ref >= 1"
), "../src/libsystemd-network/sd-dhcp-server.c", 152, __PRETTY_FUNCTION__
); } while (0)
;
153 server->n_ref--;
154
155 if (server->n_ref > 0)
156 return NULL((void*)0);
157
158 log_dhcp_server(server, "UNREF")log_internal_realm(((LOG_REALM_SYSTEMD) << 10 | ((7))),
0, "../src/libsystemd-network/sd-dhcp-server.c", 158, __func__
, "DHCP SERVER: " "UNREF")
;
159
160 sd_dhcp_server_stop(server);
161
162 sd_event_unref(server->event);
163
164 free(server->timezone);
165 free(server->dns);
166 free(server->ntp);
167
168 while ((lease = hashmap_steal_first(server->leases_by_client_id)))
169 dhcp_lease_free(lease);
170 hashmap_free(server->leases_by_client_id);
171
172 free(server->bound_leases);
173 return mfree(server);
174}
175
176int sd_dhcp_server_new(sd_dhcp_server **ret, int ifindex) {
177 _cleanup_(sd_dhcp_server_unrefp)__attribute__((cleanup(sd_dhcp_server_unrefp))) sd_dhcp_server *server = NULL((void*)0);
178
179 assert_return(ret, -EINVAL)do { if (!(((__builtin_expect(!!(ret),1))) ? (1) : (log_assert_failed_return_realm
(LOG_REALM_SYSTEMD, ("ret"), "../src/libsystemd-network/sd-dhcp-server.c"
, 179, __PRETTY_FUNCTION__), 0))) return (-22); } while (0)
;
1
Assuming 'ret' is non-null
2
'?' condition is true
3
Taking false branch
4
Loop condition is false. Exiting loop
180 assert_return(ifindex > 0, -EINVAL)do { if (!(((__builtin_expect(!!(ifindex > 0),1))) ? (1) :
(log_assert_failed_return_realm(LOG_REALM_SYSTEMD, ("ifindex > 0"
), "../src/libsystemd-network/sd-dhcp-server.c", 180, __PRETTY_FUNCTION__
), 0))) return (-22); } while (0)
;
5
Assuming 'ifindex' is > 0
6
'?' condition is true
7
Taking false branch
8
Loop condition is false. Exiting loop
181
182 server = new0(sd_dhcp_server, 1)((sd_dhcp_server*) calloc((1), sizeof(sd_dhcp_server)));
9
Memory is allocated
183 if (!server)
10
Assuming 'server' is non-null
11
Taking false branch
184 return -ENOMEM12;
185
186 server->n_ref = 1;
187 server->fd_raw = -1;
188 server->fd = -1;
189 server->address = htobe32(INADDR_ANY((in_addr_t) 0x00000000));
190 server->netmask = htobe32(INADDR_ANY((in_addr_t) 0x00000000));
191 server->ifindex = ifindex;
192
193 server->leases_by_client_id = hashmap_new(&client_id_hash_ops)internal_hashmap_new(&client_id_hash_ops );
194 if (!server->leases_by_client_id)
12
Assuming field 'leases_by_client_id' is null
13
Taking true branch
195 return -ENOMEM12;
14
Potential leak of memory pointed to by 'server'
196
197 server->default_lease_time = DIV_ROUND_UP(DHCP_DEFAULT_LEASE_TIME_USEC, USEC_PER_SEC)({ const typeof((((usec_t) (60ULL*((usec_t) (60ULL*((usec_t) 1000000ULL
))))))) __unique_prefix_X6 = ((((usec_t) (60ULL*((usec_t) (60ULL
*((usec_t) 1000000ULL))))))); const typeof((((usec_t) 1000000ULL
))) __unique_prefix_Y7 = ((((usec_t) 1000000ULL))); (__unique_prefix_X6
/ __unique_prefix_Y7 + !!(__unique_prefix_X6 % __unique_prefix_Y7
)); })
;
198 server->max_lease_time = DIV_ROUND_UP(DHCP_MAX_LEASE_TIME_USEC, USEC_PER_SEC)({ const typeof(((((usec_t) (60ULL*((usec_t) (60ULL*((usec_t)
1000000ULL)))))*12))) __unique_prefix_X8 = (((((usec_t) (60ULL
*((usec_t) (60ULL*((usec_t) 1000000ULL)))))*12))); const typeof
((((usec_t) 1000000ULL))) __unique_prefix_Y9 = ((((usec_t) 1000000ULL
))); (__unique_prefix_X8 / __unique_prefix_Y9 + !!(__unique_prefix_X8
% __unique_prefix_Y9)); })
;
199
200 *ret = TAKE_PTR(server)({ typeof(server) _ptr_ = (server); (server) = ((void*)0); _ptr_
; })
;
201
202 return 0;
203}
204
205int sd_dhcp_server_attach_event(sd_dhcp_server *server, sd_event *event, int64_t priority) {
206 int r;
207
208 assert_return(server, -EINVAL)do { if (!(((__builtin_expect(!!(server),1))) ? (1) : (log_assert_failed_return_realm
(LOG_REALM_SYSTEMD, ("server"), "../src/libsystemd-network/sd-dhcp-server.c"
, 208, __PRETTY_FUNCTION__), 0))) return (-22); } while (0)
;
209 assert_return(!server->event, -EBUSY)do { if (!(((__builtin_expect(!!(!server->event),1))) ? (1
) : (log_assert_failed_return_realm(LOG_REALM_SYSTEMD, ("!server->event"
), "../src/libsystemd-network/sd-dhcp-server.c", 209, __PRETTY_FUNCTION__
), 0))) return (-16); } while (0)
;
210
211 if (event)
212 server->event = sd_event_ref(event);
213 else {
214 r = sd_event_default(&server->event);
215 if (r < 0)
216 return r;
217 }
218
219 server->event_priority = priority;
220
221 return 0;
222}
223
224int sd_dhcp_server_detach_event(sd_dhcp_server *server) {
225 assert_return(server, -EINVAL)do { if (!(((__builtin_expect(!!(server),1))) ? (1) : (log_assert_failed_return_realm
(LOG_REALM_SYSTEMD, ("server"), "../src/libsystemd-network/sd-dhcp-server.c"
, 225, __PRETTY_FUNCTION__), 0))) return (-22); } while (0)
;
226
227 server->event = sd_event_unref(server->event);
228
229 return 0;
230}
231
232sd_event *sd_dhcp_server_get_event(sd_dhcp_server *server) {
233 assert_return(server, NULL)do { if (!(((__builtin_expect(!!(server),1))) ? (1) : (log_assert_failed_return_realm
(LOG_REALM_SYSTEMD, ("server"), "../src/libsystemd-network/sd-dhcp-server.c"
, 233, __PRETTY_FUNCTION__), 0))) return (((void*)0)); } while
(0)
;
234
235 return server->event;
236}
237
238int sd_dhcp_server_stop(sd_dhcp_server *server) {
239 assert_return(server, -EINVAL)do { if (!(((__builtin_expect(!!(server),1))) ? (1) : (log_assert_failed_return_realm
(LOG_REALM_SYSTEMD, ("server"), "../src/libsystemd-network/sd-dhcp-server.c"
, 239, __PRETTY_FUNCTION__), 0))) return (-22); } while (0)
;
240
241 server->receive_message =
242 sd_event_source_unref(server->receive_message);
243
244 server->fd_raw = safe_close(server->fd_raw);
245 server->fd = safe_close(server->fd);
246
247 log_dhcp_server(server, "STOPPED")log_internal_realm(((LOG_REALM_SYSTEMD) << 10 | ((7))),
0, "../src/libsystemd-network/sd-dhcp-server.c", 247, __func__
, "DHCP SERVER: " "STOPPED")
;
248
249 return 0;
250}
251
252static int dhcp_server_send_unicast_raw(sd_dhcp_server *server,
253 DHCPPacket *packet, size_t len) {
254 union sockaddr_union link = {
255 .ll.sll_family = AF_PACKET17,
256 .ll.sll_protocol = htobe16(ETH_P_IP0x0800),
257 .ll.sll_ifindex = server->ifindex,
258 .ll.sll_halen = ETH_ALEN6,
259 };
260
261 assert(server)do { if ((__builtin_expect(!!(!(server)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("server"), "../src/libsystemd-network/sd-dhcp-server.c"
, 261, __PRETTY_FUNCTION__); } while (0)
;
262 assert(server->ifindex > 0)do { if ((__builtin_expect(!!(!(server->ifindex > 0)),0
))) log_assert_failed_realm(LOG_REALM_SYSTEMD, ("server->ifindex > 0"
), "../src/libsystemd-network/sd-dhcp-server.c", 262, __PRETTY_FUNCTION__
); } while (0)
;
263 assert(server->address)do { if ((__builtin_expect(!!(!(server->address)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("server->address"), "../src/libsystemd-network/sd-dhcp-server.c"
, 263, __PRETTY_FUNCTION__); } while (0)
;
264 assert(packet)do { if ((__builtin_expect(!!(!(packet)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("packet"), "../src/libsystemd-network/sd-dhcp-server.c"
, 264, __PRETTY_FUNCTION__); } while (0)
;
265 assert(len > sizeof(DHCPPacket))do { if ((__builtin_expect(!!(!(len > sizeof(DHCPPacket)))
,0))) log_assert_failed_realm(LOG_REALM_SYSTEMD, ("len > sizeof(DHCPPacket)"
), "../src/libsystemd-network/sd-dhcp-server.c", 265, __PRETTY_FUNCTION__
); } while (0)
;
266
267 memcpy(&link.ll.sll_addr, &packet->dhcp.chaddr, ETH_ALEN6);
268
269 dhcp_packet_append_ip_headers(packet, server->address, DHCP_PORT_SERVER,
270 packet->dhcp.yiaddr,
271 DHCP_PORT_CLIENT, len);
272
273 return dhcp_network_send_raw_socket(server->fd_raw, &link, packet, len);
274}
275
276static int dhcp_server_send_udp(sd_dhcp_server *server, be32_t destination,
277 uint16_t destination_port,
278 DHCPMessage *message, size_t len) {
279 union sockaddr_union dest = {
280 .in.sin_family = AF_INET2,
281 .in.sin_port = htobe16(destination_port),
282 .in.sin_addr.s_addr = destination,
283 };
284 struct iovec iov = {
285 .iov_base = message,
286 .iov_len = len,
287 };
288 uint8_t cmsgbuf[CMSG_LEN(sizeof(struct in_pktinfo))((((sizeof (struct cmsghdr)) + sizeof (size_t) - 1) & (size_t
) ~(sizeof (size_t) - 1)) + (sizeof(struct in_pktinfo)))
] = {};
289 struct msghdr msg = {
290 .msg_name = &dest,
291 .msg_namelen = sizeof(dest.in),
292 .msg_iov = &iov,
293 .msg_iovlen = 1,
294 .msg_control = cmsgbuf,
295 .msg_controllen = sizeof(cmsgbuf),
296 };
297 struct cmsghdr *cmsg;
298 struct in_pktinfo *pktinfo;
299
300 assert(server)do { if ((__builtin_expect(!!(!(server)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("server"), "../src/libsystemd-network/sd-dhcp-server.c"
, 300, __PRETTY_FUNCTION__); } while (0)
;
301 assert(server->fd >= 0)do { if ((__builtin_expect(!!(!(server->fd >= 0)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("server->fd >= 0"), "../src/libsystemd-network/sd-dhcp-server.c"
, 301, __PRETTY_FUNCTION__); } while (0)
;
302 assert(message)do { if ((__builtin_expect(!!(!(message)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("message"), "../src/libsystemd-network/sd-dhcp-server.c"
, 302, __PRETTY_FUNCTION__); } while (0)
;
303 assert(len > sizeof(DHCPMessage))do { if ((__builtin_expect(!!(!(len > sizeof(DHCPMessage))
),0))) log_assert_failed_realm(LOG_REALM_SYSTEMD, ("len > sizeof(DHCPMessage)"
), "../src/libsystemd-network/sd-dhcp-server.c", 303, __PRETTY_FUNCTION__
); } while (0)
;
304
305 cmsg = CMSG_FIRSTHDR(&msg)((size_t) (&msg)->msg_controllen >= sizeof (struct cmsghdr
) ? (struct cmsghdr *) (&msg)->msg_control : (struct cmsghdr
*) 0)
;
306 assert(cmsg)do { if ((__builtin_expect(!!(!(cmsg)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("cmsg"), "../src/libsystemd-network/sd-dhcp-server.c"
, 306, __PRETTY_FUNCTION__); } while (0)
;
307
308 cmsg->cmsg_level = IPPROTO_IPIPPROTO_IP;
309 cmsg->cmsg_type = IP_PKTINFO8;
310 cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo))((((sizeof (struct cmsghdr)) + sizeof (size_t) - 1) & (size_t
) ~(sizeof (size_t) - 1)) + (sizeof(struct in_pktinfo)))
;
311
312 /* we attach source interface and address info to the message
313 rather than binding the socket. This will be mostly useful
314 when we gain support for arbitrary number of server addresses
315 */
316 pktinfo = (struct in_pktinfo*) CMSG_DATA(cmsg)((cmsg)->__cmsg_data);
317 assert(pktinfo)do { if ((__builtin_expect(!!(!(pktinfo)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("pktinfo"), "../src/libsystemd-network/sd-dhcp-server.c"
, 317, __PRETTY_FUNCTION__); } while (0)
;
318
319 pktinfo->ipi_ifindex = server->ifindex;
320 pktinfo->ipi_spec_dst.s_addr = server->address;
321
322 if (sendmsg(server->fd, &msg, 0) < 0)
323 return -errno(*__errno_location ());
324
325 return 0;
326}
327
328static bool_Bool requested_broadcast(DHCPRequest *req) {
329 assert(req)do { if ((__builtin_expect(!!(!(req)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("req"), "../src/libsystemd-network/sd-dhcp-server.c"
, 329, __PRETTY_FUNCTION__); } while (0)
;
330
331 return req->message->flags & htobe16(0x8000);
332}
333
334int dhcp_server_send_packet(sd_dhcp_server *server,
335 DHCPRequest *req, DHCPPacket *packet,
336 int type, size_t optoffset) {
337 be32_t destination = INADDR_ANY((in_addr_t) 0x00000000);
338 uint16_t destination_port = DHCP_PORT_CLIENT;
339 int r;
340
341 assert(server)do { if ((__builtin_expect(!!(!(server)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("server"), "../src/libsystemd-network/sd-dhcp-server.c"
, 341, __PRETTY_FUNCTION__); } while (0)
;
342 assert(req)do { if ((__builtin_expect(!!(!(req)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("req"), "../src/libsystemd-network/sd-dhcp-server.c"
, 342, __PRETTY_FUNCTION__); } while (0)
;
343 assert(req->max_optlen)do { if ((__builtin_expect(!!(!(req->max_optlen)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("req->max_optlen"), "../src/libsystemd-network/sd-dhcp-server.c"
, 343, __PRETTY_FUNCTION__); } while (0)
;
344 assert(optoffset <= req->max_optlen)do { if ((__builtin_expect(!!(!(optoffset <= req->max_optlen
)),0))) log_assert_failed_realm(LOG_REALM_SYSTEMD, ("optoffset <= req->max_optlen"
), "../src/libsystemd-network/sd-dhcp-server.c", 344, __PRETTY_FUNCTION__
); } while (0)
;
345 assert(packet)do { if ((__builtin_expect(!!(!(packet)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("packet"), "../src/libsystemd-network/sd-dhcp-server.c"
, 345, __PRETTY_FUNCTION__); } while (0)
;
346
347 r = dhcp_option_append(&packet->dhcp, req->max_optlen, &optoffset, 0,
348 SD_DHCP_OPTION_SERVER_IDENTIFIER,
349 4, &server->address);
350 if (r < 0)
351 return r;
352
353 r = dhcp_option_append(&packet->dhcp, req->max_optlen, &optoffset, 0,
354 SD_DHCP_OPTION_END, 0, NULL((void*)0));
355 if (r < 0)
356 return r;
357
358 /* RFC 2131 Section 4.1
359
360 If the ’giaddr’ field in a DHCP message from a client is non-zero,
361 the server sends any return messages to the ’DHCP server’ port on the
362 BOOTP relay agent whose address appears in ’giaddr’. If the ’giaddr’
363 field is zero and the ’ciaddr’ field is nonzero, then the server
364 unicasts DHCPOFFER and DHCPACK messages to the address in ’ciaddr’.
365 If ’giaddr’ is zero and ’ciaddr’ is zero, and the broadcast bit is
366 set, then the server broadcasts DHCPOFFER and DHCPACK messages to
367 0xffffffff. If the broadcast bit is not set and ’giaddr’ is zero and
368 ’ciaddr’ is zero, then the server unicasts DHCPOFFER and DHCPACK
369 messages to the client’s hardware address and ’yiaddr’ address. In
370 all cases, when ’giaddr’ is zero, the server broadcasts any DHCPNAK
371 messages to 0xffffffff.
372
373 Section 4.3.2
374
375 If ’giaddr’ is set in the DHCPREQUEST message, the client is on a
376 different subnet. The server MUST set the broadcast bit in the
377 DHCPNAK, so that the relay agent will broadcast the DHCPNAK to the
378 client, because the client may not have a correct network address
379 or subnet mask, and the client may not be answering ARP requests.
380 */
381 if (req->message->giaddr) {
382 destination = req->message->giaddr;
383 destination_port = DHCP_PORT_SERVER;
384 if (type == DHCP_NAK)
385 packet->dhcp.flags = htobe16(0x8000);
386 } else if (req->message->ciaddr && type != DHCP_NAK)
387 destination = req->message->ciaddr;
388
389 if (destination != INADDR_ANY((in_addr_t) 0x00000000))
390 return dhcp_server_send_udp(server, destination,
391 destination_port, &packet->dhcp,
392 sizeof(DHCPMessage) + optoffset);
393 else if (requested_broadcast(req) || type == DHCP_NAK)
394 return dhcp_server_send_udp(server, INADDR_BROADCAST((in_addr_t) 0xffffffff),
395 destination_port, &packet->dhcp,
396 sizeof(DHCPMessage) + optoffset);
397 else
398 /* we cannot send UDP packet to specific MAC address when the
399 address is not yet configured, so must fall back to raw
400 packets */
401 return dhcp_server_send_unicast_raw(server, packet,
402 sizeof(DHCPPacket) + optoffset);
403}
404
405static int server_message_init(sd_dhcp_server *server, DHCPPacket **ret,
406 uint8_t type, size_t *_optoffset,
407 DHCPRequest *req) {
408 _cleanup_free___attribute__((cleanup(freep))) DHCPPacket *packet = NULL((void*)0);
409 size_t optoffset = 0;
410 int r;
411
412 assert(server)do { if ((__builtin_expect(!!(!(server)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("server"), "../src/libsystemd-network/sd-dhcp-server.c"
, 412, __PRETTY_FUNCTION__); } while (0)
;
413 assert(ret)do { if ((__builtin_expect(!!(!(ret)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("ret"), "../src/libsystemd-network/sd-dhcp-server.c"
, 413, __PRETTY_FUNCTION__); } while (0)
;
414 assert(_optoffset)do { if ((__builtin_expect(!!(!(_optoffset)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("_optoffset"), "../src/libsystemd-network/sd-dhcp-server.c"
, 414, __PRETTY_FUNCTION__); } while (0)
;
415 assert(IN_SET(type, DHCP_OFFER, DHCP_ACK, DHCP_NAK))do { if ((__builtin_expect(!!(!(({ _Bool _found = 0; static __attribute__
((unused)) char _static_assert__macros_need_to_be_extended[20
- sizeof((int[]){DHCP_OFFER, DHCP_ACK, DHCP_NAK})/sizeof(int
)]; switch(type) { case DHCP_OFFER: case DHCP_ACK: case DHCP_NAK
: _found = 1; break; default: break; } _found; }))),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("IN_SET(type, DHCP_OFFER, DHCP_ACK, DHCP_NAK)"
), "../src/libsystemd-network/sd-dhcp-server.c", 415, __PRETTY_FUNCTION__
); } while (0)
;
416
417 packet = malloc0(sizeof(DHCPPacket) + req->max_optlen)(calloc(1, (sizeof(DHCPPacket) + req->max_optlen)));
418 if (!packet)
419 return -ENOMEM12;
420
421 r = dhcp_message_init(&packet->dhcp, BOOTREPLY,
422 be32toh(req->message->xid), type, ARPHRD_ETHER1,
423 req->max_optlen, &optoffset);
424 if (r < 0)
425 return r;
426
427 packet->dhcp.flags = req->message->flags;
428 packet->dhcp.giaddr = req->message->giaddr;
429 memcpy(&packet->dhcp.chaddr, &req->message->chaddr, ETH_ALEN6);
430
431 *_optoffset = optoffset;
432 *ret = TAKE_PTR(packet)({ typeof(packet) _ptr_ = (packet); (packet) = ((void*)0); _ptr_
; })
;
433
434 return 0;
435}
436
437static int server_send_offer(sd_dhcp_server *server, DHCPRequest *req,
438 be32_t address) {
439 _cleanup_free___attribute__((cleanup(freep))) DHCPPacket *packet = NULL((void*)0);
440 size_t offset;
441 be32_t lease_time;
442 int r;
443
444 r = server_message_init(server, &packet, DHCP_OFFER, &offset, req);
445 if (r < 0)
446 return r;
447
448 packet->dhcp.yiaddr = address;
449
450 lease_time = htobe32(req->lifetime);
451 r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
452 SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME, 4,
453 &lease_time);
454 if (r < 0)
455 return r;
456
457 r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
458 SD_DHCP_OPTION_SUBNET_MASK, 4, &server->netmask);
459 if (r < 0)
460 return r;
461
462 if (server->emit_router) {
463 r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
464 SD_DHCP_OPTION_ROUTER, 4, &server->address);
465 if (r < 0)
466 return r;
467 }
468
469 r = dhcp_server_send_packet(server, req, packet, DHCP_OFFER, offset);
470 if (r < 0)
471 return r;
472
473 return 0;
474}
475
476static int server_send_ack(sd_dhcp_server *server, DHCPRequest *req,
477 be32_t address) {
478 _cleanup_free___attribute__((cleanup(freep))) DHCPPacket *packet = NULL((void*)0);
479 size_t offset;
480 be32_t lease_time;
481 int r;
482
483 r = server_message_init(server, &packet, DHCP_ACK, &offset, req);
484 if (r < 0)
485 return r;
486
487 packet->dhcp.yiaddr = address;
488
489 lease_time = htobe32(req->lifetime);
490 r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
491 SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME, 4,
492 &lease_time);
493 if (r < 0)
494 return r;
495
496 r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
497 SD_DHCP_OPTION_SUBNET_MASK, 4, &server->netmask);
498 if (r < 0)
499 return r;
500
501 if (server->emit_router) {
502 r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
503 SD_DHCP_OPTION_ROUTER, 4, &server->address);
504 if (r < 0)
505 return r;
506 }
507
508 if (server->n_dns > 0) {
509 r = dhcp_option_append(
510 &packet->dhcp, req->max_optlen, &offset, 0,
511 SD_DHCP_OPTION_DOMAIN_NAME_SERVER,
512 sizeof(struct in_addr) * server->n_dns, server->dns);
513 if (r < 0)
514 return r;
515 }
516
517 if (server->n_ntp > 0) {
518 r = dhcp_option_append(
519 &packet->dhcp, req->max_optlen, &offset, 0,
520 SD_DHCP_OPTION_NTP_SERVER,
521 sizeof(struct in_addr) * server->n_ntp, server->ntp);
522 if (r < 0)
523 return r;
524 }
525
526 if (server->timezone) {
527 r = dhcp_option_append(
528 &packet->dhcp, req->max_optlen, &offset, 0,
529 SD_DHCP_OPTION_NEW_TZDB_TIMEZONE,
530 strlen(server->timezone), server->timezone);
531 if (r < 0)
532 return r;
533 }
534
535 r = dhcp_server_send_packet(server, req, packet, DHCP_ACK, offset);
536 if (r < 0)
537 return r;
538
539 return 0;
540}
541
542static int server_send_nak(sd_dhcp_server *server, DHCPRequest *req) {
543 _cleanup_free___attribute__((cleanup(freep))) DHCPPacket *packet = NULL((void*)0);
544 size_t offset;
545 int r;
546
547 r = server_message_init(server, &packet, DHCP_NAK, &offset, req);
548 if (r < 0)
549 return r;
550
551 return dhcp_server_send_packet(server, req, packet, DHCP_NAK, offset);
552}
553
554static int server_send_forcerenew(sd_dhcp_server *server, be32_t address,
555 be32_t gateway, uint8_t chaddr[]) {
556 _cleanup_free___attribute__((cleanup(freep))) DHCPPacket *packet = NULL((void*)0);
557 size_t optoffset = 0;
558 int r;
559
560 assert(server)do { if ((__builtin_expect(!!(!(server)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("server"), "../src/libsystemd-network/sd-dhcp-server.c"
, 560, __PRETTY_FUNCTION__); } while (0)
;
561 assert(address != INADDR_ANY)do { if ((__builtin_expect(!!(!(address != ((in_addr_t) 0x00000000
))),0))) log_assert_failed_realm(LOG_REALM_SYSTEMD, ("address != INADDR_ANY"
), "../src/libsystemd-network/sd-dhcp-server.c", 561, __PRETTY_FUNCTION__
); } while (0)
;
562 assert(chaddr)do { if ((__builtin_expect(!!(!(chaddr)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("chaddr"), "../src/libsystemd-network/sd-dhcp-server.c"
, 562, __PRETTY_FUNCTION__); } while (0)
;
563
564 packet = malloc0(sizeof(DHCPPacket) + DHCP_MIN_OPTIONS_SIZE)(calloc(1, (sizeof(DHCPPacket) + (576 - (int32_t)(sizeof(struct
udphdr) + (int32_t)(sizeof(struct iphdr))) - (int32_t)(sizeof
(DHCPMessage))))))
;
565 if (!packet)
566 return -ENOMEM12;
567
568 r = dhcp_message_init(&packet->dhcp, BOOTREPLY, 0,
569 DHCP_FORCERENEW, ARPHRD_ETHER1,
570 DHCP_MIN_OPTIONS_SIZE(576 - (int32_t)(sizeof(struct udphdr) + (int32_t)(sizeof(struct
iphdr))) - (int32_t)(sizeof(DHCPMessage)))
, &optoffset);
571 if (r < 0)
572 return r;
573
574 r = dhcp_option_append(&packet->dhcp, DHCP_MIN_OPTIONS_SIZE(576 - (int32_t)(sizeof(struct udphdr) + (int32_t)(sizeof(struct
iphdr))) - (int32_t)(sizeof(DHCPMessage)))
,
575 &optoffset, 0, SD_DHCP_OPTION_END, 0, NULL((void*)0));
576 if (r < 0)
577 return r;
578
579 memcpy(&packet->dhcp.chaddr, chaddr, ETH_ALEN6);
580
581 r = dhcp_server_send_udp(server, address, DHCP_PORT_CLIENT,
582 &packet->dhcp,
583 sizeof(DHCPMessage) + optoffset);
584 if (r < 0)
585 return r;
586
587 return 0;
588}
589
590static int parse_request(uint8_t code, uint8_t len, const void *option, void *userdata) {
591 DHCPRequest *req = userdata;
592
593 assert(req)do { if ((__builtin_expect(!!(!(req)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("req"), "../src/libsystemd-network/sd-dhcp-server.c"
, 593, __PRETTY_FUNCTION__); } while (0)
;
594
595 switch(code) {
596 case SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME:
597 if (len == 4)
598 req->lifetime = unaligned_read_be32(option);
599
600 break;
601 case SD_DHCP_OPTION_REQUESTED_IP_ADDRESS:
602 if (len == 4)
603 memcpy(&req->requested_ip, option, sizeof(be32_t));
604
605 break;
606 case SD_DHCP_OPTION_SERVER_IDENTIFIER:
607 if (len == 4)
608 memcpy(&req->server_id, option, sizeof(be32_t));
609
610 break;
611 case SD_DHCP_OPTION_CLIENT_IDENTIFIER:
612 if (len >= 2) {
613 uint8_t *data;
614
615 data = memdup(option, len);
616 if (!data)
617 return -ENOMEM12;
618
619 free(req->client_id.data);
620 req->client_id.data = data;
621 req->client_id.length = len;
622 }
623
624 break;
625 case SD_DHCP_OPTION_MAXIMUM_MESSAGE_SIZE:
626
627 if (len == 2 && unaligned_read_be16(option) >= sizeof(DHCPPacket))
628 req->max_optlen = unaligned_read_be16(option) - sizeof(DHCPPacket);
629
630 break;
631 }
632
633 return 0;
634}
635
636static void dhcp_request_free(DHCPRequest *req) {
637 if (!req)
638 return;
639
640 free(req->client_id.data);
641 free(req);
642}
643
644DEFINE_TRIVIAL_CLEANUP_FUNC(DHCPRequest*, dhcp_request_free)static inline void dhcp_request_freep(DHCPRequest* *p) { if (
*p) dhcp_request_free(*p); }
;
645
646static int ensure_sane_request(sd_dhcp_server *server, DHCPRequest *req, DHCPMessage *message) {
647 assert(req)do { if ((__builtin_expect(!!(!(req)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("req"), "../src/libsystemd-network/sd-dhcp-server.c"
, 647, __PRETTY_FUNCTION__); } while (0)
;
648 assert(message)do { if ((__builtin_expect(!!(!(message)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("message"), "../src/libsystemd-network/sd-dhcp-server.c"
, 648, __PRETTY_FUNCTION__); } while (0)
;
649
650 req->message = message;
651
652 /* set client id based on MAC address if client did not send an explicit
653 one */
654 if (!req->client_id.data) {
655 void *data;
656
657 data = malloc0(ETH_ALEN + 1)(calloc(1, (6 + 1)));
658 if (!data)
659 return -ENOMEM12;
660
661 ((uint8_t*) data)[0] = 0x01;
662 memcpy((uint8_t*) data + 1, &message->chaddr, ETH_ALEN6);
663
664 req->client_id.length = ETH_ALEN6 + 1;
665 req->client_id.data = data;
666 }
667
668 if (req->max_optlen < DHCP_MIN_OPTIONS_SIZE(576 - (int32_t)(sizeof(struct udphdr) + (int32_t)(sizeof(struct
iphdr))) - (int32_t)(sizeof(DHCPMessage)))
)
669 req->max_optlen = DHCP_MIN_OPTIONS_SIZE(576 - (int32_t)(sizeof(struct udphdr) + (int32_t)(sizeof(struct
iphdr))) - (int32_t)(sizeof(DHCPMessage)))
;
670
671 if (req->lifetime <= 0)
672 req->lifetime = MAX(1ULL, server->default_lease_time)__extension__ ({ const typeof((1ULL)) __unique_prefix_A10 = (
(1ULL)); const typeof((server->default_lease_time)) __unique_prefix_B11
= ((server->default_lease_time)); __unique_prefix_A10 >
__unique_prefix_B11 ? __unique_prefix_A10 : __unique_prefix_B11
; })
;
673
674 if (server->max_lease_time > 0 && req->lifetime > server->max_lease_time)
675 req->lifetime = server->max_lease_time;
676
677 return 0;
678}
679
680static int get_pool_offset(sd_dhcp_server *server, be32_t requested_ip) {
681 assert(server)do { if ((__builtin_expect(!!(!(server)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("server"), "../src/libsystemd-network/sd-dhcp-server.c"
, 681, __PRETTY_FUNCTION__); } while (0)
;
682
683 if (!server->pool_size)
684 return -EINVAL22;
685
686 if (be32toh(requested_ip) < (be32toh(server->subnet) | server->pool_offset) ||
687 be32toh(requested_ip) >= (be32toh(server->subnet) | (server->pool_offset + server->pool_size)))
688 return -ERANGE34;
689
690 return be32toh(requested_ip & ~server->netmask) - server->pool_offset;
691}
692
693#define HASH_KEY((const sd_id128_t) { .bytes = { 0x0d, 0x1d, 0xfe, 0xbd, 0xf1
, 0x24, 0xbd, 0xb3, 0x47, 0xf1, 0xdd, 0x6e, 0x73, 0x21, 0x93,
0x30 }})
SD_ID128_MAKE(0d,1d,fe,bd,f1,24,bd,b3,47,f1,dd,6e,73,21,93,30)((const sd_id128_t) { .bytes = { 0x0d, 0x1d, 0xfe, 0xbd, 0xf1
, 0x24, 0xbd, 0xb3, 0x47, 0xf1, 0xdd, 0x6e, 0x73, 0x21, 0x93,
0x30 }})
694
695int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
696 size_t length) {
697 _cleanup_(dhcp_request_freep)__attribute__((cleanup(dhcp_request_freep))) DHCPRequest *req = NULL((void*)0);
698 _cleanup_free___attribute__((cleanup(freep))) char *error_message = NULL((void*)0);
699 DHCPLease *existing_lease;
700 int type, r;
701
702 assert(server)do { if ((__builtin_expect(!!(!(server)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("server"), "../src/libsystemd-network/sd-dhcp-server.c"
, 702, __PRETTY_FUNCTION__); } while (0)
;
703 assert(message)do { if ((__builtin_expect(!!(!(message)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("message"), "../src/libsystemd-network/sd-dhcp-server.c"
, 703, __PRETTY_FUNCTION__); } while (0)
;
704
705 if (message->op != BOOTREQUEST ||
706 message->htype != ARPHRD_ETHER1 ||
707 message->hlen != ETHER_ADDR_LEN6)
708 return 0;
709
710 req = new0(DHCPRequest, 1)((DHCPRequest*) calloc((1), sizeof(DHCPRequest)));
711 if (!req)
712 return -ENOMEM12;
713
714 type = dhcp_option_parse(message, length, parse_request, req, &error_message);
715 if (type < 0)
716 return 0;
717
718 r = ensure_sane_request(server, req, message);
719 if (r < 0)
720 /* this only fails on critical errors */
721 return r;
722
723 existing_lease = hashmap_get(server->leases_by_client_id,
724 &req->client_id);
725
726 switch(type) {
727
728 case DHCP_DISCOVER: {
729 be32_t address = INADDR_ANY((in_addr_t) 0x00000000);
730 unsigned i;
731
732 log_dhcp_server(server, "DISCOVER (0x%x)",log_internal_realm(((LOG_REALM_SYSTEMD) << 10 | ((7))),
0, "../src/libsystemd-network/sd-dhcp-server.c", 733, __func__
, "DHCP SERVER: " "DISCOVER (0x%x)", be32toh(req->message->
xid))
733 be32toh(req->message->xid))log_internal_realm(((LOG_REALM_SYSTEMD) << 10 | ((7))),
0, "../src/libsystemd-network/sd-dhcp-server.c", 733, __func__
, "DHCP SERVER: " "DISCOVER (0x%x)", be32toh(req->message->
xid))
;
734
735 if (!server->pool_size)
736 /* no pool allocated */
737 return 0;
738
739 /* for now pick a random free address from the pool */
740 if (existing_lease)
741 address = existing_lease->address;
742 else {
743 struct siphash state;
744 uint64_t hash;
745 uint32_t next_offer;
746
747 /* even with no persistence of leases, we try to offer the same client
748 the same IP address. we do this by using the hash of the client id
749 as the offset into the pool of leases when finding the next free one */
750
751 siphash24_init(&state, HASH_KEY((const sd_id128_t) { .bytes = { 0x0d, 0x1d, 0xfe, 0xbd, 0xf1
, 0x24, 0xbd, 0xb3, 0x47, 0xf1, 0xdd, 0x6e, 0x73, 0x21, 0x93,
0x30 }})
.bytes);
752 client_id_hash_func(&req->client_id, &state);
753 hash = htole64(siphash24_finalize(&state));
754 next_offer = hash % server->pool_size;
755
756 for (i = 0; i < server->pool_size; i++) {
757 if (!server->bound_leases[next_offer]) {
758 address = server->subnet | htobe32(server->pool_offset + next_offer);
759 break;
760 }
761
762 next_offer = (next_offer + 1) % server->pool_size;
763 }
764 }
765
766 if (address == INADDR_ANY((in_addr_t) 0x00000000))
767 /* no free addresses left */
768 return 0;
769
770 r = server_send_offer(server, req, address);
771 if (r < 0)
772 /* this only fails on critical errors */
773 return log_dhcp_server_errno(server, r, "Could not send offer: %m")log_internal_realm(((LOG_REALM_SYSTEMD) << 10 | ((7))),
r, "../src/libsystemd-network/sd-dhcp-server.c", 773, __func__
, "DHCP SERVER: " "Could not send offer: %m")
;
774
775 log_dhcp_server(server, "OFFER (0x%x)", be32toh(req->message->xid))log_internal_realm(((LOG_REALM_SYSTEMD) << 10 | ((7))),
0, "../src/libsystemd-network/sd-dhcp-server.c", 775, __func__
, "DHCP SERVER: " "OFFER (0x%x)", be32toh(req->message->
xid))
;
776 return DHCP_OFFER;
777 }
778 case DHCP_DECLINE:
779 log_dhcp_server(server, "DECLINE (0x%x): %s", be32toh(req->message->xid), strna(error_message))log_internal_realm(((LOG_REALM_SYSTEMD) << 10 | ((7))),
0, "../src/libsystemd-network/sd-dhcp-server.c", 779, __func__
, "DHCP SERVER: " "DECLINE (0x%x): %s", be32toh(req->message
->xid), strna(error_message))
;
780
781 /* TODO: make sure we don't offer this address again */
782
783 return 1;
784
785 case DHCP_REQUEST: {
786 be32_t address;
787 bool_Bool init_reboot = false0;
788 int pool_offset;
789
790 /* see RFC 2131, section 4.3.2 */
791
792 if (req->server_id) {
793 log_dhcp_server(server, "REQUEST (selecting) (0x%x)",log_internal_realm(((LOG_REALM_SYSTEMD) << 10 | ((7))),
0, "../src/libsystemd-network/sd-dhcp-server.c", 794, __func__
, "DHCP SERVER: " "REQUEST (selecting) (0x%x)", be32toh(req->
message->xid))
794 be32toh(req->message->xid))log_internal_realm(((LOG_REALM_SYSTEMD) << 10 | ((7))),
0, "../src/libsystemd-network/sd-dhcp-server.c", 794, __func__
, "DHCP SERVER: " "REQUEST (selecting) (0x%x)", be32toh(req->
message->xid))
;
795
796 /* SELECTING */
797 if (req->server_id != server->address)
798 /* client did not pick us */
799 return 0;
800
801 if (req->message->ciaddr)
802 /* this MUST be zero */
803 return 0;
804
805 if (!req->requested_ip)
806 /* this must be filled in with the yiaddr
807 from the chosen OFFER */
808 return 0;
809
810 address = req->requested_ip;
811 } else if (req->requested_ip) {
812 log_dhcp_server(server, "REQUEST (init-reboot) (0x%x)",log_internal_realm(((LOG_REALM_SYSTEMD) << 10 | ((7))),
0, "../src/libsystemd-network/sd-dhcp-server.c", 813, __func__
, "DHCP SERVER: " "REQUEST (init-reboot) (0x%x)", be32toh(req
->message->xid))
813 be32toh(req->message->xid))log_internal_realm(((LOG_REALM_SYSTEMD) << 10 | ((7))),
0, "../src/libsystemd-network/sd-dhcp-server.c", 813, __func__
, "DHCP SERVER: " "REQUEST (init-reboot) (0x%x)", be32toh(req
->message->xid))
;
814
815 /* INIT-REBOOT */
816 if (req->message->ciaddr)
817 /* this MUST be zero */
818 return 0;
819
820 /* TODO: check more carefully if IP is correct */
821 address = req->requested_ip;
822 init_reboot = true1;
823 } else {
824 log_dhcp_server(server, "REQUEST (rebinding/renewing) (0x%x)",log_internal_realm(((LOG_REALM_SYSTEMD) << 10 | ((7))),
0, "../src/libsystemd-network/sd-dhcp-server.c", 825, __func__
, "DHCP SERVER: " "REQUEST (rebinding/renewing) (0x%x)", be32toh
(req->message->xid))
825 be32toh(req->message->xid))log_internal_realm(((LOG_REALM_SYSTEMD) << 10 | ((7))),
0, "../src/libsystemd-network/sd-dhcp-server.c", 825, __func__
, "DHCP SERVER: " "REQUEST (rebinding/renewing) (0x%x)", be32toh
(req->message->xid))
;
826
827 /* REBINDING / RENEWING */
828 if (!req->message->ciaddr)
829 /* this MUST be filled in with clients IP address */
830 return 0;
831
832 address = req->message->ciaddr;
833 }
834
835 pool_offset = get_pool_offset(server, address);
836
837 /* verify that the requested address is from the pool, and either
838 owned by the current client or free */
839 if (pool_offset >= 0 &&
840 server->bound_leases[pool_offset] == existing_lease) {
841 DHCPLease *lease;
842 usec_t time_now = 0;
843
844 if (!existing_lease) {
845 lease = new0(DHCPLease, 1)((DHCPLease*) calloc((1), sizeof(DHCPLease)));
846 if (!lease)
847 return -ENOMEM12;
848 lease->address = address;
849 lease->client_id.data = memdup(req->client_id.data,
850 req->client_id.length);
851 if (!lease->client_id.data) {
852 free(lease);
853 return -ENOMEM12;
854 }
855 lease->client_id.length = req->client_id.length;
856 memcpy(&lease->chaddr, &req->message->chaddr,
857 ETH_ALEN6);
858 lease->gateway = req->message->giaddr;
859 } else
860 lease = existing_lease;
861
862 r = sd_event_now(server->event,
863 clock_boottime_or_monotonic(),
864 &time_now);
865 if (r < 0) {
866 if (!existing_lease)
867 dhcp_lease_free(lease);
868 return r;
869 }
870
871 lease->expiration = req->lifetime * USEC_PER_SEC((usec_t) 1000000ULL) + time_now;
872
873 r = server_send_ack(server, req, address);
874 if (r < 0) {
875 /* this only fails on critical errors */
876 log_dhcp_server_errno(server, r, "Could not send ack: %m")log_internal_realm(((LOG_REALM_SYSTEMD) << 10 | ((7))),
r, "../src/libsystemd-network/sd-dhcp-server.c", 876, __func__
, "DHCP SERVER: " "Could not send ack: %m")
;
877
878 if (!existing_lease)
879 dhcp_lease_free(lease);
880
881 return r;
882 } else {
883 log_dhcp_server(server, "ACK (0x%x)",log_internal_realm(((LOG_REALM_SYSTEMD) << 10 | ((7))),
0, "../src/libsystemd-network/sd-dhcp-server.c", 884, __func__
, "DHCP SERVER: " "ACK (0x%x)", be32toh(req->message->xid
))
884 be32toh(req->message->xid))log_internal_realm(((LOG_REALM_SYSTEMD) << 10 | ((7))),
0, "../src/libsystemd-network/sd-dhcp-server.c", 884, __func__
, "DHCP SERVER: " "ACK (0x%x)", be32toh(req->message->xid
))
;
885
886 server->bound_leases[pool_offset] = lease;
887 hashmap_put(server->leases_by_client_id,
888 &lease->client_id, lease);
889
890 return DHCP_ACK;
891 }
892
893 } else if (init_reboot) {
894 r = server_send_nak(server, req);
895 if (r < 0)
896 /* this only fails on critical errors */
897 return log_dhcp_server_errno(server, r, "Could not send nak: %m")log_internal_realm(((LOG_REALM_SYSTEMD) << 10 | ((7))),
r, "../src/libsystemd-network/sd-dhcp-server.c", 897, __func__
, "DHCP SERVER: " "Could not send nak: %m")
;
898
899 log_dhcp_server(server, "NAK (0x%x)", be32toh(req->message->xid))log_internal_realm(((LOG_REALM_SYSTEMD) << 10 | ((7))),
0, "../src/libsystemd-network/sd-dhcp-server.c", 899, __func__
, "DHCP SERVER: " "NAK (0x%x)", be32toh(req->message->xid
))
;
900 return DHCP_NAK;
901 }
902
903 break;
904 }
905
906 case DHCP_RELEASE: {
907 int pool_offset;
908
909 log_dhcp_server(server, "RELEASE (0x%x)",log_internal_realm(((LOG_REALM_SYSTEMD) << 10 | ((7))),
0, "../src/libsystemd-network/sd-dhcp-server.c", 910, __func__
, "DHCP SERVER: " "RELEASE (0x%x)", be32toh(req->message->
xid))
910 be32toh(req->message->xid))log_internal_realm(((LOG_REALM_SYSTEMD) << 10 | ((7))),
0, "../src/libsystemd-network/sd-dhcp-server.c", 910, __func__
, "DHCP SERVER: " "RELEASE (0x%x)", be32toh(req->message->
xid))
;
911
912 if (!existing_lease)
913 return 0;
914
915 if (existing_lease->address != req->message->ciaddr)
916 return 0;
917
918 pool_offset = get_pool_offset(server, req->message->ciaddr);
919 if (pool_offset < 0)
920 return 0;
921
922 if (server->bound_leases[pool_offset] == existing_lease) {
923 server->bound_leases[pool_offset] = NULL((void*)0);
924 hashmap_remove(server->leases_by_client_id, existing_lease);
925 dhcp_lease_free(existing_lease);
926 }
927
928 return 0;
929 }}
930
931 return 0;
932}
933
934static int server_receive_message(sd_event_source *s, int fd,
935 uint32_t revents, void *userdata) {
936 _cleanup_free___attribute__((cleanup(freep))) DHCPMessage *message = NULL((void*)0);
937 uint8_t cmsgbuf[CMSG_LEN(sizeof(struct in_pktinfo))((((sizeof (struct cmsghdr)) + sizeof (size_t) - 1) & (size_t
) ~(sizeof (size_t) - 1)) + (sizeof(struct in_pktinfo)))
];
938 sd_dhcp_server *server = userdata;
939 struct iovec iov = {};
940 struct msghdr msg = {
941 .msg_iov = &iov,
942 .msg_iovlen = 1,
943 .msg_control = cmsgbuf,
944 .msg_controllen = sizeof(cmsgbuf),
945 };
946 struct cmsghdr *cmsg;
947 ssize_t buflen, len;
948 int r;
949
950 assert(server)do { if ((__builtin_expect(!!(!(server)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("server"), "../src/libsystemd-network/sd-dhcp-server.c"
, 950, __PRETTY_FUNCTION__); } while (0)
;
951
952 buflen = next_datagram_size_fd(fd);
953 if (buflen < 0)
954 return buflen;
955
956 message = malloc(buflen);
957 if (!message)
958 return -ENOMEM12;
959
960 iov.iov_base = message;
961 iov.iov_len = buflen;
962
963 len = recvmsg(fd, &msg, 0);
964 if (len < 0) {
965 if (IN_SET(errno, EAGAIN, EINTR)({ _Bool _found = 0; static __attribute__ ((unused)) char _static_assert__macros_need_to_be_extended
[20 - sizeof((int[]){11, 4})/sizeof(int)]; switch((*__errno_location
())) { case 11: case 4: _found = 1; break; default: break; }
_found; })
)
966 return 0;
967
968 return -errno(*__errno_location ());
969 }
970 if ((size_t)len < sizeof(DHCPMessage))
971 return 0;
972
973 CMSG_FOREACH(cmsg, &msg)for ((cmsg) = ((size_t) (&msg)->msg_controllen >= sizeof
(struct cmsghdr) ? (struct cmsghdr *) (&msg)->msg_control
: (struct cmsghdr *) 0); (cmsg); (cmsg) = __cmsg_nxthdr ((&
msg), (cmsg)))
{
974 if (cmsg->cmsg_level == IPPROTO_IPIPPROTO_IP &&
975 cmsg->cmsg_type == IP_PKTINFO8 &&
976 cmsg->cmsg_len == CMSG_LEN(sizeof(struct in_pktinfo))((((sizeof (struct cmsghdr)) + sizeof (size_t) - 1) & (size_t
) ~(sizeof (size_t) - 1)) + (sizeof(struct in_pktinfo)))
) {
977 struct in_pktinfo *info = (struct in_pktinfo*)CMSG_DATA(cmsg)((cmsg)->__cmsg_data);
978
979 /* TODO figure out if this can be done as a filter on
980 * the socket, like for IPv6 */
981 if (server->ifindex != info->ipi_ifindex)
982 return 0;
983
984 break;
985 }
986 }
987
988 r = dhcp_server_handle_message(server, message, (size_t) len);
989 if (r < 0)
990 log_dhcp_server_errno(server, r, "Couldn't process incoming message: %m")log_internal_realm(((LOG_REALM_SYSTEMD) << 10 | ((7))),
r, "../src/libsystemd-network/sd-dhcp-server.c", 990, __func__
, "DHCP SERVER: " "Couldn't process incoming message: %m")
;
991
992 return 0;
993}
994
995int sd_dhcp_server_start(sd_dhcp_server *server) {
996 int r;
997
998 assert_return(server, -EINVAL)do { if (!(((__builtin_expect(!!(server),1))) ? (1) : (log_assert_failed_return_realm
(LOG_REALM_SYSTEMD, ("server"), "../src/libsystemd-network/sd-dhcp-server.c"
, 998, __PRETTY_FUNCTION__), 0))) return (-22); } while (0)
;
999 assert_return(server->event, -EINVAL)do { if (!(((__builtin_expect(!!(server->event),1))) ? (1)
: (log_assert_failed_return_realm(LOG_REALM_SYSTEMD, ("server->event"
), "../src/libsystemd-network/sd-dhcp-server.c", 999, __PRETTY_FUNCTION__
), 0))) return (-22); } while (0)
;
1000 assert_return(!server->receive_message, -EBUSY)do { if (!(((__builtin_expect(!!(!server->receive_message)
,1))) ? (1) : (log_assert_failed_return_realm(LOG_REALM_SYSTEMD
, ("!server->receive_message"), "../src/libsystemd-network/sd-dhcp-server.c"
, 1000, __PRETTY_FUNCTION__), 0))) return (-16); } while (0)
;
1001 assert_return(server->fd_raw < 0, -EBUSY)do { if (!(((__builtin_expect(!!(server->fd_raw < 0),1)
)) ? (1) : (log_assert_failed_return_realm(LOG_REALM_SYSTEMD,
("server->fd_raw < 0"), "../src/libsystemd-network/sd-dhcp-server.c"
, 1001, __PRETTY_FUNCTION__), 0))) return (-16); } while (0)
;
1002 assert_return(server->fd < 0, -EBUSY)do { if (!(((__builtin_expect(!!(server->fd < 0),1))) ?
(1) : (log_assert_failed_return_realm(LOG_REALM_SYSTEMD, ("server->fd < 0"
), "../src/libsystemd-network/sd-dhcp-server.c", 1002, __PRETTY_FUNCTION__
), 0))) return (-16); } while (0)
;
1003 assert_return(server->address != htobe32(INADDR_ANY), -EUNATCH)do { if (!(((__builtin_expect(!!(server->address != htobe32
(((in_addr_t) 0x00000000))),1))) ? (1) : (log_assert_failed_return_realm
(LOG_REALM_SYSTEMD, ("server->address != htobe32(INADDR_ANY)"
), "../src/libsystemd-network/sd-dhcp-server.c", 1003, __PRETTY_FUNCTION__
), 0))) return (-49); } while (0)
;
1004
1005 r = socket(AF_PACKET17, SOCK_DGRAMSOCK_DGRAM | SOCK_NONBLOCKSOCK_NONBLOCK, 0);
1006 if (r < 0) {
1007 r = -errno(*__errno_location ());
1008 sd_dhcp_server_stop(server);
1009 return r;
1010 }
1011 server->fd_raw = r;
1012
1013 r = dhcp_network_bind_udp_socket(server->ifindex, INADDR_ANY((in_addr_t) 0x00000000), DHCP_PORT_SERVER);
1014 if (r < 0) {
1015 sd_dhcp_server_stop(server);
1016 return r;
1017 }
1018 server->fd = r;
1019
1020 r = sd_event_add_io(server->event, &server->receive_message,
1021 server->fd, EPOLLINEPOLLIN,
1022 server_receive_message, server);
1023 if (r < 0) {
1024 sd_dhcp_server_stop(server);
1025 return r;
1026 }
1027
1028 r = sd_event_source_set_priority(server->receive_message,
1029 server->event_priority);
1030 if (r < 0) {
1031 sd_dhcp_server_stop(server);
1032 return r;
1033 }
1034
1035 log_dhcp_server(server, "STARTED")log_internal_realm(((LOG_REALM_SYSTEMD) << 10 | ((7))),
0, "../src/libsystemd-network/sd-dhcp-server.c", 1035, __func__
, "DHCP SERVER: " "STARTED")
;
1036
1037 return 0;
1038}
1039
1040int sd_dhcp_server_forcerenew(sd_dhcp_server *server) {
1041 unsigned i;
1042 int r = 0;
1043
1044 assert_return(server, -EINVAL)do { if (!(((__builtin_expect(!!(server),1))) ? (1) : (log_assert_failed_return_realm
(LOG_REALM_SYSTEMD, ("server"), "../src/libsystemd-network/sd-dhcp-server.c"
, 1044, __PRETTY_FUNCTION__), 0))) return (-22); } while (0)
;
1045 assert(server->bound_leases)do { if ((__builtin_expect(!!(!(server->bound_leases)),0))
) log_assert_failed_realm(LOG_REALM_SYSTEMD, ("server->bound_leases"
), "../src/libsystemd-network/sd-dhcp-server.c", 1045, __PRETTY_FUNCTION__
); } while (0)
;
1046
1047 for (i = 0; i < server->pool_size; i++) {
1048 DHCPLease *lease = server->bound_leases[i];
1049
1050 if (!lease || lease == &server->invalid_lease)
1051 continue;
1052
1053 r = server_send_forcerenew(server, lease->address,
1054 lease->gateway,
1055 lease->chaddr);
1056 if (r < 0)
1057 return r;
1058
1059 log_dhcp_server(server, "FORCERENEW")log_internal_realm(((LOG_REALM_SYSTEMD) << 10 | ((7))),
0, "../src/libsystemd-network/sd-dhcp-server.c", 1059, __func__
, "DHCP SERVER: " "FORCERENEW")
;
1060 }
1061
1062 return r;
1063}
1064
1065int sd_dhcp_server_set_timezone(sd_dhcp_server *server, const char *tz) {
1066 int r;
1067
1068 assert_return(server, -EINVAL)do { if (!(((__builtin_expect(!!(server),1))) ? (1) : (log_assert_failed_return_realm
(LOG_REALM_SYSTEMD, ("server"), "../src/libsystemd-network/sd-dhcp-server.c"
, 1068, __PRETTY_FUNCTION__), 0))) return (-22); } while (0)
;
1069 assert_return(timezone_is_valid(tz, LOG_DEBUG), -EINVAL)do { if (!(((__builtin_expect(!!(timezone_is_valid(tz, 7)),1)
)) ? (1) : (log_assert_failed_return_realm(LOG_REALM_SYSTEMD,
("timezone_is_valid(tz, LOG_DEBUG)"), "../src/libsystemd-network/sd-dhcp-server.c"
, 1069, __PRETTY_FUNCTION__), 0))) return (-22); } while (0)
;
1070
1071 if (streq_ptr(tz, server->timezone))
1072 return 0;
1073
1074 r = free_and_strdup(&server->timezone, tz);
1075 if (r < 0)
1076 return r;
1077
1078 return 1;
1079}
1080
1081int sd_dhcp_server_set_max_lease_time(sd_dhcp_server *server, uint32_t t) {
1082 assert_return(server, -EINVAL)do { if (!(((__builtin_expect(!!(server),1))) ? (1) : (log_assert_failed_return_realm
(LOG_REALM_SYSTEMD, ("server"), "../src/libsystemd-network/sd-dhcp-server.c"
, 1082, __PRETTY_FUNCTION__), 0))) return (-22); } while (0)
;
1083
1084 if (t == server->max_lease_time)
1085 return 0;
1086
1087 server->max_lease_time = t;
1088 return 1;
1089}
1090
1091int sd_dhcp_server_set_default_lease_time(sd_dhcp_server *server, uint32_t t) {
1092 assert_return(server, -EINVAL)do { if (!(((__builtin_expect(!!(server),1))) ? (1) : (log_assert_failed_return_realm
(LOG_REALM_SYSTEMD, ("server"), "../src/libsystemd-network/sd-dhcp-server.c"
, 1092, __PRETTY_FUNCTION__), 0))) return (-22); } while (0)
;
1093
1094 if (t == server->default_lease_time)
1095 return 0;
1096
1097 server->default_lease_time = t;
1098 return 1;
1099}
1100
1101int sd_dhcp_server_set_dns(sd_dhcp_server *server, const struct in_addr dns[], unsigned n) {
1102 assert_return(server, -EINVAL)do { if (!(((__builtin_expect(!!(server),1))) ? (1) : (log_assert_failed_return_realm
(LOG_REALM_SYSTEMD, ("server"), "../src/libsystemd-network/sd-dhcp-server.c"
, 1102, __PRETTY_FUNCTION__), 0))) return (-22); } while (0)
;
1103 assert_return(dns || n <= 0, -EINVAL)do { if (!(((__builtin_expect(!!(dns || n <= 0),1))) ? (1)
: (log_assert_failed_return_realm(LOG_REALM_SYSTEMD, ("dns || n <= 0"
), "../src/libsystemd-network/sd-dhcp-server.c", 1103, __PRETTY_FUNCTION__
), 0))) return (-22); } while (0)
;
1104
1105 if (server->n_dns == n &&
1106 memcmp(server->dns, dns, sizeof(struct in_addr) * n) == 0)
1107 return 0;
1108
1109 if (n <= 0) {
1110 server->dns = mfree(server->dns);
1111 server->n_dns = 0;
1112 } else {
1113 struct in_addr *c;
1114
1115 c = newdup(struct in_addr, dns, n)((struct in_addr*) memdup_multiply(dns, sizeof(struct in_addr
), (n)))
;
1116 if (!c)
1117 return -ENOMEM12;
1118
1119 free(server->dns);
1120 server->dns = c;
1121 server->n_dns = n;
1122 }
1123
1124 return 1;
1125}
1126
1127int sd_dhcp_server_set_ntp(sd_dhcp_server *server, const struct in_addr ntp[], unsigned n) {
1128 assert_return(server, -EINVAL)do { if (!(((__builtin_expect(!!(server),1))) ? (1) : (log_assert_failed_return_realm
(LOG_REALM_SYSTEMD, ("server"), "../src/libsystemd-network/sd-dhcp-server.c"
, 1128, __PRETTY_FUNCTION__), 0))) return (-22); } while (0)
;
1129 assert_return(ntp || n <= 0, -EINVAL)do { if (!(((__builtin_expect(!!(ntp || n <= 0),1))) ? (1)
: (log_assert_failed_return_realm(LOG_REALM_SYSTEMD, ("ntp || n <= 0"
), "../src/libsystemd-network/sd-dhcp-server.c", 1129, __PRETTY_FUNCTION__
), 0))) return (-22); } while (0)
;
1130
1131 if (server->n_ntp == n &&
1132 memcmp(server->ntp, ntp, sizeof(struct in_addr) * n) == 0)
1133 return 0;
1134
1135 if (n <= 0) {
1136 server->ntp = mfree(server->ntp);
1137 server->n_ntp = 0;
1138 } else {
1139 struct in_addr *c;
1140
1141 c = newdup(struct in_addr, ntp, n)((struct in_addr*) memdup_multiply(ntp, sizeof(struct in_addr
), (n)))
;
1142 if (!c)
1143 return -ENOMEM12;
1144
1145 free(server->ntp);
1146 server->ntp = c;
1147 server->n_ntp = n;
1148 }
1149
1150 return 1;
1151}
1152
1153int sd_dhcp_server_set_emit_router(sd_dhcp_server *server, int enabled) {
1154 assert_return(server, -EINVAL)do { if (!(((__builtin_expect(!!(server),1))) ? (1) : (log_assert_failed_return_realm
(LOG_REALM_SYSTEMD, ("server"), "../src/libsystemd-network/sd-dhcp-server.c"
, 1154, __PRETTY_FUNCTION__), 0))) return (-22); } while (0)
;
1155
1156 if (enabled == server->emit_router)
1157 return 0;
1158
1159 server->emit_router = enabled;
1160
1161 return 1;
1162}