wfb-ng 开源代码之wfb_tx&wfb_rx
1. 源由
在《wfb-ng 开源工程结构&代码框架简明介绍》中已经简单介绍了工程框架。
接下来,就针对代码的一些研读,以及基于源码的基本功能了解。
2. 目标
- wfb_rx: src/rx.o src/radiotap.o src/fec.o src/wifibroadcast.o
- wfb_tx: src/tx.o src/fec.o src/wifibroadcast.o
- wfb_keygen: src/keygen.o
- wfb_tx_cmd: src/tx_cmd.o
- python tools
wfb-cli
:执行wfb_ng.cli
模块中的main
函数wfb-test-latency
:执行wfb_ng.latency_test
模块中的main
函数wfb-server
:执行wfb_ng.server
模块中的main
函数wfb-log-parser
:执行wfb_ng.log_parser
模块中的main
函数
注:本章重点介绍c源代码生成的目标代码wfb_rx
和wfb_tx
。
3. wfb_rx
main(int argc, char* const *argv)
│
├── 初始化变量
│ ├── int opt
│ ├── uint8_t radio_port
│ ├── uint32_t link_id
│ ├── uint64_t epoch
│ ├── int log_interval
│ ├── int client_port
│ ├── int srv_port
│ ├── string client_addr
│ ├── rx_mode_t rx_mode
│ ├── int rcv_buf
│ └── string keypair
│
├── 处理命令行参数 (while getopt)
│ ├── case 'K': 设置 keypair
│ ├── case 'f': 设置 rx_mode 为 FORWARDER
│ ├── case 'a': 设置 rx_mode 为 AGGREGATOR 和 srv_port
│ ├── case 'c': 设置 client_addr
│ ├── case 'u': 设置 client_port
│ ├── case 'p': 设置 radio_port
│ ├── case 'R': 设置 rcv_buf
│ ├── case 'l': 设置 log_interval
│ ├── case 'i': 设置 link_id
│ ├── case 'e': 设置 epoch
│ └── default: 显示用法信息并退出
│
├── 检查系统熵池 (open "/dev/random")
│ └── 如果熵不足, 显示警告信息
│
├── 初始化 Libsodium 库 (sodium_init)
│ └── 如果初始化失败, 显示错误信息并退出
│
├── 主逻辑 (try-catch)
│ ├── 计算 channel_id
│ │
│ ├── 根据 rx_mode 分支逻辑
│ │ ├── if rx_mode == LOCAL or FORWARDER
│ │ │ ├── 检查命令行参数 (optind)
│ │ │ ├── 创建 Aggregator 或 Forwarder 实例
│ │ │ ├── 调用 radio_loop 处理接收逻辑
│ │ │
│ │ ├── else if rx_mode == AGGREGATOR
│ │ │ ├── 检查命令行参数 (optind)
│ │ │ ├── 创建 Aggregator 实例
│ │ │ ├── 调用 network_loop 处理网络通信
│ │ │
│ │ └── else
│ │ └── 抛出未知 rx_mode 异常
│ │
│ └── 捕获 runtime_error 异常并显示错误信息
│
└── return 0 表示程序正常结束
3.1 rx_mode(LOCAL)
通过wfb-ng
物理RF网卡接收数据,加密解包,并绑定五元组[UDP, client_addr, client_port]
WiFi broadcast – secure line --> client[client_addr, client_port]
LOCAL
├──> Aggregator(client_addr, client_port, keypair, epoch, channel_id)
└──> radio_loop(argc, argv, optind, channel_id, agg, log_interval)
└──> Receiver::loop_iter
└──> Aggregator::process_packet(pkt + sizeof(ieee80211_header), pktlen - sizeof(ieee80211_header),
wlan_idx, antenna, rssi, noise, freq, mcs_index, bandwidth, NULL)
3.2 rx_mode(FORWARDER)
通过wfb-ng
物理RF网卡接收数据,并绑定五元组[UDP, client_addr, client_port]
WiFi broadcast – local udp --> client[client_addr, client_port]
FORWARDER
├──> Forwarder(client_addr, client_port)
└──> radio_loop(argc, argv, optind, channel_id, agg, log_interval)
└──> Receiver::loop_iter
└──> Forwarder::process_packet(pkt + sizeof(ieee80211_header), pktlen - sizeof(ieee80211_header),
wlan_idx, antenna, rssi, noise, freq, mcs_index, bandwidth, NULL)
3.3 rx_mode(AGGREGATOR)
本地启动UDP server[srv_port]监听报文,加密解包,并绑定五元组[UDP, client_addr, client_port]
- Local ethernet udp – secure line --> client[client_addr, client_port]
AGGREGATOR
├──> Aggregator(client_addr, client_port, keypair, epoch, channel_id)
└──> network_loop(srv_port, agg, log_interval, rcv_buf)
└──> Aggregator::process_packet(buf, rsize - sizeof(wrxfwd_t),
fwd_hdr.wlan_idx, fwd_hdr.antenna,
fwd_hdr.rssi, fwd_hdr.noise, ntohs(fwd_hdr.freq),
fwd_hdr.mcs_index, fwd_hdr.bandwidth, &sockaddr)
4. wfb_tx
main(int argc, char * const *argv)
│
├── 初始化变量
│ ├── int opt
│ ├── uint8_t k, n, radio_port
│ ├── uint32_t fec_delay, link_id
│ ├── uint64_t epoch
│ ├── int udp_port, control_port, log_interval
│ ├── int bandwidth, short_gi, stbc, ldpc, mcs_index, vht_nss, debug_port, fec_timeout, rcv_buf
│ ├── bool mirror, vht_mode
│ ├── string keypair
│ ├── uint8_t frame_type
│ ├── bool use_qdisc
│ └── uint32_t fwmark
│
├── 处理命令行参数 (while getopt)
│ ├── case 'K': 设置 keypair
│ ├── case 'k': 设置 k
│ ├── case 'n': 设置 n
│ ├── case 'u': 设置 udp_port
│ ├── case 'p': 设置 radio_port
│ ├── case 'F': 设置 fec_delay
│ ├── case 'R': 设置 rcv_buf
│ ├── case 'B': 设置 bandwidth (强制 VHT 模式如果 bandwidth >= 80)
│ ├── case 'G': 设置 short_gi
│ ├── case 'S': 设置 stbc
│ ├── case 'L': 设置 ldpc
│ ├── case 'M': 设置 mcs_index
│ ├── case 'N': 设置 vht_nss
│ ├── case 'D': 设置 debug_port
│ ├── case 'T': 设置 fec_timeout
│ ├── case 'l': 设置 log_interval
│ ├── case 'i': 设置 link_id
│ ├── case 'e': 设置 epoch
│ ├── case 'm': 启用 mirror 模式
│ ├── case 'V': 启用 VHT 模式
│ ├── case 'f': 设置 frame_type (data 或 rts)
│ ├── case 'Q': 启用 use_qdisc
│ ├── case 'P': 设置 fwmark
│ ├── case 'C': 设置 control_port
│ └── default: 显示使用信息并退出
│
├── 检查系统熵池 (open "/dev/random")
│ └── 如果熵不足, 显示警告信息
│
├── 初始化 Libsodium 库 (sodium_init)
│ └── 如果初始化失败, 显示错误信息并退出
│
├── 主逻辑 (try-catch)
│ ├── 初始化 radiotap_header
│ │
│ ├── 初始化控制端口 (open_udp_socket_for_rx)
│ │ └── 如果 control_port 为 0, 获取实际绑定的端口号
│ │
│ ├── 处理每个网络接口 (for loop over interfaces)
│ │ ├── 创建 UDP 接收套接字 (open_udp_socket_for_rx)
│ │ ├── 记录绑定的端口号和接口名称
│ │ └── 添加到 rx_fd 和 wlans 列表
│ │
│ ├── 初始化 Transmitter 对象
│ │ ├── 如果启用 debug_port, 使用 UdpTransmitter
│ │ └── 否则, 使用 RawSocketTransmitter
│ │
│ └── 启动数据源处理 (data_source)
│ └── 传递 Transmitter 对象和相关参数
│
├── 捕获 runtime_error 异常并显示错误信息
│
└── return 0 表示程序正常结束
4.1 debug
接收本地UDP报文,加密转发到[“127.0.0.1”, debug_port]
Local UDP port[“127.0.0.1”, control_port] – secure line --> Local debug port[“127.0.0.1”, debug_port]
main
└──> UdpTransmitter(k, n, keypair, "127.0.0.1", debug_port, epoch, channel_id,
│ fec_delay, tags, use_qdisc, fwmark)
└──> data_source(t, rx_fd, control_fd, fec_timeout, mirror, log_interval)
└──> Transmitter::send_packet
└──> Transmitter::send_block_fragment
└──> UdpTransmitter::inject_packet
4.2 WiFi RF
接收本地UDP报文,加密,通过wfb-ng
物理RF网卡发送数据
Local UDP port[“127.0.0.1”, control_port] – secure line --> WiFi broadcast
main
└──> RawSocketTransmitter(k, n, keypair, epoch, channel_id, fec_delay, tags,
│ wlans, radiotap_header, frame_type, use_qdisc, fwmark)
└──> data_source(t, rx_fd, control_fd, fec_timeout, mirror, log_interval)
└──> Transmitter::send_packet
└──> Transmitter::send_block_fragment
└──> RawSocketTransmitter::inject_packet
5. 重要例程
5.1 radio_loop
检查所有用于wfb-ng
的物理网卡接口,如果有事件发生则进行处理。
radio_loop(int argc, char* const *argv, int optind, uint32_t channel_id, shared_ptr<BaseAggregator> agg, int log_interval)
│
├── 初始化变量
│ ├── int nfds = argc - optind
│ ├── uint64_t log_send_ts = 0
│ ├── struct pollfd fds[MAX_RX_INTERFACES]
│ └── Receiver* rx[MAX_RX_INTERFACES]
│
├── 检查接口数量 (nfds)
│ └── 如果 nfds > MAX_RX_INTERFACES, 抛出异常
│
├── 初始化 fds 和 rx 数组
│ └── for 循环 (int i = 0; i < nfds; i++)
│ ├── 初始化 rx[i] (new Receiver)
│ └── 设置 fds[i].fd 和 fds[i].events
│
└── 主循环 (无限循环 for(;;))
├── 获取当前时间戳 cur_ts = get_time_ms()
├── 调用 poll 函数
│ └── 处理 poll 错误 (rc < 0)
│ ├── 如果错误类型为 EINTR 或 EAGAIN, 则继续循环
│ └── 否则抛出异常
│
├── 更新当前时间戳 cur_ts
├── 如果达到 log_send_ts 时间, 打印统计数据并更新 log_send_ts
│
├── 如果 poll 超时 (rc == 0), 继续下一次循环
│
└── 遍历所有接口 (for 循环)
├── 检查接口上的错误事件 (POLLERR 或 POLLNVAL)
│ ├── 如果有错误事件, 抛出异常
│
└── 处理可读事件 (POLLIN)
├── 调用 rx[i]->loop_iter()
└── 减少剩余可处理事件数 rc -= 1
5.2 Receiver::loop_iter
接收ppcap
报文,并进行逻辑处理,所有报文头处理以后就是数据包内容。
Receiver::loop_iter
├── 初始化变量和数组
└── 无限循环
├── 使用 pcap_next 获取数据包
├── 初始化 Radiotap 迭代器
├── 迭代 Radiotap 字段
│ ├── 处理天线索引
│ ├── 提取频率
│ ├── 获取 RSSI
│ ├── 获取噪声水平
│ ├── 存储标志
│ ├── 检查自我注入
│ ├── 提取 MCS 索引和带宽
│ └── 提取 VHT 参数
├── 处理错误
│ ├── 短数据包
│ ├── 自我注入
│ ├── 错误的 FCS
│ └── Radiotap 头部错误
└── 处理数据包数据
└── 调用 agg->process_packet
5.3 network_loop
检查所有用于wfb-ng
的网络接口,如果有事件发生则进行处理。
network_loop(int srv_port, Aggregator &agg, int log_interval, int rcv_buf_size)
│
├── 初始化变量
│ ├── wrxfwd_t fwd_hdr
│ ├── struct sockaddr_in sockaddr
│ ├── uint8_t buf[MAX_FORWARDER_PACKET_SIZE]
│ ├── uint64_t log_send_ts = 0
│ ├── struct pollfd fds[1]
│ └── int fd = open_udp_socket_for_rx(srv_port, rcv_buf_size)
│
├── 初始化 fds 数组
│ ├── memset(fds, '\0', sizeof(fds))
│ └── 设置 fds[0].fd 和 fds[0].events (POLLIN)
│
└── 主循环 (无限循环 for(;;))
├── 获取当前时间戳 cur_ts = get_time_ms()
├── 调用 poll 函数
│ └── 处理 poll 错误 (rc < 0)
│ ├── 如果错误类型为 EINTR 或 EAGAIN, 则继续循环
│ └── 否则抛出异常
│
├── 更新当前时间戳 cur_ts
├── 如果达到 log_send_ts 时间, 打印统计数据并更新 log_send_ts
│
├── 如果 poll 超时 (rc == 0), 继续下一次循环
│
└── 检查 poll 事件
├── 检查错误事件 (POLLERR 或 POLLNVAL)
│ ├── 如果有错误事件, 抛出异常
│
└── 处理可读事件 (POLLIN)
├── 内部循环 (for(;;) 处理挂起的接收数据)
│ ├── 初始化 sockaddr 和 iov 数组
│ ├── 初始化 msghdr 结构体
│ ├── 调用 recvmsg 函数接收数据
│ │ ├── 如果接收失败 (rsize < 0), 退出内部循环
│ │ └── 如果接收数据长度不足 (rsize < sizeof(wrxfwd_t)), 输出错误信息并继续内部循环
│ └── 调用 agg.process_packet 处理接收到的数据包
└── 处理非阻塞接收错误 (如果 errno 不是 EWOULDBLOCK, 抛出异常)
5.4 data_source
检查数据链路发送数据包,将数据加密后发送到debug端口或者wfb-ng
的网络接口
data_source
│
├── 1. 初始化变量
│ ├── 初始化 pollfd 结构体数组 `fds`
│ ├── 初始化统计变量 `count_p_fec_timeouts`, `count_p_incoming`, 等等
│ └── 初始化时间变量 `session_key_announce_ts`, `log_send_ts`, `fec_close_ts`
│
└── 2. 无限循环 for(;;) 处理数据源
├── 更新当前时间 `cur_ts`
├── 计算 `poll_timeout`
├── 调用 `poll` 等待事件
│ └── 如果 `poll` 返回错误,抛出异常
│
├── 3. 日志超时处理
│ ├── 如果日志超时,调用 `t->dump_stats` 输出统计数据
│ ├── 重置统计变量
│ └── 更新下次日志时间 `log_send_ts`
│
├── 4. 控制 socket 处理
│ ├── 检查控制 socket 是否有错误
│ └── 处理控制 socket 的 POLLIN 事件
│ ├── 循环接收命令请求并处理
│ ├── 根据不同命令类型(`CMD_SET_FEC`, `CMD_SET_RADIO`, `CMD_GET_FEC`, `CMD_GET_RADIO`)处理
│ └── 向请求方发送命令响应
│
├── 5. FEC 超时处理
│ └── 如果 FEC 超时,发送空数据包关闭 FEC 块
│
├── 6. 处理接收到的数据包
│ ├── 根据上次处理的 fd 索引 `start_fd_idx` 处理数据包
│ └── 处理每个 fd 的 POLLIN 事件
│ ├── 接收数据包 `recvmsg`
│ ├── 更新统计数据 `count_p_incoming`, `count_b_incoming`, 等等
│ ├── 处理数据包,调用 `t->send_packet`
│ └── 如果有 FEC 超时,重置超时时间 `fec_close_ts`
│
└── 7. 结束循环,继续下一次事件处理
6. 参考资料
【1】Ardupilot & OpenIPC & 基于WFB-NG构架分析和数据链路思考
【2】wfb-ng 开源工程结构&代码框架简明介绍