xref: /linux/tools/testing/selftests/acct/netlink_helper.c (revision e5f92606156a6a823992294d214c285b49cd72e9)
1 // SPDX-License-Identifier: GPL-2.0
2 
3 #include <errno.h>
4 #include <stdint.h>
5 #include <string.h>
6 #include <sys/socket.h>
7 #include <sys/time.h>
8 #include <unistd.h>
9 #include <linux/genetlink.h>
10 
11 #include "netlink_helper.h"
12 
13 int netlink_open(void)
14 {
15 	struct timeval tv = { .tv_sec = ACCT_RCV_TIMEOUT_SEC };
16 	struct sockaddr_nl addr = {
17 		.nl_family = AF_NETLINK,
18 		.nl_pid = getpid(),
19 	};
20 	int fd;
21 
22 	fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC);
23 	if (fd < 0)
24 		return -errno;
25 
26 	if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) {
27 		int err = -errno;
28 
29 		close(fd);
30 		return err;
31 	}
32 
33 	if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
34 		int err = -errno;
35 
36 		close(fd);
37 		return err;
38 	}
39 
40 	return fd;
41 }
42 
43 int send_request(int fd, void *buf, size_t len)
44 {
45 	struct sockaddr_nl addr = {
46 		.nl_family = AF_NETLINK,
47 	};
48 
49 	if (sendto(fd, buf, len, 0, (struct sockaddr *)&addr, sizeof(addr)) < 0)
50 		return -errno;
51 
52 	return 0;
53 }
54 
55 /*
56  * Resolve the generic netlink family ID for @name.
57  * Returns the family ID (>= 0) on success, negative errno on failure.
58  */
59 int get_family_id(int fd, const char *name)
60 {
61 	struct {
62 		struct nlmsghdr nlh;
63 		struct genlmsghdr genl;
64 		char buf[256];
65 	} req = { 0 };
66 	char resp[8192];
67 	struct nlmsghdr *nlh;
68 	struct genlmsghdr *genl;
69 	struct nlattr *na;
70 	int len;
71 	int rem;
72 	int ret;
73 
74 	req.nlh.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
75 	req.nlh.nlmsg_type = GENL_ID_CTRL;
76 	req.nlh.nlmsg_flags = NLM_F_REQUEST;
77 	req.nlh.nlmsg_seq = 1;
78 	req.nlh.nlmsg_pid = getpid();
79 
80 	req.genl.cmd = CTRL_CMD_GETFAMILY;
81 	req.genl.version = 1;
82 
83 	na = (struct nlattr *)((char *)&req + NLMSG_ALIGN(req.nlh.nlmsg_len));
84 	na->nla_type = CTRL_ATTR_FAMILY_NAME;
85 	na->nla_len = NLA_HDRLEN + strlen(name) + 1;
86 	memcpy(nla_data(na), name, strlen(name) + 1);
87 	req.nlh.nlmsg_len = NLMSG_ALIGN(req.nlh.nlmsg_len) + NLA_ALIGN(na->nla_len);
88 
89 	ret = send_request(fd, &req, req.nlh.nlmsg_len);
90 	if (ret)
91 		return ret;
92 
93 	len = recv(fd, resp, sizeof(resp), 0);
94 	if (len < 0)
95 		return -errno;
96 
97 	for (nlh = (struct nlmsghdr *)resp; NLMSG_OK(nlh, len);
98 	     nlh = NLMSG_NEXT(nlh, len)) {
99 		if (nlh->nlmsg_type == NLMSG_ERROR) {
100 			struct nlmsgerr *err = NLMSG_DATA(nlh);
101 
102 			return err->error ? err->error : -ENOENT;
103 		}
104 
105 		genl = (struct genlmsghdr *)NLMSG_DATA(nlh);
106 		rem = nlh->nlmsg_len - NLMSG_HDRLEN - GENL_HDRLEN;
107 		na = (struct nlattr *)((char *)genl + GENL_HDRLEN);
108 		while (nla_ok(na, rem)) {
109 			if (na->nla_type == CTRL_ATTR_FAMILY_ID)
110 				return *(uint16_t *)nla_data(na);
111 			na = nla_next(na, &rem);
112 		}
113 	}
114 
115 	return -ENOENT;
116 }
117