vppinfra---socket api

2023-03-07 17:04:26 浏览数 (3)

今天来学习一下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
}

1 人点赞