1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <net/if.h>
4 #include <linux/if_addrlabel.h>
5
6 #include "alloc-util.h"
7 #include "netlink-util.h"
8 #include "networkd-address-label.h"
9 #include "networkd-link.h"
10 #include "networkd-manager.h"
11 #include "networkd-network.h"
12 #include "networkd-queue.h"
13 #include "parse-util.h"
14
address_label_free(AddressLabel * label)15 AddressLabel *address_label_free(AddressLabel *label) {
16 if (!label)
17 return NULL;
18
19 if (label->network) {
20 assert(label->section);
21 hashmap_remove(label->network->address_labels_by_section, label->section);
22 }
23
24 config_section_free(label->section);
25 return mfree(label);
26 }
27
28 DEFINE_SECTION_CLEANUP_FUNCTIONS(AddressLabel, address_label_free);
29
address_label_new_static(Network * network,const char * filename,unsigned section_line,AddressLabel ** ret)30 static int address_label_new_static(Network *network, const char *filename, unsigned section_line, AddressLabel **ret) {
31 _cleanup_(config_section_freep) ConfigSection *n = NULL;
32 _cleanup_(address_label_freep) AddressLabel *label = NULL;
33 int r;
34
35 assert(network);
36 assert(ret);
37 assert(filename);
38 assert(section_line > 0);
39
40 r = config_section_new(filename, section_line, &n);
41 if (r < 0)
42 return r;
43
44 label = hashmap_get(network->address_labels_by_section, n);
45 if (label) {
46 *ret = TAKE_PTR(label);
47 return 0;
48 }
49
50 label = new(AddressLabel, 1);
51 if (!label)
52 return -ENOMEM;
53
54 *label = (AddressLabel) {
55 .network = network,
56 .section = TAKE_PTR(n),
57 .label = UINT32_MAX,
58 };
59
60 r = hashmap_ensure_put(&network->address_labels_by_section, &config_section_hash_ops, label->section, label);
61 if (r < 0)
62 return r;
63
64 *ret = TAKE_PTR(label);
65 return 0;
66 }
67
address_label_configure_handler(sd_netlink * rtnl,sd_netlink_message * m,Request * req,Link * link,void * userdata)68 static int address_label_configure_handler(
69 sd_netlink *rtnl,
70 sd_netlink_message *m,
71 Request *req,
72 Link *link,
73 void *userdata) {
74
75 int r;
76
77 assert(m);
78 assert(link);
79
80 r = sd_netlink_message_get_errno(m);
81 if (r < 0 && r != -EEXIST) {
82 log_link_message_warning_errno(link, m, r, "Could not set address label");
83 link_enter_failed(link);
84 return 1;
85 }
86
87 if (link->static_address_label_messages == 0) {
88 log_link_debug(link, "Addresses label set");
89 link->static_address_labels_configured = true;
90 link_check_ready(link);
91 }
92
93 return 1;
94 }
95
address_label_configure(AddressLabel * label,Link * link,Request * req)96 static int address_label_configure(AddressLabel *label, Link *link, Request *req) {
97 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
98 int r;
99
100 assert(label);
101 assert(link);
102 assert(link->ifindex > 0);
103 assert(link->manager);
104 assert(link->manager->rtnl);
105 assert(req);
106
107 r = sd_rtnl_message_new_addrlabel(link->manager->rtnl, &m, RTM_NEWADDRLABEL,
108 link->ifindex, AF_INET6);
109 if (r < 0)
110 return r;
111
112 r = sd_rtnl_message_addrlabel_set_prefixlen(m, label->prefixlen);
113 if (r < 0)
114 return r;
115
116 r = sd_netlink_message_append_u32(m, IFAL_LABEL, label->label);
117 if (r < 0)
118 return r;
119
120 r = sd_netlink_message_append_in6_addr(m, IFA_ADDRESS, &label->prefix);
121 if (r < 0)
122 return r;
123
124 return request_call_netlink_async(link->manager->rtnl, m, req);
125 }
126
address_label_process_request(Request * req,Link * link,void * userdata)127 static int address_label_process_request(Request *req, Link *link, void *userdata) {
128 AddressLabel *label = ASSERT_PTR(userdata);
129 int r;
130
131 assert(req);
132 assert(link);
133
134 if (!link_is_ready_to_configure(link, false))
135 return 0;
136
137 r = address_label_configure(label, link, req);
138 if (r < 0)
139 return log_link_warning_errno(link, r, "Failed to configure address label: %m");
140
141 return 1;
142 }
143
link_request_static_address_labels(Link * link)144 int link_request_static_address_labels(Link *link) {
145 AddressLabel *label;
146 int r;
147
148 assert(link);
149 assert(link->network);
150
151 link->static_address_labels_configured = false;
152
153 HASHMAP_FOREACH(label, link->network->address_labels_by_section) {
154 r = link_queue_request_full(link, REQUEST_TYPE_ADDRESS_LABEL,
155 label, NULL, trivial_hash_func, trivial_compare_func,
156 address_label_process_request,
157 &link->static_address_label_messages,
158 address_label_configure_handler, NULL);
159 if (r < 0)
160 return log_link_warning_errno(link, r, "Failed to request address label: %m");
161 }
162
163 if (link->static_address_label_messages == 0) {
164 link->static_address_labels_configured = true;
165 link_check_ready(link);
166 } else {
167 log_link_debug(link, "Setting address labels.");
168 link_set_state(link, LINK_STATE_CONFIGURING);
169 }
170
171 return 0;
172 }
173
address_label_section_verify(AddressLabel * label)174 static int address_label_section_verify(AddressLabel *label) {
175 assert(label);
176 assert(label->section);
177
178 if (section_is_invalid(label->section))
179 return -EINVAL;
180
181 if (!label->prefix_set)
182 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
183 "%s: [IPv6AddressLabel] section without Prefix= setting specified. "
184 "Ignoring [IPv6AddressLabel] section from line %u.",
185 label->section->filename, label->section->line);
186
187 if (label->label == UINT32_MAX)
188 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
189 "%s: [IPv6AddressLabel] section without Label= setting specified. "
190 "Ignoring [IPv6AddressLabel] section from line %u.",
191 label->section->filename, label->section->line);
192
193 return 0;
194 }
195
network_drop_invalid_address_labels(Network * network)196 void network_drop_invalid_address_labels(Network *network) {
197 AddressLabel *label;
198
199 assert(network);
200
201 HASHMAP_FOREACH(label, network->address_labels_by_section)
202 if (address_label_section_verify(label) < 0)
203 address_label_free(label);
204 }
205
config_parse_address_label_prefix(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)206 int config_parse_address_label_prefix(
207 const char *unit,
208 const char *filename,
209 unsigned line,
210 const char *section,
211 unsigned section_line,
212 const char *lvalue,
213 int ltype,
214 const char *rvalue,
215 void *data,
216 void *userdata) {
217
218 _cleanup_(address_label_free_or_set_invalidp) AddressLabel *n = NULL;
219 Network *network = userdata;
220 unsigned char prefixlen;
221 union in_addr_union a;
222 int r;
223
224 assert(filename);
225 assert(section);
226 assert(lvalue);
227 assert(rvalue);
228 assert(data);
229
230 r = address_label_new_static(network, filename, section_line, &n);
231 if (r < 0)
232 return log_oom();
233
234 r = in_addr_prefix_from_string(rvalue, AF_INET6, &a, &prefixlen);
235 if (r < 0) {
236 log_syntax(unit, LOG_WARNING, filename, line, r,
237 "Invalid prefix for address label, ignoring assignment: %s", rvalue);
238 return 0;
239 }
240 if (in6_addr_is_ipv4_mapped_address(&a.in6) && prefixlen > 96) {
241 /* See ip6addrlbl_alloc() in net/ipv6/addrlabel.c of kernel. */
242 log_syntax(unit, LOG_WARNING, filename, line, 0,
243 "The prefix length of IPv4 mapped address for address label must be equal to or smaller than 96, "
244 "ignoring assignment: %s", rvalue);
245 return 0;
246 }
247
248 n->prefix = a.in6;
249 n->prefixlen = prefixlen;
250 n->prefix_set = true;
251
252 TAKE_PTR(n);
253 return 0;
254 }
255
config_parse_address_label(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)256 int config_parse_address_label(
257 const char *unit,
258 const char *filename,
259 unsigned line,
260 const char *section,
261 unsigned section_line,
262 const char *lvalue,
263 int ltype,
264 const char *rvalue,
265 void *data,
266 void *userdata) {
267
268 _cleanup_(address_label_free_or_set_invalidp) AddressLabel *n = NULL;
269 Network *network = userdata;
270 uint32_t k;
271 int r;
272
273 assert(filename);
274 assert(section);
275 assert(lvalue);
276 assert(rvalue);
277 assert(data);
278
279 r = address_label_new_static(network, filename, section_line, &n);
280 if (r < 0)
281 return log_oom();
282
283 r = safe_atou32(rvalue, &k);
284 if (r < 0) {
285 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse address label, ignoring: %s", rvalue);
286 return 0;
287 }
288
289 if (k == UINT_MAX) {
290 log_syntax(unit, LOG_WARNING, filename, line, 0, "Address label is invalid, ignoring: %s", rvalue);
291 return 0;
292 }
293
294 n->label = k;
295 TAKE_PTR(n);
296
297 return 0;
298 }
299