Linux用户态与内核态交互数据---socket函数
linux-dash
A beautiful web dashboard for Linux
项目地址:https://gitcode.com/gh_mirrors/li/linux-dash
免费下载资源
·
1、socket函数:
功能描述:
socket函数是任何套接口网络编程中第一个使用的函数,它向用户提供一个套接字,即套接口描述文件字,它是一个整数,如同文件描述符一样,是内核标识一个IO结构的索引。通过socket函数,我们指定一个套接口的协议相关的属性,为进行使用socket api做好准备。
int socket(int family, int type, int protocol)
返回:非负描述字──成功, -1──出错
参数family
这个参数指定一个协议簇,也往往被称为协议域。系统存在许多可以的协议簇,常见有AF_INET──指定为IPv4协议,AF_INET6──指定为IPv6,AF_LOCAL──指定为UNIX 协议域等等。它值都是系统预先定义的宏,系统支持哪些协议我们才可以使用,否则会调用失败。协议簇是网络层的协议。
可以到内核源码linux/socket.h中查看支持的协议簇有哪些。 另外有#define PF_* AF_*
参数type
这个参数指定一个套接口的类型,套接口可能的类型有:SOCK_STREAM、SOCK_DGRAM、SOCK_SEQPACKET、SOCK_RAW等等,它们分别表明字节流、数据报、有序分组、原始套接口。这实际上是指定内核为我们提供的服务抽象,比如我们要一个字节流。需要注意的,并不是每一种协议簇都支持这里的所有的类型,所以类型与协议簇要匹配。
参数protocol
指定相应的传输协议,也就是诸如TCP或UDP协议等等,系统针对每一个协议簇与类型提供了一个默认的协议,我们通过把protocol设置为0来使用这个默认的值。注意这里的协议与上面的协议簇是两个不同的概念,前者是指网络层的协议,由于它对于到传输层会出现许多协议,比如IPv4可以用来实现TCP或UDP等等传输层协议,所以称为协议簇。相应的传输层的协议就简单地称为协议。常见的协议有TCP、UDP、SCTP,要指定它们分别使用宏IPPROTO_TCP、IPPROTO_UPD、IPPROTO_SCTP来指定。
到linux/in.h看可以使用哪些传输层的协议
返回值
socket函数返回一个套接字,即套接口描述字。如果出现错误,它返回-1,并设置errno为相应的值,用户应该检测以判断出现什么错误。
2、getsockopt/setsockopt 函数:
功能描述:
获取或者设置与某个套接字关联的选 项。选项可能存在于多层协议中,它们总会出现在最上面的套接字层。当操作套接字选项时,选项位于的层和选项的名称必须给出。为了操作套接字层的选项,应该 将层的值指定为SOL_SOCKET。为了操作其它层的选项,控制选项的合适协议号必须给出。例如,为了表示一个选项由TCP协议解析,层应该设定为协议 号TCP。
功能描述:
获取或者设置与某个套接字关联的选 项。选项可能存在于多层协议中,它们总会出现在最上面的套接字层。当操作套接字选项时,选项位于的层和选项的名称必须给出。为了操作套接字层的选项,应该 将层的值指定为SOL_SOCKET。为了操作其它层的选项,控制选项的合适协议号必须给出。例如,为了表示一个选项由TCP协议解析,层应该设定为协议 号TCP。
#include <sys/types.h>
#include <sys/socket.h>
int getsockopt(int sock, int level, int optname, void *optval, socklen_t *optlen);
int setsockopt(int sock, int level, int optname, const void *optval, socklen_t optlen);
参数:
sock:将要被设置或者获取选项的套接字。
level:选项所在的协议层。
optname:需要访问的选项名。
optval:对于getsockopt(),指向返回选项值的缓冲。对于setsockopt(),指向包含新选项值的缓冲。
optlen:对于getsockopt(),作为入口参数时,选项值的最大长度。作为出口参数时,选项值的实际长度。对于setsockopt(),现选项的长度。
sock:将要被设置或者获取选项的套接字。
level:选项所在的协议层。
optname:需要访问的选项名。
optval:对于getsockopt(),指向返回选项值的缓冲。对于setsockopt(),指向包含新选项值的缓冲。
optlen:对于getsockopt(),作为入口参数时,选项值的最大长度。作为出口参数时,选项值的实际长度。对于setsockopt(),现选项的长度。
返回说明:
成功执行时,返回0。失败返回-1,errno被设为以下的某个值
EBADF:sock不是有效的文件描述词
EFAULT:optval指向的内存并非有效的进程空间
EINVAL:在调用setsockopt()时,optlen无效
ENOPROTOOPT:指定的协议层不能识别选项
ENOTSOCK:sock描述的不是套接字
成功执行时,返回0。失败返回-1,errno被设为以下的某个值
EBADF:sock不是有效的文件描述词
EFAULT:optval指向的内存并非有效的进程空间
EINVAL:在调用setsockopt()时,optlen无效
ENOPROTOOPT:指定的协议层不能识别选项
ENOTSOCK:sock描述的不是套接字
首先在内核态注册socket,使用函数:
nf_register_sockopt();
nf_register_sockopt函数需要参数为nf_sockopt_ops结构体,nf_sockopt_ops结构体实现:
static struct nf_sockopt_ops imp1_sockops =
{
.pf = PF_INET,
.set_optmin = IMP1_SET,
.set_optmax = IMP1_MAX,
.set = data_to_kernel,
.get_optmin = IMP1_GET,
.get_optmax = IMP1_MAX,
.get = data_from_kernel,
};
应用程序在调用的setsockopt函数后内核态程序会使用data_to_kernel函数处理,把传入setsocketopt的参数传递给data_to_kernel函数。
如果应用程序调用getsockopt函数后内核态程序会使用data_from_kernel函数处理,同样把传入getsockopt的参数传递给data_from_kernel函数。
data_to_kernel、data_from_kernel这两个函数为数据处理函数。
set_optmin、set_optmax、get_optmin、get_optmax这四个参数设置传入参数范围。
应用程序创建socket套接字,然后使用setsocketopt或者getsockopt函数就可以实现用户态与内核态的数据交互。
用户态代码:
#include <unistd.h>
#include <stdio.h>
#include <sys/socket.h>
#include <linux/in.h>
#include "imp1.h"
#define UMSG "a message from userspace\n"
#define UMSG_LEN sizeof("a message from userspace\n")
char kmsg[64];
int main(void)
{
int sockfd;
int len;
sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
if(sockfd < 0)
{
printf("can not create a socket\n");
return -1;
}
/*call function data_to_kernel()*/
setsockopt(sockfd, IPPROTO_IP, IMP1_SET, UMSG, UMSG_LEN);
len = sizeof(char)*64;
/*call function data_from_kernel()*/
getsockopt(sockfd, IPPROTO_IP, IMP1_GET, kmsg, &len);
printf("kmsg: %s", kmsg);
close(sockfd);
return 0;
}
内核态代码:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/string.h>
#include <linux/netfilter_ipv4.h>
#include <linux/init.h>
#include <asm/uaccess.h>
#include "imp1.h"
#define KMSG "a message from kernel\n"
#define KMSG_LEN sizeof("a message from kernel\n")
MODULE_LICENSE("GPL");
static int data_to_kernel(struct sock *sk, int cmd, void *user,unsigned int len)
{
switch(cmd)
{
case IMP1_SET:
{
char umsg[64];
memset(umsg, 0, sizeof(char)*64);
copy_from_user(umsg, user, sizeof(char)*64);
printk("umsg: %s", umsg);
}
break;
}
return 0;
}
static int data_from_kernel(struct sock *sk, int cmd, void *user, int *len)
{
switch(cmd)
{
case IMP1_GET:
{
copy_to_user(user, KMSG, KMSG_LEN);
}
break;
}
return 0;
}
static struct nf_sockopt_ops imp1_sockops =
{
.pf = PF_INET,
.set_optmin = IMP1_SET,
.set_optmax = IMP1_MAX,
.set = data_to_kernel,
.get_optmin = IMP1_GET,
.get_optmax = IMP1_MAX,
.get = data_from_kernel,
};
static int __init init(void)
{
return nf_register_sockopt(&imp1_sockops);
}
static void __exit fini(void)
{
nf_unregister_sockopt(&imp1_sockops);
}
module_init(init);
module_exit(fini);
GitHub 加速计划 / li / linux-dash
10.39 K
1.2 K
下载
A beautiful web dashboard for Linux
最近提交(Master分支:2 个月前 )
186a802e
added ecosystem file for PM2 4 年前
5def40a3
Add host customization support for the NodeJS version 4 年前
更多推荐
已为社区贡献2条内容
所有评论(0)