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