/src/libwebsockets/lib/roles/netlink/ops-netlink.c
Line | Count | Source |
1 | | /* |
2 | | * libwebsockets - small server side websockets and web server implementation |
3 | | * |
4 | | * Copyright (C) 2010 - 2021 Andy Green <andy@warmcat.com> |
5 | | * |
6 | | * Permission is hereby granted, free of charge, to any person obtaining a copy |
7 | | * of this software and associated documentation files (the "Software"), to |
8 | | * deal in the Software without restriction, including without limitation the |
9 | | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or |
10 | | * sell copies of the Software, and to permit persons to whom the Software is |
11 | | * furnished to do so, subject to the following conditions: |
12 | | * |
13 | | * The above copyright notice and this permission notice shall be included in |
14 | | * all copies or substantial portions of the Software. |
15 | | * |
16 | | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
17 | | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
18 | | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
19 | | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
20 | | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
21 | | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
22 | | * IN THE SOFTWARE. |
23 | | * |
24 | | * We mainly focus on the routing table / gateways because those are the |
25 | | * elements that decide if we can get on to the internet or not. |
26 | | * |
27 | | * We also need to understand the source addresses of possible outgoing routes, |
28 | | * and follow LINK down (ifconfig down) to clean up routes on the interface idx |
29 | | * going down that are not otherwise cleaned. |
30 | | */ |
31 | | |
32 | | #include <private-lib-core.h> |
33 | | |
34 | | #include <asm/types.h> |
35 | | #include <sys/socket.h> |
36 | | #include <linux/netlink.h> |
37 | | #include <linux/rtnetlink.h> |
38 | | |
39 | | #ifndef IFA_FLAGS |
40 | 0 | #define IFA_FLAGS 8 |
41 | | #endif |
42 | | |
43 | | //#define lwsl_netlink lwsl_notice |
44 | 0 | #define lwsl_cx_netlink lwsl_cx_info |
45 | 0 | #define lwsl_cx_netlink_debug lwsl_cx_debug |
46 | | |
47 | | static void |
48 | | lws_netlink_coldplug_done_cb(lws_sorted_usec_list_t *sul) |
49 | 0 | { |
50 | 0 | struct lws_context *ctx = lws_container_of(sul, struct lws_context, |
51 | 0 | sul_nl_coldplug); |
52 | 0 | ctx->nl_initial_done = 1; |
53 | 0 | #if defined(LWS_WITH_SYS_STATE) |
54 | | /* if nothing is there to intercept anything, go all the way */ |
55 | 0 | lws_state_transition_steps(&ctx->mgr_system, LWS_SYSTATE_OPERATIONAL); |
56 | 0 | #endif |
57 | 0 | } |
58 | | |
59 | | static lws_handling_result_t |
60 | | rops_handle_POLLIN_netlink(struct lws_context_per_thread *pt, struct lws *wsi, |
61 | | struct lws_pollfd *pollfd) |
62 | 0 | { |
63 | 0 | struct lws_context *cx = pt->context; |
64 | 0 | uint8_t s[4096] |
65 | 0 | #if defined(_DEBUG) |
66 | 0 | , route_change = 0 |
67 | 0 | #endif |
68 | 0 | #if defined(LWS_WITH_SYS_SMD) |
69 | 0 | , gateway_change = 0 |
70 | 0 | #endif |
71 | 0 | ; |
72 | 0 | struct sockaddr_nl nladdr; |
73 | 0 | lws_route_t robj, *rou; |
74 | 0 | struct nlmsghdr *h; |
75 | 0 | struct msghdr msg; |
76 | 0 | struct iovec iov; |
77 | 0 | unsigned int n, removed; |
78 | 0 | char buf[72]; |
79 | |
|
80 | 0 | if (!(pollfd->revents & LWS_POLLIN)) |
81 | 0 | return LWS_HPI_RET_HANDLED; |
82 | | |
83 | 0 | memset(&msg, 0, sizeof(msg)); |
84 | |
|
85 | 0 | iov.iov_base = (void *)s; |
86 | 0 | iov.iov_len = sizeof(s); |
87 | |
|
88 | 0 | msg.msg_name = (void *)&(nladdr); |
89 | 0 | msg.msg_namelen = sizeof(nladdr); |
90 | |
|
91 | 0 | msg.msg_iov = &iov; |
92 | 0 | msg.msg_iovlen = 1; |
93 | |
|
94 | 0 | n = (unsigned int)recvmsg(wsi->desc.sockfd, &msg, 0); |
95 | 0 | if ((int)n < 0) { |
96 | 0 | lwsl_cx_notice(cx, "recvmsg failed"); |
97 | 0 | return LWS_HPI_RET_PLEASE_CLOSE_ME; |
98 | 0 | } |
99 | | |
100 | | // lwsl_hexdump_notice(s, (size_t)n); |
101 | | |
102 | 0 | h = (struct nlmsghdr *)s; |
103 | | |
104 | | /* we can get a bunch of messages coalesced in one read*/ |
105 | |
|
106 | 0 | for ( ; NLMSG_OK(h, n); h = NLMSG_NEXT(h, n)) { |
107 | 0 | struct ifaddrmsg *ifam; |
108 | 0 | struct rtattr *ra; |
109 | 0 | struct rtmsg *rm; |
110 | 0 | #if !defined(LWS_WITH_NO_LOGS) && defined(_DEBUG) |
111 | 0 | struct ndmsg *nd; |
112 | 0 | #endif |
113 | 0 | unsigned int ra_len; |
114 | 0 | uint8_t *p; |
115 | |
|
116 | 0 | struct ifinfomsg *ifi; |
117 | 0 | struct rtattr *attribute; |
118 | 0 | unsigned int len; |
119 | |
|
120 | 0 | lwsl_cx_netlink(cx, "RTM %d", h->nlmsg_type); |
121 | |
|
122 | 0 | memset(&robj, 0, sizeof(robj)); |
123 | 0 | robj.if_idx = -1; |
124 | 0 | robj.priority = -1; |
125 | 0 | rm = (struct rtmsg *)NLMSG_DATA(h); |
126 | | |
127 | | /* |
128 | | * We have to care about NEWLINK so we can understand when a |
129 | | * network interface went down, and clear the related routes. |
130 | | * |
131 | | * We don't get individual DELROUTEs for these. |
132 | | */ |
133 | |
|
134 | 0 | switch (h->nlmsg_type) { |
135 | 0 | case RTM_NEWLINK: |
136 | |
|
137 | 0 | ifi = NLMSG_DATA(h); |
138 | 0 | len = (unsigned int)(h->nlmsg_len - NLMSG_LENGTH(sizeof(*ifi))); |
139 | | |
140 | | /* loop over all attributes for the NEWLINK message */ |
141 | 0 | for (attribute = IFLA_RTA(ifi); RTA_OK(attribute, len); |
142 | 0 | attribute = RTA_NEXT(attribute, len)) { |
143 | 0 | lwsl_cx_netlink(cx, "if attr %d", |
144 | 0 | (int)attribute->rta_type); |
145 | 0 | switch(attribute->rta_type) { |
146 | 0 | case IFLA_IFNAME: |
147 | 0 | lwsl_cx_netlink(cx, "NETLINK ifidx %d : %s", |
148 | 0 | ifi->ifi_index, |
149 | 0 | (char *)RTA_DATA(attribute)); |
150 | 0 | break; |
151 | 0 | default: |
152 | 0 | break; |
153 | 0 | } /* switch */ |
154 | 0 | } /* for loop */ |
155 | | |
156 | 0 | lwsl_cx_netlink(cx, "NEWLINK ifi_index %d, flags 0x%x", |
157 | 0 | ifi->ifi_index, ifi->ifi_flags); |
158 | | |
159 | | /* |
160 | | * Despite "New"link this is actually telling us there |
161 | | * is some change on the network interface IFF_ state |
162 | | */ |
163 | |
|
164 | 0 | if (!(ifi->ifi_flags & IFF_UP)) { |
165 | | /* |
166 | | * Interface is down, so scrub all routes that |
167 | | * applied to it |
168 | | */ |
169 | 0 | lwsl_cx_netlink(cx, "NEWLINK: ifdown %d", |
170 | 0 | ifi->ifi_index); |
171 | 0 | lws_pt_lock(pt, __func__); |
172 | 0 | _lws_route_table_ifdown(pt, ifi->ifi_index); |
173 | 0 | lws_pt_unlock(pt); |
174 | 0 | } |
175 | 0 | continue; /* ie, not break, no second half */ |
176 | | |
177 | 0 | case RTM_NEWADDR: |
178 | 0 | case RTM_DELADDR: |
179 | |
|
180 | 0 | ifam = (struct ifaddrmsg *)NLMSG_DATA(h); |
181 | |
|
182 | 0 | robj.source_ads = 1; |
183 | 0 | robj.dest_len = ifam->ifa_prefixlen; |
184 | 0 | robj.if_idx = (int)ifam->ifa_index; |
185 | 0 | robj.scope = ifam->ifa_scope; |
186 | 0 | robj.ifa_flags = ifam->ifa_flags; |
187 | 0 | robj.dest.sa4.sin_family = ifam->ifa_family; |
188 | | |
189 | | /* address attributes */ |
190 | 0 | ra = (struct rtattr *)IFA_RTA(ifam); |
191 | 0 | ra_len = (unsigned int)IFA_PAYLOAD(h); |
192 | |
|
193 | 0 | lwsl_cx_netlink(cx, "%s", |
194 | 0 | h->nlmsg_type == RTM_NEWADDR ? "NEWADDR" : "DELADDR"); |
195 | | |
196 | | // Parse attributes. |
197 | 0 | for ( ; RTA_OK(ra, ra_len); ra = RTA_NEXT(ra, ra_len)) { |
198 | | //lwsl_cx_netlink_debug(cx, "%s: IFA %d\n", __func__, ra->rta_type); |
199 | 0 | switch (ra->rta_type) { |
200 | 0 | case IFA_LOCAL: |
201 | | // Local address |
202 | 0 | lws_sa46_copy_address(&robj.src, RTA_DATA(ra), rm->rtm_family); |
203 | 0 | robj.src_len = rm->rtm_src_len; |
204 | 0 | lws_sa46_write_numeric_address(&robj.src, buf, sizeof(buf)); |
205 | 0 | lwsl_cx_netlink_debug(cx, "IFA_LOCAL: %s/%d", buf, robj.src_len); |
206 | 0 | break; |
207 | 0 | case IFA_ADDRESS: |
208 | | // Prefix address, not local interface. |
209 | 0 | lws_sa46_copy_address(&robj.dest, RTA_DATA(ra), rm->rtm_family); |
210 | 0 | robj.dest_len = rm->rtm_dst_len; |
211 | 0 | lws_sa46_write_numeric_address(&robj.dest, buf, sizeof(buf)); |
212 | 0 | lwsl_cx_netlink_debug(cx, "IFA_ADDRESS: %s/%d", buf, robj.dest_len); |
213 | 0 | break; |
214 | 0 | case IFA_FLAGS: |
215 | 0 | lwsl_cx_netlink_debug(cx, "IFA_FLAGS: 0x%x (not handled)", |
216 | 0 | *(unsigned int*)RTA_DATA(ra)); |
217 | 0 | break; |
218 | 0 | case IFA_BROADCAST: |
219 | 0 | lwsl_cx_netlink_debug(cx, "IFA_BROADCAST (not handled)"); |
220 | 0 | break; |
221 | 0 | case IFA_ANYCAST: |
222 | 0 | lwsl_cx_netlink_debug(cx, "IFA_ANYCAST (not handled)"); |
223 | 0 | break; |
224 | 0 | case IFA_CACHEINFO: |
225 | 0 | lwsl_cx_netlink_debug(cx, "IFA_CACHEINFO (not handled)"); |
226 | 0 | break; |
227 | 0 | case IFA_LABEL: |
228 | 0 | strncpy(buf, RTA_DATA(ra), sizeof(buf)); |
229 | 0 | buf[sizeof(buf)-1] = '\0'; |
230 | 0 | lwsl_cx_netlink_debug(cx, "IFA_LABEL: %s (not used)", buf); |
231 | 0 | break; |
232 | 0 | default: |
233 | 0 | lwsl_cx_netlink_debug(cx, "unknown IFA attr type %d", ra->rta_type); |
234 | 0 | break; |
235 | 0 | } |
236 | | //lwsl_cx_debug(cx, "rta payload length: %ld", RTA_PAYLOAD(ra)); |
237 | 0 | } /* for */ |
238 | | |
239 | | /* |
240 | | * almost nothing interesting within IFA_* attributes: |
241 | | * so skip it and goto to the second half |
242 | | */ |
243 | 0 | goto second_half; |
244 | | |
245 | 0 | case RTM_NEWROUTE: |
246 | 0 | case RTM_DELROUTE: |
247 | |
|
248 | 0 | lwsl_cx_netlink(cx, "%s", |
249 | 0 | h->nlmsg_type == RTM_NEWROUTE ? |
250 | 0 | "NEWROUTE" : "DELROUTE"); |
251 | | |
252 | | /* route attributes */ |
253 | 0 | ra = (struct rtattr *)RTM_RTA(rm); |
254 | 0 | ra_len = (unsigned int)RTM_PAYLOAD(h); |
255 | 0 | break; |
256 | | |
257 | 0 | case RTM_DELNEIGH: |
258 | 0 | case RTM_NEWNEIGH: |
259 | 0 | lwsl_cx_netlink(cx, "%s", h->nlmsg_type == |
260 | 0 | RTM_NEWNEIGH ? "NEWNEIGH" : |
261 | 0 | "DELNEIGH"); |
262 | 0 | #if !defined(LWS_WITH_NO_LOGS) && defined(_DEBUG) |
263 | 0 | nd = (struct ndmsg *)rm; |
264 | 0 | lwsl_cx_netlink(cx, "fam %u, ifidx %u, flags 0x%x", |
265 | 0 | nd->ndm_family, nd->ndm_ifindex, |
266 | 0 | nd->ndm_flags); |
267 | 0 | #endif |
268 | 0 | ra = (struct rtattr *)RTM_RTA(rm); |
269 | 0 | ra_len = (unsigned int)RTM_PAYLOAD(h); |
270 | 0 | for ( ; RTA_OK(ra, ra_len); ra = RTA_NEXT(ra, ra_len)) { |
271 | 0 | lwsl_cx_netlink(cx, "atr %d", ra->rta_type); |
272 | 0 | switch (ra->rta_type) { |
273 | 0 | case NDA_DST: |
274 | 0 | lwsl_cx_netlink(cx, "dst len %d", |
275 | 0 | ra->rta_len); |
276 | 0 | break; |
277 | 0 | } |
278 | 0 | } |
279 | 0 | lws_pt_lock(pt, __func__); |
280 | 0 | _lws_route_pt_close_unroutable(pt); |
281 | 0 | lws_pt_unlock(pt); |
282 | 0 | continue; |
283 | | |
284 | 0 | default: |
285 | 0 | lwsl_cx_netlink(cx, "*** Unknown RTM_%d", |
286 | 0 | h->nlmsg_type); |
287 | 0 | continue; |
288 | 0 | } /* switch */ |
289 | | |
290 | 0 | robj.proto = rm->rtm_protocol; |
291 | | |
292 | | // iterate over route attributes |
293 | 0 | for ( ; RTA_OK(ra, ra_len); ra = RTA_NEXT(ra, ra_len)) { |
294 | | // lwsl_netlink("%s: atr %d\n", __func__, ra->rta_type); |
295 | 0 | switch (ra->rta_type) { |
296 | 0 | case RTA_PREFSRC: /* protocol ads: preferred src ads */ |
297 | 0 | case RTA_SRC: |
298 | 0 | lws_sa46_copy_address(&robj.src, RTA_DATA(ra), |
299 | 0 | rm->rtm_family); |
300 | 0 | robj.src_len = rm->rtm_src_len; |
301 | 0 | lws_sa46_write_numeric_address(&robj.src, buf, sizeof(buf)); |
302 | 0 | if (ra->rta_type == RTA_SRC) |
303 | 0 | lwsl_cx_netlink_debug(cx, "RTA_SRC: %s/%d", buf, robj.src_len); |
304 | 0 | else |
305 | 0 | lwsl_cx_netlink_debug(cx, "RTA_PREFSRC: %s/%d", buf, robj.src_len); |
306 | 0 | break; |
307 | 0 | case RTA_DST: |
308 | | /* check if is local addr -> considering it as src addr too */ |
309 | 0 | if (rm->rtm_type == RTN_LOCAL && |
310 | 0 | ((rm->rtm_family == AF_INET && rm->rtm_dst_len == 32) || |
311 | 0 | (rm->rtm_family == AF_INET6 && rm->rtm_dst_len == 128))) { |
312 | 0 | lws_sa46_copy_address(&robj.src, RTA_DATA(ra), |
313 | 0 | rm->rtm_family); |
314 | 0 | lwsl_cx_netlink_debug(cx, "Local addr: RTA_DST -> added to RTA_SRC"); |
315 | 0 | } |
316 | |
|
317 | 0 | lws_sa46_copy_address(&robj.dest, RTA_DATA(ra), |
318 | 0 | rm->rtm_family); |
319 | 0 | robj.dest_len = rm->rtm_dst_len; |
320 | 0 | lws_sa46_write_numeric_address(&robj.dest, buf, sizeof(buf)); |
321 | 0 | lwsl_cx_netlink_debug(cx, "RTA_DST: %s/%d", buf, robj.dest_len); |
322 | 0 | break; |
323 | 0 | case RTA_GATEWAY: |
324 | 0 | lws_sa46_copy_address(&robj.gateway, RTA_DATA(ra), |
325 | 0 | rm->rtm_family); |
326 | |
|
327 | 0 | lws_sa46_write_numeric_address(&robj.gateway, buf, sizeof(buf)); |
328 | 0 | lwsl_cx_netlink_debug(cx, "RTA_GATEWAY: %s", buf); |
329 | 0 | #if defined(LWS_WITH_SYS_SMD) |
330 | 0 | gateway_change = 1; |
331 | 0 | #endif |
332 | 0 | break; |
333 | 0 | case RTA_IIF: /* int: input interface index */ |
334 | 0 | case RTA_OIF: /* int: output interface index */ |
335 | 0 | robj.if_idx = *(int *)RTA_DATA(ra); |
336 | 0 | lwsl_cx_netlink_debug(cx, "RTA_IIF/RTA_OIF: %d", robj.if_idx); |
337 | 0 | break; |
338 | 0 | case RTA_PRIORITY: /* int: priority of route */ |
339 | 0 | p = RTA_DATA(ra); |
340 | 0 | robj.priority = p[3] << 24 | p[2] << 16 | |
341 | 0 | p[1] << 8 | p[0]; |
342 | 0 | lwsl_cx_netlink_debug(cx, "RTA_PRIORITY: %d", robj.priority); |
343 | 0 | break; |
344 | 0 | case RTA_CACHEINFO: /* struct rta_cacheinfo */ |
345 | 0 | lwsl_cx_netlink_debug(cx, "RTA_CACHEINFO (not handled)"); |
346 | 0 | break; |
347 | 0 | #if defined(LWS_HAVE_RTA_PREF) |
348 | 0 | case RTA_PREF: /* char: RFC4191 v6 router preference */ |
349 | 0 | lwsl_cx_netlink_debug(cx, "RTA_PREF (not handled)"); |
350 | 0 | break; |
351 | 0 | #endif |
352 | 0 | case RTA_TABLE: /* int */ |
353 | 0 | lwsl_cx_netlink_debug(cx, "RTA_TABLE (not handled)"); |
354 | 0 | break; |
355 | | |
356 | 0 | default: |
357 | 0 | lwsl_cx_netlink_debug(cx, "unknown attr type %d", ra->rta_type); |
358 | 0 | break; |
359 | 0 | } |
360 | | //lwsl_cx_debug(cx, "rta payload length: %ld", RTA_PAYLOAD(ra)); |
361 | 0 | } /* for */ |
362 | | |
363 | | /* |
364 | | * the second half, once all the attributes were collected |
365 | | */ |
366 | 0 | second_half: |
367 | 0 | switch (h->nlmsg_type) { |
368 | | |
369 | 0 | case RTM_DELROUTE: |
370 | | /* |
371 | | * This will also take down wsi marked as using it |
372 | | */ |
373 | 0 | lwsl_cx_netlink(cx, "DELROUTE: if_idx %d", |
374 | 0 | robj.if_idx); |
375 | 0 | lws_pt_lock(pt, __func__); |
376 | 0 | _lws_route_remove(pt, &robj, 0); |
377 | 0 | lws_pt_unlock(pt); |
378 | 0 | goto inform_l; |
379 | | |
380 | 0 | case RTM_NEWROUTE: |
381 | | |
382 | | /* |
383 | | * We don't want any routing debris like /32 or broadcast |
384 | | * in our routing table... we will collect source addresses |
385 | | * bound to interfaces via NEWADDR |
386 | | */ |
387 | 0 | if (rm->rtm_type != RTN_UNICAST |
388 | 0 | && rm->rtm_type != RTN_LOCAL) { |
389 | 0 | lwsl_cx_netlink(cx, "NEWROUTE: IGNORED (%s)", |
390 | 0 | rm->rtm_type == RTN_BROADCAST ? "broadcast" : |
391 | 0 | rm->rtm_type == RTN_ANYCAST ? "anycast" : |
392 | 0 | rm->rtm_type == RTN_MULTICAST ? "multicast" : |
393 | 0 | rm->rtm_type == RTN_UNREACHABLE ? "unreachable" : |
394 | 0 | rm->rtm_type == RTN_NAT ? "nat" : |
395 | 0 | rm->rtm_type == RTN_UNSPEC ? "unspecified" : |
396 | 0 | "other"); |
397 | 0 | break; |
398 | 0 | } |
399 | | |
400 | 0 | if (rm->rtm_flags & RTM_F_CLONED) { |
401 | 0 | lwsl_cx_netlink(cx, "NEWROUTE: IGNORED (cloned)"); |
402 | 0 | break; |
403 | 0 | } |
404 | | |
405 | 0 | lwsl_cx_netlink(cx, "NEWROUTE: ACCEPTED (if_idx %d)", |
406 | 0 | robj.if_idx); |
407 | |
|
408 | 0 | #if defined(_DEBUG) |
409 | 0 | _lws_routing_entry_dump(cx, &robj); |
410 | 0 | #endif |
411 | | |
412 | | /* |
413 | | * 1. Allocate new route for linked-list. |
414 | | * (robj is on stack, do NOT use) |
415 | | */ |
416 | 0 | rou = lws_malloc(sizeof(*rou), __func__); |
417 | 0 | if (!rou) { |
418 | 0 | lwsl_cx_err(cx, "oom"); |
419 | 0 | return LWS_HPI_RET_HANDLED; |
420 | 0 | } |
421 | 0 | *rou = robj; |
422 | | |
423 | | // 2. Remove duplicates and add route (both under a lock). |
424 | 0 | lws_pt_lock(pt, __func__); |
425 | | |
426 | | /* |
427 | | * Is robj a dupe in the routing table already? |
428 | | * |
429 | | * match on pri ignore == set pri and skip |
430 | | * no match == add |
431 | | * |
432 | | * returns zero ALWAYS |
433 | | * |
434 | | * We could be adding a route to the same destination with |
435 | | * a higher or lower priority from a different source, so why |
436 | | * all existing routes? Only remove if its the same source and |
437 | | * destination, effectively a change in priority. |
438 | | */ |
439 | 0 | _lws_route_remove(pt, &robj, |
440 | 0 | LRR_MATCH_DST | LRR_MATCH_SRC | LRR_IGNORE_PRI); |
441 | | |
442 | | /* add route to linked-list */ |
443 | 0 | rou->uidx = _lws_route_get_uidx(cx); |
444 | 0 | lws_dll2_add_tail(&rou->list, &cx->routing_table); |
445 | 0 | lws_pt_unlock(pt); |
446 | |
|
447 | 0 | lwsl_cx_netlink_debug(cx, "route list size %u", |
448 | 0 | cx->routing_table.count); |
449 | | |
450 | | /* |
451 | | * 3. Close anyything we cant reach anymore due to the removal. |
452 | | * (don't need to or want to do this under lock) |
453 | | */ |
454 | 0 | _lws_route_pt_close_unroutable(pt); |
455 | |
|
456 | 0 | inform_l: |
457 | 0 | #if defined(_DEBUG) |
458 | 0 | route_change = 1; |
459 | 0 | #endif |
460 | 0 | #if defined(LWS_WITH_SYS_SMD) |
461 | | /* |
462 | | * Reflect the route add / del event using SMD. |
463 | | * Participants interested can refer to the pt |
464 | | * routing table |
465 | | */ |
466 | 0 | (void)lws_smd_msg_printf(cx, LWSSMDCL_NETWORK, |
467 | 0 | "{\"rt\":\"%s\"}\n", |
468 | 0 | (h->nlmsg_type == RTM_NEWROUTE) ? |
469 | 0 | "add" : "del"); |
470 | 0 | #endif |
471 | |
|
472 | 0 | break; |
473 | | |
474 | 0 | case RTM_DELADDR: |
475 | 0 | lwsl_cx_notice(cx, "DELADDR"); |
476 | 0 | #if defined(_DEBUG) |
477 | 0 | _lws_routing_entry_dump(cx, &robj); |
478 | 0 | #endif |
479 | 0 | lws_pt_lock(pt, __func__); |
480 | 0 | removed = cx->routing_table.count; |
481 | 0 | _lws_route_remove(pt, &robj, LRR_MATCH_SRC | LRR_IGNORE_PRI); |
482 | 0 | removed -= cx->routing_table.count; |
483 | 0 | lws_pt_unlock(pt); |
484 | 0 | _lws_route_pt_close_unroutable(pt); |
485 | 0 | if (removed > 0) |
486 | 0 | goto inform_l; |
487 | 0 | break; |
488 | | |
489 | 0 | case RTM_NEWADDR: |
490 | 0 | lwsl_cx_netlink(cx, "NEWADDR (nothing to do)"); |
491 | 0 | #if defined(_DEBUG) |
492 | 0 | _lws_routing_entry_dump(cx, &robj); |
493 | 0 | #endif |
494 | | /* |
495 | | * An address alone does not provide new routes. |
496 | | * NEWADDR will happen when the DHCP lease is being |
497 | | * renewed, and will likely not change any routes. |
498 | | */ |
499 | 0 | break; |
500 | | |
501 | 0 | default: |
502 | | // lwsl_info("%s: unknown msg type %d\n", __func__, |
503 | | // h->nlmsg_type); |
504 | 0 | break; |
505 | 0 | } |
506 | 0 | } /* message iterator */ |
507 | | |
508 | 0 | #if defined(LWS_WITH_SYS_SMD) |
509 | 0 | if (gateway_change) |
510 | | /* |
511 | | * If a route with a gw was added or deleted, retrigger captive |
512 | | * portal detection if we have that |
513 | | */ |
514 | 0 | (void)lws_smd_msg_printf(cx, LWSSMDCL_NETWORK, |
515 | 0 | "{\"trigger\": \"cpdcheck\", " |
516 | 0 | "\"src\":\"gw-change\"}"); |
517 | 0 | #endif |
518 | |
|
519 | 0 | #if defined(_DEBUG) |
520 | 0 | if (route_change) { |
521 | 0 | lws_context_lock(cx, __func__); |
522 | 0 | _lws_routing_table_dump(cx); |
523 | 0 | lws_context_unlock(cx); |
524 | 0 | } |
525 | 0 | #endif |
526 | |
|
527 | 0 | if (!cx->nl_initial_done && |
528 | 0 | pt == &cx->pt[0] && |
529 | 0 | cx->routing_table.count) { |
530 | | /* |
531 | | * While netlink info still coming, keep moving the timer for |
532 | | * calling it "done" to +100ms until after it stops coming |
533 | | */ |
534 | 0 | lws_context_lock(cx, __func__); |
535 | 0 | lws_sul_schedule(cx, 0, &cx->sul_nl_coldplug, |
536 | 0 | lws_netlink_coldplug_done_cb, |
537 | 0 | 100 * LWS_US_PER_MS); |
538 | 0 | lws_context_unlock(cx); |
539 | 0 | } |
540 | |
|
541 | 0 | return LWS_HPI_RET_HANDLED; |
542 | 0 | } |
543 | | |
544 | | struct nl_req_s { |
545 | | struct nlmsghdr hdr; |
546 | | struct rtmsg gen; |
547 | | }; |
548 | | |
549 | | int |
550 | | rops_pt_init_destroy_netlink(struct lws_context *context, |
551 | | const struct lws_context_creation_info *info, |
552 | | struct lws_context_per_thread *pt, int destroy) |
553 | 0 | { |
554 | 0 | struct sockaddr_nl sanl; |
555 | 0 | struct nl_req_s req; |
556 | 0 | struct msghdr msg; |
557 | 0 | struct iovec iov; |
558 | 0 | struct lws *wsi; |
559 | 0 | int n, ret = 1; |
560 | |
|
561 | 0 | if (destroy) { |
562 | | |
563 | | /* |
564 | | * pt netlink wsi closed + freed as part of pt's destroy |
565 | | * wsi mass close, just need to take down the routing table |
566 | | */ |
567 | 0 | _lws_route_table_empty(pt); |
568 | |
|
569 | 0 | return 0; |
570 | 0 | } |
571 | | |
572 | 0 | if (context->netlink) |
573 | 0 | return 0; |
574 | | |
575 | 0 | if (pt > &context->pt[0]) |
576 | | /* we can only have one netlink socket */ |
577 | 0 | return 0; |
578 | | |
579 | 0 | lwsl_cx_info(context, "creating netlink skt"); |
580 | | |
581 | | /* |
582 | | * We want a netlink socket per pt as well |
583 | | */ |
584 | |
|
585 | 0 | lws_context_lock(context, __func__); |
586 | 0 | wsi = __lws_wsi_create_with_role(context, (int)(pt - &context->pt[0]), |
587 | 0 | &role_ops_netlink, NULL); |
588 | 0 | lws_context_unlock(context); |
589 | 0 | if (!wsi) |
590 | 0 | goto bail; |
591 | | |
592 | 0 | wsi->desc.sockfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); |
593 | 0 | if (wsi->desc.sockfd == LWS_SOCK_INVALID) { |
594 | 0 | lwsl_cx_err(context, "unable to open netlink"); |
595 | 0 | goto bail1; |
596 | 0 | } |
597 | | |
598 | 0 | lws_plat_set_nonblocking(wsi->desc.sockfd); |
599 | |
|
600 | 0 | __lws_lc_tag(context, &context->lcg[LWSLCG_VHOST], &wsi->lc, |
601 | 0 | "netlink"); |
602 | |
|
603 | 0 | memset(&sanl, 0, sizeof(sanl)); |
604 | 0 | sanl.nl_family = AF_NETLINK; |
605 | 0 | sanl.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_ROUTE | RTMGRP_IPV4_IFADDR |
606 | 0 | #if defined(LWS_WITH_IPV6) |
607 | 0 | | RTMGRP_IPV6_ROUTE | RTMGRP_IPV6_IFADDR |
608 | 0 | #endif |
609 | 0 | ; |
610 | |
|
611 | 0 | if (lws_fi(&context->fic, "netlink_bind") || |
612 | 0 | bind(wsi->desc.sockfd, (struct sockaddr*)&sanl, sizeof(sanl)) < 0) { |
613 | 0 | lwsl_cx_warn(context, "netlink bind failed"); |
614 | 0 | ret = 0; /* some systems deny access, just ignore */ |
615 | 0 | goto bail2; |
616 | 0 | } |
617 | | |
618 | 0 | context->netlink = wsi; |
619 | 0 | if (lws_wsi_inject_to_loop(pt, wsi)) |
620 | 0 | goto bail2; |
621 | | |
622 | | /* if (lws_change_pollfd(wsi, 0, LWS_POLLIN)) { |
623 | | lwsl_err("%s: pollfd in fail\n", __func__); |
624 | | goto bail2; |
625 | | } |
626 | | */ |
627 | | /* |
628 | | * Since we're starting the PT, ask to be sent all the existing routes. |
629 | | * |
630 | | * This requires CAP_ADMIN, or root... we do this early before dropping |
631 | | * privs |
632 | | */ |
633 | | |
634 | 0 | memset(&sanl, 0, sizeof(sanl)); |
635 | 0 | memset(&msg, 0, sizeof(msg)); |
636 | 0 | memset(&req, 0, sizeof(req)); |
637 | |
|
638 | 0 | sanl.nl_family = AF_NETLINK; |
639 | |
|
640 | 0 | req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(req.gen)); |
641 | 0 | req.hdr.nlmsg_type = RTM_GETROUTE; |
642 | 0 | req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; |
643 | 0 | req.hdr.nlmsg_seq = 1; |
644 | 0 | req.hdr.nlmsg_pid = (uint32_t)getpid(); |
645 | 0 | req.gen.rtm_family = AF_PACKET; |
646 | 0 | req.gen.rtm_table = RT_TABLE_DEFAULT; |
647 | |
|
648 | 0 | iov.iov_base = &req; |
649 | 0 | iov.iov_len = req.hdr.nlmsg_len; |
650 | 0 | msg.msg_iov = &iov; |
651 | 0 | msg.msg_iovlen = 1; |
652 | 0 | msg.msg_name = &sanl; |
653 | 0 | msg.msg_namelen = sizeof(sanl); |
654 | |
|
655 | 0 | n = (int)sendmsg(wsi->desc.sockfd, (struct msghdr *)&msg, 0); |
656 | 0 | if (n < 0) { |
657 | 0 | lwsl_cx_notice(context, "rt dump req failed... permissions? errno %d", |
658 | 0 | LWS_ERRNO); |
659 | 0 | } |
660 | | |
661 | | /* |
662 | | * Responses are going to come asynchronously, let's block moving |
663 | | * off state IFACE_COLDPLUG until we have had them. This is important |
664 | | * since if we don't hold there, when we do get the responses we may |
665 | | * cull any ongoing connections as unroutable otherwise |
666 | | */ |
667 | |
|
668 | 0 | lwsl_cx_debug(context, "starting netlink coldplug wait"); |
669 | |
|
670 | 0 | return 0; |
671 | | |
672 | 0 | bail2: |
673 | 0 | __lws_lc_untag(wsi->a.context, &wsi->lc); |
674 | 0 | compatible_close(wsi->desc.sockfd); |
675 | 0 | bail1: |
676 | 0 | lws_dll2_remove(&wsi->pre_natal); |
677 | 0 | lws_free(wsi); |
678 | 0 | bail: |
679 | 0 | return ret; |
680 | 0 | } |
681 | | |
682 | | static const lws_rops_t rops_table_netlink[] = { |
683 | | /* 1 */ { .pt_init_destroy = rops_pt_init_destroy_netlink }, |
684 | | /* 2 */ { .handle_POLLIN = rops_handle_POLLIN_netlink }, |
685 | | }; |
686 | | |
687 | | const struct lws_role_ops role_ops_netlink = { |
688 | | /* role name */ "netlink", |
689 | | /* alpn id */ NULL, |
690 | | |
691 | | /* rops_table */ rops_table_netlink, |
692 | | /* rops_idx */ { |
693 | | /* LWS_ROPS_check_upgrades */ |
694 | | /* LWS_ROPS_pt_init_destroy */ 0x01, |
695 | | /* LWS_ROPS_init_vhost */ |
696 | | /* LWS_ROPS_destroy_vhost */ 0x00, |
697 | | /* LWS_ROPS_service_flag_pending */ |
698 | | /* LWS_ROPS_handle_POLLIN */ 0x02, |
699 | | /* LWS_ROPS_handle_POLLOUT */ |
700 | | /* LWS_ROPS_perform_user_POLLOUT */ 0x00, |
701 | | /* LWS_ROPS_callback_on_writable */ |
702 | | /* LWS_ROPS_tx_credit */ 0x00, |
703 | | /* LWS_ROPS_write_role_protocol */ |
704 | | /* LWS_ROPS_encapsulation_parent */ 0x00, |
705 | | /* LWS_ROPS_alpn_negotiated */ |
706 | | /* LWS_ROPS_close_via_role_protocol */ 0x00, |
707 | | /* LWS_ROPS_close_role */ |
708 | | /* LWS_ROPS_close_kill_connection */ 0x00, |
709 | | /* LWS_ROPS_destroy_role */ |
710 | | /* LWS_ROPS_adoption_bind */ 0x00, |
711 | | /* LWS_ROPS_client_bind */ |
712 | | /* LWS_ROPS_issue_keepalive */ 0x00, |
713 | | }, |
714 | | |
715 | | /* adoption_cb clnt, srv */ { 0, 0 }, |
716 | | /* rx_cb clnt, srv */ { 0, 0 }, |
717 | | /* writeable cb clnt, srv */ { 0, 0 }, |
718 | | /* close cb clnt, srv */ { 0, 0 }, |
719 | | /* protocol_bind_cb c,s */ { 0, 0 }, |
720 | | /* protocol_unbind_cb c,s */ { 0, 0 }, |
721 | | /* file_handle */ 0, |
722 | | }; |