]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
95b74ef6 SS |
2 | |
3 | #include <net/if.h> | |
4 | #include <linux/if_addrlabel.h> | |
5 | ||
6 | #include "alloc-util.h" | |
95b74ef6 | 7 | #include "netlink-util.h" |
fb486c90 YW |
8 | #include "networkd-address-label.h" |
9 | #include "networkd-link.h" | |
95b74ef6 | 10 | #include "networkd-manager.h" |
fb486c90 | 11 | #include "networkd-network.h" |
95b74ef6 | 12 | #include "parse-util.h" |
95b74ef6 | 13 | |
cae418a3 | 14 | AddressLabel *address_label_free(AddressLabel *label) { |
95b74ef6 | 15 | if (!label) |
cae418a3 | 16 | return NULL; |
95b74ef6 SS |
17 | |
18 | if (label->network) { | |
d6a2a0f9 YW |
19 | assert(label->section); |
20 | hashmap_remove(label->network->address_labels_by_section, label->section); | |
95b74ef6 SS |
21 | } |
22 | ||
d6a2a0f9 | 23 | network_config_section_free(label->section); |
cae418a3 | 24 | return mfree(label); |
95b74ef6 SS |
25 | } |
26 | ||
fb486c90 YW |
27 | DEFINE_NETWORK_SECTION_FUNCTIONS(AddressLabel, address_label_free); |
28 | ||
95b74ef6 | 29 | static int address_label_new_static(Network *network, const char *filename, unsigned section_line, AddressLabel **ret) { |
8e766630 LP |
30 | _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL; |
31 | _cleanup_(address_label_freep) AddressLabel *label = NULL; | |
95b74ef6 SS |
32 | int r; |
33 | ||
34 | assert(network); | |
35 | assert(ret); | |
d6a2a0f9 YW |
36 | assert(filename); |
37 | assert(section_line > 0); | |
95b74ef6 | 38 | |
d6a2a0f9 YW |
39 | r = network_config_section_new(filename, section_line, &n); |
40 | if (r < 0) | |
41 | return r; | |
95b74ef6 | 42 | |
d6a2a0f9 YW |
43 | label = hashmap_get(network->address_labels_by_section, n); |
44 | if (label) { | |
45 | *ret = TAKE_PTR(label); | |
46 | return 0; | |
95b74ef6 SS |
47 | } |
48 | ||
0f7f2769 YW |
49 | label = new(AddressLabel, 1); |
50 | if (!label) | |
51 | return -ENOMEM; | |
95b74ef6 | 52 | |
0f7f2769 YW |
53 | *label = (AddressLabel) { |
54 | .network = network, | |
d6a2a0f9 | 55 | .section = TAKE_PTR(n), |
0f7f2769 | 56 | }; |
95b74ef6 | 57 | |
fb8ac4cf | 58 | r = hashmap_ensure_put(&network->address_labels_by_section, &network_config_hash_ops, label->section, label); |
d6a2a0f9 YW |
59 | if (r < 0) |
60 | return r; | |
0f7f2769 | 61 | |
1cc6c93a | 62 | *ret = TAKE_PTR(label); |
95b74ef6 SS |
63 | return 0; |
64 | } | |
65 | ||
302a796f | 66 | static int address_label_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { |
cccf9517 YW |
67 | int r; |
68 | ||
69 | assert(rtnl); | |
70 | assert(m); | |
71 | assert(link); | |
72 | assert(link->ifname); | |
73 | assert(link->address_label_messages > 0); | |
74 | ||
75 | link->address_label_messages--; | |
76 | ||
77 | if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) | |
78 | return 1; | |
79 | ||
80 | r = sd_netlink_message_get_errno(m); | |
4ff296b0 | 81 | if (r < 0 && r != -EEXIST) { |
5ecb131d | 82 | log_link_message_warning_errno(link, m, r, "Could not set address label"); |
4ff296b0 YW |
83 | link_enter_failed(link); |
84 | return 1; | |
3a1dfdb4 | 85 | } |
cccf9517 YW |
86 | |
87 | if (link->address_label_messages == 0) | |
88 | log_link_debug(link, "Addresses label set"); | |
89 | ||
90 | return 1; | |
91 | } | |
92 | ||
fe2bc17c | 93 | static int address_label_configure(AddressLabel *label, Link *link) { |
95b74ef6 SS |
94 | _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; |
95 | int r; | |
96 | ||
97 | assert(label); | |
98 | assert(link); | |
99 | assert(link->ifindex > 0); | |
100 | assert(link->manager); | |
101 | assert(link->manager->rtnl); | |
102 | ||
103 | r = sd_rtnl_message_new_addrlabel(link->manager->rtnl, &req, RTM_NEWADDRLABEL, | |
f7bf1abe | 104 | link->ifindex, AF_INET6); |
95b74ef6 | 105 | if (r < 0) |
98b02994 | 106 | return log_link_error_errno(link, r, "Could not allocate RTM_NEWADDR message: %m"); |
95b74ef6 SS |
107 | |
108 | r = sd_rtnl_message_addrlabel_set_prefixlen(req, label->prefixlen); | |
109 | if (r < 0) | |
98b02994 | 110 | return log_link_error_errno(link, r, "Could not set prefixlen: %m"); |
95b74ef6 SS |
111 | |
112 | r = sd_netlink_message_append_u32(req, IFAL_LABEL, label->label); | |
113 | if (r < 0) | |
98b02994 | 114 | return log_link_error_errno(link, r, "Could not append IFAL_LABEL attribute: %m"); |
95b74ef6 | 115 | |
4c0c8d1e | 116 | r = sd_netlink_message_append_in6_addr(req, IFA_ADDRESS, &label->in_addr); |
95b74ef6 | 117 | if (r < 0) |
98b02994 | 118 | return log_link_error_errno(link, r, "Could not append IFA_ADDRESS attribute: %m"); |
95b74ef6 | 119 | |
302a796f | 120 | r = netlink_call_async(link->manager->rtnl, NULL, req, |
fe2bc17c | 121 | address_label_handler, |
302a796f | 122 | link_netlink_destroy_callback, link); |
95b74ef6 | 123 | if (r < 0) |
98b02994 | 124 | return log_link_error_errno(link, r, "Could not send rtnetlink message: %m"); |
95b74ef6 SS |
125 | |
126 | link_ref(link); | |
127 | ||
128 | return 0; | |
129 | } | |
130 | ||
fe2bc17c YW |
131 | int link_set_address_labels(Link *link) { |
132 | AddressLabel *label; | |
133 | int r; | |
134 | ||
135 | assert(link); | |
136 | assert(link->network); | |
137 | ||
138 | HASHMAP_FOREACH(label, link->network->address_labels_by_section) { | |
139 | r = address_label_configure(label, link); | |
140 | if (r < 0) | |
141 | return log_link_warning_errno(link, r, "Could not set address label: %m"); | |
142 | ||
143 | link->address_label_messages++; | |
144 | } | |
145 | ||
146 | return 0; | |
147 | } | |
148 | ||
13ffa39f | 149 | void network_drop_invalid_address_labels(Network *network) { |
ab316813 YW |
150 | AddressLabel *label; |
151 | ||
152 | assert(network); | |
153 | ||
154 | HASHMAP_FOREACH(label, network->address_labels_by_section) | |
155 | if (section_is_invalid(label->section)) | |
156 | address_label_free(label); | |
157 | } | |
158 | ||
95b74ef6 SS |
159 | int config_parse_address_label_prefix(const char *unit, |
160 | const char *filename, | |
161 | unsigned line, | |
162 | const char *section, | |
163 | unsigned section_line, | |
164 | const char *lvalue, | |
165 | int ltype, | |
166 | const char *rvalue, | |
167 | void *data, | |
168 | void *userdata) { | |
169 | ||
fcbf4cb7 | 170 | _cleanup_(address_label_free_or_set_invalidp) AddressLabel *n = NULL; |
95b74ef6 | 171 | Network *network = userdata; |
2551b422 YW |
172 | unsigned char prefixlen; |
173 | union in_addr_union a; | |
f7bf1abe | 174 | int r; |
95b74ef6 SS |
175 | |
176 | assert(filename); | |
177 | assert(section); | |
178 | assert(lvalue); | |
179 | assert(rvalue); | |
180 | assert(data); | |
181 | ||
182 | r = address_label_new_static(network, filename, section_line, &n); | |
183 | if (r < 0) | |
d96edb2c | 184 | return log_oom(); |
95b74ef6 | 185 | |
2551b422 | 186 | r = in_addr_prefix_from_string(rvalue, AF_INET6, &a, &prefixlen); |
95b74ef6 | 187 | if (r < 0) { |
2551b422 YW |
188 | log_syntax(unit, LOG_WARNING, filename, line, r, |
189 | "Invalid prefix for address label, ignoring assignment: %s", rvalue); | |
95b74ef6 SS |
190 | return 0; |
191 | } | |
2551b422 YW |
192 | if (in6_addr_is_ipv4_mapped_address(&a.in6) && prefixlen > 96) { |
193 | /* See ip6addrlbl_alloc() in net/ipv6/addrlabel.c of kernel. */ | |
194 | log_syntax(unit, LOG_WARNING, filename, line, 0, | |
195 | "The prefix length of IPv4 mapped address for address label must be equal to or smaller than 96, " | |
196 | "ignoring assignment: %s", rvalue); | |
197 | return 0; | |
198 | } | |
199 | ||
4c0c8d1e | 200 | n->in_addr = a.in6; |
2551b422 | 201 | n->prefixlen = prefixlen; |
95b74ef6 | 202 | |
dea161d9 | 203 | TAKE_PTR(n); |
95b74ef6 SS |
204 | return 0; |
205 | } | |
206 | ||
207 | int config_parse_address_label( | |
208 | const char *unit, | |
209 | const char *filename, | |
210 | unsigned line, | |
211 | const char *section, | |
212 | unsigned section_line, | |
213 | const char *lvalue, | |
214 | int ltype, | |
215 | const char *rvalue, | |
216 | void *data, | |
217 | void *userdata) { | |
218 | ||
fcbf4cb7 | 219 | _cleanup_(address_label_free_or_set_invalidp) AddressLabel *n = NULL; |
95b74ef6 SS |
220 | Network *network = userdata; |
221 | uint32_t k; | |
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) | |
d96edb2c | 232 | return log_oom(); |
95b74ef6 SS |
233 | |
234 | r = safe_atou32(rvalue, &k); | |
235 | if (r < 0) { | |
d96edb2c | 236 | log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse address label, ignoring: %s", rvalue); |
95b74ef6 SS |
237 | return 0; |
238 | } | |
239 | ||
99b5f4f7 | 240 | if (k == UINT32_C(0xffffffff)) { |
d96edb2c | 241 | log_syntax(unit, LOG_WARNING, filename, line, 0, "Address label is invalid, ignoring: %s", rvalue); |
95b74ef6 SS |
242 | return 0; |
243 | } | |
244 | ||
245 | n->label = k; | |
dea161d9 | 246 | TAKE_PTR(n); |
95b74ef6 SS |
247 | |
248 | return 0; | |
249 | } |