forked from a-zaki/genl_ex
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
An example on generic netlink communications between the kernel and user-space processes.
- Loading branch information
0 parents
commit 012008d
Showing
4 changed files
with
461 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
|
||
PKG_CONFIG=$(shell dirname ${CROSS_COMPILE})/pkg-config | ||
NL_LIBNAME=libnl-genl-3.0 | ||
|
||
NL_LIB_FLAGS=$(shell $(PKG_CONFIG) --cflags $(NL_LIBNAME)) | ||
NL_LIBS_L=$(shell $(PKG_CONFIG) --libs-only-L $(NL_LIBNAME)) | ||
NL_LIBS_l=$(shell $(PKG_CONFIG) --libs-only-l $(NL_LIBNAME)) | ||
|
||
CC=${CROSS_COMPILE}gcc | ||
|
||
.PHONY:all | ||
all: genl_ex genl_ex.ko | ||
|
||
genl_ex: genl_ex.h genl_ex.c | ||
$(CC) -Wextra -Wall -Werror -Wno-unused-parameter $(NL_LIB_FLAGS) $(NL_LIBS_L) $(NL_LIBS_l) genl_ex.c -o genl_ex | ||
|
||
genl_ex.ko: | ||
$(MAKE) -C $(shell pwd)/kernel KERNEL_DIR=$(KERNEL_DIR) | ||
|
||
.PHONY: install | ||
install: | ||
$(MAKE) -C $(shell pwd)/kernel install | ||
|
||
.PHONY: clean | ||
clean: | ||
rm -f *.o genl_ex | ||
$(MAKE) -C $(shell pwd)/kernel KERNEL_DIR=$(KERNEL_DIR) clean | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,239 @@ | ||
#include <stdio.h> | ||
#include <stdlib.h> | ||
#include <unistd.h> | ||
#include <ctype.h> | ||
#include <errno.h> | ||
#include <string.h> | ||
#include <sys/socket.h> | ||
#include <sys/time.h> | ||
#include <netlink/msg.h> | ||
#include <netlink/attr.h> | ||
|
||
#include "genl_ex.h" | ||
|
||
static char message[GENL_TEST_ATTR_MSG_MAX]; | ||
static int send_to_kernel; | ||
static unsigned int mcgroups; /* Mask of groups */ | ||
|
||
static void usage(char* name) | ||
{ | ||
printf("Usage: %s\n" | ||
" -h : this help message\n" | ||
" -l : listen on one or more groups, comma separated\n" | ||
" -m : the message to send, required with -s\n" | ||
" -s : send to kernel\n" | ||
"NOTE: specify either l or s, not both\n", | ||
name); | ||
} | ||
|
||
static void add_group(char* group) | ||
{ | ||
unsigned int grp = strtoul(group, NULL, 10); | ||
|
||
if (grp > GENL_TEST_MCGRP_MAX-1) { | ||
fprintf(stderr, "Invalid group number %u. Values allowed 0:%u\n", | ||
grp, GENL_TEST_MCGRP_MAX-1); | ||
exit(EXIT_FAILURE); | ||
} | ||
|
||
mcgroups |= 1 << (grp); | ||
} | ||
|
||
static void parse_cmd_line(int argc, char** argv) | ||
{ | ||
char* opt_val; | ||
|
||
while (1) { | ||
int opt = getopt(argc, argv, "hl:m:s"); | ||
|
||
if (opt == EOF) | ||
break; | ||
|
||
switch (opt) { | ||
case 'h': | ||
usage(argv[0]); | ||
exit(EXIT_SUCCESS); | ||
|
||
case 'l': | ||
opt_val = strtok(optarg, ","); | ||
add_group(opt_val); | ||
while ((opt_val = strtok(NULL, ","))) | ||
add_group(opt_val); | ||
|
||
break; | ||
|
||
case 'm': | ||
strncpy(message, optarg, GENL_TEST_ATTR_MSG_MAX); | ||
message[GENL_TEST_ATTR_MSG_MAX - 1] = '\0'; | ||
break; | ||
|
||
case 's': | ||
send_to_kernel = 1; | ||
break; | ||
|
||
default: | ||
fprintf(stderr, "Unkown option %c !!\n", opt); | ||
exit(EXIT_FAILURE); | ||
} | ||
|
||
} | ||
|
||
/* sanity checks */ | ||
if (send_to_kernel && mcgroups) { | ||
fprintf(stderr, "I can either receive or send messages.\n\n"); | ||
usage(argv[0]); | ||
exit(EXIT_FAILURE); | ||
} | ||
if (!send_to_kernel && !mcgroups) { | ||
fprintf(stderr, "Nothing to do!\n"); | ||
usage(argv[0]); | ||
exit(EXIT_SUCCESS); | ||
} | ||
if (send_to_kernel && message[0]=='\0') { | ||
fprintf(stderr, "What is the message you want to send?\n"); | ||
usage(argv[0]); | ||
exit(EXIT_FAILURE); | ||
} | ||
} | ||
static int send_msg_to_kernel(struct nl_sock *sock) | ||
{ | ||
struct nl_msg* msg; | ||
int family_id, err = 0; | ||
|
||
family_id = genl_ctrl_resolve(sock, GENL_TEST_FAMILY_NAME); | ||
if(family_id < 0){ | ||
fprintf(stderr, "Unable to resolve family name!\n"); | ||
exit(EXIT_FAILURE); | ||
} | ||
|
||
msg = nlmsg_alloc(); | ||
if (!msg) { | ||
fprintf(stderr, "failed to allocate netlink message\n"); | ||
exit(EXIT_FAILURE); | ||
} | ||
|
||
if(!genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, family_id, 0, | ||
NLM_F_REQUEST, GENL_TEST_C_MSG, 0)) { | ||
fprintf(stderr, "failed to put nl hdr!\n"); | ||
err = -ENOMEM; | ||
goto out; | ||
} | ||
|
||
err = nla_put_string(msg, GENL_TEST_ATTR_MSG, message); | ||
if (err) { | ||
fprintf(stderr, "failed to put nl string!\n"); | ||
goto out; | ||
} | ||
|
||
err = nl_send_auto(sock, msg); | ||
if (err < 0) { | ||
fprintf(stderr, "failed to send nl message!\n"); | ||
} | ||
|
||
out: | ||
nlmsg_free(msg); | ||
return err; | ||
} | ||
|
||
static int skip_seq_check(struct nl_msg *msg, void *arg) | ||
{ | ||
return NL_OK; | ||
} | ||
|
||
static int print_rx_msg(struct nl_msg *msg, void* arg) | ||
{ | ||
struct nlattr *attr[GENL_TEST_ATTR_MAX+1]; | ||
|
||
genlmsg_parse(nlmsg_hdr(msg), 0, attr, | ||
GENL_TEST_ATTR_MAX, genl_test_policy); | ||
|
||
if (!attr[GENL_TEST_ATTR_MSG]) { | ||
fprintf(stdout, "Kernel sent empty message!!\n"); | ||
return NL_OK; | ||
} | ||
|
||
fprintf(stdout, "Kernel says: %s \n", | ||
nla_get_string(attr[GENL_TEST_ATTR_MSG])); | ||
|
||
return NL_OK; | ||
} | ||
|
||
static void prep_nl_sock(struct nl_sock** nlsock) | ||
{ | ||
int family_id, grp_id; | ||
unsigned int bit = 0; | ||
|
||
*nlsock = nl_socket_alloc(); | ||
if(!*nlsock) { | ||
fprintf(stderr, "Unable to alloc nl socket!\n"); | ||
exit(EXIT_FAILURE); | ||
} | ||
|
||
/* disable seq checks on multicast sockets */ | ||
nl_socket_disable_seq_check(*nlsock); | ||
nl_socket_disable_auto_ack(*nlsock); | ||
|
||
/* connect to genl */ | ||
if (genl_connect(*nlsock)) { | ||
fprintf(stderr, "Unable to connect to genl!\n"); | ||
exit(EXIT_FAILURE); | ||
} | ||
|
||
/* resolve the generic nl family id*/ | ||
family_id = genl_ctrl_resolve(*nlsock, GENL_TEST_FAMILY_NAME); | ||
if(family_id < 0){ | ||
fprintf(stderr, "Unable to resolve family name!\n"); | ||
exit(EXIT_FAILURE); | ||
} | ||
|
||
if (!mcgroups) | ||
return; | ||
|
||
while (bit < sizeof(unsigned int)) { | ||
if (!(mcgroups & (1 << bit))) | ||
goto next; | ||
|
||
grp_id = genl_ctrl_resolve_grp(*nlsock, GENL_TEST_FAMILY_NAME, | ||
genl_test_mcgrp_names[bit]); | ||
|
||
if (grp_id < 0) { | ||
fprintf(stderr, "Unable to resolve group name for %u!\n", | ||
(1 << bit)); | ||
exit(EXIT_FAILURE); | ||
} | ||
if (nl_socket_add_membership(*nlsock, grp_id)) { | ||
fprintf(stderr, "Unable to join group %u!\n", | ||
(1 << bit)); | ||
exit(EXIT_FAILURE); | ||
} | ||
next: | ||
bit++; | ||
} | ||
} | ||
|
||
int main(int argc, char** argv) | ||
{ | ||
struct nl_sock* nlsock = NULL; | ||
struct nl_cb *cb = NULL; | ||
int ret; | ||
|
||
parse_cmd_line(argc, argv); | ||
|
||
prep_nl_sock(&nlsock); | ||
|
||
if (send_to_kernel) { | ||
ret = send_msg_to_kernel(nlsock); | ||
exit(EXIT_SUCCESS); | ||
} | ||
|
||
/* prep the cb */ | ||
cb = nl_cb_alloc(NL_CB_DEFAULT); | ||
nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, skip_seq_check, NULL); | ||
nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, print_rx_msg, NULL); | ||
do { | ||
ret = nl_recvmsgs(nlsock, cb); | ||
} while (!ret); | ||
|
||
nl_cb_put(cb); | ||
return 0; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
|
||
#ifndef GENL_TEST_H | ||
#define GENL_TEST_H | ||
|
||
#include <linux/netlink.h> | ||
|
||
#ifndef __KERNEL__ | ||
#include <netlink/genl/genl.h> | ||
#include <netlink/genl/family.h> | ||
#include <netlink/genl/ctrl.h> | ||
#endif | ||
|
||
#define GENL_TEST_FAMILY_NAME "genl_test" | ||
#define GENL_TEST_MCGRP0_NAME "genl_mcgrp0" | ||
#define GENL_TEST_MCGRP1_NAME "genl_mcgrp1" | ||
#define GENL_TEST_MCGRP2_NAME "genl_mcgrp2" | ||
|
||
#define GENL_TEST_ATTR_MSG_MAX 256 | ||
#define GENL_TEST_HELLO_INTERVAL 5000 | ||
|
||
enum { | ||
GENL_TEST_C_UNSPEC, /* Must NOT use element 0 */ | ||
GENL_TEST_C_MSG, | ||
}; | ||
|
||
enum genl_test_multicast_groups { | ||
GENL_TEST_MCGRP0, | ||
GENL_TEST_MCGRP1, | ||
GENL_TEST_MCGRP2, | ||
}; | ||
#define GENL_TEST_MCGRP_MAX 3 | ||
|
||
static char* genl_test_mcgrp_names[GENL_TEST_MCGRP_MAX] = { | ||
GENL_TEST_MCGRP0_NAME, | ||
GENL_TEST_MCGRP1_NAME, | ||
GENL_TEST_MCGRP2_NAME, | ||
}; | ||
|
||
enum genl_test_attrs { | ||
GENL_TEST_ATTR_UNSPEC, /* Must NOT use element 0 */ | ||
|
||
GENL_TEST_ATTR_MSG, | ||
|
||
__GENL_TEST_ATTR__MAX, | ||
}; | ||
#define GENL_TEST_ATTR_MAX (__GENL_TEST_ATTR__MAX - 1) | ||
|
||
static struct nla_policy genl_test_policy[GENL_TEST_ATTR_MAX+1] = { | ||
[GENL_TEST_ATTR_MSG] = { | ||
.type = NLA_STRING, | ||
#ifdef __KERNEL__ | ||
.len = GENL_TEST_ATTR_MSG_MAX | ||
#else | ||
.maxlen = GENL_TEST_ATTR_MSG_MAX | ||
#endif | ||
}, | ||
}; | ||
|
||
#endif |
Oops, something went wrong.