provide simple genl library

So we can get rid of genl-family-get.c to look up for the genetlink
family ID.
This commit is contained in:
Pablo Neira Ayuso 2014-01-21 14:12:25 +01:00
parent d539883390
commit b0ff3b67ef
5 changed files with 146 additions and 274 deletions

View File

@ -6,8 +6,7 @@ obj-m += gtp.o
default:
$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
$(CC) -lmnl gtp-link-add.c -o gtp-link-add
$(CC) -lmnl genl-family-get.c -o genl-family-get
$(CC) -lmnl gtp-tunnel-add.c -o gtp-tunnel-add
$(CC) -lmnl gtp-tunnel-add.c genl.c -o gtp-tunnel-add
clean:
rm -rf *.o *.mod.* modules.order Module.symvers *.ko .tmp_versions .*.cmd
rm -f genl-family-get gtp-link-add gtp-tunnel-add

View File

@ -1,240 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <libmnl/libmnl.h>
#include <linux/genetlink.h>
static int parse_mc_grps_cb(const struct nlattr *attr, void *data)
{
const struct nlattr **tb = data;
int type = mnl_attr_get_type(attr);
/* skip unsupported attribute in user-space */
if (mnl_attr_type_valid(attr, CTRL_ATTR_MCAST_GRP_MAX) < 0)
return MNL_CB_OK;
switch(type) {
case CTRL_ATTR_MCAST_GRP_ID:
if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
perror("mnl_attr_validate");
return MNL_CB_ERROR;
}
break;
case CTRL_ATTR_MCAST_GRP_NAME:
if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) {
perror("mnl_attr_validate");
return MNL_CB_ERROR;
}
break;
}
tb[type] = attr;
return MNL_CB_OK;
}
static void parse_genl_mc_grps(struct nlattr *nested)
{
struct nlattr *pos;
mnl_attr_for_each_nested(pos, nested) {
struct nlattr *tb[CTRL_ATTR_MCAST_GRP_MAX+1] = {};
mnl_attr_parse_nested(pos, parse_mc_grps_cb, tb);
if (tb[CTRL_ATTR_MCAST_GRP_ID]) {
printf("id-0x%x ",
mnl_attr_get_u32(tb[CTRL_ATTR_MCAST_GRP_ID]));
}
if (tb[CTRL_ATTR_MCAST_GRP_NAME]) {
printf("name: %s ",
mnl_attr_get_str(tb[CTRL_ATTR_MCAST_GRP_NAME]));
}
printf("\n");
}
}
static int parse_family_ops_cb(const struct nlattr *attr, void *data)
{
const struct nlattr **tb = data;
int type = mnl_attr_get_type(attr);
if (mnl_attr_type_valid(attr, CTRL_ATTR_OP_MAX) < 0)
return MNL_CB_OK;
switch(type) {
case CTRL_ATTR_OP_ID:
if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
perror("mnl_attr_validate");
return MNL_CB_ERROR;
}
break;
case CTRL_ATTR_OP_MAX:
break;
default:
return MNL_CB_OK;
}
tb[type] = attr;
return MNL_CB_OK;
}
static void parse_genl_family_ops(struct nlattr *nested)
{
struct nlattr *pos;
mnl_attr_for_each_nested(pos, nested) {
struct nlattr *tb[CTRL_ATTR_OP_MAX+1] = {};
mnl_attr_parse_nested(pos, parse_family_ops_cb, tb);
if (tb[CTRL_ATTR_OP_ID]) {
printf("id-0x%x ",
mnl_attr_get_u32(tb[CTRL_ATTR_OP_ID]));
}
if (tb[CTRL_ATTR_OP_MAX]) {
printf("flags ");
}
printf("\n");
}
}
static int data_attr_cb(const struct nlattr *attr, void *data)
{
const struct nlattr **tb = data;
int type = mnl_attr_get_type(attr);
if (mnl_attr_type_valid(attr, CTRL_ATTR_MAX) < 0)
return MNL_CB_OK;
switch(type) {
case CTRL_ATTR_FAMILY_NAME:
if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) {
perror("mnl_attr_validate");
return MNL_CB_ERROR;
}
break;
case CTRL_ATTR_FAMILY_ID:
if (mnl_attr_validate(attr, MNL_TYPE_U16) < 0) {
perror("mnl_attr_validate");
return MNL_CB_ERROR;
}
break;
case CTRL_ATTR_VERSION:
case CTRL_ATTR_HDRSIZE:
case CTRL_ATTR_MAXATTR:
if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
perror("mnl_attr_validate");
return MNL_CB_ERROR;
}
break;
case CTRL_ATTR_OPS:
case CTRL_ATTR_MCAST_GROUPS:
if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0) {
perror("mnl_attr_validate");
return MNL_CB_ERROR;
}
break;
}
tb[type] = attr;
return MNL_CB_OK;
}
static int data_cb(const struct nlmsghdr *nlh, void *data)
{
struct nlattr *tb[CTRL_ATTR_MAX+1] = {};
struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
mnl_attr_parse(nlh, sizeof(*genl), data_attr_cb, tb);
if (tb[CTRL_ATTR_FAMILY_NAME]) {
printf("name=%s\t",
mnl_attr_get_str(tb[CTRL_ATTR_FAMILY_NAME]));
}
if (tb[CTRL_ATTR_FAMILY_ID]) {
printf("id=%u\t",
mnl_attr_get_u16(tb[CTRL_ATTR_FAMILY_ID]));
}
if (tb[CTRL_ATTR_VERSION]) {
printf("version=%u\t",
mnl_attr_get_u32(tb[CTRL_ATTR_VERSION]));
}
if (tb[CTRL_ATTR_HDRSIZE]) {
printf("hdrsize=%u\t",
mnl_attr_get_u32(tb[CTRL_ATTR_HDRSIZE]));
}
if (tb[CTRL_ATTR_MAXATTR]) {
printf("maxattr=%u\t",
mnl_attr_get_u32(tb[CTRL_ATTR_MAXATTR]));
}
printf("\n");
if (tb[CTRL_ATTR_OPS]) {
printf("ops:\n");
parse_genl_family_ops(tb[CTRL_ATTR_OPS]);
}
if (tb[CTRL_ATTR_MCAST_GROUPS]) {
printf("grps:\n");
parse_genl_mc_grps(tb[CTRL_ATTR_MCAST_GROUPS]);
}
printf("\n");
return MNL_CB_OK;
}
int main(int argc, char *argv[])
{
struct mnl_socket *nl;
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nlmsghdr *nlh;
struct genlmsghdr *genl;
int ret;
unsigned int seq, portid;
if (argc > 2) {
printf("%s [family name]\n", argv[0]);
exit(EXIT_FAILURE);
}
nlh = mnl_nlmsg_put_header(buf);
nlh->nlmsg_type = GENL_ID_CTRL;
nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
nlh->nlmsg_seq = seq = time(NULL);
genl = mnl_nlmsg_put_extra_header(nlh, sizeof(struct genlmsghdr));
genl->cmd = CTRL_CMD_GETFAMILY;
genl->version = 1;
mnl_attr_put_u32(nlh, CTRL_ATTR_FAMILY_ID, GENL_ID_CTRL);
if (argc >= 2)
mnl_attr_put_strz(nlh, CTRL_ATTR_FAMILY_NAME, argv[1]);
else
nlh->nlmsg_flags |= NLM_F_DUMP;
nl = mnl_socket_open(NETLINK_GENERIC);
if (nl == NULL) {
perror("mnl_socket_open");
exit(EXIT_FAILURE);
}
if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
perror("mnl_socket_bind");
exit(EXIT_FAILURE);
}
portid = mnl_socket_get_portid(nl);
if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
perror("mnl_socket_send");
exit(EXIT_FAILURE);
}
ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
while (ret > 0) {
ret = mnl_cb_run(buf, ret, seq, portid, data_cb, NULL);
if (ret <= 0)
break;
ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
}
if (ret == -1) {
perror("error");
exit(EXIT_FAILURE);
}
mnl_socket_close(nl);
return 0;
}

