今天来学习一下vpp底层基础库--socket相关api及结构体,往期相关的文章请翻看文末链接。
结构体说明
socket.h文件就下面一个结构体clib_socket_t,保存socket一些基本属性。
代码语言:javascript复制typedef struct _socket_t
{
i32 fd;/*socket 文件描述符 */
char *config;/*配置字符串,用于解析hostname及端口号 Host:port or host*/
u32 flags;
#define CLIB_SOCKET_F_IS_SERVER (1 << 0)/*表示当前是server端*/
#define CLIB_SOCKET_F_IS_CLIENT (0 << 0)/*表示当前是client端*/
#define CLIB_SOCKET_F_RX_END_OF_FILE (1 << 2)/*表示对端关闭了socket链接*/
#define CLIB_SOCKET_F_NON_BLOCKING_CONNECT (1 << 3)/*需要设置成非阻塞模式*/
/*PF_LOCAL:本地域套接字类型下,置为下面标识,标识设置套接字对应文件的属性是用户组可写权限*/
#define CLIB_SOCKET_F_ALLOW_GROUP_WRITE (1 << 4)
#define CLIB_SOCKET_F_SEQPACKET (1 << 5)/*socket 类型SOCK_SEQPACKET : SOCK_STREAM*/
/*设置套接字SO_PASSCRED属性:允许或禁止SCM_CREDENTIALS 控制消息的接收。*/
#define CLIB_SOCKET_F_PASSCRED (1 << 6)
/* 报文发送缓存区*/
u8 *tx_buffer;
/* 报文接收缓存区*/
u8 *rx_buffer;
/* socket连接成功后,保存对端socket 信息*/
struct sockaddr_in peer;
/* Credentials, populated if CLIB_SOCKET_F_PASSCRED is set */
pid_t pid;
uid_t uid;
gid_t gid;
/*对应的处理函数指针,可以初始化时由用户指定,也可以使用缺省函数*/
clib_error_t *(*write_func) (struct _socket_t * sock);
clib_error_t *(*read_func) (struct _socket_t * sock, int min_bytes);
clib_error_t *(*close_func) (struct _socket_t * sock);
clib_error_t *(*recvmsg_func) (struct _socket_t * s, void *msg, int msglen,
int fds[], int num_fds);
clib_error_t *(*sendmsg_func) (struct _socket_t * s, void *msg, int msglen,
int fds[], int num_fds);
/* 私有字段*/
uword private_data;
} clib_socket_t;
flags:在flags字段由用户指定,在clib_socket_init初始化时根据不同的flags参数设置socket信息。其中下面几个字段比较特殊: 1、CLIB_SOCKET_F_SEQPACKET:设置socket 类型,置位时设置成SOCK_SEQPACKET,否则设置成:SOCK_STREAM。 SOCK_SEQPACKET套接字类型类似于SOCK_STREAM类型,也是面向连接的。区别是使用SOCK_SEQPACKET类型维护记录边界。一个记录可以使用一个或多个输出操作发送,也可以使用一个或多个输入操作接收,但是一个操作从不传输多个记录的部分。通过recvmsg()函数返回的接收消息标志中的MSG_EOR标志,接收方可以看到记录边界。是否施加最大记录大小取决于协议。
在vpp代码中使用stat_segment模块在初始化时,flag标识位置位CLIB_SOCKET_F_SEQPACKET,对应在发送和接收函数使用sendmsg_func及recvmsg_func。
linux 系统使用man 2 socket 类型查询说明: SOCK_STREAM:提供提供顺序的、可靠的、双向的、基于连接的字节流稳定数据传输,即TCP协议。 SOCK_DGRAM:支持数据报(固定最大长度的无连接、不可靠的消息,UDP协议。 SOCK_SEQPACKET:提供连续可靠的数据包连接。为固定最大长度的数据报提供顺序的、可靠的、基于双向连接的数据传输路径;消费者需要读取每个输入系统调用的整个数据包。 SOCK_RAW:提供原始网络协议存取。 SOCK_RDM:提供可靠的数据包连接。 SOCK_PACKET:与网络驱动程序直接通信。
2、CLIB_SOCKET_F_PASSCRED 用于设置unix域套接字SO_PASSCRED属性。 下面man手册给的解释,看来这个字段置位时,会存储uid,gid,pid信息。此字段置位后,通信应该是recvmsg、secnmsg接口函数。
SO_PASSCRED启用此套接字选项将导致在随后接收到的每个消息中的SCM_CREDENTIALS辅助消息中接收到发送进程的凭据。返回的凭据是由发送方使用SCM_CREDENTIALS指定的凭据,或者是包含发送方的PID、真实用户ID和真实组ID(如果发送方没有指定SCM_CREDENTIALS辅助数据)的默认凭据。当设置了这个选项并且套接字还没有连接时,抽象命名空间中的唯一名称将自动生成。
3、收发报文函数指针。用户可以自己设定,如果不指定默认使用缺省函数。
在代码中,在插件memif接口信息使用的时注册时提供的函数接口。其他的目前看都是使用的缺省函数。
api函数
主要说明一下初始化和accept函数。其他api相对简单一些。 1、初始化
代码语言:javascript复制__clib_export clib_error_t *clib_socket_init (clib_socket_t * s)
根据用户设置s的参数完成socket初始化动作,包含回调函数初始化,socket初始化,server端(bind、listen)、client端(完成connet动作)等。并根据flags参数设定socket属性。
2、clib_socket_accept接收客户端连接
代码语言:javascript复制__clib_export clib_error_t *
clib_socket_accept (clib_socket_t * server, clib_socket_t * client)
服务器端accept客户端连接请求后,设置客户端套接字非阻塞模式,并保存客户端的地址信息。设置flag为CLIB_SOCKET_F_IS_CLIENT,初始化处理函数等。
这里需要主要client参数在clib_socket_accept 函数中会被初始化,所以需要主要clinet函数内的一些资源(config、tx or rx buffer),代码书写不当可能会导致泄漏
示例说明
下面以命令行cli为例说明socket使用: 1、startup设置cli-listen config vpp可以提供了三种cli-listen模式(如下所示)
代码语言:javascript复制unix {
#cli-listen /run/vpp/cli.sock #unix域套接字。使用vppctl命令登录
#cli-listen localhost:5002 #INADDR_LOOPBACK, 也就是绑定地址LOOPBAC, 往往是127.0.0.1, 只能收到127.0.0.1上面的连接请求。
cli-listen 0.0.0.0:5002 #INADDR_ANY是ANY,是绑定地址0.0.0.0上的监听, 能收到任意一块网卡的连接。
}
2、解析配置文件并完成cli socket初始化
代码语言:javascript复制unix_cli_config
| |--unix_config()/*用于解析startup.conf中unix模块字段*/
| |--判断已经配置cli-listen,调用clib_socket_init完成初始化。
| |--clib_file_add()/*调用file add将套接字加入到epool中*/
cli_file_add相关的可以看一下之前的文章:vppinfra--- file.h: unix file handling。
3、查询命令行
代码语言:javascript复制#1、查询linux系统cli建立连接情况
[root@edge_auto_6 bin]# netstat -lantp | grep vpp
tcp 0 0 0.0.0.0:5002 0.0.0.0:* LISTEN 17649/vpp #server
tcp 0 0 127.0.0.1:5002 127.0.0.1:51534 ESTABLISHED 17649/vpp #telnet 127.0.0.0 5002登录vppcli模式
tcp 0 0 192.168.0.102:5002 192.168.0.100:64064 ESTABLISHED 17649/vpp #在主机192.168.0.100上telnet 192.168,0.102 5002主机
#2、查询unix files信息
其中文件描述符30:是cli server端,32-34时登录cli后建立的tcp连接
learning_vpp# show unix files
FD Thread Read Write Error File Name Description
11 0 0 0 0 socket:[86752] stats segment listener /run/vpp/stats.sock
13 0 1 0 0 pipe:[86755] DPDK logging pipe
30 0 3 0 0 socket:[86767] cli listener 0.0.0.0:5002
31 0 0 0 0 socket:[86769] socksvr /run/vpp/api.sock
32 0 34 1 0 socket:[86774] 127.0.0.1:51534
33 0 30 1 0 socket:[86785] 192.168.0.100:64064
34 0 1 1 0 socket:[87403] 127.0.0.1:51536
#查询unix-cli 节点
cli建立后,为每个连接创建一个process 类型的node节点,用于接收cli输入。
unix-cli-127.0.0.1:51536 event wait 0 0 2 2.13e9 0.00
unix-cli-127.0.0.1:51538 active 1 0 68 6.24e9 0.00
unix-cli-192.168.0.100:64064 event wait 0 0 31 1.13e8 0.00
总结
本文简单了解一下socket结构体及一些api接口,对于上层如何使用未深入介绍(如下面unix_cli_listen_read_ready中四个全局变量之间的关联关系),后续有机会需要深入介绍
代码语言:javascript复制static clib_error_t *
unix_cli_listen_read_ready (clib_file_t * uf)
{
unix_main_t *um = &unix_main;
clib_file_main_t *fm = &file_main;
unix_cli_main_t *cm = &unix_cli_main;
clib_socket_t *s = &um->cli_listen_socket
}