]>
Commit | Line | Data |
---|---|---|
e4a71bf3 WKI |
1 | /* SPDX-License-Identifier: LGPL-2.1+ */ |
2 | ||
3 | #include "sd-netlink.h" | |
4 | ||
5 | #include "alloc-util.h" | |
6 | #include "conf-parser.h" | |
7 | #include "ether-addr-util.h" | |
8 | #include "hashmap.h" | |
9 | #include "in-addr-util.h" | |
10 | #include "netlink-util.h" | |
11 | #include "networkd-link.h" | |
12 | #include "networkd-manager.h" | |
13 | #include "networkd-neighbor.h" | |
14 | ||
15 | void neighbor_free(Neighbor *neighbor) { | |
16 | if (!neighbor) | |
17 | return; | |
18 | ||
19 | if (neighbor->network) { | |
20 | LIST_REMOVE(neighbors, neighbor->network->neighbors, neighbor); | |
21 | assert(neighbor->network->n_neighbors > 0); | |
22 | neighbor->network->n_neighbors--; | |
23 | ||
24 | if (neighbor->section) { | |
25 | hashmap_remove(neighbor->network->neighbors_by_section, neighbor->section); | |
26 | network_config_section_free(neighbor->section); | |
27 | } | |
28 | } | |
29 | ||
30 | free(neighbor); | |
31 | } | |
32 | ||
33 | static int neighbor_new_static(Network *network, const char *filename, unsigned section_line, Neighbor **ret) { | |
34 | _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL; | |
35 | _cleanup_(neighbor_freep) Neighbor *neighbor = NULL; | |
36 | int r; | |
37 | ||
38 | assert(network); | |
39 | assert(ret); | |
40 | assert(!!filename == (section_line > 0)); | |
41 | ||
42 | if (filename) { | |
43 | r = network_config_section_new(filename, section_line, &n); | |
44 | if (r < 0) | |
45 | return r; | |
46 | ||
47 | neighbor = hashmap_get(network->neighbors_by_section, n); | |
48 | if (neighbor) { | |
49 | *ret = TAKE_PTR(neighbor); | |
50 | ||
51 | return 0; | |
52 | } | |
53 | } | |
54 | ||
55 | neighbor = new(Neighbor, 1); | |
56 | if (!neighbor) | |
57 | return -ENOMEM; | |
58 | ||
59 | *neighbor = (Neighbor) { | |
60 | .network = network, | |
61 | .family = AF_UNSPEC, | |
b956364d | 62 | .lladdr_type = _NEIGHBOR_LLADDR_INVALID, |
e4a71bf3 WKI |
63 | }; |
64 | ||
65 | LIST_APPEND(neighbors, network->neighbors, neighbor); | |
66 | network->n_neighbors++; | |
67 | ||
68 | if (filename) { | |
69 | neighbor->section = TAKE_PTR(n); | |
70 | ||
71 | r = hashmap_ensure_allocated(&network->neighbors_by_section, &network_config_hash_ops); | |
72 | if (r < 0) | |
73 | return r; | |
74 | ||
75 | r = hashmap_put(network->neighbors_by_section, neighbor->section, neighbor); | |
76 | if (r < 0) | |
77 | return r; | |
78 | } | |
79 | ||
80 | *ret = TAKE_PTR(neighbor); | |
81 | ||
82 | return 0; | |
83 | } | |
84 | ||
85 | static int neighbor_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { | |
86 | int r; | |
87 | ||
88 | assert(link); | |
89 | assert(link->neighbor_messages > 0); | |
90 | ||
91 | link->neighbor_messages--; | |
92 | ||
93 | if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) | |
94 | return 1; | |
95 | ||
96 | r = sd_netlink_message_get_errno(m); | |
97 | if (r < 0 && r != -EEXIST) | |
4ff296b0 YW |
98 | /* Neighbor may not exist yet. So, do not enter failed state here. */ |
99 | log_link_warning_errno(link, r, "Could not set neighbor, ignoring: %m"); | |
e4a71bf3 WKI |
100 | |
101 | if (link->neighbor_messages == 0) { | |
102 | log_link_debug(link, "Neighbors set"); | |
103 | link->neighbors_configured = true; | |
104 | link_check_ready(link); | |
105 | } | |
106 | ||
107 | return 1; | |
108 | } | |
109 | ||
110 | int neighbor_configure(Neighbor *neighbor, Link *link, link_netlink_message_handler_t callback) { | |
111 | _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; | |
112 | int r; | |
113 | ||
114 | assert(neighbor); | |
115 | assert(link); | |
116 | assert(link->ifindex > 0); | |
117 | assert(link->manager); | |
118 | assert(link->manager->rtnl); | |
119 | ||
e4a71bf3 WKI |
120 | r = sd_rtnl_message_new_neigh(link->manager->rtnl, &req, RTM_NEWNEIGH, |
121 | link->ifindex, neighbor->family); | |
122 | if (r < 0) | |
123 | return log_error_errno(r, "Could not allocate RTM_NEWNEIGH message: %m"); | |
124 | ||
125 | r = sd_rtnl_message_neigh_set_state(req, NUD_PERMANENT); | |
126 | if (r < 0) | |
127 | return log_error_errno(r, "Could not set state: %m"); | |
128 | ||
129 | r = sd_netlink_message_set_flags(req, NLM_F_REQUEST | NLM_F_CREATE | NLM_F_REPLACE); | |
130 | if (r < 0) | |
131 | return log_error_errno(r, "Could not set flags: %m"); | |
132 | ||
b956364d YW |
133 | if (neighbor->lladdr_type == NEIGHBOR_LLADDR_MAC) |
134 | r = sd_netlink_message_append_data(req, NDA_LLADDR, &neighbor->lladdr.mac, sizeof(neighbor->lladdr.mac)); | |
135 | else | |
136 | r = sd_netlink_message_append_data(req, NDA_LLADDR, &neighbor->lladdr.ip.in, sizeof(neighbor->lladdr.ip.in)); | |
e4a71bf3 WKI |
137 | if (r < 0) |
138 | return log_error_errno(r, "Could not append NDA_LLADDR attribute: %m"); | |
139 | ||
43409486 YW |
140 | r = netlink_message_append_in_addr_union(req, NDA_DST, neighbor->family, &neighbor->in_addr); |
141 | if (r < 0) | |
142 | return log_error_errno(r, "Could not append NDA_DST attribute: %m"); | |
e4a71bf3 WKI |
143 | |
144 | r = netlink_call_async(link->manager->rtnl, NULL, req, callback ?: neighbor_handler, | |
145 | link_netlink_destroy_callback, link); | |
146 | if (r < 0) | |
147 | return log_error_errno(r, "Could not send rtnetlink message: %m"); | |
148 | ||
149 | link->neighbor_messages++; | |
150 | link_ref(link); | |
151 | ||
152 | return 0; | |
153 | } | |
154 | ||
044d4b40 YW |
155 | int neighbor_section_verify(Neighbor *neighbor) { |
156 | if (section_is_invalid(neighbor->section)) | |
157 | return -EINVAL; | |
158 | ||
159 | if (neighbor->family == AF_UNSPEC) | |
160 | return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), | |
161 | "%s: Neighbor section without Address= configured. " | |
162 | "Ignoring [Neighbor] section from line %u.", | |
163 | neighbor->section->filename, neighbor->section->line); | |
164 | ||
165 | if (neighbor->lladdr_type < 0) | |
166 | return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), | |
167 | "%s: Neighbor section without LinkLayerAddress= configured. " | |
168 | "Ignoring [Neighbor] section from line %u.", | |
169 | neighbor->section->filename, neighbor->section->line); | |
170 | ||
171 | return 0; | |
172 | } | |
173 | ||
b956364d YW |
174 | int config_parse_neighbor_address( |
175 | const char *unit, | |
176 | const char *filename, | |
177 | unsigned line, | |
178 | const char *section, | |
179 | unsigned section_line, | |
180 | const char *lvalue, | |
181 | int ltype, | |
182 | const char *rvalue, | |
183 | void *data, | |
184 | void *userdata) { | |
e4a71bf3 WKI |
185 | |
186 | Network *network = userdata; | |
fcbf4cb7 | 187 | _cleanup_(neighbor_free_or_set_invalidp) Neighbor *n = NULL; |
e4a71bf3 WKI |
188 | int r; |
189 | ||
190 | assert(filename); | |
191 | assert(section); | |
192 | assert(lvalue); | |
193 | assert(rvalue); | |
194 | assert(data); | |
195 | ||
196 | r = neighbor_new_static(network, filename, section_line, &n); | |
197 | if (r < 0) | |
198 | return r; | |
199 | ||
200 | r = in_addr_from_string_auto(rvalue, &n->family, &n->in_addr); | |
201 | if (r < 0) { | |
202 | log_syntax(unit, LOG_ERR, filename, line, r, "Neighbor Address is invalid, ignoring assignment: %s", rvalue); | |
203 | return 0; | |
204 | } | |
205 | ||
206 | TAKE_PTR(n); | |
207 | ||
208 | return 0; | |
209 | } | |
210 | ||
b956364d YW |
211 | int config_parse_neighbor_lladdr( |
212 | const char *unit, | |
213 | const char *filename, | |
214 | unsigned line, | |
215 | const char *section, | |
216 | unsigned section_line, | |
217 | const char *lvalue, | |
218 | int ltype, | |
219 | const char *rvalue, | |
220 | void *data, | |
221 | void *userdata) { | |
e4a71bf3 WKI |
222 | |
223 | Network *network = userdata; | |
fcbf4cb7 | 224 | _cleanup_(neighbor_free_or_set_invalidp) Neighbor *n = NULL; |
e4a71bf3 WKI |
225 | int r; |
226 | ||
227 | assert(filename); | |
228 | assert(section); | |
229 | assert(lvalue); | |
230 | assert(rvalue); | |
231 | assert(data); | |
232 | ||
233 | r = neighbor_new_static(network, filename, section_line, &n); | |
234 | if (r < 0) | |
235 | return r; | |
236 | ||
b956364d YW |
237 | r = ether_addr_from_string(rvalue, &n->lladdr.mac); |
238 | if (r >= 0) | |
239 | n->lladdr_type = NEIGHBOR_LLADDR_MAC; | |
240 | else { | |
241 | r = in_addr_from_string(AF_INET, rvalue, &n->lladdr.ip); | |
242 | if (r < 0) { | |
243 | log_syntax(unit, LOG_ERR, filename, line, r, "Neighbor LinkLayerAddress= is invalid, ignoring assignment: %s", rvalue); | |
244 | return 0; | |
245 | } | |
246 | n->lladdr_type = NEIGHBOR_LLADDR_IP; | |
247 | } | |
248 | ||
249 | TAKE_PTR(n); | |
250 | ||
251 | return 0; | |
252 | } | |
253 | ||
254 | int config_parse_neighbor_hwaddr( | |
255 | const char *unit, | |
256 | const char *filename, | |
257 | unsigned line, | |
258 | const char *section, | |
259 | unsigned section_line, | |
260 | const char *lvalue, | |
261 | int ltype, | |
262 | const char *rvalue, | |
263 | void *data, | |
264 | void *userdata) { | |
265 | ||
266 | Network *network = userdata; | |
267 | _cleanup_(neighbor_free_or_set_invalidp) Neighbor *n = NULL; | |
268 | int r; | |
269 | ||
270 | assert(filename); | |
271 | assert(section); | |
272 | assert(lvalue); | |
273 | assert(rvalue); | |
274 | assert(data); | |
275 | ||
276 | r = neighbor_new_static(network, filename, section_line, &n); | |
277 | if (r < 0) | |
278 | return r; | |
279 | ||
280 | r = ether_addr_from_string(rvalue, &n->lladdr.mac); | |
e4a71bf3 | 281 | if (r < 0) { |
b956364d | 282 | log_syntax(unit, LOG_ERR, filename, line, r, "Neighbor MACAddress= is invalid, ignoring assignment: %s", rvalue); |
e4a71bf3 WKI |
283 | return 0; |
284 | } | |
285 | ||
b956364d | 286 | n->lladdr_type = NEIGHBOR_LLADDR_MAC; |
e4a71bf3 WKI |
287 | TAKE_PTR(n); |
288 | ||
289 | return 0; | |
290 | } |