5月27日
基本上,我想要的功能都已经完成了,接下来需要做的事情就是修修补补.还有,需要写博文记录一下到底干了什么以及怎么去干.
5月24日
c语言最让人头疼的事情是指针,真的,一旦指针出了一点儿故障,你都找不到原因.我跟你说.
今天的问题最终还是解决了,原因很简单,是我的指针越界了,然后我往越界的地方写入了一点东西,今天这个玩意折腾了我一个下午.郁闷.
5月21日
现在已经基本上对这部分代码非常熟悉了,所以修改起来也得心应手,很爽.
5月20日
昨天一天改了很多代码,添加了很多代码,删除了很多代码,总之关于tcp部分的工作已经做得差不多了.我觉得努力一把,在5月底的时候完成这个简陋的该死的协议栈,还是有望的.
5月16日
最近突然有一项任务要完成,抽不出太多时间来弄协议栈啦,所以,协议栈的事情可能要延迟了,应该不会超过一个月吧,一个月后,我会继续来干的.
5月14日
造一个轮子代价昂贵,我太想吐槽这一点了,直到今天,我感觉还有非常多的事情要干,不知道一个简单的协议栈什么时候才能搞得完.
5月12日
今天改进和精简了一点代码,发现要实现一个简易的posix标准的网络函数,如bind
, accept
,listen
等,还有一大段代码要写,而这,真是乐趣之所在,鬼知道还有多久能够弄完这个协议栈,不过,在读协议栈的实现和精简协议栈的过程中,unp的不少内容,现在已经能够读懂了,记住,是真正懂了,原来unp上一些东西我压根就不知道在讲什么,包括那些高级的部分.
5月9日
拨开云雾见青天.终于调试正确了,tap
设备正常工作.今天是非常非常重要的一天,因为今天终于将苦大仇深的代码调试成功了.
5月8日
最近被tun/tap
这个概念折磨得很惨,使用百度搜索了一下这个玩意,没有一篇文章将这个东西完全讲清楚了,自己对着书,对着自己的想法,鼓捣了很久,依旧不得法,我接近放弃的时候,明智地用了一下谷歌搜索,很幸运,搜到了几篇文章,将tun/tap
这个概念讲清楚了,接下来的事情就好办了.
5月6日
流年不利,我发现用tun/tap实现协议栈,貌似没有很好的调试方法,特别蛋疼.或许是我还没有找到一种好的调试办法吧,今天尝试了很多,试图让实现的这个协议栈连接上外网,但是失败了,老是失败,我要重新理解一下,到底什么才是tun/tap.
大概能花在这个协议栈上的时间,只能有1个月啦,6月份开始,要立马刷题了.希望最近几天能够找到一种好的方法来将它调试成功.
5月5日
废了好大的劲,终于将昨天那个malloc
,free
的问题给解决了.这里我稍微记录一下思路,以便给将来引以为戒.
昨天遇到的问题是,一旦我使用malloc
,然后调用free
,类似于这样:
void *address = malloc(42);
free(address); // 代码没有任何问题
立马就报类似于这样的错误,同时程序立马崩溃:
free(): invalid next size (fast):
是不是非常诡异,我昨天调试了一个晚上,愣是没有找出原因,今天想了一点小办法,终于干掉了这个bug
.
事实上,上面的代码并没有错,真正的错误原因在于,我在调用上面的代码之前,已经在别的地方用malloc
分配了一块内存,然后一不小心,在操纵这块内存的时候,往越界的地址写入了一些数据,此后,我调用malloc
不会出问题,但是调用free
有很大的几率程序会立即崩溃掉,同时会爆出上面的错误.
个人怀疑是我越界写入的那些数据覆盖了malloc
分配内存时记录的一些关于内存块大小的信息,使得调用free
的时候无法找到这些信息,也就无从回收内存,导致出错.八九不离十啦.
我这里试图还原一下当时的场景:
#define ETH_HDR_LEN 10
#define IP_HDR_LEN 20
#define TCP_HDR_LEN 20
struct sk_buff {
...
uint32_t dlen; // 数据的大小,不包含头部(以太网,ip,tcp头部)
uint8_t *end;
uint8_t *head;
uint8_t *data;
uint8_t *payload;
};
// skb_reserve丢弃掉前len个数据,或者说是保留前面长度为len的数据
void *
skb_reserve(struct sk_buff *skb, unsigned int len)
{
skb->data += len;
return skb->data;
}
void
free_skb(struct sk_buff *skb)
{
free(skb->head);
free(skb);
}
struct sk_buff *
alloc_skb(unsigned int size)
{
struct sk_buff *skb = malloc(sizeof(struct sk_buff));
memset(skb, 0, sizeof(struct sk_buff));
skb->data = malloc(size); // 记录下数据
memset(skb->data, 0, size);
skb->refcnt = 0;
skb->head = skb->data; // 数据开始的地方
skb->end = skb->data + size; // 数据结束的地方
list_init(&skb->list);
return skb;
}
static struct sk_buff *
tcp_alloc_skb(int optlen, int size)
{
// optlen表示tcp首部选项的大小
// 这里要特别注意一下,因为忘记了TCP_HDR_LEN导致出错
// ===============================================
int reserved = ETH_HDR_LEN + IP_HDR_LEN + /*TCP_HDR_LEN +*/ optlen + size; // 如果这里丢了一个TCP_HDR_LEN
struct sk_buff *skb = alloc_skb(reserved);
skb_reserve(skb, reserved); // skb->data部分留出reserved个字节
skb->dlen = size; // dlen表示数据的大小
return skb;
}
void manipulate()
{
struct sk_buff *skb = tcp_alloc_skb(0, 0);
// 因为optlen和size都为0
// 用一个tcphdr的结构的指针tcp获取skb中的tcp头部部分,并填入数据,其实在这里就越界了.
...
// 用一个iphdr的结构的指针ip获取skb中关于ip头部部分,填入数据
...
// 用一个ethhdr结构的指针eth获取skb中关于以太网头部部分,填入数据
// 用一副示意图表示如下,其实在第一步,tcp就指向了不属于自己的内存的首部,对其进行填充之后
// 当时没有任何问题,但是到了free_skb的时候,立马出错,我怀疑malloc函数将内存块的信息恰好填
// 入到了tcphdr那一部分
/*
|-------------------| <--- skb->head, eth
| ethhdr |
|-------------------| <--- iphdr, ip
| iphdr |
| |
|-------------------| <--- skb->end, tcp
| tcphdr | bad memory
| |
*/
free_skb(skb); // 立马出错,因为在不属于你的内存部分写入了数据
}
上面的问题看似很简单,或许你会拍一拍胸膛,我绝对不会犯这种错误,但是真的当代码多了之后,估计你就不会说这样的话了,如果尝试过调试了7
,8
个小时,连bug
的半点头绪都找不到这种绝望的话,估计你也会说--c
语言呀,指针这个东西,真的需要特别小心.
5月4日
原来没有想到的一个问题是,应用程序究竟是如何同协议栈进行交流的,现在明白了,使用的是IPC
机制,从昨天一直到现在,终于将unix
本机通信的程序调通了.接下来就只要将三次握手调试成功就差不多了.
妈蛋,c
语言的内存分配真是个大问题,调了半天不知道错在哪里,先malloc
,然后free
,立马出错,这玩意和玄学差不多.
5月3日
学习网络编程最好的方式,的确是摸透一个TCP IP
协议栈的实现.读level-ip
,结合tcp ip详解卷,再配合上手实验,个人感觉收获非常大,毕竟看到了抽象背后的东西.
越是读源代码实现,我越是觉得,真正要在linux
做系统编程的话,对操作系统原理的理解是必不可少的,最好的操作系统教程,不是书本,而是源码.当然,书本比不可少,它记录了一些原理性的东西,但是源码会使我们对一些东西明白得更加透彻.xv6
, ucore
都是不错的教材.
=======================
这篇文章什么也不干,我就是想记录一下自己读tcp ip
协议栈的实现,自己的一点感想.仅此而已.