123
genl.c Normal file
View File

@ -0,0 +1,123 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <libmnl/libmnl.h>
#include <linux/genetlink.h>
static int genl_ctrl_validate_cb(const struct nlattr *attr, void *data)
{
const struct nlattr **tb = data;
int type = mnl_attr_get_type(attr);
if (mnl_attr_type_valid(attr, CTRL_ATTR_MAX) < 0)
return MNL_CB_OK;
switch(type) {
case CTRL_ATTR_FAMILY_ID:
if (mnl_attr_validate(attr, MNL_TYPE_U16) < 0) {
perror("mnl_attr_validate");
return MNL_CB_ERROR;
}
break;
}
tb[type] = attr;
return MNL_CB_OK;
}
static int genl_ctrl_cb(const struct nlmsghdr *nlh, void *data)
{
struct nlattr *tb[CTRL_ATTR_MAX + 1] = {};
struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
int32_t *genl_id = data;
mnl_attr_parse(nlh, sizeof(*genl), genl_ctrl_validate_cb, tb);
if (tb[CTRL_ATTR_FAMILY_ID])
*genl_id = mnl_attr_get_u16(tb[CTRL_ATTR_FAMILY_ID]);
else
*genl_id = -1;
return MNL_CB_OK;
}
struct mnl_socket *genl_socket_open(void)
{
struct mnl_socket *nl;
nl = mnl_socket_open(NETLINK_GENERIC);
if (nl == NULL) {
perror("mnl_socket_open");
return NULL;
}
if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
perror("mnl_socket_bind");
return NULL;
}
return nl;
}
int genl_socket_talk(struct mnl_socket *nl, struct nlmsghdr *nlh, uint32_t seq,
int (*cb)(const struct nlmsghdr *nlh, void *data),
void *data)
{
char buf[MNL_SOCKET_BUFFER_SIZE];
int ret;
if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
perror("mnl_socket_send");
return -1;
}
ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
while (ret > 0) {
ret = mnl_cb_run(buf, ret, seq, mnl_socket_get_portid(nl),
cb, data);
if (ret <= 0)
break;
ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
}
if (ret == -1) {
perror("error");
return -1;
}
return 0;
}
static struct nlmsghdr *
genl_nlmsg_build_lookup(char *buf, const char *subsys_name)
{
struct nlmsghdr *nlh;
struct genlmsghdr *genl;
nlh = mnl_nlmsg_put_header(buf);
nlh->nlmsg_type = GENL_ID_CTRL;
nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
nlh->nlmsg_seq = time(NULL);
genl = mnl_nlmsg_put_extra_header(nlh, sizeof(struct genlmsghdr));
genl->cmd = CTRL_CMD_GETFAMILY;
genl->version = 1;
mnl_attr_put_u32(nlh, CTRL_ATTR_FAMILY_ID, GENL_ID_CTRL);
mnl_attr_put_strz(nlh, CTRL_ATTR_FAMILY_NAME, subsys_name);
return nlh;
}
int genl_lookup_family(struct mnl_socket *nl, const char *family)
{
int32_t genl_id;
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nlmsghdr *nlh = genl_nlmsg_build_lookup(buf, "gtp");
int err;
err = genl_socket_talk(nl, nlh, nlh->nlmsg_seq, genl_ctrl_cb, &genl_id);
if (err < 0)
return -1;
return genl_id;
}

