1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include "netdev.h"
4 #include "netlink-util.h"
5 #include "networkd-link.h"
6 #include "networkd-manager.h"
7 #include "networkd-queue.h"
8 #include "string-table.h"
9
request_free(Request * req)10 static Request *request_free(Request *req) {
11 if (!req)
12 return NULL;
13
14 /* To prevent from triggering assertions in the hash and compare functions, remove this request
15 * before freeing userdata below. */
16 if (req->manager)
17 ordered_set_remove(req->manager->request_queue, req);
18
19 if (req->free_func)
20 req->free_func(req->userdata);
21
22 if (req->counter)
23 (*req->counter)--;
24
25 link_unref(req->link); /* link may be NULL, but link_unref() can handle it gracefully. */
26
27 return mfree(req);
28 }
29
30 DEFINE_TRIVIAL_REF_UNREF_FUNC(Request, request, request_free);
31 DEFINE_TRIVIAL_DESTRUCTOR(request_destroy_callback, Request, request_unref);
32
request_detach(Manager * manager,Request * req)33 void request_detach(Manager *manager, Request *req) {
34 assert(manager);
35
36 if (!req)
37 return;
38
39 req = ordered_set_remove(manager->request_queue, req);
40 if (!req)
41 return;
42
43 req->manager = NULL;
44 request_unref(req);
45 }
46
request_hash_func(const Request * req,struct siphash * state)47 static void request_hash_func(const Request *req, struct siphash *state) {
48 assert(req);
49 assert(state);
50
51 siphash24_compress_boolean(req->link, state);
52 if (req->link)
53 siphash24_compress(&req->link->ifindex, sizeof(req->link->ifindex), state);
54
55 siphash24_compress(&req->type, sizeof(req->type), state);
56
57 siphash24_compress(&req->hash_func, sizeof(req->hash_func), state);
58 siphash24_compress(&req->compare_func, sizeof(req->compare_func), state);
59
60 if (req->hash_func)
61 req->hash_func(req->userdata, state);
62 }
63
request_compare_func(const struct Request * a,const struct Request * b)64 static int request_compare_func(const struct Request *a, const struct Request *b) {
65 int r;
66
67 assert(a);
68 assert(b);
69
70 r = CMP(!!a->link, !!b->link);
71 if (r != 0)
72 return r;
73
74 if (a->link) {
75 r = CMP(a->link->ifindex, b->link->ifindex);
76 if (r != 0)
77 return r;
78 }
79
80 r = CMP(a->type, b->type);
81 if (r != 0)
82 return r;
83
84 r = CMP(PTR_TO_UINT64(a->hash_func), PTR_TO_UINT64(b->hash_func));
85 if (r != 0)
86 return r;
87
88 r = CMP(PTR_TO_UINT64(a->compare_func), PTR_TO_UINT64(b->compare_func));
89 if (r != 0)
90 return r;
91
92 if (a->compare_func)
93 return a->compare_func(a->userdata, b->userdata);
94
95 return 0;
96 }
97
98 DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
99 request_hash_ops,
100 Request,
101 request_hash_func,
102 request_compare_func,
103 request_unref);
104
request_new(Manager * manager,Link * link,RequestType type,void * userdata,mfree_func_t free_func,hash_func_t hash_func,compare_func_t compare_func,request_process_func_t process,unsigned * counter,request_netlink_handler_t netlink_handler,Request ** ret)105 static int request_new(
106 Manager *manager,
107 Link *link,
108 RequestType type,
109 void *userdata,
110 mfree_func_t free_func,
111 hash_func_t hash_func,
112 compare_func_t compare_func,
113 request_process_func_t process,
114 unsigned *counter,
115 request_netlink_handler_t netlink_handler,
116 Request **ret) {
117
118 _cleanup_(request_unrefp) Request *req = NULL;
119 Request *existing;
120 int r;
121
122 assert(manager);
123 assert(process);
124
125 req = new(Request, 1);
126 if (!req) {
127 if (free_func)
128 free_func(userdata);
129 return -ENOMEM;
130 }
131
132 *req = (Request) {
133 .n_ref = 1,
134 .link = link_ref(link), /* link may be NULL, but link_ref() handles it gracefully. */
135 .type = type,
136 .userdata = userdata,
137 .free_func = free_func,
138 .hash_func = hash_func,
139 .compare_func = compare_func,
140 .process = process,
141 .netlink_handler = netlink_handler,
142 };
143
144 existing = ordered_set_get(manager->request_queue, req);
145 if (existing) {
146 if (ret)
147 *ret = existing;
148 return 0;
149 }
150
151 r = ordered_set_ensure_put(&manager->request_queue, &request_hash_ops, req);
152 if (r < 0)
153 return r;
154
155 req->manager = manager;
156 req->counter = counter;
157 if (req->counter)
158 (*req->counter)++;
159
160 if (ret)
161 *ret = req;
162
163 TAKE_PTR(req);
164 return 1;
165 }
166
netdev_queue_request(NetDev * netdev,request_process_func_t process,Request ** ret)167 int netdev_queue_request(
168 NetDev *netdev,
169 request_process_func_t process,
170 Request **ret) {
171
172 assert(netdev);
173
174 return request_new(netdev->manager, NULL, REQUEST_TYPE_NETDEV_INDEPENDENT,
175 netdev_ref(netdev), (mfree_func_t) netdev_unref,
176 trivial_hash_func, trivial_compare_func,
177 process, NULL, NULL, ret);
178 }
179
link_queue_request_full(Link * link,RequestType type,void * userdata,mfree_func_t free_func,hash_func_t hash_func,compare_func_t compare_func,request_process_func_t process,unsigned * counter,request_netlink_handler_t netlink_handler,Request ** ret)180 int link_queue_request_full(
181 Link *link,
182 RequestType type,
183 void *userdata,
184 mfree_func_t free_func,
185 hash_func_t hash_func,
186 compare_func_t compare_func,
187 request_process_func_t process,
188 unsigned *counter,
189 request_netlink_handler_t netlink_handler,
190 Request **ret) {
191
192 assert(link);
193
194 return request_new(link->manager, link, type,
195 userdata, free_func, hash_func, compare_func,
196 process, counter, netlink_handler, ret);
197 }
198
manager_process_requests(sd_event_source * s,void * userdata)199 int manager_process_requests(sd_event_source *s, void *userdata) {
200 Manager *manager = ASSERT_PTR(userdata);
201 int r;
202
203 for (;;) {
204 bool processed = false;
205 Request *req;
206
207 ORDERED_SET_FOREACH(req, manager->request_queue) {
208 _unused_ _cleanup_(request_unrefp) Request *ref = request_ref(req);
209 _cleanup_(link_unrefp) Link *link = link_ref(req->link);
210
211 assert(req->process);
212
213 r = req->process(req, link, req->userdata);
214 if (r == 0)
215 continue;
216
217 processed = true;
218 request_detach(manager, req);
219
220 if (r < 0 && link) {
221 link_enter_failed(link);
222 /* link_enter_failed() may remove multiple requests,
223 * hence we need to exit from the loop. */
224 break;
225 }
226 }
227
228 /* When at least one request is processed, then another request may be ready now. */
229 if (!processed)
230 break;
231 }
232
233 return 0;
234 }
235
request_netlink_handler(sd_netlink * nl,sd_netlink_message * m,Request * req)236 static int request_netlink_handler(sd_netlink *nl, sd_netlink_message *m, Request *req) {
237 assert(req);
238
239 if (req->counter) {
240 assert(*req->counter > 0);
241 (*req->counter)--;
242 req->counter = NULL; /* To prevent double decrement on free. */
243 }
244
245 if (req->link && IN_SET(req->link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
246 return 0;
247
248 if (req->netlink_handler)
249 return req->netlink_handler(nl, m, req, req->link, req->userdata);
250
251 return 0;
252 }
253
request_call_netlink_async(sd_netlink * nl,sd_netlink_message * m,Request * req)254 int request_call_netlink_async(sd_netlink *nl, sd_netlink_message *m, Request *req) {
255 int r;
256
257 assert(nl);
258 assert(m);
259 assert(req);
260
261 r = netlink_call_async(nl, NULL, m, request_netlink_handler, request_destroy_callback, req);
262 if (r < 0)
263 return r;
264
265 request_ref(req);
266 return 0;
267 }
268
269 static const char *const request_type_table[_REQUEST_TYPE_MAX] = {
270 [REQUEST_TYPE_ACTIVATE_LINK] = "activate link",
271 [REQUEST_TYPE_ADDRESS] = "address",
272 [REQUEST_TYPE_ADDRESS_LABEL] = "address label",
273 [REQUEST_TYPE_BRIDGE_FDB] = "bridge FDB",
274 [REQUEST_TYPE_BRIDGE_MDB] = "bridge MDB",
275 [REQUEST_TYPE_DHCP_SERVER] = "DHCP server",
276 [REQUEST_TYPE_DHCP4_CLIENT] = "DHCPv4 client",
277 [REQUEST_TYPE_DHCP6_CLIENT] = "DHCPv6 client",
278 [REQUEST_TYPE_IPV6_PROXY_NDP] = "IPv6 proxy NDP",
279 [REQUEST_TYPE_NDISC] = "NDisc",
280 [REQUEST_TYPE_NEIGHBOR] = "neighbor",
281 [REQUEST_TYPE_NETDEV_INDEPENDENT] = "independent netdev",
282 [REQUEST_TYPE_NETDEV_STACKED] = "stacked netdev",
283 [REQUEST_TYPE_NEXTHOP] = "nexthop",
284 [REQUEST_TYPE_RADV] = "RADV",
285 [REQUEST_TYPE_ROUTE] = "route",
286 [REQUEST_TYPE_ROUTING_POLICY_RULE] = "routing policy rule",
287 [REQUEST_TYPE_SET_LINK_ADDRESS_GENERATION_MODE] = "IPv6LL address generation mode",
288 [REQUEST_TYPE_SET_LINK_BOND] = "bond configurations",
289 [REQUEST_TYPE_SET_LINK_BRIDGE] = "bridge configurations",
290 [REQUEST_TYPE_SET_LINK_BRIDGE_VLAN] = "bridge VLAN configurations",
291 [REQUEST_TYPE_SET_LINK_CAN] = "CAN interface configurations",
292 [REQUEST_TYPE_SET_LINK_FLAGS] = "link flags",
293 [REQUEST_TYPE_SET_LINK_GROUP] = "interface group",
294 [REQUEST_TYPE_SET_LINK_IPOIB] = "IPoIB configurations",
295 [REQUEST_TYPE_SET_LINK_MAC] = "MAC address",
296 [REQUEST_TYPE_SET_LINK_MASTER] = "master interface",
297 [REQUEST_TYPE_SET_LINK_MTU] = "MTU",
298 [REQUEST_TYPE_SRIOV] = "SR-IOV",
299 [REQUEST_TYPE_TC_QDISC] = "QDisc",
300 [REQUEST_TYPE_TC_CLASS] = "TClass",
301 [REQUEST_TYPE_UP_DOWN] = "bring link up or down",
302 };
303
304 DEFINE_STRING_TABLE_LOOKUP_TO_STRING(request_type, RequestType);
305