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