12
genl.h Normal file
View File

@ -0,0 +1,12 @@
#ifndef _OSMO_SGSN_GENL_H_
#define _OSMO_SGSN_GENL_H_
#include <libmnl/libmnl.h>
struct mnl_socket *genl_socket_open(void);
int genl_socket_talk(struct mnl_socket *nl, struct nlmsghdr *nlh,
int (*cb)(const struct nlmsghdr *nlh, void *data),
void *data);
int genl_lookup_family(struct mnl_socket *nl, const char *family);
#endif

View File

@ -7,6 +7,7 @@
#include <linux/genetlink.h>
#include "gtp.h"
#include "genl.h"
static uint32_t seq;
@ -33,30 +34,6 @@ static struct nlmsghdr *build_msg(int genl_type, char *buf, int i, uint32_t ifid
return nlh;
}
static int my_mnl_talk(struct mnl_socket *nl, struct nlmsghdr *nlh,
uint32_t portid)
{
char buf[MNL_SOCKET_BUFFER_SIZE];
int ret;
if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
perror("mnl_socket_send");
exit(EXIT_FAILURE);
}
ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
while (ret > 0) {
ret = mnl_cb_run(buf, ret, seq, portid, NULL, NULL);
if (ret <= 0)
break;
ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
}
if (ret == -1) {
perror("error");
exit(EXIT_FAILURE);
}
}
int main(int argc, char *argv[])
{
struct mnl_socket *nl;
@ -64,31 +41,32 @@ int main(int argc, char *argv[])
struct nlmsghdr *nlh;
struct genlmsghdr *genl;
unsigned int portid;
int32_t genl_id;
int i;
if (argc != 3) {
printf("%s <GTP genetlink family id> <gtp device>\n", argv[0]);
if (argc != 2) {
printf("%s <gtp device>\n", argv[0]);
exit(EXIT_FAILURE);
}
nl = mnl_socket_open(NETLINK_GENERIC);
nl = genl_socket_open();
if (nl == NULL) {
perror("mnl_socket_open");
exit(EXIT_FAILURE);
}
if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
perror("mnl_socket_bind");
genl_id = genl_lookup_family(nl, "gtp");
if (genl_id < 0) {
printf("not found gtp genl family\n");
exit(EXIT_FAILURE);
}
portid = mnl_socket_get_portid(nl);
printf("adding 1000000 tunnels\n");
for (i = 0; i < 1000000; i++) {
nlh = build_msg(atoi(argv[1]), buf, i, if_nametoindex(argv[2]));
nlh = build_msg(genl_id, buf, i, if_nametoindex(argv[1]));
if (my_mnl_talk(nl, nlh, portid) < 0)
if (genl_socket_talk(nl, nlh, NULL, NULL) < 0)
break;
}