1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
|
/* SPDX-License-Identifier: GPL-2.0 */
/*
* libgenl.c GENL library
*/
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <linux/genetlink.h>
#include "libgenl.h"
static int genl_parse_getfamily(struct nlmsghdr *nlh)
{
struct rtattr *tb[CTRL_ATTR_MAX + 1];
struct genlmsghdr *ghdr = NLMSG_DATA(nlh);
int len = nlh->nlmsg_len;
struct rtattr *attrs;
if (nlh->nlmsg_type != GENL_ID_CTRL) {
fprintf(stderr, "Not a controller message, nlmsg_len=%d "
"nlmsg_type=0x%x\n", nlh->nlmsg_len, nlh->nlmsg_type);
return -1;
}
len -= NLMSG_LENGTH(GENL_HDRLEN);
if (len < 0) {
fprintf(stderr, "wrong controller message len %d\n", len);
return -1;
}
if (ghdr->cmd != CTRL_CMD_NEWFAMILY) {
fprintf(stderr, "Unknown controller command %d\n", ghdr->cmd);
return -1;
}
attrs = (struct rtattr *) ((char *) ghdr + GENL_HDRLEN);
parse_rtattr(tb, CTRL_ATTR_MAX, attrs, len);
if (tb[CTRL_ATTR_FAMILY_ID] == NULL) {
fprintf(stderr, "Missing family id TLV\n");
return -1;
}
return rta_getattr_u16(tb[CTRL_ATTR_FAMILY_ID]);
}
int genl_resolve_family(struct rtnl_handle *grth, const char *family)
{
GENL_REQUEST(req, 1024, GENL_ID_CTRL, 0, 0, CTRL_CMD_GETFAMILY,
NLM_F_REQUEST);
struct nlmsghdr *answer;
int fnum;
addattr_l(&req.n, sizeof(req), CTRL_ATTR_FAMILY_NAME,
family, strlen(family) + 1);
if (rtnl_talk(grth, &req.n, &answer) < 0) {
fprintf(stderr, "Error talking to the kernel\n");
return -2;
}
fnum = genl_parse_getfamily(answer);
free(answer);
return fnum;
}
static int genl_parse_grps(struct rtattr *attr, const char *name, unsigned int *id)
{
const struct rtattr *pos;
rtattr_for_each_nested(pos, attr) {
struct rtattr *tb[CTRL_ATTR_MCAST_GRP_MAX + 1];
parse_rtattr_nested(tb, CTRL_ATTR_MCAST_GRP_MAX, pos);
if (tb[CTRL_ATTR_MCAST_GRP_NAME] && tb[CTRL_ATTR_MCAST_GRP_ID]) {
if (strcmp(name, rta_getattr_str(tb[CTRL_ATTR_MCAST_GRP_NAME])) == 0) {
*id = rta_getattr_u32(tb[CTRL_ATTR_MCAST_GRP_ID]);
return 0;
}
}
}
errno = ENOENT;
return -1;
}
int genl_add_mcast_grp(struct rtnl_handle *grth, __u16 fnum, const char *group)
{
GENL_REQUEST(req, 1024, GENL_ID_CTRL, 0, 0, CTRL_CMD_GETFAMILY,
NLM_F_REQUEST);
struct rtattr *tb[CTRL_ATTR_MAX + 1];
struct nlmsghdr *answer = NULL;
struct genlmsghdr *ghdr;
struct rtattr *attrs;
int len, ret = -1;
unsigned int id;
addattr16(&req.n, sizeof(req), CTRL_ATTR_FAMILY_ID, fnum);
if (rtnl_talk(grth, &req.n, &answer) < 0) {
fprintf(stderr, "Error talking to the kernel\n");
return -2;
}
ghdr = NLMSG_DATA(answer);
len = answer->nlmsg_len;
if (answer->nlmsg_type != GENL_ID_CTRL) {
errno = EINVAL;
goto err_free;
}
len -= NLMSG_LENGTH(GENL_HDRLEN);
if (len < 0) {
errno = EINVAL;
goto err_free;
}
attrs = (struct rtattr *) ((char *) ghdr + GENL_HDRLEN);
parse_rtattr(tb, CTRL_ATTR_MAX, attrs, len);
if (tb[CTRL_ATTR_MCAST_GROUPS] == NULL) {
errno = ENOENT;
fprintf(stderr, "Missing mcast groups TLV\n");
goto err_free;
}
if (genl_parse_grps(tb[CTRL_ATTR_MCAST_GROUPS], group, &id) < 0)
goto err_free;
ret = rtnl_add_nl_group(grth, id);
err_free:
free(answer);
return ret;
}
int genl_init_handle(struct rtnl_handle *grth, const char *family,
int *genl_family)
{
if (*genl_family >= 0)
return 0;
if (rtnl_open_byproto(grth, 0, NETLINK_GENERIC) < 0) {
fprintf(stderr, "Cannot open generic netlink socket\n");
return -1;
}
*genl_family = genl_resolve_family(grth, family);
if (*genl_family < 0)
return -1;
return 0;
}
|