通過netlink實現內核模塊和應用層通信

貼上代碼,方便以後查閱。

skb常用操作函數,比較容易弄混

skb_put : skb->tail += len, skb->len += len
skb_pull: skb->data += len, skb->len -= len
skb_push: skb->data -= len, skb->len += len


內核版本linux2.6.38,編譯環境gcc 4.4.4,centos6.0

內核模塊代碼:


/*
 * author: [email protected]
 * date	 : 2011-10-29
 */
#include <linux/module.h>
#include <linux/netlink.h>
#include <linux/sched.h>
#include <net/sock.h>
#include <linux/proc_fs.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/icmp.h>
#include <linux/udp.h>

#define NETLINK_TEST 30

/* 調試信息 */
#define LOGMSG(fmt, arg...) \
do{ \
    printk("[func:%s,line:%d]: "fmt, __FUNCTION__, __LINE__, ##arg); \
}while(0)
/* 錯誤信息 */
#define LOGERR(fmt, arg...) \
do{ \
    printk("[func:%s,line:%d]: "fmt, __FUNCTION__, __LINE__, ##arg); \
}while(0)
/* 斷言 */
#define ASSERT(expr) \
if (unlikely(!(expr))) { \
    printk("Assertion failed! %s,%s,%s,line=%d\n", \
    #expr, __FILE__, __func__, __LINE__); \
}
/* 消息最大值 */
#define MAX_MSG_LEN 1024
enum{
    NLMSG_TYPE_NONE = 0,
    NLMSG_TYPE_SETPID,  /* 設置PID */
    NLMSG_TYPE_KERNEL,  /* 消息來自內核 */
    NLMSG_TYPE_APP,     /* 消息來自應用層 */
};
struct nlmsg{
    int type;               /* 消息類型 */
    int len;                /* 消息長度,包括頭部 */
    char msg[MAX_MSG_LEN];  /* 消息正文 */
};
/* netlink socket */
static struct sock *g_nl_sk = NULL;
static int g_nlpid = -1;    /* 應用層接收程序PID */

/*
 * 發送整個從ip頭開始的skb數據到應用層
 * 
 * param[in]: sk, skb發送目的socket
 * param[in]: skb, 待發送的skb
 * return -1, 失敗; 0, 成功
 * */
int nl_sendskb(struct sock *sk, struct sk_buff *skb)
{
    struct iphdr *iph = NULL;
    struct nlmsghdr *nlh = NULL;
    struct sk_buff *nl_skb = NULL;

    int skb_len = 0;

    ASSERT(skb != NULL);
    ASSERT(sk != NULL);
    if(g_nlpid < 0)
        return 0;

    iph = ip_hdr(skb);
    skb_len = iph->tot_len;
    /* NLMSG_SPACE: sizeof(struct nlmsghdr) + len按4字節對齊 */
    nl_skb = alloc_skb(NLMSG_SPACE(skb_len), GFP_ATOMIC);
    if(!nl_skb)
    {
        LOGERR("nl_skb == NULL, failed!\n");
        return -1;
    }
    /*
     * static inline struct nlmsghdr *nlmsg_put(struct sk_buff *skb, u32 pid, u32 seq,
     *               int type, int payload, int flags);
     * 設置skb->tail指針指向skb->data + sizeof(struct nlmsghdr) + payload
     * skb->len = sizeof(struct nlmsghdr) + payload 
     */
    nlh = nlmsg_put(nl_skb, 0, 0, 0, NLMSG_SPACE(skb_len) - sizeof(struct nlmsghdr), 0); 
    NETLINK_CB(nl_skb).pid = 0; /* 0代表數據來自內核 */
    memcpy(NLMSG_DATA(nlh), (char *)iph, htons(iph->tot_len));
    /* 將數據發送給進程號22345的進程 */
    return netlink_unicast(sk, nl_skb, g_nlpid , MSG_DONTWAIT);
}
/*
 * 發送字符串到應用層
 *
 * param[in]: sk, 數據發往的socket
 * param[in]: pmsg, 待發送字符串
 * param[in]: msglen, 待發送字符串長度
 * 
 * return: -1, 失敗; 0, 成功
 * */
int nl_sendmsg(struct sock *sk, struct nlmsg *pmsg)
{
    struct nlmsghdr *nlh = NULL;
    struct sk_buff *nl_skb = NULL;
    int msglen = pmsg->len;

    ASSERT(pmsg != NULL);
    ASSERT(sk != NULL);

    if(g_nlpid < 0)
        return 0;
    nl_skb = alloc_skb(NLMSG_SPACE(msglen), GFP_ATOMIC);
    if(!nl_skb)
    {
        LOGERR("nl_skb == NULL, msglen = %d, failed!\n", msglen);
        return -1;
    }

    nlh = nlmsg_put(nl_skb, 0, 0, 0, 
                    NLMSG_SPACE(msglen) - NLMSG_HDRLEN, 0); 
    NETLINK_CB(nl_skb).pid = 0;
    memcpy(NLMSG_DATA(nlh), pmsg, msglen);

    return netlink_unicast(sk, nl_skb, g_nlpid , MSG_DONTWAIT);
}
/* 
 * 從應用層接收數據, netlink_kernel_create註冊的回調
 * param[in]: skb, 包含netlink數據的skb
 *
 * skb常用操作函數
 * skb_put : skb->tail += len, skb->len += len
 * skb_pull: skb->data += len, skb->len -= len
 * skb_push: skb->data -= len, skb->len += len
 */
static void nl_recvmsg(struct sk_buff *skb)
{
    struct nlmsg *pmsg = NULL;
    struct nlmsghdr *nlh = NULL;
    uint32_t rlen = 0;
    
    while(skb->len >= NLMSG_SPACE(0))
    {
        nlh = nlmsg_hdr(skb);
        if(nlh->nlmsg_len < sizeof(*nlh) || skb->len < nlh->nlmsg_len)
            return;
        rlen = NLMSG_ALIGN(nlh->nlmsg_len);
        if(rlen > skb->len)
            rlen = skb->len;
        pmsg = (struct nlmsg*)NLMSG_DATA(nlh);
        switch(pmsg->type)
        {
        case NLMSG_TYPE_SETPID:
            g_nlpid = nlh->nlmsg_pid;
            LOGMSG("pid: %d\n", g_nlpid);
            LOGMSG("msg: %s\n", pmsg->msg);
            break;
        case NLMSG_TYPE_KERNEL:
            break;
        case NLMSG_TYPE_APP:
            break;
        }
        /* 獲取下一條netlink消息 */
        skb_pull(skb, rlen);
    }
}

/* 
 * netfilter PRE_ROUTING鉤子
 * */
unsigned int pre_routing_hook(unsigned int hooknum, 
                           struct sk_buff *skb, 
                           const struct net_device *in,
                           const struct net_device *out,
                           int (*okfn)(struct sk_buff *))
{
    char *psend = "msg for kernel";
    struct nlmsg msg;
    int ret = 0;

    msg.type = NLMSG_TYPE_KERNEL;
    msg.len = strlen(psend) + offsetof(struct nlmsg, msg) + 1;
    memcpy(msg.msg, psend, msg.len);
    //ret = nl_sendskb(g_nl_sk, skb);
    ret = nl_sendmsg(g_nl_sk, &msg);
    //LOGMSG("ok\n");
    return NF_ACCEPT;
}

static struct nf_hook_ops local_in_ops __read_mostly = {
    .hook = pre_routing_hook,
    .owner = THIS_MODULE,
    .pf = PF_INET,
    .hooknum = NF_INET_PRE_ROUTING,
    .priority = NF_IP_PRI_FIRST
};

static int __init nl_init(void)
{
    int ret = 0;
    /* 
     * struct sock *netlink_kernel_create(struct net *net, int unit, unsigned int groups,
     *                                    void (*input)(struct sk_buff *skb),
     *                                    struct mutex *cb_mutex, struct module *module)
     */
    g_nl_sk = netlink_kernel_create(&init_net, NETLINK_TEST, 0, nl_recvmsg, NULL, THIS_MODULE);
    if (!g_nl_sk) {
        LOGERR("Fail to create netlink socket.\n");
        return -1;
    }

    ret = nf_register_hook(&local_in_ops);
    if(ret < 0)
    {
        LOGMSG("nf_register_hook failed!\n");
        goto sock_release;
    }
    LOGMSG("ok!\n");
    return 0;

sock_release:
    if(g_nl_sk)
        sock_release(g_nl_sk->sk_socket);
    return -1;
}

static void __exit nl_exit(void)
{
    synchronize_sched();
    if(g_nl_sk)
        sock_release(g_nl_sk->sk_socket);
    nf_unregister_hook(&local_in_ops);
    LOGMSG("ok!\n");
}

module_init(nl_init);
module_exit(nl_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("[email protected]");


內核模塊Makefile

module := netlink

obj-m := $(module).o

#$(module)-objs :=  $(module).o

KDIR=/root/kernel_new
KHDR=/root/kernel_new/include
EXTRA_CFLAGS += $(FLAG) -I$(KHDR) -O2 -D__KERNEL__ -DMODULE $(INCLUDE) -DEXPORT_SYMTAB
CPPFLAGS += $(FLAG) -I$(KHDR)

OUTDIR =
TARGET = netlink.ko
TARGETDIR = /lib/modules/

all:
	make -C $(KDIR) M=$(PWD) modules

clean:
	make -C $(KDIR) M=$(PWD) clean 


install: all
	install --verbose --mode=0755 $(OUTDIR)$(TARGET) $(INSTALLROOT)$(TARGETDIR)


應用層代碼:

/*
 * author: [email protected]
 * date	 : 2011-10-29
 */
#include <sys/stat.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 <stddef.h>
#include <errno.h>   

#define FREE_INIT(ptr) do{ \
    free(ptr); \
    ptr = NULL; \
}while(0)

/* 消息最大值 */
#define MAX_MSG_LEN 1024
enum{
    NLMSG_TYPE_NONE = 0,
    NLMSG_TYPE_SETPID,  /* 設置PID */
    NLMSG_TYPE_KERNEL,  /* 消息來自內核 */
    NLMSG_TYPE_APP,     /* 消息來自應用層 */
};
struct nlmsg{
    int type;               /* 消息類型 */
    int len;                /* 消息長度,包括頭部 */
    char msg[MAX_MSG_LEN];  /* 消息正文 */
};

#define NETLINK_TEST 30   

/*
 * 打開netlink
 * return: 0, 成功; -1, 失敗
 * */
int netlink_open(void)
{
    struct sockaddr_nl saddr;
    int sockfd = -1, ret = 0;
    
    sockfd = socket(PF_NETLINK, SOCK_RAW, NETLINK_TEST);  
    if(sockfd < -1){  
        perror("create socket!\n");
        return -1;  
    }  
    
    memset(&saddr, 0, sizeof(saddr));  
    saddr.nl_family = PF_NETLINK;   
    saddr.nl_pid = getpid();    // self pid    
    saddr.nl_groups = 0;        // multi cast 
    
    ret = bind(sockfd, (struct sockaddr*)&saddr, sizeof(saddr));  
    if(ret < 0){  
        perror("bind failed!\n");  
        close(sockfd);  
        return -1;  
    } 

    return sockfd;
}
/*
 * 發送信息
 * param[in]: sockfd
 * param[in]: pmsg, 待發送信息
 * 
 * return: 0, 發送成功; -1: 發送失敗
 * */
int netlink_send(int sockfd, struct nlmsg *pmsg)
{  
    struct msghdr msg;
    struct iovec iov;
    struct nlmsghdr *nlh = NULL;
    
    int msglen = pmsg->len;
    int totlen = NLMSG_SPACE(pmsg->len);
    int ret = 0;
    
    nlh = malloc(totlen);
    if(!nlh)
    {
        fprintf(stderr, "malloc failed!\n");
        return -1;
    }
    nlh->nlmsg_len = totlen;
    nlh->nlmsg_flags = 0;
    nlh->nlmsg_pid = getpid();
    
    iov.iov_base = (void *)nlh;  
    iov.iov_len = nlh->nlmsg_len;  
  
    memset(&msg, 0, sizeof(msg));  
    msg.msg_iov = &iov;  
    msg.msg_iovlen = 1; 
    
    
    memcpy(NLMSG_DATA(nlh), pmsg, msglen);
    ret = sendmsg(sockfd, &msg, 0);
    if(ret < 0)
    {
        fprintf(stderr, "sendmsg failed!\n");
        FREE_INIT(nlh);
        return -1;
    }
    return 0;
    
}
/*
 * 接收信息
 * param[in]: sockfd
 * param[out]: pmsg
 * 
 * return 0, 成功; -1, 失敗
 * */
int netlink_recv(int sockfd, struct nlmsg *pmsg)
{
    struct msghdr msg;
    struct iovec iov;
    struct nlmsghdr *nlh = NULL;
    
    int msglen = sizeof(*pmsg);
    int totlen = NLMSG_SPACE(sizeof(*pmsg));
    int ret = 0;
    
    nlh = malloc(totlen);
    if(!nlh)
    {
        fprintf(stderr, "malloc failed!\n");
        return -1;
    }
    
    iov.iov_base = (void *)nlh;  
    iov.iov_len = totlen;  
  
    memset(&msg, 0, sizeof(msg));  
    msg.msg_iov = &iov;  
    msg.msg_iovlen = 1; 
    
    memcpy(NLMSG_DATA(nlh), pmsg, msglen);
    ret = recvmsg(sockfd, &msg, 0);
    if(ret < 0)
    {
        fprintf(stderr, "recvmsg failed!\n");
        FREE_INIT(nlh);
        return -1;
    }
    memcpy(pmsg, NLMSG_DATA(nlh), msglen);
    return 0;
}
/* 
 * 關閉netlink
 * param[in]: sockfd, netlink socket號
 * */
void netlink_close(int sockfd)
{
    if(sockfd > 0)
        close(sockfd);
}

int main(int argc, char* argv[])  
{  
    int sockfd = -1;
    int ret = 0;

    struct nlmsg msg;

    char *psend = "msg from app!";
    
    memset(&msg, 0, sizeof(msg));
    /* 創建netlink socket */
    sockfd = netlink_open();
    if(sockfd < 0)
    {
        fprintf(stderr, "netlink_open failed!\n");
        return -1;
    }
    /* 將進程號通知內核 */
    msg.type = NLMSG_TYPE_SETPID;
    msg.len = strlen(psend) + offsetof(struct nlmsg, msg) + 1;
    memcpy(msg.msg, psend, strlen(psend));
    ret = netlink_send(sockfd, &msg);
    if(ret < 0)
    {
        fprintf(stderr, "netlink_send failed!\n");
        return -1;
    }
    /* 接收消息 */
    while(1)
    {
        ret = netlink_recv(sockfd, &msg);
        if(ret < 0)
        {
            fprintf(stderr, "netlink_recv failed!\n");
            return -1;
        }
        printf("msg: %s\n", msg.msg);
    }
    /* 關閉netlink */
    netlink_close(sockfd);
    return 0;  
}  
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章