Line data Source code
1 : /*
2 : SSSD - Service monitor - netlink support
3 :
4 : Authors:
5 : Jakub Hrozek <jhrozek@redhat.com>
6 : Parts of this code were borrowed from NetworkManager
7 :
8 : Copyright (C) 2010 Red Hat
9 :
10 : This program is free software; you can redistribute it and/or modify
11 : it under the terms of the GNU General Public License as published by
12 : the Free Software Foundation; either version 3 of the License, or
13 : (at your option) any later version.
14 :
15 : This program is distributed in the hope that it will be useful,
16 : but WITHOUT ANY WARRANTY; without even the implied warranty of
17 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 : GNU General Public License for more details.
19 :
20 : You should have received a copy of the GNU General Public License
21 : along with this program. If not, see <http://www.gnu.org/licenses/>.
22 : */
23 :
24 : #include <talloc.h>
25 : #include <tevent.h>
26 : #include <sys/types.h>
27 : #include <sys/ioctl.h>
28 : #define __USE_GNU /* needed for struct ucred */
29 : #include <sys/socket.h>
30 : #include <netinet/in.h>
31 : #include <arpa/inet.h>
32 : #include <unistd.h>
33 : #include <fcntl.h>
34 :
35 : #include "monitor/monitor.h"
36 : #include "util/util.h"
37 :
38 : #ifdef HAVE_LIBNL
39 : #include <linux/if.h>
40 : #include <linux/socket.h>
41 : #include <linux/rtnetlink.h>
42 : #include <linux/wireless.h>
43 : #include <netlink/netlink.h>
44 : #include <netlink/utils.h>
45 : #include <netlink/route/addr.h>
46 : #include <netlink/route/link.h>
47 : #include <netlink/route/rtnl.h>
48 : #include <netlink/route/route.h>
49 : #include <netlink/handlers.h>
50 : #include <netlink/socket.h>
51 : #endif
52 :
53 : /* Linux header file confusion causes this to be undefined. */
54 : #ifndef SOL_NETLINK
55 : #define SOL_NETLINK 270
56 : #endif
57 :
58 : #define SYSFS_IFACE_TEMPLATE "/sys/class/net/%s"
59 : #define SYSFS_IFACE_PATH_MAX (16+IFNAMSIZ)
60 :
61 : #define PHY_80211_SUBDIR "phy80211"
62 : /* 9 = strlen(PHY_80211_SUBDIR)+1, 1 = path delimeter */
63 : #define SYSFS_SUBDIR_PATH_MAX (SYSFS_IFACE_PATH_MAX+9+1)
64 :
65 : #define TYPE_FILE "type"
66 : /* 5 = strlen(TYPE_FILE)+1, 1 = path delimeter */
67 : #define SYSFS_TYPE_PATH_MAX (SYSFS_IFACE_PATH_MAX+5+1)
68 :
69 : #define BUFSIZE 8
70 :
71 : #ifdef HAVE_LIBNL
72 : /* Wrappers determining use of libnl version 1 or 3 */
73 : #ifdef HAVE_LIBNL3
74 :
75 : #define nlw_destroy_handle nl_socket_free
76 : #define nlw_alloc nl_socket_alloc
77 : #define nlw_disable_seq_check nl_socket_disable_seq_check
78 :
79 : #define nlw_geterror(error) nl_geterror(error)
80 :
81 : #define nlw_handle nl_sock
82 :
83 : #elif defined(HAVE_LIBNL1)
84 :
85 : #define nlw_destroy_handle nl_handle_destroy
86 : #define nlw_alloc nl_handle_alloc
87 : #define nlw_disable_seq_check nl_disable_sequence_check
88 :
89 : #define nlw_geterror(error) nl_geterror()
90 :
91 : #define nlw_handle nl_handle
92 :
93 : #endif /* HAVE_LIBNL3 */
94 :
95 : #endif /* HAVE_LIBNL */
96 :
97 : enum nlw_msg_type {
98 : NLW_LINK,
99 : NLW_ROUTE,
100 : NLW_ADDR,
101 : NLW_OTHER
102 : };
103 :
104 : struct netlink_ctx {
105 : #ifdef HAVE_LIBNL
106 : struct nlw_handle *nlp;
107 : #endif
108 : struct tevent_fd *tefd;
109 :
110 : network_change_cb change_cb;
111 : void *cb_data;
112 : };
113 :
114 : #ifdef HAVE_LIBNL
115 0 : static int netlink_ctx_destructor(void *ptr)
116 : {
117 : struct netlink_ctx *nlctx;
118 0 : nlctx = talloc_get_type(ptr, struct netlink_ctx);
119 :
120 0 : nlw_destroy_handle(nlctx->nlp);
121 0 : return 0;
122 : }
123 :
124 : /*******************************************************************
125 : * Utility functions
126 : *******************************************************************/
127 :
128 : /* rtnl_route_get_oif removed from libnl3 */
129 : int
130 0 : rtnlw_route_get_oif(struct rtnl_route * route)
131 : {
132 : #ifndef HAVE_RTNL_ROUTE_GET_OIF
133 : struct rtnl_nexthop * nh;
134 : int hops;
135 :
136 0 : hops = rtnl_route_get_nnexthops(route);
137 0 : if (hops <= 0) {
138 0 : return 0;
139 : }
140 :
141 0 : nh = rtnl_route_nexthop_n(route, 0);
142 :
143 0 : return rtnl_route_nh_get_ifindex(nh);
144 : #else
145 : return rtnl_route_get_oif(route);
146 : #endif
147 : }
148 :
149 0 : static bool has_wireless_extension(const char *ifname)
150 : {
151 : int s;
152 : errno_t ret;
153 : struct iwreq iwr;
154 :
155 0 : s = socket(PF_INET, SOCK_DGRAM, 0);
156 0 : if (s == -1) {
157 0 : ret = errno;
158 0 : DEBUG(SSSDBG_OP_FAILURE,
159 : "Could not open socket: [%d] %s\n", ret, strerror(ret));
160 0 : return false;
161 : }
162 :
163 0 : strncpy(iwr.ifr_ifrn.ifrn_name, ifname, IFNAMSIZ-1);
164 0 : iwr.ifr_ifrn.ifrn_name[IFNAMSIZ-1] = '\0';
165 : /* Does the interface support a wireless extension? */
166 0 : ret = ioctl(s, SIOCGIWNAME, &iwr);
167 0 : close(s);
168 :
169 0 : return ret == 0;
170 : }
171 :
172 0 : static bool has_ethernet_encapsulation(const char *sysfs_path)
173 : {
174 : char type_path[SYSFS_TYPE_PATH_MAX];
175 : errno_t ret;
176 0 : int fd = -1;
177 : char buf[BUFSIZE];
178 :
179 0 : ret = snprintf(type_path, SYSFS_TYPE_PATH_MAX,
180 : "%s/%s", sysfs_path, TYPE_FILE);
181 0 : if (ret < 0) {
182 0 : DEBUG(SSSDBG_OP_FAILURE, "snprintf failed\n");
183 0 : return false;
184 0 : } else if (ret >= SYSFS_TYPE_PATH_MAX) {
185 0 : DEBUG(SSSDBG_OP_FAILURE, "path too long?!?!\n");
186 0 : return false;
187 : }
188 :
189 0 : errno = 0;
190 0 : fd = open(type_path, O_RDONLY);
191 0 : if (fd == -1) {
192 0 : ret = errno;
193 0 : DEBUG(SSSDBG_OP_FAILURE, "Could not open sysfs file %s: [%d] %s\n",
194 : type_path, ret, strerror(ret));
195 0 : return false;
196 : }
197 :
198 0 : memset(buf, 0, BUFSIZE);
199 0 : errno = 0;
200 0 : ret = sss_atomic_read_s(fd, buf, BUFSIZE);
201 0 : if (ret == -1) {
202 0 : ret = errno;
203 0 : DEBUG(SSSDBG_OP_FAILURE,
204 : "read failed [%d][%s].\n", ret, strerror(ret));
205 0 : close(fd);
206 0 : return false;
207 : }
208 0 : close(fd);
209 0 : buf[BUFSIZE-1] = '\0';
210 :
211 0 : return strncmp(buf, "1\n", BUFSIZE) == 0;
212 : }
213 :
214 0 : static bool has_phy_80211_subdir(const char *sysfs_path)
215 : {
216 : char phy80211_path[SYSFS_SUBDIR_PATH_MAX];
217 : struct stat statbuf;
218 : errno_t ret;
219 :
220 0 : ret = snprintf(phy80211_path, SYSFS_SUBDIR_PATH_MAX,
221 : "%s/%s", sysfs_path, PHY_80211_SUBDIR);
222 0 : if (ret < 0) {
223 0 : DEBUG(SSSDBG_OP_FAILURE, "snprintf failed\n");
224 0 : return false;
225 0 : } else if (ret >= SYSFS_SUBDIR_PATH_MAX) {
226 0 : DEBUG(SSSDBG_OP_FAILURE, "path too long?!?!\n");
227 0 : return false;
228 : }
229 :
230 0 : errno = 0;
231 0 : ret = stat(phy80211_path, &statbuf);
232 0 : if (ret == -1) {
233 0 : ret = errno;
234 0 : if (ret == ENOENT || ret == ENOTDIR) {
235 0 : DEBUG(SSSDBG_TRACE_LIBS, "No %s directory in sysfs, probably "
236 : "not a wireless interface\n", PHY_80211_SUBDIR);
237 : } else {
238 0 : DEBUG(SSSDBG_OP_FAILURE, "stat failed: [%d] %s\n",
239 : ret, strerror(ret));
240 : }
241 0 : return false;
242 : }
243 :
244 0 : if (statbuf.st_mode & S_IFDIR) {
245 0 : DEBUG(SSSDBG_TRACE_LIBS, "Directory %s found in sysfs, looks like "
246 : "a wireless iface\n", PHY_80211_SUBDIR);
247 0 : return true;
248 : }
249 :
250 0 : return false;
251 : }
252 :
253 0 : static bool discard_iff_up(const char *ifname)
254 : {
255 : char path[SYSFS_IFACE_PATH_MAX];
256 : errno_t ret;
257 :
258 : /* This catches most of the new 80211 drivers */
259 0 : if (has_wireless_extension(ifname)) {
260 0 : DEBUG(SSSDBG_TRACE_FUNC, "%s has a wireless extension\n", ifname);
261 0 : return true;
262 : }
263 :
264 0 : ret = snprintf(path, SYSFS_IFACE_PATH_MAX, SYSFS_IFACE_TEMPLATE, ifname);
265 0 : if (ret < 0) {
266 0 : DEBUG(SSSDBG_OP_FAILURE, "snprintf failed\n");
267 0 : return false;
268 0 : } else if (ret >= SYSFS_IFACE_PATH_MAX) {
269 0 : DEBUG(SSSDBG_OP_FAILURE, "path too long?!?!\n");
270 0 : return false;
271 : }
272 :
273 : /* This will filter PPP and such. Both wired and wireless
274 : * interfaces have the encapsulation. */
275 0 : if (!has_ethernet_encapsulation(path)) {
276 0 : DEBUG(SSSDBG_TRACE_FUNC, "%s does not have ethernet encapsulation, "
277 : "filtering out\n", ifname);
278 0 : return true;
279 : }
280 :
281 : /* This captures old WEXT drivers, the new mac8011 would
282 : * be caught by the ioctl check */
283 0 : if (has_phy_80211_subdir(path)) {
284 0 : DEBUG(SSSDBG_TRACE_FUNC, "%s has a 802_11 subdir, filtering out\n",
285 : ifname);
286 0 : return true;
287 : }
288 :
289 0 : return false;
290 : }
291 :
292 0 : static void nladdr_to_string(struct nl_addr *nl, char *buf, size_t bufsize)
293 : {
294 : int addr_family;
295 : void *addr;
296 :
297 0 : addr_family = nl_addr_get_family(nl);
298 0 : if (addr_family != AF_INET && addr_family != AF_INET6) {
299 0 : strncpy(buf, "unknown", bufsize);
300 0 : return;
301 : }
302 :
303 0 : addr = nl_addr_get_binary_addr(nl);
304 0 : if (!addr) return;
305 :
306 0 : if (inet_ntop(addr_family, addr, buf, bufsize) == NULL) {
307 0 : DEBUG(SSSDBG_MINOR_FAILURE, "inet_ntop failed\n");
308 0 : snprintf(buf, bufsize, "unknown");
309 : }
310 : }
311 :
312 : /*******************************************************************
313 : * Wrappers for different capabilities of different libnl versions
314 : *******************************************************************/
315 :
316 0 : static bool nlw_accept_message(struct nlw_handle *nlp,
317 : const struct sockaddr_nl *snl,
318 : struct nlmsghdr *hdr)
319 : {
320 0 : bool accept_msg = false;
321 : uint32_t local_port;
322 :
323 0 : if (snl == NULL) {
324 0 : DEBUG(SSSDBG_MINOR_FAILURE, "Malformed message, skipping\n");
325 0 : return false;
326 : }
327 :
328 : /* Accept any messages from the kernel */
329 0 : if (hdr->nlmsg_pid == 0 || snl->nl_pid == 0) {
330 0 : accept_msg = true;
331 : }
332 :
333 : /* And any multicast message directed to our netlink PID, since multicast
334 : * currently requires CAP_ADMIN to use.
335 : */
336 0 : local_port = nl_socket_get_local_port(nlp);
337 0 : if ((hdr->nlmsg_pid == local_port) && snl->nl_groups) {
338 0 : accept_msg = true;
339 : }
340 :
341 0 : if (accept_msg == false) {
342 0 : DEBUG(SSSDBG_TRACE_ALL,
343 : "ignoring netlink message from PID %d\n", hdr->nlmsg_pid);
344 : }
345 :
346 0 : return accept_msg;
347 : }
348 :
349 0 : static bool nlw_is_addr_object(struct nl_object *obj)
350 : {
351 0 : bool is_addr_object = true;
352 : struct rtnl_addr *filter;
353 :
354 0 : filter = rtnl_addr_alloc();
355 0 : if (!filter) {
356 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Allocation error!\n");
357 0 : is_addr_object = false;
358 : }
359 :
360 : /* Ensure it's an addr object */
361 0 : if (!nl_object_match_filter(obj, OBJ_CAST(filter))) {
362 0 : DEBUG(SSSDBG_MINOR_FAILURE, "Not an addr object\n");
363 0 : is_addr_object = false;
364 : }
365 :
366 0 : rtnl_addr_put(filter);
367 0 : return is_addr_object;
368 : }
369 :
370 0 : static bool nlw_is_route_object(struct nl_object *obj)
371 : {
372 0 : bool is_route_object = true;
373 : struct rtnl_route *filter;
374 :
375 0 : filter = rtnl_route_alloc();
376 0 : if (!filter) {
377 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Allocation error!\n");
378 0 : is_route_object = false;
379 : }
380 :
381 : /* Ensure it's a route object */
382 0 : if (!nl_object_match_filter(obj, OBJ_CAST(filter))) {
383 0 : DEBUG(SSSDBG_MINOR_FAILURE, "Not a route object\n");
384 0 : is_route_object = false;
385 : }
386 :
387 0 : rtnl_route_put(filter);
388 0 : return is_route_object;
389 : }
390 :
391 0 : static bool nlw_is_link_object(struct nl_object *obj)
392 : {
393 0 : bool is_link_object = true;
394 : struct rtnl_link *filter;
395 :
396 0 : filter = rtnl_link_alloc();
397 0 : if (!filter) {
398 0 : DEBUG(SSSDBG_FATAL_FAILURE, "Allocation error!\n");
399 0 : is_link_object = false;
400 : }
401 :
402 : /* Ensure it's a link object */
403 0 : if (!nl_object_match_filter(obj, OBJ_CAST(filter))) {
404 0 : DEBUG(SSSDBG_OP_FAILURE, "Not a link object\n");
405 0 : is_link_object = false;
406 : }
407 :
408 0 : rtnl_link_put(filter);
409 0 : return is_link_object;
410 : }
411 :
412 0 : static int nlw_enable_passcred(struct nlw_handle *nlp)
413 : {
414 : #ifdef HAVE_NL_SET_PASSCRED
415 : return nl_set_passcred(nlp, 1); /* 1 = enabled */
416 : #elif defined(HAVE_NL_SOCKET_SET_PASSCRED)
417 0 : return nl_socket_set_passcred(nlp, 1);
418 : #else
419 : return EOK; /* not available in this version */
420 : #endif
421 : }
422 :
423 0 : static int nlw_group_subscribe(struct nlw_handle *nlp, int group)
424 : {
425 : int ret;
426 :
427 : #ifdef HAVE_NL_SOCKET_ADD_MEMBERSHIP
428 0 : ret = nl_socket_add_membership(nlp, group);
429 0 : if (ret != 0) {
430 0 : DEBUG(SSSDBG_CRIT_FAILURE,
431 : "Unable to add membership: %s\n", nlw_geterror(ret));
432 0 : return ret;
433 : }
434 : #else
435 : int nlfd = nl_socket_get_fd(nlp);
436 :
437 : errno = 0;
438 : ret = setsockopt(nlfd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP,
439 : &group, sizeof(group));
440 : if (ret < 0) {
441 : ret = errno;
442 : DEBUG(SSSDBG_CRIT_FAILURE,
443 : "setsockopt failed (%d): %s\n", ret, strerror(ret));
444 : return ret;
445 : }
446 : #endif
447 :
448 0 : return 0;
449 : }
450 :
451 0 : static int nlw_groups_subscribe(struct nlw_handle *nlp, int *groups)
452 : {
453 : int ret;
454 : int i;
455 :
456 0 : for (i=0; groups[i]; i++) {
457 0 : ret = nlw_group_subscribe(nlp, groups[i]);
458 0 : if (ret != EOK) return ret;
459 : }
460 :
461 0 : return EOK;
462 : }
463 :
464 : /*******************************************************************
465 : * Callbacks for validating and receiving messages
466 : *******************************************************************/
467 :
468 0 : static int event_msg_recv(struct nl_msg *msg, void *arg)
469 : {
470 0 : struct netlink_ctx *ctx = (struct netlink_ctx *) arg;
471 : struct nlmsghdr *hdr;
472 : const struct sockaddr_nl *snl;
473 : struct ucred *creds;
474 :
475 0 : creds = nlmsg_get_creds(msg);
476 0 : if (!creds || creds->uid != 0) {
477 0 : DEBUG(SSSDBG_TRACE_ALL,
478 : "Ignoring netlink message from UID %"SPRIuid"\n",
479 : creds ? creds->uid : (uid_t)-1);
480 0 : return NL_SKIP;
481 : }
482 :
483 0 : hdr = nlmsg_hdr(msg);
484 0 : snl = nlmsg_get_src(msg);
485 :
486 0 : if (!nlw_accept_message(ctx->nlp, snl, hdr)) {
487 0 : return NL_SKIP;
488 : }
489 :
490 0 : return NL_OK;
491 : }
492 :
493 : static void link_msg_handler(struct nl_object *obj, void *arg);
494 : static void route_msg_handler(struct nl_object *obj, void *arg);
495 : static void addr_msg_handler(struct nl_object *obj, void *arg);
496 :
497 0 : static enum nlw_msg_type message_type(struct nlmsghdr *hdr)
498 : {
499 0 : DEBUG(SSSDBG_FUNC_DATA, "netlink Message type: %d\n", hdr->nlmsg_type);
500 0 : switch (hdr->nlmsg_type) {
501 : /* network interface added */
502 : case RTM_NEWLINK:
503 0 : return NLW_LINK;
504 : /* routing table changed */
505 : case RTM_NEWROUTE:
506 : case RTM_DELROUTE:
507 0 : return NLW_ROUTE;
508 : /* IP address added or deleted */
509 : case RTM_NEWADDR:
510 : case RTM_DELADDR:
511 0 : return NLW_ADDR;
512 : /* Something else happened, but we don't care (typically RTM_GET* ) */
513 : default:
514 0 : return NLW_OTHER;
515 : }
516 :
517 : return NLW_OTHER;
518 : }
519 :
520 0 : static int event_msg_ready(struct nl_msg *msg, void *arg)
521 : {
522 0 : struct nlmsghdr *hdr = nlmsg_hdr(msg);
523 :
524 0 : switch (message_type(hdr)) {
525 : case NLW_LINK:
526 0 : nl_msg_parse(msg, &link_msg_handler, arg);
527 0 : break;
528 : case NLW_ROUTE:
529 0 : nl_msg_parse(msg, &route_msg_handler, arg);
530 0 : break;
531 : case NLW_ADDR:
532 0 : nl_msg_parse(msg, &addr_msg_handler, arg);
533 0 : break;
534 : default:
535 0 : return EOK; /* Don't care */
536 : }
537 :
538 0 : return NL_OK;
539 : }
540 :
541 0 : static int nlw_set_callbacks(struct nlw_handle *nlp, void *data)
542 : {
543 0 : int ret = EIO;
544 :
545 : #ifdef HAVE_NL_SOCKET_MODIFY_CB
546 0 : ret = nl_socket_modify_cb(nlp, NL_CB_MSG_IN, NL_CB_CUSTOM, event_msg_recv,
547 : data);
548 : #else
549 : struct nl_cb *cb = nl_handle_get_cb(nlp);
550 : ret = nl_cb_set(cb, NL_CB_MSG_IN, NL_CB_CUSTOM, event_msg_recv, data);
551 : #endif
552 0 : if (ret != 0) {
553 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Unable to set validation callback\n");
554 0 : return ret;
555 : }
556 :
557 : #ifdef HAVE_NL_SOCKET_MODIFY_CB
558 0 : ret = nl_socket_modify_cb(nlp, NL_CB_VALID, NL_CB_CUSTOM, event_msg_ready,
559 : data);
560 : #else
561 : ret = nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, event_msg_ready, data);
562 : #endif
563 0 : if (ret != 0) {
564 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Unable to set receive callback\n");
565 0 : return ret;
566 : }
567 :
568 0 : return ret;
569 : }
570 :
571 0 : static void route_msg_debug_print(struct rtnl_route *route_obj)
572 : {
573 : int prefixlen;
574 : char buf[INET6_ADDRSTRLEN];
575 : struct nl_addr *nl;
576 :
577 0 : nl = rtnl_route_get_dst(route_obj);
578 0 : if (nl) {
579 0 : nladdr_to_string(nl, buf, INET6_ADDRSTRLEN);
580 0 : prefixlen = nl_addr_get_prefixlen(nl);
581 : } else {
582 0 : strncpy(buf, "unknown", INET6_ADDRSTRLEN);
583 0 : prefixlen = 0;
584 : }
585 :
586 0 : DEBUG(SSSDBG_TRACE_LIBS, "route idx %d flags %#X family %d addr %s/%d\n",
587 : rtnlw_route_get_oif(route_obj), rtnl_route_get_flags(route_obj),
588 : rtnl_route_get_family(route_obj), buf, prefixlen);
589 :
590 0 : }
591 :
592 : /*
593 : * If a bridge interface is configured it sets up a timer to requery for
594 : * multicast group memberships periodically. We need to discard such
595 : * messages.
596 : */
597 0 : static bool route_is_multicast(struct rtnl_route *route_obj)
598 : {
599 : struct nl_addr *nl;
600 0 : struct in6_addr *addr6 = NULL;
601 0 : struct in_addr *addr4 = NULL;
602 :
603 0 : nl = rtnl_route_get_dst(route_obj);
604 0 : if (!nl) {
605 0 : DEBUG(SSSDBG_MINOR_FAILURE, "A route with no destination?\n");
606 0 : return false;
607 : }
608 :
609 0 : if (nl_addr_get_family(nl) == AF_INET) {
610 0 : addr4 = nl_addr_get_binary_addr(nl);
611 0 : if (!addr4) {
612 0 : return false;
613 : }
614 :
615 0 : return IN_MULTICAST(ntohl(addr4->s_addr));
616 0 : } else if (nl_addr_get_family(nl) == AF_INET6) {
617 0 : addr6 = nl_addr_get_binary_addr(nl);
618 0 : if (!addr6) {
619 0 : return false;
620 : }
621 :
622 0 : return IN6_IS_ADDR_MULTICAST(addr6);
623 : }
624 :
625 0 : DEBUG(SSSDBG_MINOR_FAILURE, "Unknown route address family\n");
626 0 : return false;
627 : }
628 :
629 0 : static void route_msg_handler(struct nl_object *obj, void *arg)
630 : {
631 : struct rtnl_route *route_obj;
632 0 : struct netlink_ctx *ctx = (struct netlink_ctx *) arg;
633 :
634 0 : if (!nlw_is_route_object(obj)) return;
635 :
636 0 : route_obj = (struct rtnl_route *) obj;
637 :
638 0 : if (route_is_multicast(route_obj)) {
639 0 : DEBUG(SSSDBG_TRACE_INTERNAL,
640 : "Discarding multicast route message\n");
641 0 : return;
642 : }
643 :
644 0 : if (debug_level & SSSDBG_TRACE_LIBS) {
645 0 : route_msg_debug_print(route_obj);
646 : }
647 :
648 0 : ctx->change_cb(ctx->cb_data);
649 : }
650 :
651 0 : static void addr_msg_debug_print(struct rtnl_addr *addr_obj)
652 : {
653 : unsigned int flags;
654 : char str_flags[512];
655 : int ifidx;
656 : struct nl_addr *local_addr;
657 : char buf[INET6_ADDRSTRLEN];
658 :
659 0 : flags = rtnl_addr_get_flags(addr_obj);
660 0 : ifidx = rtnl_addr_get_ifindex(addr_obj);
661 0 : local_addr = rtnl_addr_get_local(addr_obj);
662 :
663 0 : rtnl_addr_flags2str(flags, str_flags, 512);
664 0 : nladdr_to_string(local_addr, buf, INET6_ADDRSTRLEN);
665 :
666 0 : DEBUG(SSSDBG_TRACE_LIBS, "netlink addr message: iface idx %u "
667 : "addr %s flags 0x%X (%s)\n", ifidx, buf, flags, str_flags);
668 0 : }
669 :
670 0 : static void addr_msg_handler(struct nl_object *obj, void *arg)
671 : {
672 0 : struct netlink_ctx *ctx = (struct netlink_ctx *) arg;
673 : struct rtnl_addr *addr_obj;
674 :
675 0 : if (!nlw_is_addr_object(obj)) return;
676 :
677 0 : addr_obj = (struct rtnl_addr *) obj;
678 0 : if (debug_level & SSSDBG_TRACE_LIBS) {
679 0 : addr_msg_debug_print(addr_obj);
680 : }
681 :
682 0 : ctx->change_cb(ctx->cb_data);
683 : }
684 :
685 0 : static void link_msg_handler(struct nl_object *obj, void *arg)
686 : {
687 0 : struct netlink_ctx *ctx = (struct netlink_ctx *) arg;
688 : struct rtnl_link *link_obj;
689 : unsigned int flags;
690 : char str_flags[512];
691 : int ifidx;
692 : const char *ifname;
693 :
694 0 : if (!nlw_is_link_object(obj)) return;
695 :
696 0 : link_obj = (struct rtnl_link *) obj;
697 0 : flags = rtnl_link_get_flags(link_obj);
698 0 : ifidx = rtnl_link_get_ifindex(link_obj);
699 :
700 0 : rtnl_link_flags2str(flags, str_flags, 512);
701 :
702 0 : ifname = rtnl_link_get_name(link_obj);
703 0 : DEBUG(SSSDBG_TRACE_LIBS, "netlink link message: iface idx %u (%s) "
704 : "flags 0x%X (%s)\n", ifidx, ifname, flags, str_flags);
705 :
706 : /* IFF_LOWER_UP is the indicator of carrier status */
707 0 : if ((flags & IFF_RUNNING) && (flags & IFF_LOWER_UP) &&
708 0 : !discard_iff_up(ifname)) {
709 0 : ctx->change_cb(ctx->cb_data);
710 : }
711 : }
712 :
713 0 : static void netlink_fd_handler(struct tevent_context *ev, struct tevent_fd *fde,
714 : uint16_t flags, void *data)
715 : {
716 0 : struct netlink_ctx *nlctx = talloc_get_type(data, struct netlink_ctx);
717 : int ret;
718 :
719 0 : if (!nlctx || !nlctx->nlp) {
720 0 : DEBUG(SSSDBG_CRIT_FAILURE,
721 : "Invalid netlink handle, this is most likely a bug!\n");
722 0 : return;
723 : }
724 :
725 0 : ret = nl_recvmsgs_default(nlctx->nlp);
726 0 : if (ret != EOK) {
727 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Error while reading from netlink fd\n");
728 0 : return;
729 : }
730 : }
731 :
732 : /*******************************************************************
733 : * Set up the netlink library
734 : *******************************************************************/
735 :
736 0 : int setup_netlink(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
737 : network_change_cb change_cb, void *cb_data,
738 : struct netlink_ctx **_nlctx)
739 : {
740 : struct netlink_ctx *nlctx;
741 : int ret;
742 : int nlfd;
743 : unsigned flags;
744 0 : int groups[] = { RTNLGRP_LINK, RTNLGRP_IPV4_ROUTE, RTNLGRP_IPV6_ROUTE,
745 : RTNLGRP_IPV4_IFADDR, RTNLGRP_IPV6_IFADDR, 0 };
746 :
747 0 : nlctx = talloc_zero(mem_ctx, struct netlink_ctx);
748 0 : if (!nlctx) return ENOMEM;
749 0 : talloc_set_destructor((TALLOC_CTX *) nlctx, netlink_ctx_destructor);
750 :
751 0 : nlctx->change_cb = change_cb;
752 0 : nlctx->cb_data = cb_data;
753 :
754 : /* allocate the libnl handle/socket and register the default filter set */
755 0 : nlctx->nlp = nlw_alloc();
756 0 : if (!nlctx->nlp) {
757 0 : DEBUG(SSSDBG_CRIT_FAILURE,
758 : "unable to allocate netlink handle: %s\n", nlw_geterror(ENOMEM));
759 0 : ret = ENOMEM;
760 0 : goto fail;
761 : }
762 :
763 : /* Register our custom message validation filter */
764 0 : ret = nlw_set_callbacks(nlctx->nlp, nlctx);
765 0 : if (ret != 0) {
766 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Unable to set callbacks\n");
767 0 : ret = EIO;
768 0 : goto fail;
769 : }
770 :
771 : /* Try to start talking to netlink */
772 0 : ret = nl_connect(nlctx->nlp, NETLINK_ROUTE);
773 0 : if (ret != 0) {
774 0 : DEBUG(SSSDBG_CRIT_FAILURE,
775 : "Unable to connect to netlink: %s\n", nlw_geterror(ret));
776 0 : ret = EIO;
777 0 : goto fail;
778 : }
779 :
780 0 : ret = nlw_enable_passcred(nlctx->nlp);
781 0 : if (ret != 0) {
782 0 : DEBUG(SSSDBG_CRIT_FAILURE,
783 : "Cannot enable credential passing: %s\n", nlw_geterror(ret));
784 0 : ret = EIO;
785 0 : goto fail;
786 : }
787 :
788 : /* Subscribe to the LINK group for internal carrier signals */
789 0 : ret = nlw_groups_subscribe(nlctx->nlp, groups);
790 0 : if (ret != 0) {
791 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Unable to subscribe to netlink monitor\n");
792 0 : ret = EIO;
793 0 : goto fail;
794 : }
795 :
796 0 : nlw_disable_seq_check(nlctx->nlp);
797 :
798 0 : nlfd = nl_socket_get_fd(nlctx->nlp);
799 0 : flags = fcntl(nlfd, F_GETFL, 0);
800 :
801 0 : errno = 0;
802 0 : ret = fcntl(nlfd, F_SETFL, flags | O_NONBLOCK);
803 0 : if (ret < 0) {
804 0 : ret = errno;
805 0 : DEBUG(SSSDBG_CRIT_FAILURE,
806 : "Cannot set the netlink fd to nonblocking\n");
807 0 : goto fail;
808 : }
809 :
810 0 : nlctx->tefd = tevent_add_fd(ev, nlctx, nlfd, TEVENT_FD_READ,
811 : netlink_fd_handler, nlctx);
812 0 : if (nlctx->tefd == NULL) {
813 0 : DEBUG(SSSDBG_CRIT_FAILURE, "tevent_add_fd() failed\n");
814 0 : ret = EIO;
815 0 : goto fail;
816 : }
817 :
818 0 : *_nlctx = nlctx;
819 0 : return EOK;
820 :
821 : fail:
822 0 : talloc_free(nlctx);
823 0 : return ret;
824 : }
825 :
826 : #else /* HAVE_LIBNL not defined */
827 : int setup_netlink(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
828 : network_change_cb change_cb, void *cb_data,
829 : struct netlink_ctx **_nlctx)
830 : {
831 : if (_nlctx) *_nlctx = NULL;
832 : return EOK;
833 : }
834 : #endif
|