]>
Commit | Line | Data |
---|---|---|
f579559b TG |
1 | /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ |
2 | ||
3 | /*** | |
4 | This file is part of systemd. | |
5 | ||
6 | Copyright 2013 Tom Gundersen <teg@jklm.no> | |
7 | ||
8 | systemd is free software; you can redistribute it and/or modify it | |
9 | under the terms of the GNU Lesser General Public License as published by | |
10 | the Free Software Foundation; either version 2.1 of the License, or | |
11 | (at your option) any later version. | |
12 | ||
13 | systemd is distributed in the hope that it will be useful, but | |
14 | WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
16 | Lesser General Public License for more details. | |
17 | ||
18 | You should have received a copy of the GNU Lesser General Public License | |
19 | along with systemd; If not, see <http://www.gnu.org/licenses/>. | |
20 | ***/ | |
21 | ||
22 | #include <net/if.h> | |
23 | ||
24 | #include "networkd.h" | |
25 | ||
26 | #include "utf8.h" | |
27 | #include "util.h" | |
28 | #include "conf-parser.h" | |
29 | #include "net-util.h" | |
30 | ||
f048a16b | 31 | int address_new_static(Network *network, unsigned section, Address **ret) { |
f579559b TG |
32 | _cleanup_address_free_ Address *address = NULL; |
33 | ||
6ae115c1 TG |
34 | if (section) { |
35 | uint64_t key = section; | |
36 | address = hashmap_get(network->addresses_by_section, &key); | |
37 | if (address) { | |
38 | *ret = address; | |
39 | address = NULL; | |
40 | ||
41 | return 0; | |
42 | } | |
43 | } | |
44 | ||
f579559b TG |
45 | address = new0(Address, 1); |
46 | if (!address) | |
47 | return -ENOMEM; | |
48 | ||
801bd9e8 TG |
49 | address->family = AF_UNSPEC; |
50 | ||
f579559b TG |
51 | address->network = network; |
52 | ||
f048a16b | 53 | LIST_PREPEND(static_addresses, network->static_addresses, address); |
f579559b | 54 | |
6ae115c1 TG |
55 | if (section) { |
56 | address->section = section; | |
57 | hashmap_put(network->addresses_by_section, &address->section, address); | |
58 | } | |
59 | ||
f579559b TG |
60 | *ret = address; |
61 | address = NULL; | |
62 | ||
63 | return 0; | |
64 | } | |
65 | ||
f048a16b TG |
66 | int address_new_dynamic(Address **ret) { |
67 | _cleanup_address_free_ Address *address = NULL; | |
68 | ||
69 | address = new0(Address, 1); | |
70 | if (!address) | |
71 | return -ENOMEM; | |
72 | ||
801bd9e8 TG |
73 | address->family = AF_UNSPEC; |
74 | ||
f048a16b TG |
75 | *ret = address; |
76 | address = NULL; | |
77 | ||
78 | return 0; | |
79 | } | |
80 | ||
f579559b TG |
81 | void address_free(Address *address) { |
82 | if (!address) | |
83 | return; | |
84 | ||
f048a16b TG |
85 | if (address->network) { |
86 | LIST_REMOVE(static_addresses, address->network->static_addresses, address); | |
f579559b | 87 | |
f048a16b TG |
88 | if (address->section) |
89 | hashmap_remove(address->network->addresses_by_section, | |
90 | &address->section); | |
91 | } | |
6ae115c1 | 92 | |
f579559b TG |
93 | free(address); |
94 | } | |
95 | ||
407fe036 TG |
96 | int address_drop(Address *address, Link *link, |
97 | sd_rtnl_message_handler_t callback) { | |
cf6a8911 | 98 | _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL; |
407fe036 TG |
99 | int r; |
100 | ||
101 | assert(address); | |
102 | assert(address->family == AF_INET || address->family == AF_INET6); | |
103 | assert(link); | |
104 | assert(link->ifindex > 0); | |
105 | assert(link->manager); | |
106 | assert(link->manager->rtnl); | |
107 | ||
4fb7242c TG |
108 | r = sd_rtnl_message_new_addr(link->manager->rtnl, RTM_DELADDR, |
109 | link->ifindex, address->family, &req); | |
407fe036 TG |
110 | if (r < 0) { |
111 | log_error("Could not allocate RTM_DELADDR message: %s", | |
112 | strerror(-r)); | |
113 | return r; | |
114 | } | |
115 | ||
5a723174 TG |
116 | r = sd_rtnl_message_addr_set_prefixlen(req, address->prefixlen); |
117 | if (r < 0) { | |
118 | log_error("Could not set prefixlen: %s", strerror(-r)); | |
119 | return r; | |
120 | } | |
121 | ||
407fe036 TG |
122 | if (address->family == AF_INET) |
123 | r = sd_rtnl_message_append_in_addr(req, IFA_LOCAL, &address->in_addr.in); | |
124 | else if (address->family == AF_INET6) | |
125 | r = sd_rtnl_message_append_in6_addr(req, IFA_LOCAL, &address->in_addr.in6); | |
126 | if (r < 0) { | |
127 | log_error("Could not append IFA_LOCAL attribute: %s", | |
128 | strerror(-r)); | |
129 | return r; | |
130 | } | |
131 | ||
132 | r = sd_rtnl_call_async(link->manager->rtnl, req, callback, link, 0, NULL); | |
133 | if (r < 0) { | |
134 | log_error("Could not send rtnetlink message: %s", strerror(-r)); | |
135 | return r; | |
136 | } | |
137 | ||
138 | return 0; | |
139 | } | |
140 | ||
f882c247 TG |
141 | int address_configure(Address *address, Link *link, |
142 | sd_rtnl_message_handler_t callback) { | |
cf6a8911 | 143 | _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL; |
f579559b TG |
144 | int r; |
145 | ||
c166a070 TG |
146 | assert(address); |
147 | assert(address->family == AF_INET || address->family == AF_INET6); | |
148 | assert(link); | |
149 | assert(link->ifindex > 0); | |
f882c247 | 150 | assert(link->manager); |
c166a070 | 151 | assert(link->manager->rtnl); |
f882c247 | 152 | |
4fb7242c TG |
153 | r = sd_rtnl_message_new_addr(link->manager->rtnl, RTM_NEWADDR, |
154 | link->ifindex, address->family, &req); | |
f579559b TG |
155 | if (r < 0) { |
156 | log_error("Could not allocate RTM_NEWADDR message: %s", | |
157 | strerror(-r)); | |
158 | return r; | |
159 | } | |
160 | ||
5a723174 TG |
161 | r = sd_rtnl_message_addr_set_prefixlen(req, address->prefixlen); |
162 | if (r < 0) { | |
163 | log_error("Could not set prefixlen: %s", strerror(-r)); | |
164 | return r; | |
165 | } | |
166 | ||
167 | r = sd_rtnl_message_addr_set_flags(req, IFA_F_PERMANENT); | |
168 | if (r < 0) { | |
169 | log_error("Could not set flags: %s", strerror(-r)); | |
170 | return r; | |
171 | } | |
172 | ||
173 | r = sd_rtnl_message_addr_set_scope(req, RT_SCOPE_UNIVERSE); | |
174 | if (r < 0) { | |
175 | log_error("Could not set scope: %s", strerror(-r)); | |
176 | return r; | |
177 | } | |
178 | ||
0a0dc69b TG |
179 | if (address->family == AF_INET) |
180 | r = sd_rtnl_message_append_in_addr(req, IFA_LOCAL, &address->in_addr.in); | |
181 | else if (address->family == AF_INET6) | |
182 | r = sd_rtnl_message_append_in6_addr(req, IFA_LOCAL, &address->in_addr.in6); | |
f579559b TG |
183 | if (r < 0) { |
184 | log_error("Could not append IFA_LOCAL attribute: %s", | |
185 | strerror(-r)); | |
186 | return r; | |
187 | } | |
188 | ||
189 | if (address->family == AF_INET) { | |
eb0ea358 | 190 | r = sd_rtnl_message_append_in_addr(req, IFA_BROADCAST, &address->broadcast); |
f579559b TG |
191 | if (r < 0) { |
192 | log_error("Could not append IFA_BROADCAST attribute: %s", | |
193 | strerror(-r)); | |
194 | return r; | |
195 | } | |
196 | } | |
197 | ||
198 | if (address->label) { | |
0a0dc69b | 199 | r = sd_rtnl_message_append_string(req, IFA_LABEL, address->label); |
f579559b TG |
200 | if (r < 0) { |
201 | log_error("Could not append IFA_LABEL attribute: %s", | |
202 | strerror(-r)); | |
203 | return r; | |
204 | } | |
205 | } | |
206 | ||
f882c247 | 207 | r = sd_rtnl_call_async(link->manager->rtnl, req, callback, link, 0, NULL); |
f579559b | 208 | if (r < 0) { |
f882c247 TG |
209 | log_error("Could not send rtnetlink message: %s", strerror(-r)); |
210 | return r; | |
f579559b TG |
211 | } |
212 | ||
f579559b TG |
213 | return 0; |
214 | } | |
215 | ||
3bef724f TG |
216 | int config_parse_dns(const char *unit, |
217 | const char *filename, | |
218 | unsigned line, | |
219 | const char *section, | |
220 | unsigned section_line, | |
221 | const char *lvalue, | |
222 | int ltype, | |
223 | const char *rvalue, | |
224 | void *data, | |
225 | void *userdata) { | |
226 | Address **dns = data; | |
227 | _cleanup_address_free_ Address *n = NULL; | |
228 | int r; | |
229 | ||
230 | assert(filename); | |
231 | assert(section); | |
232 | assert(lvalue); | |
233 | assert(rvalue); | |
234 | assert(data); | |
235 | ||
236 | r = address_new_dynamic(&n); | |
237 | if (r < 0) | |
238 | return r; | |
239 | ||
240 | r = net_parse_inaddr(rvalue, &n->family, &n->in_addr); | |
241 | if (r < 0) { | |
242 | log_syntax(unit, LOG_ERR, filename, line, EINVAL, | |
243 | "DNS address is invalid, ignoring assignment: %s", rvalue); | |
244 | return 0; | |
245 | } | |
246 | ||
247 | *dns = n; | |
248 | n = NULL; | |
249 | ||
250 | return 0; | |
251 | } | |
252 | ||
eb0ea358 TG |
253 | int config_parse_broadcast(const char *unit, |
254 | const char *filename, | |
255 | unsigned line, | |
256 | const char *section, | |
257 | unsigned section_line, | |
258 | const char *lvalue, | |
259 | int ltype, | |
260 | const char *rvalue, | |
261 | void *data, | |
262 | void *userdata) { | |
263 | Network *network = userdata; | |
264 | _cleanup_address_free_ Address *n = NULL; | |
265 | _cleanup_free_ char *address = NULL; | |
266 | int r; | |
267 | ||
268 | assert(filename); | |
269 | assert(section); | |
270 | assert(lvalue); | |
271 | assert(rvalue); | |
272 | assert(data); | |
273 | ||
274 | r = address_new_static(network, section_line, &n); | |
275 | if (r < 0) | |
276 | return r; | |
277 | ||
278 | r = net_parse_inaddr(address, &n->family, &n->broadcast); | |
279 | if (r < 0) { | |
280 | log_syntax(unit, LOG_ERR, filename, line, EINVAL, | |
281 | "Broadcast is invalid, ignoring assignment: %s", address); | |
282 | return 0; | |
283 | } | |
284 | ||
285 | n = NULL; | |
286 | ||
287 | return 0; | |
288 | } | |
289 | ||
f579559b TG |
290 | int config_parse_address(const char *unit, |
291 | const char *filename, | |
292 | unsigned line, | |
293 | const char *section, | |
71a61510 | 294 | unsigned section_line, |
f579559b TG |
295 | const char *lvalue, |
296 | int ltype, | |
297 | const char *rvalue, | |
298 | void *data, | |
299 | void *userdata) { | |
6ae115c1 | 300 | Network *network = userdata; |
f579559b TG |
301 | _cleanup_address_free_ Address *n = NULL; |
302 | _cleanup_free_ char *address = NULL; | |
303 | const char *e; | |
304 | int r; | |
305 | ||
306 | assert(filename); | |
6ae115c1 | 307 | assert(section); |
f579559b TG |
308 | assert(lvalue); |
309 | assert(rvalue); | |
310 | assert(data); | |
311 | ||
92fe133a TG |
312 | if (streq(section, "Network")) { |
313 | /* we are not in an Address section, so treat | |
314 | * this as the special '0' section */ | |
315 | section_line = 0; | |
316 | } | |
317 | ||
f048a16b | 318 | r = address_new_static(network, section_line, &n); |
f579559b TG |
319 | if (r < 0) |
320 | return r; | |
321 | ||
322 | /* Address=address/prefixlen */ | |
323 | ||
324 | /* prefixlen */ | |
325 | e = strchr(rvalue, '/'); | |
326 | if (e) { | |
327 | unsigned i; | |
328 | r = safe_atou(e + 1, &i); | |
329 | if (r < 0) { | |
330 | log_syntax(unit, LOG_ERR, filename, line, EINVAL, | |
331 | "Interface prefix length is invalid, " | |
332 | "ignoring assignment: %s", e + 1); | |
333 | return 0; | |
334 | } | |
335 | ||
336 | n->prefixlen = (unsigned char) i; | |
8cd11a0f | 337 | |
f579559b TG |
338 | address = strndup(rvalue, e - rvalue); |
339 | if (!address) | |
340 | return log_oom(); | |
341 | } else { | |
342 | address = strdup(rvalue); | |
343 | if (!address) | |
344 | return log_oom(); | |
345 | } | |
346 | ||
347 | r = net_parse_inaddr(address, &n->family, &n->in_addr); | |
348 | if (r < 0) { | |
349 | log_syntax(unit, LOG_ERR, filename, line, EINVAL, | |
350 | "Address is invalid, ignoring assignment: %s", address); | |
351 | return 0; | |
352 | } | |
353 | ||
eb0ea358 TG |
354 | if (n->family == AF_INET && !n->broadcast.s_addr) |
355 | n->broadcast.s_addr = n->in_addr.in.s_addr | | |
356 | htonl(0xfffffffflu >> n->prefixlen); | |
357 | ||
f579559b TG |
358 | n = NULL; |
359 | ||
360 | return 0; | |
361 | } | |
6ae115c1 TG |
362 | |
363 | int config_parse_label(const char *unit, | |
364 | const char *filename, | |
365 | unsigned line, | |
366 | const char *section, | |
367 | unsigned section_line, | |
368 | const char *lvalue, | |
369 | int ltype, | |
370 | const char *rvalue, | |
371 | void *data, | |
372 | void *userdata) { | |
373 | Network *network = userdata; | |
374 | _cleanup_address_free_ Address *n = NULL; | |
6ae115c1 TG |
375 | char *label; |
376 | int r; | |
377 | ||
378 | assert(filename); | |
379 | assert(section); | |
380 | assert(lvalue); | |
381 | assert(rvalue); | |
382 | assert(data); | |
383 | ||
f048a16b | 384 | r = address_new_static(network, section_line, &n); |
6ae115c1 TG |
385 | if (r < 0) |
386 | return r; | |
387 | ||
388 | label = strdup(rvalue); | |
389 | if (!label) | |
390 | return log_oom(); | |
391 | ||
392 | if (!ascii_is_valid(label) || strlen(label) >= IFNAMSIZ) { | |
393 | log_syntax(unit, LOG_ERR, filename, line, EINVAL, | |
394 | "Interface label is not ASCII clean or is too" | |
395 | " long, ignoring assignment: %s", rvalue); | |
396 | free(label); | |
397 | return 0; | |
398 | } | |
399 | ||
400 | free(n->label); | |
401 | if (*label) | |
402 | n->label = label; | |
403 | else { | |
404 | free(label); | |
405 | n->label = NULL; | |
406 | } | |
407 | ||
408 | n = NULL; | |
409 | ||
410 | return 0; | |
411 | } |