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