カーネルとユーザ空間の間のNetlink通信
昨年、私はカーネルスペースからいくつかのイベントを収集するために、Networkを使用しました.私はこのポストに私がNetLinkでプログラムすることを学んだとき、私がした若干の基本的な実行に出席します.
導入
netlinkはLinuxカーネルのインターフェースです.Unixドメインソケットと同様に、inetソケットとは異なり、netlink通信はホスト境界を横断できません.NetLinkは、ユーザ空間プロセスのための標準的なソケットベースのインタフェースとカーネルモジュールによる内部の使用のためのカーネル側APIを提供します.元々NetlinkはAFHENT netlinkソケットファミリーを使用しました.netlinkはioctlへのより柔軟な後継であるように設計されているRFC 3549は詳細にプロトコルを記述します.
マイプラクティス
カーネルモジュール
以下はカーネルモジュールを生成するソースファイル"netlinktle kernel . c "の内容です.私のカスタマイズされたnetlinkプロトコルを定義するマクロ
netlinktleカーネル.c
ユーザー空間アプリケーション
以下は私のファイル"netlinkstra client . c "の内容です.このプログラムは、同じプロトコルでNETLINKソケットをオープンし、ユーザが前のセクションのカーネルモジュールへ/からMSGを送受するのを許します.
"NetLinkCountクライアント. c "
Makefileを作成し、ソースファイルを簡単にコンパイルできるようになります.
Makefile
通信をテストする
ファイル"netlinktle kernel . c ", "netlinkstra client . c "および"makefile "が同じディレクトリにあることを確認してください.Linuxターミナルウィンドウで、このディレクトリに
生成されたカーネルモジュール"netlinktle kernel . ko "をLinuxカーネルにロードします.
導入
netlinkはLinuxカーネルのインターフェースです.Unixドメインソケットと同様に、inetソケットとは異なり、netlink通信はホスト境界を横断できません.NetLinkは、ユーザ空間プロセスのための標準的なソケットベースのインタフェースとカーネルモジュールによる内部の使用のためのカーネル側APIを提供します.元々NetlinkはAFHENT netlinkソケットファミリーを使用しました.netlinkはioctlへのより柔軟な後継であるように設計されているRFC 3549は詳細にプロトコルを記述します.
マイプラクティス
カーネルモジュール
以下はカーネルモジュールを生成するソースファイル"netlinktle kernel . c "の内容です.私のカスタマイズされたnetlinkプロトコルを定義するマクロ
MY_NETLINK 30
があります.NetLinkRuleルートやNetLinkCount Inetstra Diagのような利用可能なプロトコルを選ぶこともできます.関数netlinktle kernelount create ()は、ユーザ空間アプリケーションと通信するためのnetlinkソケットを作成します.netlinkプログラミングの詳細については、this page .netlinktleカーネル.c
#include <linux/module.h>
#include <net/sock.h>
#include <linux/netlink.h>
#include <linux/skbuff.h>
/*
refer to https://elixir.bootlin.com/linux/v5.15.13/source/include/linux/netlink.h
*/
#define MY_NETLINK 30 // cannot be larger than 31, otherwise we shall get "insmod: ERROR: could not insert module netlink_kernel.ko: No child processes"
struct sock *nl_sk = NULL;
static void myNetLink_recv_msg(struct sk_buff *skb)
{
struct nlmsghdr *nlhead;
struct sk_buff *skb_out;
int pid, res, msg_size;
char *msg = "Hello msg from kernel";
printk(KERN_INFO "Entering: %s\n", __FUNCTION__);
msg_size = strlen(msg);
nlhead = (struct nlmsghdr*)skb->data; //nlhead message comes from skb's data... (sk_buff: unsigned char *data)
printk(KERN_INFO "MyNetlink has received: %s\n",(char*)nlmsg_data(nlhead));
pid = nlhead->nlmsg_pid; // Sending process port ID, will send new message back to the 'user space sender'
skb_out = nlmsg_new(msg_size, 0); //nlmsg_new - Allocate a new netlink message: skb_out
if(!skb_out)
{
printk(KERN_ERR "Failed to allocate new skb\n");
return;
}
nlhead = nlmsg_put(skb_out, 0, 0, NLMSG_DONE, msg_size, 0); // Add a new netlink message to an skb
NETLINK_CB(skb_out).dst_group = 0;
strncpy(nlmsg_data(nlhead), msg, msg_size); //char *strncpy(char *dest, const char *src, size_t count)
res = nlmsg_unicast(nl_sk, skb_out, pid);
if(res < 0)
printk(KERN_INFO "Error while sending back to user\n");
}
static int __init myNetLink_init(void)
{
struct netlink_kernel_cfg cfg = {
.input = myNetLink_recv_msg,
};
/*netlink_kernel_create() returns a pointer, should be checked with == NULL */
nl_sk = netlink_kernel_create(&init_net, MY_NETLINK, &cfg);
printk("Entering: %s, protocol family = %d \n",__FUNCTION__, MY_NETLINK);
if(!nl_sk)
{
printk(KERN_ALERT "Error creating socket.\n");
return -10;
}
printk("MyNetLink Init OK!\n");
return 0;
}
static void __exit myNetLink_exit(void)
{
printk(KERN_INFO "exiting myNetLink module\n");
netlink_kernel_release(nl_sk);
}
module_init(myNetLink_init);
module_exit(myNetLink_exit);
MODULE_LICENSE("GPL");
hereユーザー空間アプリケーション
以下は私のファイル"netlinkstra client . c "の内容です.このプログラムは、同じプロトコルでNETLINKソケットをオープンし、ユーザが前のセクションのカーネルモジュールへ/からMSGを送受するのを許します.
"NetLinkCountクライアント. c "
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <string.h>
#include <asm/types.h>
#include <linux/netlink.h>
#include <linux/socket.h>
#include <errno.h>
#define NETLINK_USER 30 // same customized protocol as in my kernel module
#define MAX_PAYLOAD 1024 // maximum payload size
struct sockaddr_nl src_addr, dest_addr;
struct nlmsghdr *nlh = NULL;
struct nlmsghdr *nlh2 = NULL;
struct msghdr msg, resp; // famous struct msghdr, it includes "struct iovec * msg_iov;"
struct iovec iov, iov2;
int sock_fd;
int main(int args, char *argv[])
{
//int socket(int domain, int type, int protocol);
sock_fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_USER); //NETLINK_KOBJECT_UEVENT
if(sock_fd < 0)
return -1;
memset(&src_addr, 0, sizeof(src_addr));
src_addr.nl_family = AF_NETLINK;
src_addr.nl_pid = getpid(); /* self pid */
//int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
if(bind(sock_fd, (struct sockaddr*)&src_addr, sizeof(src_addr))){
perror("bind() error\n");
close(sock_fd);
return -1;
}
memset(&dest_addr, 0, sizeof(dest_addr));
dest_addr.nl_family = AF_NETLINK;
dest_addr.nl_pid = 0; /* For Linux Kernel */
dest_addr.nl_groups = 0; /* unicast */
//nlh: contains "Hello" msg
nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PAYLOAD));
memset(nlh, 0, NLMSG_SPACE(MAX_PAYLOAD));
nlh->nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD);
nlh->nlmsg_pid = getpid(); //self pid
nlh->nlmsg_flags = 0;
//nlh2: contains received msg
nlh2 = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PAYLOAD));
memset(nlh2, 0, NLMSG_SPACE(MAX_PAYLOAD));
nlh2->nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD);
nlh2->nlmsg_pid = getpid(); //self pid
nlh2->nlmsg_flags = 0;
strcpy(NLMSG_DATA(nlh), "Hello this is a msg from userspace"); //put "Hello" msg into nlh
iov.iov_base = (void *)nlh; //iov -> nlh
iov.iov_len = nlh->nlmsg_len;
msg.msg_name = (void *)&dest_addr; //msg_name is Socket name: dest
msg.msg_namelen = sizeof(dest_addr);
msg.msg_iov = &iov; //msg -> iov
msg.msg_iovlen = 1;
iov2.iov_base = (void *)nlh2; //iov -> nlh2
iov2.iov_len = nlh2->nlmsg_len;
resp.msg_name = (void *)&dest_addr; //msg_name is Socket name: dest
resp.msg_namelen = sizeof(dest_addr);
resp.msg_iov = &iov2; //resp -> iov
resp.msg_iovlen = 1;
printf("Sending message to kernel\n");
int ret = sendmsg(sock_fd, &msg, 0);
printf("send ret: %d\n", ret);
printf("Waiting for message from kernel\n");
/* Read message from kernel */
recvmsg(sock_fd, &resp, 0); //msg is also receiver for read
printf("Received message payload: %s\n", (char *) NLMSG_DATA(nlh2));
char usermsg[MAX_PAYLOAD];
while (1) {
printf("Input your msg for sending to kernel: ");
scanf("%s", usermsg);
strcpy(NLMSG_DATA(nlh), usermsg); //put "Hello" msg into nlh
printf("Sending message \" %s \" to kernel\n", usermsg);
ret = sendmsg(sock_fd, &msg, 0);
printf("send ret: %d\n", ret);
printf("Waiting for message from kernel\n");
/* Read message from kernel */
recvmsg(sock_fd, &resp, 0); //msg is also receiver for read
printf("Received message payload: %s\n", (char *)NLMSG_DATA(nlh2));
}
close(sock_fd);
return 0;
}
MakefileMakefileを作成し、ソースファイルを簡単にコンパイルできるようになります.
Makefile
obj-m += netlink_kernel.o
#generate the path
CURRENT_PATH:=$(shell pwd)
#the current kernel version number
LINUX_KERNEL:=$(shell uname -r)
#the absolute path
LINUX_KERNEL_PATH:=/usr/src/linux-headers-$(LINUX_KERNEL)
#complie object
# extension of "make modules" cmd with -C option and "M=dir" configuration
# this cmd will switch working directory to the given path followed by the -C option
# and will search specified source files from the given path configured by "M="
# and compile them to generate ko files
all:
@echo $(LINUX_KERNEL_PATH)
make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules
client:
gcc netlink_client.c -o netlink_client -g
#clean
clean:
make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) clean
rm netlink_client
通信をテストする
ファイル"netlinktle kernel . c ", "netlinkstra client . c "および"makefile "が同じディレクトリにあることを確認してください.Linuxターミナルウィンドウで、このディレクトリに
cd
でコンパイルを開始します.make
make client
通常カーネルモジュールファイル"netlinktle kernel . ko "とユーザアプリケーションファイル"netlinkstra client "が生成されます.生成されたカーネルモジュール"netlinktle kernel . ko "をLinuxカーネルにロードします.
sudo insmod netlink_kernel.ko
以下のCmdを新しい端末で実行し、カーネルメッセージを監視します.dmesg -Hw
次に、アプリケーションを起動して通信を開始します../netlink_client
以下は私のテストのスクリーンショットです.Reference
この問題について(カーネルとユーザ空間の間のNetlink通信), 我々は、より多くの情報をここで見つけました https://dev.to/jemaloqiu/netlink-communication-between-kernel-and-user-space-2mg1テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol