Branch data Line data Source code
1 : : /*
2 : : * Copyright (c) 2014 Zubin Mithra <zubin.mithra@gmail.com>
3 : : * Copyright (c) 2014-2016 Dmitry V. Levin <ldv@altlinux.org>
4 : : * Copyright (c) 2014-2017 The strace developers.
5 : : * All rights reserved.
6 : : *
7 : : * Redistribution and use in source and binary forms, with or without
8 : : * modification, are permitted provided that the following conditions
9 : : * are met:
10 : : * 1. Redistributions of source code must retain the above copyright
11 : : * notice, this list of conditions and the following disclaimer.
12 : : * 2. Redistributions in binary form must reproduce the above copyright
13 : : * notice, this list of conditions and the following disclaimer in the
14 : : * documentation and/or other materials provided with the distribution.
15 : : * 3. The name of the author may not be used to endorse or promote products
16 : : * derived from this software without specific prior written permission.
17 : : *
18 : : * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 : : * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 : : * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 : : * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 : : * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 : : * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 : : * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 : : * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 : : * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 : : * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 : : */
29 : :
30 : : #include "defs.h"
31 : : #include <netinet/in.h>
32 : : #include <sys/socket.h>
33 : : #include <arpa/inet.h>
34 : : #include "netlink.h"
35 : : #include <linux/sock_diag.h>
36 : : #include <linux/inet_diag.h>
37 : : #include <linux/unix_diag.h>
38 : : #include <linux/netlink_diag.h>
39 : : #include <linux/rtnetlink.h>
40 : : #if HAVE_LINUX_GENETLINK_H
41 : : #include <linux/genetlink.h>
42 : : #endif
43 : :
44 : : #include <sys/un.h>
45 : : #ifndef UNIX_PATH_MAX
46 : : # define UNIX_PATH_MAX sizeof(((struct sockaddr_un *) 0)->sun_path)
47 : : #endif
48 : :
49 : : typedef struct {
50 : : unsigned long inode;
51 : : char *details;
52 : : } cache_entry;
53 : :
54 : : #define CACHE_SIZE 1024U
55 : : static cache_entry cache[CACHE_SIZE];
56 : : #define CACHE_MASK (CACHE_SIZE - 1)
57 : :
58 : : static int
59 : : cache_inode_details(const unsigned long inode, char *const details)
60 : : {
61 : 48 : cache_entry *e = &cache[inode & CACHE_MASK];
62 : 48 : free(e->details);
63 : 48 : e->inode = inode;
64 : 48 : e->details = details;
65 : :
66 : : return 1;
67 : : }
68 : :
69 : : static const char *
70 : : get_sockaddr_by_inode_cached(const unsigned long inode)
71 : : {
72 : 488 : const cache_entry *const e = &cache[inode & CACHE_MASK];
73 [ + + ][ + - ]: 488 : return (e && inode == e->inode) ? e->details : NULL;
[ + - ][ + - ]
[ + + ]
74 : : }
75 : :
76 : : static bool
77 : 94 : print_sockaddr_by_inode_cached(const unsigned long inode)
78 : : {
79 : 94 : const char *const details = get_sockaddr_by_inode_cached(inode);
80 [ + + ]: 94 : if (details) {
81 : 48 : tprints(details);
82 : 48 : return true;
83 : : }
84 : : return false;
85 : : }
86 : :
87 : : static bool
88 : 78 : send_query(const int fd, void *req, size_t req_size)
89 : : {
90 : 78 : struct sockaddr_nl nladdr = {
91 : : .nl_family = AF_NETLINK
92 : : };
93 : 78 : struct iovec iov = {
94 : : .iov_base = req,
95 : : .iov_len = req_size
96 : : };
97 : 78 : const struct msghdr msg = {
98 : : .msg_name = &nladdr,
99 : : .msg_namelen = sizeof(nladdr),
100 : : .msg_iov = &iov,
101 : : .msg_iovlen = 1
102 : : };
103 : :
104 : : for (;;) {
105 [ - + ]: 78 : if (sendmsg(fd, &msg, 0) < 0) {
106 [ # # ]: 0 : if (errno == EINTR)
107 : 0 : continue;
108 : : return false;
109 : : }
110 : : return true;
111 : 0 : }
112 : : }
113 : :
114 : : static bool
115 : 16 : inet_send_query(const int fd, const int family, const int proto)
116 : : {
117 : : struct {
118 : : const struct nlmsghdr nlh;
119 : : const struct inet_diag_req_v2 idr;
120 : 16 : } req = {
121 : : .nlh = {
122 : : .nlmsg_len = sizeof(req),
123 : : .nlmsg_type = SOCK_DIAG_BY_FAMILY,
124 : : .nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST
125 : : },
126 : : .idr = {
127 : : .sdiag_family = family,
128 : : .sdiag_protocol = proto,
129 : : .idiag_states = -1
130 : : }
131 : : };
132 : 16 : return send_query(fd, &req, sizeof(req));
133 : : }
134 : :
135 : : static int
136 : 457 : inet_parse_response(const void *const data, const int data_len,
137 : : const unsigned long inode, void *opaque_data)
138 : : {
139 : 457 : const char *const proto_name = opaque_data;
140 : 457 : const struct inet_diag_msg *const diag_msg = data;
141 : : static const char zero_addr[sizeof(struct in6_addr)];
142 : : socklen_t addr_size, text_size;
143 : :
144 [ + - ]: 457 : if (data_len < (int) NLMSG_LENGTH(sizeof(*diag_msg)))
145 : : return -1;
146 [ + + ]: 457 : if (diag_msg->idiag_inode != inode)
147 : : return 0;
148 : :
149 [ - + - ]: 6 : switch (diag_msg->idiag_family) {
150 : : case AF_INET:
151 : : addr_size = sizeof(struct in_addr);
152 : : text_size = INET_ADDRSTRLEN;
153 : : break;
154 : : case AF_INET6:
155 : 0 : addr_size = sizeof(struct in6_addr);
156 : 0 : text_size = INET6_ADDRSTRLEN;
157 : 0 : break;
158 : : default:
159 : : return -1;
160 : : }
161 : :
162 : 6 : char src_buf[text_size];
163 : : char *details;
164 : :
165 [ + - ]: 6 : if (!inet_ntop(diag_msg->idiag_family, diag_msg->id.idiag_src,
166 : : src_buf, text_size))
167 : : return -1;
168 : :
169 [ + + ][ - + ]: 6 : if (diag_msg->id.idiag_dport ||
170 : 4 : memcmp(zero_addr, diag_msg->id.idiag_dst, addr_size)) {
171 : 4 : char dst_buf[text_size];
172 : :
173 [ + - ]: 4 : if (!inet_ntop(diag_msg->idiag_family, diag_msg->id.idiag_dst,
174 : : dst_buf, text_size))
175 : 0 : return -1;
176 : :
177 [ + - ]: 4 : if (asprintf(&details, "%s:[%s:%u->%s:%u]", proto_name,
178 [ - + ]: 4 : src_buf, ntohs(diag_msg->id.idiag_sport),
179 [ - + ]: 4 : dst_buf, ntohs(diag_msg->id.idiag_dport)) < 0)
180 : : return false;
181 : : } else {
182 [ + - ]: 2 : if (asprintf(&details, "%s:[%s:%u]", proto_name, src_buf,
183 [ - + ]: 2 : ntohs(diag_msg->id.idiag_sport)) < 0)
184 : : return false;
185 : : }
186 : :
187 : 6 : return cache_inode_details(inode, details);
188 : : }
189 : :
190 : : static bool
191 : 78 : receive_responses(const int fd, const unsigned long inode,
192 : : const unsigned long expected_msg_type,
193 : : int (*parser)(const void *, int,
194 : : unsigned long, void *),
195 : : void *opaque_data)
196 : : {
197 : : static union {
198 : : struct nlmsghdr hdr;
199 : : long buf[8192 / sizeof(long)];
200 : : } hdr_buf;
201 : :
202 : 78 : struct sockaddr_nl nladdr = {
203 : : .nl_family = AF_NETLINK
204 : : };
205 : 78 : struct iovec iov = {
206 : : .iov_base = hdr_buf.buf,
207 : : .iov_len = sizeof(hdr_buf.buf)
208 : : };
209 : 132 : int flags = 0;
210 : :
211 : : for (;;) {
212 : 132 : struct msghdr msg = {
213 : : .msg_name = &nladdr,
214 : : .msg_namelen = sizeof(nladdr),
215 : : .msg_iov = &iov,
216 : : .msg_iovlen = 1
217 : : };
218 : :
219 : 132 : ssize_t ret = recvmsg(fd, &msg, flags);
220 [ - + ]: 132 : if (ret < 0) {
221 [ # # ]: 0 : if (errno == EINTR)
222 : 0 : continue;
223 : 78 : return false;
224 : : }
225 : :
226 : 132 : const struct nlmsghdr *h = &hdr_buf.hdr;
227 [ + - ][ + - ]: 132 : if (!NLMSG_OK(h, ret))
[ + - ]
228 : : return false;
229 [ + + ][ + - ]: 6773 : for (; NLMSG_OK(h, ret); h = NLMSG_NEXT(h, ret)) {
[ + - ]
230 [ + + ]: 6719 : if (h->nlmsg_type != expected_msg_type)
231 : : return false;
232 : 6703 : const int rc = parser(NLMSG_DATA(h),
233 : : h->nlmsg_len, inode, opaque_data);
234 [ + + ]: 6703 : if (rc > 0)
235 : : return true;
236 [ + + ]: 6655 : if (rc < 0)
237 : : return false;
238 : : }
239 : 54 : flags = MSG_DONTWAIT;
240 : : }
241 : : }
242 : :
243 : : static bool
244 : 24 : unix_send_query(const int fd, const unsigned long inode)
245 : : {
246 : : struct {
247 : : const struct nlmsghdr nlh;
248 : : const struct unix_diag_req udr;
249 : 24 : } req = {
250 : : .nlh = {
251 : : .nlmsg_len = sizeof(req),
252 : : .nlmsg_type = SOCK_DIAG_BY_FAMILY,
253 : : .nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST
254 : : },
255 : : .udr = {
256 : : .sdiag_family = AF_UNIX,
257 : : .udiag_ino = inode,
258 : : .udiag_states = -1,
259 : : .udiag_show = UDIAG_SHOW_NAME | UDIAG_SHOW_PEER
260 : : }
261 : : };
262 : 24 : return send_query(fd, &req, sizeof(req));
263 : : }
264 : :
265 : : static int
266 : 5599 : unix_parse_response(const void *data, const int data_len,
267 : : const unsigned long inode, void *opaque_data)
268 : : {
269 : 5599 : const char *proto_name = opaque_data;
270 : 5599 : const struct unix_diag_msg *diag_msg = data;
271 : : struct rtattr *attr;
272 : 5599 : int rta_len = data_len - NLMSG_LENGTH(sizeof(*diag_msg));
273 : 5599 : uint32_t peer = 0;
274 : 5599 : size_t path_len = 0;
275 : : char path[UNIX_PATH_MAX + 1];
276 : :
277 [ + - ]: 5599 : if (rta_len < 0)
278 : : return -1;
279 [ + + ]: 5599 : if (diag_msg->udiag_ino != inode)
280 : : return 0;
281 [ + - ]: 24 : if (diag_msg->udiag_family != AF_UNIX)
282 : : return -1;
283 : :
284 [ + + ]: 64 : for (attr = (struct rtattr *) (diag_msg + 1);
285 [ + - ][ + - ]: 40 : RTA_OK(attr, rta_len);
286 : 40 : attr = RTA_NEXT(attr, rta_len)) {
287 [ + + + ]: 40 : switch (attr->rta_type) {
288 : : case UNIX_DIAG_NAME:
289 [ + - ]: 8 : if (!path_len) {
290 : 8 : path_len = RTA_PAYLOAD(attr);
291 [ - + ]: 8 : if (path_len > UNIX_PATH_MAX)
292 : 0 : path_len = UNIX_PATH_MAX;
293 : 8 : memcpy(path, RTA_DATA(attr), path_len);
294 : 8 : path[path_len] = '\0';
295 : : }
296 : : break;
297 : : case UNIX_DIAG_PEER:
298 [ + - ]: 8 : if (RTA_PAYLOAD(attr) >= 4)
299 : 8 : peer = *(uint32_t *) RTA_DATA(attr);
300 : : break;
301 : : }
302 : : }
303 : :
304 : : /*
305 : : * print obtained information in the following format:
306 : : * "UNIX:[" SELF_INODE [ "->" PEER_INODE ][ "," SOCKET_FILE ] "]"
307 : : */
308 [ + + ]: 24 : if (!peer && !path_len)
309 : : return -1;
310 : :
311 : : char peer_str[3 + sizeof(peer) * 3];
312 [ + + ]: 10 : if (peer)
313 : 8 : snprintf(peer_str, sizeof(peer_str), "->%u", peer);
314 : : else
315 : 2 : peer_str[0] = '\0';
316 : :
317 : : const char *path_str;
318 [ + + ]: 10 : if (path_len) {
319 : 8 : char *outstr = alloca(4 * path_len + 4);
320 : :
321 : 8 : outstr[0] = ',';
322 [ + + ]: 8 : if (path[0] == '\0') {
323 : 2 : outstr[1] = '@';
324 : 2 : string_quote(path + 1, outstr + 2,
325 : : path_len - 1, QUOTE_0_TERMINATED);
326 : : } else {
327 : 6 : string_quote(path, outstr + 1,
328 : : path_len, QUOTE_0_TERMINATED);
329 : : }
330 : 8 : path_str = outstr;
331 : : } else {
332 : : path_str = "";
333 : : }
334 : :
335 : : char *details;
336 [ + - ]: 10 : if (asprintf(&details, "%s:[%lu%s%s]", proto_name, inode,
337 : : peer_str, path_str) < 0)
338 : : return -1;
339 : :
340 : 10 : return cache_inode_details(inode, details);
341 : : }
342 : :
343 : : static bool
344 : 72 : netlink_send_query(const int fd, const unsigned long inode)
345 : : {
346 : : struct {
347 : : const struct nlmsghdr nlh;
348 : : const struct netlink_diag_req ndr;
349 : 36 : } req = {
350 : : .nlh = {
351 : : .nlmsg_len = sizeof(req),
352 : : .nlmsg_type = SOCK_DIAG_BY_FAMILY,
353 : : .nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST
354 : : },
355 : : .ndr = {
356 : : .sdiag_family = AF_NETLINK,
357 : : .sdiag_protocol = NDIAG_PROTO_ALL,
358 : : .ndiag_show = NDIAG_SHOW_MEMINFO
359 : : }
360 : : };
361 : 36 : return send_query(fd, &req, sizeof(req));
362 : : }
363 : :
364 : : static int
365 : 633 : netlink_parse_response(const void *data, const int data_len,
366 : : const unsigned long inode, void *opaque_data)
367 : : {
368 : 633 : const char *proto_name = opaque_data;
369 : 633 : const struct netlink_diag_msg *const diag_msg = data;
370 : : const char *netlink_proto;
371 : : char *details;
372 : :
373 [ + - ]: 633 : if (data_len < (int) NLMSG_LENGTH(sizeof(*diag_msg)))
374 : : return -1;
375 [ + + ]: 633 : if (diag_msg->ndiag_ino != inode)
376 : : return 0;
377 : :
378 [ + - ]: 32 : if (diag_msg->ndiag_family != AF_NETLINK)
379 : : return -1;
380 : :
381 : 32 : netlink_proto = xlookup(netlink_protocols,
382 : 32 : diag_msg->ndiag_protocol);
383 : :
384 [ + - ]: 32 : if (netlink_proto) {
385 : 32 : netlink_proto = STR_STRIP_PREFIX(netlink_proto, "NETLINK_");
386 [ + - ]: 32 : if (asprintf(&details, "%s:[%s:%u]", proto_name,
387 : : netlink_proto, diag_msg->ndiag_portid) < 0)
388 : : return -1;
389 : : } else {
390 [ # # ]: 0 : if (asprintf(&details, "%s:[%u]", proto_name,
391 : 0 : (unsigned) diag_msg->ndiag_protocol) < 0)
392 : : return -1;
393 : : }
394 : :
395 : 32 : return cache_inode_details(inode, details);
396 : : }
397 : :
398 : : static const char *
399 : 24 : unix_get(const int fd, const unsigned long inode)
400 : : {
401 : 24 : return unix_send_query(fd, inode)
402 [ + + ]: 24 : && receive_responses(fd, inode, SOCK_DIAG_BY_FAMILY,
403 : : unix_parse_response, (void *) "UNIX")
404 [ + - ]: 34 : ? get_sockaddr_by_inode_cached(inode) : NULL;
405 : : }
406 : :
407 : : static const char *
408 : 16 : inet_get(const int fd, const int family, const int protocol,
409 : : const unsigned long inode, const char *proto_name)
410 : : {
411 : 16 : return inet_send_query(fd, family, protocol)
412 [ + + ]: 16 : && receive_responses(fd, inode, SOCK_DIAG_BY_FAMILY,
413 : : inet_parse_response, (void *) proto_name)
414 [ + - ]: 22 : ? get_sockaddr_by_inode_cached(inode) : NULL;
415 : : }
416 : :
417 : : static const char *
418 : 16 : tcp_v4_get(const int fd, const unsigned long inode)
419 : : {
420 : 16 : return inet_get(fd, AF_INET, IPPROTO_TCP, inode, "TCP");
421 : : }
422 : :
423 : : static const char *
424 : 0 : udp_v4_get(const int fd, const unsigned long inode)
425 : : {
426 : 0 : return inet_get(fd, AF_INET, IPPROTO_UDP, inode, "UDP");
427 : : }
428 : :
429 : : static const char *
430 : 0 : tcp_v6_get(const int fd, const unsigned long inode)
431 : : {
432 : 0 : return inet_get(fd, AF_INET6, IPPROTO_TCP, inode, "TCPv6");
433 : : }
434 : :
435 : : static const char *
436 : 0 : udp_v6_get(const int fd, const unsigned long inode)
437 : : {
438 : 0 : return inet_get(fd, AF_INET6, IPPROTO_UDP, inode, "UDPv6");
439 : : }
440 : :
441 : : static const char *
442 : 36 : netlink_get(const int fd, const unsigned long inode)
443 : : {
444 : 36 : return netlink_send_query(fd, inode)
445 [ + + ]: 36 : && receive_responses(fd, inode, SOCK_DIAG_BY_FAMILY,
446 : : netlink_parse_response, (void *) "NETLINK")
447 [ + - ]: 68 : ? get_sockaddr_by_inode_cached(inode) : NULL;
448 : : }
449 : :
450 : : static const struct {
451 : : const char *const name;
452 : : const char * (*const get)(int, unsigned long);
453 : : } protocols[] = {
454 : : [SOCK_PROTO_UNIX] = { "UNIX", unix_get },
455 : : [SOCK_PROTO_TCP] = { "TCP", tcp_v4_get },
456 : : [SOCK_PROTO_UDP] = { "UDP", udp_v4_get },
457 : : [SOCK_PROTO_TCPv6] = { "TCPv6", tcp_v6_get },
458 : : [SOCK_PROTO_UDPv6] = { "UDPv6", udp_v6_get },
459 : : [SOCK_PROTO_NETLINK] = { "NETLINK", netlink_get }
460 : : };
461 : :
462 : : enum sock_proto
463 : 508 : get_proto_by_name(const char *const name)
464 : : {
465 : : unsigned int i;
466 [ + - ]: 2668 : for (i = (unsigned int) SOCK_PROTO_UNKNOWN + 1;
467 : 2160 : i < ARRAY_SIZE(protocols); ++i) {
468 [ + - ][ + + ]: 2668 : if (protocols[i].name && !strcmp(name, protocols[i].name))
469 : 508 : return (enum sock_proto) i;
470 : : }
471 : : return SOCK_PROTO_UNKNOWN;
472 : : }
473 : :
474 : : static const char *
475 : 76 : get_sockaddr_by_inode_uncached(const unsigned long inode,
476 : : const enum sock_proto proto)
477 : : {
478 [ + - ][ + - ]: 76 : if ((unsigned int) proto >= ARRAY_SIZE(protocols) ||
479 [ + - ]: 76 : (proto != SOCK_PROTO_UNKNOWN && !protocols[proto].get))
480 : : return NULL;
481 : :
482 : 76 : const int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_SOCK_DIAG);
483 [ + - ]: 76 : if (fd < 0)
484 : : return NULL;
485 : 76 : const char *details = NULL;
486 : :
487 [ - + ]: 76 : if (proto != SOCK_PROTO_UNKNOWN) {
488 : 76 : details = protocols[proto].get(fd, inode);
489 : : } else {
490 : : unsigned int i;
491 [ # # ]: 0 : for (i = (unsigned int) SOCK_PROTO_UNKNOWN + 1;
492 : 0 : i < ARRAY_SIZE(protocols); ++i) {
493 [ # # ]: 0 : if (!protocols[i].get)
494 : 0 : continue;
495 : 0 : details = protocols[i].get(fd, inode);
496 [ # # ]: 0 : if (details)
497 : : break;
498 : : }
499 : : }
500 : :
501 : 76 : close(fd);
502 : 76 : return details;
503 : : }
504 : :
505 : : static bool
506 : 46 : print_sockaddr_by_inode_uncached(const unsigned long inode,
507 : : const enum sock_proto proto)
508 : : {
509 : 46 : const char *details = get_sockaddr_by_inode_uncached(inode, proto);
510 : :
511 [ + + ]: 46 : if (details) {
512 : 18 : tprints(details);
513 : 18 : return true;
514 : : }
515 : :
516 [ + - ][ + - ]: 28 : if ((unsigned int) proto < ARRAY_SIZE(protocols) &&
517 : 28 : protocols[proto].name) {
518 : 28 : tprintf("%s:[%lu]", protocols[proto].name, inode);
519 : 28 : return true;
520 : : }
521 : :
522 : : return false;
523 : : }
524 : :
525 : : /* Given an inode number of a socket, return its protocol details. */
526 : : const char *
527 : 346 : get_sockaddr_by_inode(struct tcb *const tcp, const int fd,
528 : : const unsigned long inode)
529 : : {
530 : 346 : const char *details = get_sockaddr_by_inode_cached(inode);
531 [ + + ]: 376 : return details ? details :
532 : 30 : get_sockaddr_by_inode_uncached(inode, getfdproto(tcp, fd));
533 : : }
534 : :
535 : : /* Given an inode number of a socket, print out its protocol details. */
536 : : bool
537 : 94 : print_sockaddr_by_inode(struct tcb *const tcp, const int fd,
538 : : const unsigned long inode)
539 : : {
540 [ + + ]: 94 : return print_sockaddr_by_inode_cached(inode) ? true :
541 : 46 : print_sockaddr_by_inode_uncached(inode, getfdproto(tcp, fd));
542 : : }
543 : :
544 : : #ifdef HAVE_LINUX_GENETLINK_H
545 : : /*
546 : : * Managing the cache for decoding communications of Netlink GENERIC protocol
547 : : *
548 : : * As name shown Netlink GENERIC protocol is generic protocol. The
549 : : * numbers of msg types used in the protocol are not defined
550 : : * statically. Kernel defines them on demand. So the xlat converted
551 : : * from header files doesn't help for decoding the protocol. Following
552 : : * codes are building xlat(dyxlat) at runtime.
553 : : */
554 : : static bool
555 : 2 : genl_send_dump_families(const int fd)
556 : : {
557 : : struct {
558 : : const struct nlmsghdr nlh;
559 : : struct genlmsghdr gnlh;
560 : 2 : } req = {
561 : : .nlh = {
562 : : .nlmsg_len = sizeof(req),
563 : : .nlmsg_type = GENL_ID_CTRL,
564 : : .nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
565 : : },
566 : : .gnlh = {
567 : : .cmd = CTRL_CMD_GETFAMILY,
568 : : }
569 : : };
570 : 2 : return send_query(fd, &req, sizeof(req));
571 : : }
572 : :
573 : : static int
574 : 14 : genl_parse_families_response(const void *const data,
575 : : const int data_len, const unsigned long inode,
576 : : void *opaque_data)
577 : : {
578 : 14 : struct dyxlat *const dyxlat = opaque_data;
579 : 14 : const struct genlmsghdr *const gnlh = data;
580 : : struct rtattr *attr;
581 : 14 : int rta_len = data_len - NLMSG_LENGTH(sizeof(*gnlh));
582 : :
583 : 14 : char *name = NULL;
584 : 14 : unsigned int name_len = 0;
585 : 14 : uint16_t *id = NULL;
586 : :
587 [ + - ]: 14 : if (rta_len < 0)
588 : : return -1;
589 [ + - ]: 14 : if (gnlh->cmd != CTRL_CMD_NEWFAMILY)
590 : : return -1;
591 [ + - ]: 14 : if (gnlh->version != 2)
592 : : return -1;
593 : :
594 [ + + ]: 102 : for (attr = (struct rtattr *) (gnlh + 1);
595 [ + - ][ + - ]: 88 : RTA_OK(attr, rta_len);
596 : 88 : attr = RTA_NEXT(attr, rta_len)) {
597 [ + + + ]: 88 : switch (attr->rta_type) {
598 : : case CTRL_ATTR_FAMILY_NAME:
599 [ + - ]: 14 : if (!name) {
600 : 14 : name = RTA_DATA(attr);
601 : 14 : name_len = RTA_PAYLOAD(attr);
602 : : }
603 : : break;
604 : : case CTRL_ATTR_FAMILY_ID:
605 [ + - ][ + - ]: 14 : if (!id && RTA_PAYLOAD(attr) == sizeof(*id))
606 : 14 : id = RTA_DATA(attr);
607 : : break;
608 : : }
609 : :
610 [ + + ]: 88 : if (name && id) {
611 : 14 : dyxlat_add_pair(dyxlat, *id, name, name_len);
612 : 14 : name = NULL;
613 : 14 : id = NULL;
614 : : }
615 : : }
616 : :
617 : : return 0;
618 : : }
619 : :
620 : : const struct xlat *
621 : 2 : genl_families_xlat(void)
622 : : {
623 : : static struct dyxlat *dyxlat;
624 : :
625 [ + - ]: 2 : if (!dyxlat) {
626 : 2 : dyxlat = dyxlat_alloc(32);
627 : :
628 : 2 : int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC);
629 [ + - ]: 2 : if (fd < 0)
630 : : goto out;
631 : :
632 [ + - ]: 2 : if (genl_send_dump_families(fd))
633 : 2 : receive_responses(fd, 0, GENL_ID_CTRL,
634 : : genl_parse_families_response, dyxlat);
635 : 2 : close(fd);
636 : : }
637 : :
638 : : out:
639 : 2 : return dyxlat_get(dyxlat);
640 : : }
641 : :
642 : : #else /* !HAVE_LINUX_GENETLINK_H */
643 : :
644 : : const struct xlat *
645 : : genl_families_xlat(void)
646 : : {
647 : : return NULL;
648 : : }
649 : : #endif
|