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:
parent
d539883390
commit
b0ff3b67ef
3
Makefile
3
Makefile
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue