上一篇:DIY TCP/IP 网络设备模块5
介绍DIY TCP/IP网络设备模块之上的其他模块的实现之前,本节先来完成log的分级打印。5.3.4节运行结果的打印输出已经相对复杂,目前DIY TCP/IP的log均是通过printf打印到标准输出,上层模块实现之后会有更多的log输出。避免过多的打印信息,实现log的分级打印。本节引入两个新的文件debug.c和debug.h,做为DIY TCP/IP的debug模块。
#ifndef _DEBUG_H_
#define _DEBUG_H_
#define ERROR 0
#define WARNING 1
#define DEBUG 2
#define VERBOSE 3
#define DEF_DEBUG_LEVEL DEBUG
//#define DEF_DEBUG_LEVEL VERBOSE
int log_printf(int level, const char *fmt, ...);
#endif
debug.h头文件中定义了log的优先级和暴露给其他模块使用的函数接口log_printf。ERROR到VERBOSE共4个优先级,ERROR的优先级最高,VERBOSE最低。log_printf第一个参数是log优先级。将提示性的信息,例如从pcap_callback收到的链路层数据帧的长度和时间戳,设置为VERBOSE,减少log输出。出错处理的log设置为ERROR或WARNING,关键路径代码的log优先级设置为DEBUG。DEF_DEBUG_LEVEL是默认的log优先级,用于过滤较低优先级的log,默认优先级设置为DEBUG。
debug.c
#include <stdio.h>
#include <stdarg.h>
#include "debug.h"
int log_printf(int level, const char *fmt, ...)
{
int ret = 0;
va_list ap;
if (level <= DEF_DEBUG_LEVEL) {
va_start(ap, fmt);
ret = vprintf(fmt, ap);
va_end(ap);
}
return ret;
}
输出字符串,第三个参数是C语言中的可变参数。C语言中的printf使用可变参数的典型函数,C语言中,函数参数存放在栈空间,根据格式化输出字符串fmt的地址和长度,可以找到栈空间中存放在fmt之后的参数值。va_list是可变参数列表,通过va_start可以获取可变参数列表,其实va_start的实现可以理解为(char *)fmt + sizeof(fmt),就是fmt的地址加上fmt指针本身占用的栈空间大小,得到fmt之后第一个参数的地址。printf的实现也是通过va_start先获取参数列表,再根据fmt格式字符串中的关键字%d,%s,%f等来逐个解析参数列表。
log_printf实现原理与printf类似,先判断log优先级,只有优先级小于等于默认优先级的log,才会被输出。这样就可以过滤掉优先级为VERBOSE的log,DEBUG,ERROR和WARNING优先级的log可以正常输出。
修改Makefile
tcp_ip_stack:device.o init.o debug.o
gcc -o tcp_ip_stack device.o init.o debug.o -lpcap -lpthread
device.o:device.c device.h init.h common.h
gcc -c device.c
init.o:init.c init.h
gcc -c init.c
debug.o:debug.c debug.h
gcc -c debug.c
clean:
rm -rf *.o
rm -rf tcp_ip_stack
新的debug.c和DIY TCP/IP的其他模块编译链接在一起。Makefile中,line1为tcp_ip_stack目标添加新的依赖debug.o。line 7是debug.o的依赖,debug.c和debug.h。line 8是编译生成debug.o的gcc命令。
debug模块已经实现完成,将DIY TCP/IP其他模块用到的printf均改为log_printf并赋予log优先级,不再给出具体改动细节。后续章节log打印均采用log_printf。将网络设备模块中pcap_callback函数打印链路层数据帧的时间戳和长度的log优先级改为VERBOSE,编译并运行结果如下:
gannicus@ubuntu:~/guojia/tasks/DIY_USER_SPACE_TCPIP/ch2/2$ make
gcc -c device.c
gcc -c init.c
gcc -c debug.c
gcc -o tcp_ip_stack device.o init.o debug.o -lpcap -lpthread
gannicus@ubuntu:~/guojia/tasks/DIY_USER_SPACE_TCPIP/ch2/2$ sudo ./tcp_ip_stack
[sudo] password for gannicus:
Network device init
filter: ether proto 0x0800 or ether proto 0x0806
Network device RX init
dev rx, ethernet type: 0800, dc:33:0d:1c:50:fc --> ff:ff:ff:ff:ff:ff
dev rx, ethernet type: 0800, 8c:a9:82:11:d1:de --> 01:00:5e:7f:ff:fa
dev rx, ethernet type: 0800, dc:33:0d:2a:95:33 --> ff:ff:ff:ff:ff:ff
dev rx, ethernet type: 0800, 8c:a9:82:11:d1:de --> 01:00:5e:00:00:fc
dev rx, ethernet type: 0800, dc:33:0d:1c:50:fc --> ff:ff:ff:ff:ff:ff
编译结果显示,debug.c已经加入Makefile编译,并链接到tcp_ip_stack中,运行结果显示,网络设备模块中只有优先级为DEBUG的log被打印输出。
5.6 小结
本章实现了DIY TCP/IP 的网络设备层,定义网络设备结构体,实现接收线程,定义网络设备模块数据帧的数据结构,封装pcap_callback返回的链路层数据帧。实现了链表,队列,并基于此实现了网络设备模块的接收队列,异步处理链路层数据帧。在接收线程中解析链路层数据帧的以太网头,最后又给出了debug模块的实现。DIY TCP/IP上层模块的实现均基于网络设备模块,根据解析出的以太网头部类型,将数据帧交付给后续章节将要实现的ARP,ICMP,IP或TCP模块,进行上层协议的处理。
下一篇:DIY TCP/IP Payload Buffer模块0