2011年1月27日 星期四

nf_sockopt: A Mechanism for IPC between The Kernel And User Space

今天trace ebtables如何把rule下到kernel的過程中, 學到了一種kernel/user space溝通的方式:nf_sockopt。

nf_sockopt的使用相當簡單:

1. 定義get/set options
#ifndef __MY_SOCKOPT_H__
#define __MY_SOCKOPT_H__

/* {g,s}etsockopt numbers */
#define MY_BASE_CTL            999

enum
{
    MY_SO_SET_CMD1 = MY_BASE_CTL,
    MY_SO_SET_CMD2,
    MY_SO_SET_MAX,
}MY_SO_SET;

enum
{
    MY_SO_GET_CMD1 = MY_BASE_CTL,
    MY_SO_GET_CMD2,
    MY_SO_GET_CMD3,
    MY_SO_GET_MAX
}MY_SO_GET;

#endif
2. 在kernel space註冊要handle的options
#include <linux/module.h>
#include <net/sock.h>
#include <linux/netfilter.h>
#include "my_sockopt.h"

static int do_my_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len)
{
    printk("Get from user space [%s]\n", (char*)user);
   
    return 0;
}

static int do_my_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
{
    int ret;
    printk("Send to user space [%d]\n", cmd);
    ret = cmd;
    //copy_to_user(void __user *to, const void *from, unsigned long n)
    ret = copy_to_user(user, &ret, sizeof(ret));
   
    return ret;
}

static struct nf_sockopt_ops my_sockopts =
{
    .pf         = PF_INET,
    .set_optmin = MY_BASE_CTL,
    .set_optmax = MY_SO_SET_MAX + 1,
    .set        = do_my_set_ctl,
    .get_optmin = MY_BASE_CTL,
    .get_optmax = MY_SO_GET_MAX + 1,
    .get        = do_my_get_ctl,
};

static int __init mysockopt_init(void)
{
    int ret;
   
    if ((ret = nf_register_sockopt(&my_sockopts)) < 0)
    {
        printk("nf_register_sockopt failed [%d]\n", ret);
        return ret;
    }
    return 0;
}

static void __exit mysockopt_fini(void)
{
    nf_unregister_sockopt(&my_sockopts);
}

module_init(mysockopt_init);
module_exit(mysockopt_fini);
MODULE_LICENSE("GPL");
3. 在user space開一個RAW socket, 再利用g/setsockopt透過設定socket的options, 便可以取得/設定kernel space的資訊
#include <getopt.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>

#include "my_sockopt.h"

int sockfd = -1;

static int get_sockfd()
{
    int ret = 0;
    if (sockfd == -1) {
        sockfd = socket(AF_INET, SOCK_RAW, PF_INET);
        if (sockfd < 0)
        {
            perror("get_sockfd");
            ret = -1;
        }
    }
    return ret;
}

int set_to_kernel()
{
    char buf[64] = "Hello, sockopt!!!";

    if (get_sockfd())
        return -1;
       
    if (!setsockopt(sockfd, IPPROTO_IP, MY_SO_SET_CMD1, buf, sizeof(buf)))
        return -1;

    return 0;
}

int get_from_kernel()
{
    int ret = -1;
    int len = 0;

    if (get_sockfd())
        return -1;
       
    if (getsockopt(sockfd, IPPROTO_IP, MY_SO_GET_CMD3, &ret, &len))
        return -1;
   
    printf("Get from kernel [%d]\n", ret);

    return 0;
}

int main()
{
    set_to_kernel();
    get_from_kernel();
    return 0;
}
相較於netlink, nf_sockopt容易許多。兩者間的差異, 比較能夠直接感受到的, 便是netlink可以雙向initiate session; 而nf_sockopt則是由user space去主控, kernel space只是被動地聽命行事。


參考資料:
[1] ebtables.c
[2] communication.c
[3] 使用sockopt与內核交換數据

沒有留言:

張貼留言