文章目录
- 1.tle简介
- 2.tle处理tcp
- 3.tle处理udp
- 4.tle封装包头
项目中遇到了tldk中tle的使用,不太熟悉,这里记录一下,方便以后回顾
tldk源码位置: tldk源码
简单理解:这里我们项目大概dpdk从网卡收到数据包之后,将需要处理的用户数据放入tle_stream中,再从tle数据中封装好数据头部信息并发送到dpdk网卡
项目中用到的函数:
uint16_t tle_udp_rx_bulk(struct tle_dev *dev, struct rte_mbuf *pkt[], struct rte_mbuf *rp[], int32_t rc[], uint16_t
num);
功能
:接收并分发一批输入的 UDP 数据包到相应的打开的 UDP 流。
参数
:
- dev:UDP 设备。
- pkt:输入数据包的数组。
- rp:未处理数据包的数组。
- rc:错误代码数组。
- num:输入数据包数组的元素数量。
返回值
:成功分发到 UDP 流的数据包数量。
uint16_t tle_udp_stream_recv(struct tle_stream *s, struct rte_mbuf *pkt[], uint16_t num);
功能
:接收来自指定 UDP 流的多个数据包。
参数
:
- s:UDP 流。
- pkt:接收数据包的数组。
- num:接收数据包数组的元素数量。
返回值
:成功接收到的数据包数量。
uint16_t tle_udp_stream_send(struct tle_stream *s, struct rte_mbuf *pkt[], uint16_t num, const struct sockaddr *
dst_addr);
功能
:发送多个数据包到指定的 UDP 流。
参数
:
- s:UDP 流。
- pkt:待发送的数据包数组。
- num:待发送数据包数组的元素数量。
- dst_addr:目标地址。
返回值
:成功排队等待发送的数据包数量。
uint16_t tle_udp_tx_bulk(struct tle_dev *dev, struct rte_mbuf *pkt[], uint16_t num);
功能
:获取并填充准备发送的数据包。
参数
:
- dev:UDP 设备。
- pkt:输出数据包的数组。
- num:输出数据包数组的元素数量。
返回值
:成功填充的数据包数量。
1.tle简介
这里直接上代码注释
/*
本文档简要描述了用于 L4 数据包处理库(UDP 和 TCP)的公共 API。
目的是使配置 API(上下文创建/删除,添加/删除设备等)对所有支持的 L4 协议通用。
后端(BE)数据路径和前端(FE)API 不同,但尽可能保留相似的语法(和语义)。
*/tle_ctx.h
/**<tle_ctx> - 每个这样的 ctx 代表“独立的栈副本”。
它拥有 <stream> 和 <dev> 实体的集合,并提供将输入/输出数据包从设备(解)复用到流中/
流出的功能。<dev> 是对底层设备的抽象,能够接收/发送数据包,并且可能提供一些硬件卸载功能。
用户有责任在开始对上下文进行流操作(打开/发送/接收/
关闭)之前,将上下文需要管理的所有 <dev> 添加到 <ctx> 中。目前不支持在上下文中存在打开的流时添加/删除 <dev>。
<stream> 代表一个 L4(UDP/TCP 等)端点 <地址, 端口>,类似于套接字实体。
与套接字一样,可以通过它进行接收/发送操作。
<stream> 属于特定的 <ctx>
,但在整个进程中是全局可见的,即进程中的任何线程都可以在没有进一步同步的情况下通过它进行接收/发送操作。
虽然“上层”API 是线程安全的,但底层 API(rx_bulk/
tx_bulk)不是线程安全的,不应在多个线程上并行运行。因此,单个线程可以驱动多个 <ctx> 并进行 IO
操作,但多个线程不能在没有某些显式同步的情况下驱动相同的 <ctx>。**/
struct tle_ctx;
struct tle_dev;
/*
设备参数。
*/
struct tle_dev_param {
uint32_t rx_offload; /**< 支持 DEV_RX_OFFLOAD_。 */
uint32_t tx_offload; /**< 支持 DEV_TX_OFFLOAD_。 */
struct in_addr local_addr4; /*< 分配的本地 IPv4 地址。 */
struct in6_addr local_addr6; /*< 分配的本地 IPv6 地址。 */
uint32_t nb_bl_ports; /*< 被阻止的端口数量。 */
uint16_t *bl_ports; /*< 被阻止的端口列表。 */
};
#define TLE_DST_MAX_HDR 0x60
struct tle_dest {
struct rte_mempool *head_mp; /*< 用于碎片头和控制数据包的 MP。 */
struct tle_dev *dev; /*< 用于发送数据包的设备。 */
uint16_t mtu; /*< 给定目的地的 MTU。 */
uint8_t l2_len; /*< L2 头的长度。 */
uint8_t l3_len; /*< L3 头的长度。 */
uint8_t hdr[TLE_DST_MAX_HDR]; /*< L2/L3 头。 */
};/*** 上下文创建参数。*/
enum { TLE_PROTO_UDP,TLE_PROTO_TCP,TLE_PROTO_NUM
};struct tle_ctx_param { int32_t socket_id; /**< 分配内存的套接字 ID。 */ uint32_t proto; /**< 要处理的 L4 协议。 */uint32_t max_streams; /**< 上下文中的最大流数。 */ uint32_t max_stream_rbufs; /**< 每个流的最大接收 mbuf 数。 */uint32_t max_stream_sbufs; /**< 每个流的最大发送 mbuf 数。 */uint32_t send_bulk_size; /**< 每次发送调用的预期数据包数。 */int (*lookup4)(void *opaque, const struct in_addr *addr, struct tle_dest *res); /**< 由 send() 调用以获取
IPv4 数据包的目的地信息。 */ void *lookup4_data; /**< lookup4() 回调的非透明数据指针。 */int (*lookup6)(void *opaque, const struct in6_addr *addr, struct tle_dest *res); /**< 由 send() 调用以获取
IPv6 数据包的目的地信息。 */ void *lookup6_data; /**< lookup6() 回调的非透明数据指针。 */
};/*** 创建 L4 处理上下文。* @param ctx_prm* 用于创建和初始化 L4 上下文的参数。* @return* 可用于将来操作的上下文结构指针,或错误时返回 NULL,并在 rte_errno
中设置错误代码。* * 可能的 rte_errno 错误包括:* - EINVAL - 传递给函数的参数无效* - ENOMEM - 内存不足*/
struct tle_ctx * tle_ctx_create(const struct tle_ctx_param *ctx_prm);/*** 销毁给定的上下文。** @param ctx* 要销毁的上下文*/
void tle_ctx_destroy(struct tle_ctx *ctx);/*** 在给定上下文中添加新设备。* 该函数不是多线程安全的。** @param ctx* 要添加新设备的上下文。* @param dev_prm* 用于在上下文中创建和初始化新设备的参数。* @return* 可用于将来操作的设备结构指针,或错误时返回 NULL,并在 rte_errno 中设置错误代码。* 可能的 rte_errno 错误包括:* - EINVAL - 传递给函数的参数无效* - ENODEV - 已达到打开设备的最大可能值* - ENOMEM - 内存不足*/
struct tle_dev * tle_add_dev(struct tle_ctx *ctx, const struct tle_dev_param *dev_prm);/*** 从给定上下文中移除并销毁先前添加的设备。* 该函数不是多线程安全的。** @param dev* 要移除和销毁的设备。* @return* 成功完成返回零。* - -EINVAL - 传递给函数的参数无效*/
int tle_del_dev(struct tle_dev *dev);/*** 向上下文标记目的地信息可能已更改,* 因此如果它缓存了任何目的地数据,则必须将其无效化。* @param ctx* 要无效化的上下文。*/
void tle_ctx_invalidate(struct tle_ctx *ctx);/*** 流的异步通知机制:* a) 接收/发送回调。* 流的接收/发送通知回调行为是边沿触发(ET)。* 如果流接收缓冲区为空并且有新数据包到达,接收回调将被调用。*
当流发送缓冲区已满,并且属于该流的一些数据包已发送(发送缓冲区的一部分再次变为�
�闲)时,将调用发送回调。*
请注意,接收和发送回调都是在持有该流的某种读锁的情况下调用的。因此在回调函数中�
�用 stream_close() 是不允许的,这会导致死锁。* 虽然允许在回调中调用流发送/接收函数,但不推荐这样做:回调函数将在 tle_udp_rx_bulk/
tle_udp_tx_bulk
上下文中被调用,回调函数中的一些繁重处理可能会导致性能下降甚至丢失进一步流的数�
�包。* b) 接收/发送事件。* 流的接收/发送事件行为是电平触发(LT)。* 只要流接收缓冲区内有任何剩余数据包,接收事件将由 tle_udp_rx_burst() 或 tle_udp_stream_recv
() 触发。* 只要流发送缓冲区内有任何空闲空间,发送事件将由 tle_udp_tx_burst() 或 tle_udp_stream_send()
触发。* 请注意,回调和事件在 <stream, op> 基础上是互斥的。* 不可能在指定接收事件和回调的情况下打开流。* 虽然可以在指定接收回调和发送事件或反之的情况下打开流。* 如果用户不需要该流的任何通知机制,事件和回调都可以设置为零。*/
struct tle_event;
struct tle_stream;/*** 流的接收/发送回调函数和数据。*/
struct tle_stream_cb { void (*func)(void *, struct tle_stream *); void *data;
};
2. tle_tcp.h
------------
/*** TCP 流创建参数。*/
struct tle_tcp_stream_param {struct sockaddr_storage local_addr; /**< 流本地地址。 */struct sockaddr_storage remote_addr; /**< 流远程地址。 */uint64_t linger_cycles; /**< 延迟的周期数。 */uint32_t nb_retries; /**< 最大重传尝试次数。 *//* _cb 和 _ev 是互斥的 */struct tle_event *err_ev; /**< 使用的错误事件。 */struct tle_stream_cb err_cb; /**< 使用的错误回调。 */struct tle_event *recv_ev; /**< 使用的接收事件。 */struct tle_stream_cb recv_cb; /**< 使用的接收回调。 */struct tle_event *send_ev; /**< 使用的发送事件。 */struct tle_stream_cb send_cb; /**< 使用的发送回调。 */
};/*** 在给定的 TCP 上下文中创建一个新流。* @param ctx* 要在其中创建新流的 TCP 上下文。* @param prm* 用于创建和初始化新流的参数。* @return* 可以在将来的 TCP API 调用中使用的 TCP 流结构指针,* 或错误时返回 NULL,并在 rte_errno 中设置错误代码。* 可能的 rte_errno 错误包括:* - EINVAL - 传递给函数的参数无效* - ENOFILE - 上下文中打开流的最大限制已达到*/
struct tle_stream * tle_tcp_stream_open(struct tle_ctx *ctx, const struct tle_tcp_stream_param *prm);/*** 关闭一个已打开的流。* 如果流处于连接状态,则:* - 将执行连接终止。* - 如果流包含未发送的数据,则实际关闭将被推迟,* 直到剩余数据被发送或延迟超时过期。* 所有属于该流并保留在设备 TX 队列中的数据包将被保留以供以后发送。* @param s* 要关闭的流的指针。* @return* 成功完成返回零。* - -EINVAL - 传递给函数的参数无效*/
int tle_tcp_stream_close(struct tle_stream *s);/*** 获取打开流的参数。* @param s* 流的指针。* @return* 成功完成返回零。* - EINVAL - 传递给函数的参数无效*/
int tle_tcp_stream_get_param(const struct tle_stream *s, struct tle_tcp_stream_param *prm);/*** 中止连接并关闭一个已打开的流。* 如果流处于连接状态,则:* 丢弃所有排队的数据并向对等端发送重置段。* 所有属于该流并保留在设备 TX 队列中的数据包将被保留以供以后发送。* @param s* 要中止的流的指针。* @return* 成功完成返回零。* - -EINVAL - 传递给函数的参数无效*/
int tle_tcp_stream_abort(struct tle_stream *s);/*** 客户端模式连接 API。*/
/*** 尝试与目标 TCP 端点建立连接。* 如果连接成功建立,将触发流写入事件(或回调)。* 请注意,处于监听状态的流或已建立连接的流不能进行 connect() 调用。* 如果尝试失败,将激活错误事件(或回调)。* @param s* 流的指针。* @param addr* 目标端点的地址。* @return* 成功完成返回零。* - -EINVAL - 传递给函数的参数无效*/
int tle_tcp_stream_connect(struct tle_stream *s, const struct sockaddr *addr);/** 服务器模式连接 API。* 服务器模式 API 使用的基本方案:** <stream 打开在此处进行>* tle_tcp_stream_listen(stream_to_listen);* <等待该流上的读取事件/回调>* n = tle_tcp_synreqs(stream_to_listen, syn_reqs, sizeof(syn_reqs));* for (i = 0, k = 0; i != n; i++) {* rc = <决定是否允许来自该端点的连接>;* if (rc == 0) {* // 继续进行连接建立* k++;* accept_param[k].syn = syn_reqs[i];* <为第 k 个连接填充 accept_param 其他字段>* } else {* // 静默忽略来自该端点的连接请求* rte_pktmbuf_free(syn_reqs[i]);* }* }* // 接受 k 个新连接* rc = tle_tcp_accept(stream_to_listen, accept_param, new_con_streams, k);* <处理错误>*/
/*** 将流设置为监听状态(被动打开),即使流准备接受新连接。* 当有新的 SYN 请求到达时,将激活流读取事件(或回调)。* 请注意,已建立(或正在建立)连接的流不能进行 listen() 调用。* @param s* 流的指针。* @return* 成功完成返回零。* - -EINVAL - 传递给函数的参数无效*/
int tle_tcp_stream_listen(struct tle_stream *s);/*** 返回最多 *num* 个包含给定 TCP 端点接收的 SYN 请求的 mbuf。* 请注意,流必须处于监听状态。* 对于每个返回的 mbuf:* data_off 设置为数据包的起始位置* l2_len、l3_len、l4_len 设置为适当的值* (因此用户仍然可以在需要时提取 L2/L3/L4 头信息)* packet_type 的 RTE_PTYPE_L2/L3/L4 位设置为适当的值。* L3/L4 校验和已验证。* @param s* 接收数据包的 TCP 流。* @param pkt* 一个指向 *rte_mbuf* 结构的指针数组,* 必须足够大以存储最多 *num* 个指针。* @param num* *pkt* 数组中的元素数量。* @return* 在 *pkt* 数组中填充的条目数量。*/
uint16_t tle_tcp_stream_synreqs(struct tle_stream *s, struct rte_mbuf *pkt[], uint32_t num);struct tle_tcp_accept_param {struct rte_mbuf *syn; /**< 包含传入 SYN 请求的 mbuf。 */struct tle_tcp_stream_param prm; /**< 流打开参数。 */
};/*** 接受给定流的连接请求。* 请注意,流必须处于侦听状态。* 对于每个新连接,将打开一个新流。* @param s* TCP 侦听流。* @param prm* 包含至少 *num* 个元素的 *tle_tcp_accept_param* 结构数组。* @param pkt* 一个指向 *tle_stream* 结构的指针数组,该数组必须足够大以存储最多 *num* 个指针。* @param num* *prm* 和 *rs* 数组中的元素数量。* @return* 填充在 *rs* 数组中的条目数量。* 错误情况下,在 rte_errno 中设置错误代码。* 可能的 rte_errno 错误包括:* - EINVAL - 传递给函数的参数无效* - ENFILE - 无法打开更多流。*/
int tle_tcp_stream_accept(struct tle_stream *s, const struct tle_tcp_accept_param prm[], struct tle_stream *rs[],
uint32_t num);/*** 返回为给定 TCP 流接收到的最多 *num* 个 mbuf。* 请注意,流必须处于连接状态。* 数据顺序被保留。* 对于每个返回的 mbuf:* data_off 设置为数据包的 TCP 数据起始位置* l2_len、l3_len、l4_len 设置正确* (因此用户仍然可以在需要时提取 L2/L3 地址信息)* packet_type RTE_PTYPE_L2/L3/L4 位设置正确。* 已验证 L3/L4 校验和。* @param s* 接收数据包的 TCP 流。* @param pkt* 一个指向 *rte_mbuf* 结构的指针数组,该数组必须足够大以存储最多 *num* 个指针。* @param num* *pkt* 数组中的元素数量。* @return* 填充在 *pkt* 数组中的条目数量。*/
uint16_t tle_tcp_stream_recv(struct tle_stream *s, struct rte_mbuf *pkt[], uint16_t num);/*** 消耗并排队最多 *num* 个数据包,这些数据包将最终由 tle_tcp_tx_bulk() 发送。* 请注意,流必须处于连接状态。* 该函数负责确定给定数据包要通过哪个 TCP 设备发送,并进行必要的准备。* 根据 *dst_addr* 执行路由查找,填写 L2/L3/L4 头,并在必要时对数据包进行分片。* 根据底层设备信息,它要么在软件中执行 IP/TCP 校验和计算,要么正确设置 mbuf TX
校验和卸载字段。* 对于每个输入 mbuf,必须满足以下条件:* - data_off 指向数据包的 TCP 数据起始位置。* - 有足够的头部空间预先添加 L2/L3/L4 头。* @param s* 用于发送数据包的 TCP 流。* @param pkt* 需要发送的输出数据包。* @param num* *pkt* 数组中的元素数量。* @return* 成功排队到流发送缓冲区中的数据包数量。*/
uint16_t tle_tcp_stream_send(struct tle_stream *s, struct rte_mbuf *pkt[], uint16_t num);/*** 从给定的 TCP 流中读取最多 *iovcnt* 个缓冲区。* 请注意,流必须处于连接状态。* @param s* 用于读取数据的 TCP 流。* @param iov* 一个包含 *iovec* 结构的数组,该数组必须足够大以存储最多 *iovcnt* 个元素。* @param iovcnt* *iov* 数组中的元素数量。* @return* 成功完成时读取的字节数。* - EINVAL - 传递给函数的参数无效* - ENOTCONN - 流未连接*/
ssize_t tle_tcp_readv(struct tle_stream *s, const struct iovec iov[], int iovcnt);/*** 向给定的 TCP 流写入最多 *iovcnt* 个缓冲区。* 请注意,流必须处于连接状态。* @param s* 用于写入数据的 TCP 流。* @param mp* 用于分配 mbuf 的内存池。* @param iov* 一个包含 *iovec* 结构的数组,该数组必须足够大以存储最多 *iovcnt* 个元素。* @param iovcnt* *iov* 数组中的元素数量。* @return* 成功完成时写入的字节数。* - EINVAL - 传递给函数的参数无效* - ENOTCONN - 流未连接*/
ssize_t tle_tcp_writev(struct tle_stream *s, struct rte_mempool *mp, const struct iovec iov[], int iovcnt);/*** 后端 (BE) API。* BE API 函数不是多线程安全的。* 应由 L2/L3 处理层调用。*/
/*** 获取输入的 mbuf 并将它们分配给打开的 TCP 流。* 期望每个输入数据包:* - l2_len、l3_len、l4_len 设置正确* - (packet_type & (RTE_PTYPE_L3_IPV4 | RTE_PTYPE_L3_IPV6)) != 0,* - (packet_type & RTE_PTYPE_L4_TCP) != 0,* 在传递过程中将验证 L3/L4 校验和* (要么依赖于硬件卸载,要么在软件中验证)。* 可能会导致一些额外的数据包排队等待 TX。* 此函数不是多线程安全的。* @param dev* 接收数据包的 TCP 设备。* @param pkt* 需要处理的输入数据包。* @param rp* 返回时将包含未处理数据包指针的数组。* 应包含至少 *num* 个元素。* @param rc* 将包含相应 rp[] 条目的错误代码的数组:* - ENOENT - 没有匹配此数据包的打开流。* - ENOBUFS - 目标流的接收缓冲区已满。* 应包含至少 *num* 个元素。* @param num* *pkt* 输入数组中的元素数量。* @return* 传递到 TCP 流的数据包数量。*/
uint16_t tle_tcp_rx_bulk(struct tle_dev *dev, struct rte_mbuf *pkt[], struct rte_mbuf *rp[], int32_t rc[], uint16_t
num);/*** 用指向要通过给定 TCP 设备传输的数据包的指针填充 *pkt*。* 输出数据包必须准备好直接传递给 rte_eth_tx_burst(),无需额外处理。* TCP/IPv4 校验和已计算,或适当的 mbuf 字段已正确设置以进行硬件卸载。* 此函数不是多线程安全的。* @param dev* 传输输出数据包的 TCP 设备。* @param pkt* 一个指向 *rte_mbuf* 结构的指针数组,该数组必须足够大以存储最多 *num* 个指针。* @param num* *pkt* 数组中的元素数量。* @return* 填充在 *pkt* 数组中的条目数量。*/
uint16_t tle_tcp_tx_bulk(struct tle_dev *dev, struct rte_mbuf *pkt[], uint16_t num);/*** 对给定的 TCP 上下文执行内部处理。* 检查哪些定时器已过期并执行所需的操作* (重传/连接中止等)。* 可能会导致一些额外的数据包排队等待 TX。* 此函数不是多线程安全的。* @param ctx* 要处理的 TCP 上下文。* @return* 成功完成返回零。* - EINVAL - 传递给函数的参数无效*/
int tle_tcp_process(struct tle_ctx *ctx);/*** UDP 流创建参数。*/
struct tle_udp_stream_param {struct sockaddr_storage local_addr; /**< 流的本地地址。 */struct sockaddr_storage remote_addr; /**< 流的远程地址。 *//* _cb 和 _ev 是互斥的 */struct tle_event *recv_ev; /**< 接收事件。 */struct tle_stream_cb recv_cb; /**< 接收回调。 */struct tle_event *send_ev; /**< 发送事件。 */struct tle_stream_cb send_cb; /**< 发送回调。 */
};/*** 在给定的 UDP 上下文中创建新流。* @param ctx* 创建新流的 UDP 上下文。* @param prm* 用于创建和初始化新流的参数。* @return* 指向 UDP 流结构的指针,可在将来的 UDP API 调用中使用,* 出错时返回 NULL,并在 rte_errno 中设置错误代码。* 可能的 rte_errno 错误包括:* - EINVAL - 传递给函数的参数无效* - ENOFILE - 该上下文的打开流的最大限制已达到*/
struct tle_stream * tle_udp_stream_open(struct tle_ctx *ctx, const struct tle_udp_stream_param *prm);/*** 关闭打开的流。* 所有仍保留在流接收缓冲区中的数据包将被释放。* 所有仍保留在流传输缓冲区中的数据包将被保留以供将来传输。* @param s* 指向要关闭的流的指针。* @return* 成功完成返回零。* - -EINVAL - 传递给函数的参数无效*/
int tle_udp_stream_close(struct tle_stream *s);/*** 获取打开的流参数。* @param s* 指向流的指针。* @param prm* 用于存储流参数的结构。* @return* 成功完成返回零。* - EINVAL - 传递给函数的参数无效*/
int tle_udp_stream_get_param(const struct tle_stream *s, struct tle_udp_stream_param *prm);/*** 获取输入的 mbuf 并将它们分配给打开的 UDP 流。* 期望每个输入数据包:* - l2_len、l3_len、l4_len 设置正确* - (packet_type & (RTE_PTYPE_L3_IPV4 | RTE_PTYPE_L3_IPV6)) != 0,* - (packet_type & RTE_PTYPE_L4_UDP) != 0,* 在传递过程中将验证 L3/L4 校验和* (要么依赖于硬件卸载,要么在软件中验证)。* 此函数不是多线程安全的。* @param dev* 接收数据包的 UDP 设备。* @param pkt* 需要处理的输入数据包。* @param rp* 返回时将包含未处理数据包指针的数组。* 应包含至少 *num* 个元素。* @param rc* 将包含相应 rp[] 条目的错误代码的数组:* - ENOENT - 没有匹配此数据包的打开流。* - ENOBUFS - 目标流的接收缓冲区已满。* 应包含至少 *num* 个元素。* @param num* *pkt* 输入数组中的元素数量。* @return* 传递到 UDP 流的数据包数量。*/
uint16_t tle_udp_rx_bulk(struct tle_dev *dev, struct rte_mbuf *pkt[], struct rte_mbuf *rp[], int32_t rc[], uint16_t
num);/*** 用指向要通过给定 UDP 设备传输的数据包的指针填充 *pkt*。* 输出数据包必须准备好直接传递给 rte_eth_tx_burst(),无需额外处理。* UDP/IPv4 校验和已计算,或适当的 mbuf 字段已正确设置以进行硬件卸载。* 此函数不是多线程安全的。* @param dev* 传输输出数据包的 UDP 设备。* @param pkt* 一个指向 *rte_mbuf* 结构的指针数组,该数组必须足够大以存储最多 *num* 个指针。* @param num* *pkt* 数组中的元素数量。* @return* 填充在 *pkt* 数组中的条目数量。*/
uint16_t tle_udp_tx_bulk(struct tle_dev *dev, struct rte_mbuf *pkt[], uint16_t num);/*** 返回为给定 UDP 流接收到的最多 *num* 个 mbuf。* 对于每个返回的 mbuf:* data_off 设置为数据包的 UDP 数据起始位置* l2_len、l3_len、l4_len 设置正确* (因此用户仍然可以在需要时提取 L2/L3 地址信息)* packet_type RTE_PTYPE_L2/L3/L4 位设置正确。* 已验证 L3/L4 校验和。* 数据包具有无效的 L3/L4 校验和将被默默丢弃。* @param s* 接收数据包的 UDP 流。* @param pkt* 一个指向 *rte_mbuf* 结构的指针数组,该数组必须足够大以存储最多 *num* 个指针。* @param num* *pkt* 数组中的元素数量。* @return* 填充在 *pkt* 数组中的条目数量。*/
uint16_t tle_udp_stream_recv(struct tle_stream *s, struct rte_mbuf *pkt[], uint16_t num);/*** 消耗并排队最多 *num* 个数据包,这些数据包将最终由 tle_udp_tx_bulk() 发送。* 如果 *dst_addr* 为 NULL,则将使用与该流关联的默认远程地址(如果有)。* 该函数的主要目的是确定给定数据包要通过哪个 UDP 设备发送,并进行必要的准备。* 根据 *dst_addr* 执行路由查找,填写 L2/L3/L4 头,并在必要时对数据包进行分片。* 根据底层设备信息,它要么在软件中执行 IP/UDP 校验和计算,要么正确设置 mbuf TX
校验和卸载字段。* 对于每个输入 mbuf,必须满足以下条件:* - data_off 指向数据包的 UDP 数据起始位置。* - 有足够的头部空间预先添加 L2/L3/L4 头。* @param s* 用于发送数据包的 UDP 流。* @param pkt* 需要发送的输出数据包。* @param num* *pkt* 数组中的元素数量。* @param dst_addr* 要发送数据包的目标地址。* @return* 成功排队到流发送缓冲区中的数据包数量。*/
uint16_t tle_udp_stream_send(struct tle_stream *s, struct rte_mbuf *pkt[], uint16_t num, const struct sockaddr *
dst_addr);
2.tle处理tcp
以下是一个使用TLE库进行TCP连接管理和数据传输的示例代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <rte_eal.h>
#include <rte_mbuf.h>
#include <tle_tcp.h>
#include <tle_event.h>// 回调函数,用于处理接收的数据包
void recv_callback(struct tle_stream *s, struct rte_mbuf *pkt[], uint16_t num)
{for (uint16_t i = 0; i < num; i++) {// 处理接收到的数据包char *data = rte_pktmbuf_mtod(pkt[i], char *);printf("Received data: %s\n", data);// 释放mbufrte_pktmbuf_free(pkt[i]);}
}int main(int argc, char **argv)
{// 初始化环境抽象层(EAL)if (rte_eal_init(argc, argv) < 0) {rte_exit(EXIT_FAILURE, "EAL init failed\n");}// 创建TCP上下文struct tle_ctx_param ctx_prm = {0};struct tle_ctx *ctx = tle_ctx_create(&ctx_prm);if (ctx == NULL) {rte_exit(EXIT_FAILURE, "Context creation failed\n");}// 创建TCP监听流struct tle_tcp_stream_param listen_prm = {0};struct tle_stream *listen_stream = tle_tcp_stream_open(ctx, &listen_prm);if (listen_stream == NULL) {rte_exit(EXIT_FAILURE, "Stream open failed\n");}// 设置回调函数struct tle_event recv_event = {0};recv_event.data.stream = listen_stream;recv_event.cb.func = (tle_event_cb)recv_callback;// 添加监听事件if (tle_event_add(ctx, &recv_event) < 0) {rte_exit(EXIT_FAILURE, "Event add failed\n");}// 主循环,等待事件触发while (1) {// 处理事件tle_event_process(ctx);}// 关闭流和上下文tle_tcp_stream_close(listen_stream);tle_ctx_destroy(ctx);return 0;
}
代码说明
- 初始化环境抽象层(EAL):TLE库依赖于DPDK(Data Plane Development Kit),需要首先初始化EAL。
- 创建TCP上下文:调用 tle_ctx_create 创建一个TCP上下文,用于管理TCP连接。
- 创建TCP监听流:调用 tle_tcp_stream_open 创建一个监听流,用于监听新的TCP连接请求。
- 设置回调函数:定义一个回调函数 recv_callback,用于处理接收到的数据包。并将回调函数与事件关联。
- 添加监听事件:调用 tle_event_add 将监听事件添加到上下文中。
- 主循环:在主循环中调用 tle_event_process 处理事件,等待并处理接收到的数据包。
- 关闭流和上下文:当不再需要时,关闭TCP流并销毁上下文。
3.tle处理udp
以下是一个使用TLE库进行UDP连接管理和数据传输的示例代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <rte_eal.h>
#include <rte_mbuf.h>
#include <tle_udp.h>
#include <tle_event.h>// 回调函数,用于处理接收的数据包
void recv_callback(struct tle_stream *s, struct rte_mbuf *pkt[], uint16_t num)
{for (uint16_t i = 0; i < num; i++) {// 处理接收到的数据包char *data = rte_pktmbuf_mtod(pkt[i], char *);printf("Received data: %s\n", data);// 释放mbufrte_pktmbuf_free(pkt[i]);}
}int main(int argc, char **argv)
{// 初始化环境抽象层(EAL)if (rte_eal_init(argc, argv) < 0) {rte_exit(EXIT_FAILURE, "EAL init failed\n");}// 创建UDP上下文struct tle_ctx_param ctx_prm = {0};struct tle_ctx *ctx = tle_ctx_create(&ctx_prm);if (ctx == NULL) {rte_exit(EXIT_FAILURE, "Context creation failed\n");}// 创建UDP流参数struct tle_udp_stream_param stream_prm = {0};// 设置本地地址和端口struct sockaddr_in *local_addr = (struct sockaddr_in *)&stream_prm.local_addr;local_addr->sin_family = AF_INET;local_addr->sin_addr.s_addr = htonl(INADDR_ANY); // 监听所有本地地址local_addr->sin_port = htons(12345); // 设置本地端口为12345// 设置回调函数stream_prm.recv_cb.func = (tle_stream_cb)recv_callback;// 创建UDP流struct tle_stream *udp_stream = tle_udp_stream_open(ctx, &stream_prm);if (udp_stream == NULL) {rte_exit(EXIT_FAILURE, "Stream open failed\n");}// 主循环,等待事件触发while (1) {// 处理接收数据包struct rte_mbuf *pkts[32];uint16_t nb_rx = tle_udp_stream_recv(udp_stream, pkts, 32);if (nb_rx > 0) {// 调用回调函数处理接收的数据包recv_callback(udp_stream, pkts, nb_rx);}}// 关闭流和上下文tle_udp_stream_close(udp_stream);tle_ctx_destroy(ctx);return 0;
}
代码说明
:
-
初始化环境抽象层(EAL):TLE库依赖于DPDK(Data Plane Development Kit),需要首先初始化EAL。
-
创建UDP上下文:调用 tle_ctx_create 创建一个UDP上下文,用于管理UDP连接。
-
创建UDP流参数:设置UDP流的参数,包括本地地址和端口,以及接收回调函数。
-
设置本地地址和端口:通过 struct sockaddr_in 设置本地地址为 INADDR_ANY(监听所有本地地址),并设置本地端口为12345。
-
设置回调函数:定义一个回调函数 recv_callback,用于处理接收到的数据包,并将回调函数与接收事件关联。
-
创建UDP流:调用 tle_udp_stream_open 创建一个UDP流,用于接收和发送UDP数据包。
-
主循环:在主循环中调用 tle_udp_stream_recv 接收数据包,并调用回调函数 recv_callback 处理接收到的数据包
-
关闭流和上下文:当不再需要时,关闭UDP流并销毁上下文。
这个示例代码展示了如何使用TLE库进行UDP数据包的接收和处理。通过设置回调函数,接收到的数据包会被自动传递到回调函数进行处理。TLE库简化了UDP连接的管理和数据传输,使得开发高性能的网络应用程序变得更加容易。
4.tle封装包头
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <rte_eal.h>
#include <rte_mbuf.h>
#include <tle_udp.h>
#include <tle_event.h>// 自定义包头结构
struct custom_header {uint32_t id;uint32_t length;
};// 回调函数,用于处理接收的数据包并添加自定义包头
void recv_callback(struct tle_stream *s, struct rte_mbuf *pkt[], uint16_t num)
{for (uint16_t i = 0; i < num; i++) {// 获取接收到的数据char *data = rte_pktmbuf_mtod(pkt[i], char *);uint16_t data_len = rte_pktmbuf_data_len(pkt[i]);// 分配新的mbuf来存储带有自定义包头的数据包struct rte_mbuf *new_pkt = rte_pktmbuf_alloc(pkt[i]->pool);if (new_pkt == NULL) {printf("Failed to allocate new mbuf\n");rte_pktmbuf_free(pkt[i]);continue;}// 添加自定义包头struct custom_header *hdr = (struct custom_header *)rte_pktmbuf_prepend(new_pkt, sizeof(struct custom_header));if (hdr == NULL) {printf("Failed to prepend custom header\n");rte_pktmbuf_free(pkt[i]);rte_pktmbuf_free(new_pkt);continue;}hdr->id = htonl(12345); // 示例IDhdr->length = htonl(data_len); // 数据长度// 复制接收到的数据到新的mbufchar *new_data = rte_pktmbuf_append(new_pkt, data_len);if (new_data == NULL) {printf("Failed to append data\n");rte_pktmbuf_free(pkt[i]);rte_pktmbuf_free(new_pkt);continue;}rte_memcpy(new_data, data, data_len);// 处理新的数据包printf("Added custom header and copied data. New packet length: %u\n", rte_pktmbuf_pkt_len(new_pkt));// 释放原来的mbufrte_pktmbuf_free(pkt[i]);// 假设发送或进一步处理新的数据包// 这里可以调用tle_udp_stream_send等函数发送新的数据包}
}int main(int argc, char **argv)
{// 初始化环境抽象层(EAL)if (rte_eal_init(argc, argv) < 0) {rte_exit(EXIT_FAILURE, "EAL init failed\n");}// 创建UDP上下文struct tle_ctx_param ctx_prm = {0};struct tle_ctx *ctx = tle_ctx_create(&ctx_prm);if (ctx == NULL) {rte_exit(EXIT_FAILURE, "Context creation failed\n");}// 创建UDP流参数struct tle_udp_stream_param stream_prm = {0};// 设置本地地址和端口struct sockaddr_in *local_addr = (struct sockaddr_in *)&stream_prm.local_addr;local_addr->sin_family = AF_INET;local_addr->sin_addr.s_addr = htonl(INADDR_ANY); // 监听所有本地地址local_addr->sin_port = htons(12345); // 设置本地端口为12345// 设置回调函数stream_prm.recv_cb.func = (tle_stream_cb)recv_callback;// 创建UDP流struct tle_stream *udp_stream = tle_udp_stream_open(ctx, &stream_prm);if (udp_stream == NULL) {rte_exit(EXIT_FAILURE, "Stream open failed\n");}// 主循环,等待事件触发while (1) {// 处理接收数据包struct rte_mbuf *pkts[32];uint16_t nb_rx = tle_udp_stream_recv(udp_stream, pkts, 32);if (nb_rx > 0) {// 调用回调函数处理接收的数据包recv_callback(udp_stream, pkts, nb_rx);}}// 关闭流和上下文tle_udp_stream_close(udp_stream);tle_ctx_destroy(ctx);return 0;
}
代码流程说明
-
初始化环境抽象层(EAL):初始化DPDK的EAL。
-
创建UDP上下文:调用 tle_ctx_create 创建一个UDP上下文。
-
设置UDP流参数:配置UDP流参数,包括本地地址和端口,以及接收回调函数。
-
创建UDP流:调用 tle_udp_stream_open 创建UDP流。
-
回调函数 recv_callback:
-
在接收数据包后,创建一个新的mbuf。
-
在新的mbuf上添加自定义包头( struct custom_header )。
-
将接收到的数据复制到新的mbuf中。
-
处理新的数据包(可以是发送或进一步处理)。
-
主循环:在主循环中不断接收数据包,并调用回调函数 recv_callback 进行处理。
-
关闭流和上下文:当不再需要时,关闭UDP流并销毁上下文。
总结
这个示例展示了如何在接收数据包后,通过回调函数在数据包头部添加自定义包头,并处理新的数据包。TLE库通过这种方式简化了复杂的网络数据处理任务。