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