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与內核交換數据