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