http://www.linuxfoundation.org/collaborate/workgroups/networking/generic_netlink_howto
http://www.carisma.slowglass.com/~tgr/libnl/
= 펌> use Netlink Protocol Library Suite (libnl).
Generic Netlink sockets - example code
linux/kernels/mips-linux-2.6.15/include/net/netlink.h
/* ======================================================================== |
= 삭제 대비 카피
Generic Netlink sockets - example code
If you want to use netlink as a userspace-kernelspace interface for your
own non-networking custom use, make sure to go the Generic Netlink path
- get a family id assigned and then used that to exchange messages
between your userspace and kernelspace. That said, its better to just
use Netlink Protocol Library Suite (libnl). If for some extremely compelling reason you can't, use libnl, here is some sample code to get you started.
Its based on Ariane Kellar's code from here.
I have simplified and commented the userspace side code a lot. The
kernel space code is mostly unchanged. The kernel side code required a
minor change in genlmsg_unicast() call to ensure compatibility with the
newer kernel versions.
nl_kern.c :
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 160 161 162 163 164 165 166 167 168 169 | #include <net/genetlink.h> #include <linux/module.h> #include <linux/kernel.h> //Code based on http://people.ee.ethz.ch/~arkeller/linux/multi/kernel_user_space_howto-3.html /* attributes (variables): * the index in this enum is used as a reference for the type, * userspace application has to indicate the corresponding type * the policy is used for security considerations */ enum { DOC_EXMPL_A_UNSPEC, DOC_EXMPL_A_MSG, __DOC_EXMPL_A_MAX, }; #define DOC_EXMPL_A_MAX (__DOC_EXMPL_A_MAX - 1) /* attribute policy: defines which attribute has which type (e.g int, char * etc) * possible values defined in net/netlink.h */ static struct nla_policy doc_exmpl_genl_policy[DOC_EXMPL_A_MAX + 1] = { [DOC_EXMPL_A_MSG] = { .type = NLA_NUL_STRING }, }; #define VERSION_NR 1 //family definition static struct genl_family doc_exmpl_gnl_family = { .id = GENL_ID_GENERATE, //Genetlink should generate an id .hdrsize = 0, .name = "CONTROL_EXMPL", //The name of this family, used by userspace application .version = VERSION_NR, //Version number .maxattr = DOC_EXMPL_A_MAX, }; /* commands: enumeration of all commands (functions), * used by userspace application to identify command to be executed */ enum { DOC_EXMPL_C_UNSPEC, DOC_EXMPL_C_ECHO, __DOC_EXMPL_C_MAX, }; #define DOC_EXMPL_C_MAX (__DOC_EXMPL_C_MAX - 1) //An echo command, receives a message, prints it and sends another message back int doc_exmpl_echo(struct sk_buff *skb_2, struct genl_info *info) { struct nlattr *na; struct sk_buff *skb; int rc; void *msg_head; char * mydata; if (info == NULL) { goto out; } /* For each attribute there is an index in info->attrs which points to a nlattr structure * in this structure the data is given */ na = info->attrs[DOC_EXMPL_A_MSG]; if (na) { mydata = (char *)nla_data(na); if (mydata == NULL) { printk("error while receiving data\n"); } else { printk("received: %s\n", mydata); } } else { printk("no info->attrs %i\n", DOC_EXMPL_A_MSG); } //Send a message back //Allocate some memory, since the size is not yet known use NLMSG_GOODSIZE skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); if (skb == NULL) { goto out; } //Create the message headers /* arguments of genlmsg_put: struct sk_buff *, int (sending) pid, int sequence number, struct genl_family *, int flags, u8 command index (why do we need this?) */ msg_head = genlmsg_put(skb, 0, info->snd_seq+1, &doc_exmpl_gnl_family, 0, DOC_EXMPL_C_ECHO); if (msg_head == NULL) { rc = -ENOMEM; goto out; } //Add a DOC_EXMPL_A_MSG attribute (actual value to be sent) rc = nla_put_string(skb, DOC_EXMPL_A_MSG, "Hello World from kernel space"); if (rc != 0) { goto out; } //Finalize the message genlmsg_end(skb, msg_head); //Send the message back rc = genlmsg_unicast(genl_info_net(info), skb,info->snd_pid ); if (rc != 0) { goto out; } return 0; out: printk("An error occured in doc_exmpl_echo:\n"); return 0; } //Commands: mapping between the command enumeration and the actual function struct genl_ops doc_exmpl_gnl_ops_echo = { .cmd = DOC_EXMPL_C_ECHO, .flags = 0, .policy = doc_exmpl_genl_policy, .doit = doc_exmpl_echo, .dumpit = NULL, }; static int __init gnKernel_init(void) { int rc; printk("Generic Netlink Example Module inserted.\n"); //Register the new family rc = genl_register_family(&doc_exmpl_gnl_family); if (rc != 0) { goto failure; } //Register functions (commands) of the new family rc = genl_register_ops(&doc_exmpl_gnl_family, &doc_exmpl_gnl_ops_echo); if (rc != 0) { printk("Register ops: %i\n",rc); genl_unregister_family(&doc_exmpl_gnl_family); goto failure; } return 0; failure: printk("An error occured while inserting the generic netlink example module\n"); return -1; } static void __exit gnKernel_exit(void) { int ret; printk("Generic Netlink Example Module unloaded.\n"); //Unregister the functions ret = genl_unregister_ops(&doc_exmpl_gnl_family, &doc_exmpl_gnl_ops_echo); if(ret != 0) { printk("Unregister ops: %i\n",ret); return; } //Unregister the family ret = genl_unregister_family(&doc_exmpl_gnl_family); if(ret !=0) { printk("Unregister family %i\n",ret); } } module_init(gnKernel_init); module_exit(gnKernel_exit); MODULE_LICENSE("GPL"); |
nl_user.c :
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 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 | #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <unistd.h> #include <poll.h> #include <string.h> #include <fcntl.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/socket.h> #include <sys/types.h> #include <signal.h> #include <linux/genetlink.h> //Code based on http://people.ee.ethz.ch/~arkeller/linux/multi/kernel_user_space_howto-3.html /* Generic macros for dealing with netlink sockets. Might be duplicated * elsewhere. It is recommended that commercial grade applications use * libnl or libnetlink and use the interfaces provided by the library */ #define GENLMSG_DATA(glh) ((void *)(NLMSG_DATA(glh) + GENL_HDRLEN)) #define GENLMSG_PAYLOAD(glh) (NLMSG_PAYLOAD(glh, 0) - GENL_HDRLEN) #define NLA_DATA(na) ((void *)((char*)(na) + NLA_HDRLEN)) #define MESSAGE_TO_KERNEL "Hello World!" //Variables used for netlink int nl_fd; //netlink socket's file descriptor struct sockaddr_nl nl_address; //netlink socket address int nl_family_id; //The family ID resolved by the netlink controller for this userspace program int nl_rxtx_length; //Number of bytes sent or received via send() or recv() struct nlattr *nl_na; //pointer to netlink attributes structure within the payload struct { //memory for netlink request and response messages - headers are included struct nlmsghdr n; struct genlmsghdr g; char buf[256]; } nl_request_msg, nl_response_msg; int main(void) { //Step 1: Open the socket. Note that protocol = NETLINK_GENERIC nl_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC); if (nl_fd < 0) { perror("socket()"); return -1; } //Step 2: Bind the socket. memset(&nl_address, 0, sizeof(nl_address)); nl_address.nl_family = AF_NETLINK; nl_address.nl_groups = 0; if (bind(nl_fd, (struct sockaddr *) &nl_address, sizeof(nl_address)) < 0) { perror("bind()"); close(nl_fd); return -1; } //Step 3. Resolve the family ID corresponding to the string "CONTROL_EXMPL" //Populate the netlink header nl_request_msg.n.nlmsg_type = GENL_ID_CTRL; nl_request_msg.n.nlmsg_flags = NLM_F_REQUEST; nl_request_msg.n.nlmsg_seq = 0; nl_request_msg.n.nlmsg_pid = getpid(); nl_request_msg.n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN); //Populate the payload's "family header" : which in our case is genlmsghdr nl_request_msg.g.cmd = CTRL_CMD_GETFAMILY; nl_request_msg.g.version = 0x1; //Populate the payload's "netlink attributes" nl_na = (struct nlattr *) GENLMSG_DATA(&nl_request_msg); nl_na->nla_type = CTRL_ATTR_FAMILY_NAME; nl_na->nla_len = strlen("CONTROL_EXMPL") + 1 + NLA_HDRLEN; strcpy(NLA_DATA(nl_na), "CONTROL_EXMPL"); //Family name length can be upto 16 chars including \0 nl_request_msg.n.nlmsg_len += NLMSG_ALIGN(nl_na->nla_len); memset(&nl_address, 0, sizeof(nl_address)); nl_address.nl_family = AF_NETLINK; //Send the family ID request message to the netlink controller nl_rxtx_length = sendto(nl_fd, (char *) &nl_request_msg, nl_request_msg.n.nlmsg_len, 0, (struct sockaddr *) &nl_address, sizeof(nl_address)); if (nl_rxtx_length != nl_request_msg.n.nlmsg_len) { perror("sendto()"); close(nl_fd); return -1; } //Wait for the response message nl_rxtx_length = recv(nl_fd, &nl_response_msg, sizeof(nl_response_msg), 0); if (nl_rxtx_length < 0) { perror("recv()"); return -1; } //Validate response message if (!NLMSG_OK((&nl_response_msg.n), nl_rxtx_length)) { fprintf(stderr, "family ID request : invalid message\n"); return -1; } if (nl_response_msg.n.nlmsg_type == NLMSG_ERROR) { //error fprintf(stderr, "family ID request : receive error\n"); return -1; } //Extract family ID nl_na = (struct nlattr *) GENLMSG_DATA(&nl_response_msg); nl_na = (struct nlattr *) ((char *) nl_na + NLA_ALIGN(nl_na->nla_len)); if (nl_na->nla_type == CTRL_ATTR_FAMILY_ID) { nl_family_id = *(__u16 *) NLA_DATA(nl_na); } //Step 4. Send own custom message memset(&nl_request_msg, 0, sizeof(nl_request_msg)); memset(&nl_response_msg, 0, sizeof(nl_response_msg)); nl_request_msg.n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN); nl_request_msg.n.nlmsg_type = nl_family_id; nl_request_msg.n.nlmsg_flags = NLM_F_REQUEST; nl_request_msg.n.nlmsg_seq = 60; nl_request_msg.n.nlmsg_pid = getpid(); nl_request_msg.g.cmd = 1; //corresponds to DOC_EXMPL_C_ECHO; nl_na = (struct nlattr *) GENLMSG_DATA(&nl_request_msg); nl_na->nla_type = 1; // corresponds to DOC_EXMPL_A_MSG nl_na->nla_len = sizeof(MESSAGE_TO_KERNEL)+NLA_HDRLEN; //Message length memcpy(NLA_DATA(nl_na), MESSAGE_TO_KERNEL, sizeof(MESSAGE_TO_KERNEL)); nl_request_msg.n.nlmsg_len += NLMSG_ALIGN(nl_na->nla_len); memset(&nl_address, 0, sizeof(nl_address)); nl_address.nl_family = AF_NETLINK; //Send the custom message nl_rxtx_length = sendto(nl_fd, (char *) &nl_request_msg, nl_request_msg.n.nlmsg_len, 0, (struct sockaddr *) &nl_address, sizeof(nl_address)); if (nl_rxtx_length != nl_request_msg.n.nlmsg_len) { perror("sendto()"); close(nl_fd); return -1; } printf("Sent to kernel: %s\n",MESSAGE_TO_KERNEL); //Receive reply from kernel nl_rxtx_length = recv(nl_fd, &nl_response_msg, sizeof(nl_response_msg), 0); if (nl_rxtx_length < 0) { perror("recv()"); return -1; } //Validate response message if (nl_response_msg.n.nlmsg_type == NLMSG_ERROR) { //Error printf("Error while receiving reply from kernel: NACK Received\n"); close(nl_fd); return -1; } if (nl_rxtx_length < 0) { printf("Error while receiving reply from kernel\n"); close(nl_fd); return -1; } if (!NLMSG_OK((&nl_response_msg.n), nl_rxtx_length)) { printf("Error while receiving reply from kernel: Invalid Message\n"); close(nl_fd); return -1; } //Parse the reply message nl_rxtx_length = GENLMSG_PAYLOAD(&nl_response_msg.n); nl_na = (struct nlattr *) GENLMSG_DATA(&nl_response_msg); printf("Kernel replied: %s\n",(char *)NLA_DATA(nl_na)); //Step 5. Close the socket and quit close(nl_fd); return 0; } |
Makefile :
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 | obj-m += nl_kern.o nl_kern-objs := kern/nl_kern.o all: kernel-module-uninstall kernel-clean-ring-buffer kernel-build kernel-clean-temporary kernel-module-install user-build @tput setaf 3 @echo " done: all" @tput sgr0 clean: kernel-module-uninstall kernel-clean user-clean @tput setaf 3 @echo " done: clean" @tput sgr0 kernel-build: @tput setaf 1 @echo " kernel-build" @tput sgr0 make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules kernel-clean: @tput setaf 1 @echo " kernel-clean" @tput sgr0 make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean kernel-clean-temporary: @tput setaf 1 @echo " kernel-clean-temporary" @tput sgr0 -rm -rf *.o *~ core .depend .*.cmd *.mod.c .tmp_versions -rm -rf kern/*.o kern/*~ kern/core kern/.depend kern/.*.cmd kern/*.mod.c kern/.tmp_versions -rm -rf Module.symvers modules.order kernel-module-install: @tput setaf 1 @echo " kernel-module-install" @tput sgr0 -sudo insmod nl_kern.ko kernel-module-uninstall: @tput setaf 1 @echo " kernel-module-uninstall" @tput sgr0 -sudo rmmod nl_kern kernel-clean-ring-buffer: @tput setaf 1 @echo " kernel-clean-ring-buffer" @tput sgr0 sudo dmesg -c > /dev/null user-build: @tput setaf 1 @echo " user-build" @tput sgr0 gcc user/nl_user.c -o nl_user.out user-clean: @tput setaf 1 @echo " user-clean" @tput sgr0 rm -rf *.out |
'프로그램... > 프로....Kernel' 카테고리의 다른 글
[Memory] 커널 메모리 관리 (0) | 2017.09.19 |
---|---|
[Timer] hrtimer (0) | 2015.04.13 |
[Kernel-Document] (0) | 2014.10.31 |
[Kernel-Make] the Kernel Build System (kbuild) (0) | 2014.10.31 |
[고급 스킬] from Linux Kernel (0) | 2014.10.29 |