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; #endif2. 在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与內核交換數据