轉自:http://blog.csdn.net/jk110333/article/details/8642261
用戶態與內核態交互通信的方法不止一種,sockopt是比較方便的一個,寫法也簡單.
缺點就是使用 copy_from_user()/copy_to_user()完成內核和用戶的通信, 效率其實不高, 多用在傳遞控制 選項 信息,不適合做大量的數據傳輸
用戶態函數:
發送:int setsockopt ( int sockfd, int proto, int cmd, void *data, int datelen);
接收:int getsockopt(int sockfd, int proto, int cmd, void *data, int datalen)
第一個參數是socket描述符;
第二個參數proto是sock協議,IP RAW的就用SOL_SOCKET/SOL_IP等,TCP/UDP socket的可用SOL_SOCKET/SOL_IP/SOL_TCP/SOL_UDP等,即高層的socket是都可以使用低層socket的命令字 的,IPPROTO_IP;
第三個參數cmd是操作命令字,由自己定義;
第四個參數是數據緩沖區起始位置指針,set操作時是將緩沖區數據寫入內核,get的時候是將內核中的數 據讀入該緩沖區;
第五個參數數據長度
內核態函數
注冊:nf_register_sockopt(struct nf_sockopt_ops *sockops)
解除:nf_unregister_sockopt(struct nf_sockopt_ops *sockops)
setsockopt():在內核調用kernel_setsockopt函數
結構體 nf_sockopt_ops test_sockops
static struct nf_sockopt_ops nso = {
.pf = PF_INET, // 協議族
.set_optmin = 常數, // 定義最小set命令字
.set_optmax = 常數+N, // 定義最大set命令字
.set = recv_msg, // 定義set處理函數
.get_optmin = 常數, // 定義最小get命令字
.get_optmax = 常數+N, // 定義最大get命令字
.get = send_msg, // 定義set處理函數
};
static struct nf_sockopt_ops nso = {
.pf = PF_INET, // 協議族
.set_optmin = 常數, // 定義最小set命令字
.set_optmax = 常數+N, // 定義最大set命令字
.set = recv_msg, // 定義set處理函數
.get_optmin = 常數, // 定義最小get命令字
.get_optmax = 常數+N, // 定義最大get命令字
.get = send_msg, // 定義set處理函數
};
其中命令字不能和內核已有的重復,宜大不宜小。命令字很重要,是用來做標識符的。而且用戶態和內核態要定義的相同,
#define SOCKET_OPS_BASE 128
#define SOCKET_OPS_SET (SOCKET_OPS_BASE)
#define SOCKET_OPS_GET (SOCKET_OPS_BASE)
#define SOCKET_OPS_MAX (SOCKET_OPS_BASE + 1)
#define SOCKET_OPS_SET (SOCKET_OPS_BASE)
#define SOCKET_OPS_GET (SOCKET_OPS_BASE)
#define SOCKET_OPS_MAX (SOCKET_OPS_BASE + 1)
set/get處理函數是直接由用戶空間的 set/getsockopt函數調用的。 setsockopt函數向內核寫數據,用getsockopt向內核讀數據。
另外set和get的處理函數的參數應該是這樣的
int recv_msg(struct sock *sk, int cmd, void __user *user, unsigned int len)
int send_msg(struct sock *sk, int cmd, void __user *user, unsigned int *len)

附:
int kernel_setsockopt(struct socket *sock, int level, int optname,
char *optval, unsigned int optlen)
{
mm_segment_t oldfs = get_fs();
char __user *uoptval;
int err;
uoptval = (char __user __force *) optval;
set_fs(KERNEL_DS);
if (level == SOL_SOCKET)
err = sock_setsockopt(sock, level, optname, uoptval, optlen);
else
err = sock->ops->setsockopt(sock, level, optname, uoptval,
optlen);
set_fs(oldfs);
return err;
}
如果用戶態level == SOL_SOCKET時,那么直接調用socket層統一的接口:sock_setsockopt,在socket層處理了,就像我之前說的那樣,無論哪種協議,都在一個函數里面處理。
如果level不是SOL_SOCKET(也就是level是SOL_TCP/SOL_UDP,IPPROTO_IP),那么調用各自協議棧初始化時指向的setsockopt函數,11行的:sock->ops->setsockopt,