libnids中TCP/IP栈实现细节分析——TCP会话重组
libnids中TCP/IP栈实现细节分析——TCP会话重组
libnids是网络安全方面的一个库,可以用来检测网络上的攻击行为。其中最有价值的部分是,它模拟了linux内核中3层和4层的协议栈。可以供我们进一步研究linux内核中的TCP/IP协议栈做一些有价值的参考。这里简单谈谈这个库中模拟3、4层协议的实现细节(在继续读下去之前,有必要复习一下TCP/IP协议相关理论,主要是滑动窗口协议)。这里送上一张网上到处都有的TCP状态转化图,算是开胃小菜:
在TCP/IP协议栈中,3层对应的是IP层,4层对应TCP层,在这里,从3层到4层转化主要做了两件重要的事情:IP分片重组和TCP会话重组。本篇先分析其中TCP会话重组的部分(自顶向下嘛,哈哈)。
OK,先看下重要的数据结构,在tcp.h中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
这个结构体就是模仿的内核中的sk_buff结构体,只不过比内核中的要小很多(你懂的,因为这里只做会话重组)。
下面是在nids.h中的
1 2 3 4 5 6 7 |
|
这是用来表示一个TCP连接的,不解释。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
|
这个是用来表示“半个TCP会话”,其实就是一个方向上的TCP流。
还有
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
显然,这是用来表示一个完整的TCP会话了,最后是static struct tcp_stream **tcp_stream_table;一个TCP会话指针的数组,其实就是hash表了。
下面来看处理过程,先是初始化:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
|
很简单,做了两件事:1.初始化tcp会话哈希表。2.初始化会话池。这个初始化函数只在库初始化时执行一次。
初始化完成之后,就进入了pcap_loop中了,nids中的回调函数是nids_pcap_handler,在这个函数里面做了些ip分片重组(等下篇再说)后(tcp包)便来到了process_tcp函数,这里tcp会话重组开始了。来看看。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 |
|
好了,tcp包的基本处理流程就这些了,主要做了连接的建立、释放、状态迁移这些工作,下面看看连接的缓冲区是如何维护的(主要就是如何更新的)。来看tcp_queue函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
|
好了,主体函数就这些了,其他的函数比较简单,不解释。协议栈模拟,考验的就是细心周到啊。
好了,有时间了,来看看libnids中IP分片重组的方法吧。
在此之前,如果不懂IP分片技术的话,请参照这里。IP分片技术比较简单暴力,没有TCP那样复杂复杂的窗口协议。基本上只是暴力的拆分和重组,代码基本在ip_defragment.c中。
先从总体上说说。首先,每个IP(主机)都会有IP分片包(注意是IP,不是IP对)。所以,每个IP都有一个如下的结构体来维护上面的所以IP分片:
1
2
3
4
5
6
7
8
9
10
11
|
struct hostfrags {
struct ipq *ipqueue; //这里维护IP碎片队列
int ip_frag_mem;
u_int ip; //主机对应的IP地址
//很明显,下面三行告诉我们,这是哈希表的一个元素
int hash_index;
struct hostfrags *prev;
struct hostfrags *next;
};
//下面这个就是维护所有IP的哈希表了。
static struct hostfrags **fragtable;
|
每个IP下面又有很多的被分片的IP包——IP碎片队列,IP碎片队列的定义在这:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
/* Describe an entry in the "incomplete datagrams" queue. */
struct ipq {
unsigned char *mac; /* pointer to MAC header */
struct ip *iph; /* pointer to IP header */
int len; /* total length of original datagram */
short ihlen; /* length of the IP header */
short maclen; /* length of the MAC header */
struct timer_list timer; /* when will this queue expire? */
struct ipfrag *fragments; /* linked list of received fragments */
struct hostfrags *hf;
struct ipq *next; /* linked list pointers */
struct ipq *prev;
// struct device *dev; /* Device - for icmp replies */
};
|
最终的IP碎片的定义在这:
1
2
3
4
5
6
7
8
9
10
|
/* Describe an IP fragment. */
struct ipfrag {
int offset; /* offset of fragment in IP datagram */
int end; /* last byte of data in datagram */
int len; /* length of this fragment */
struct sk_buff *skb; /* complete received fragment */
unsigned char *ptr; /* pointer into real fragment data */
struct ipfrag *next; /* linked list pointers */
struct ipfrag *prev;
};
|
由于libnids中的分片重组代码是从内核中拿出来修改的,所以保留了内核的注释。这里就不多做解释了。
好了步入处理逻辑,照例,先看初始化:
1
2
3
4
5
6
7
8
9
10
11
12
|
void
ip_frag_init( int n)
{
struct timeval tv;
gettimeofday(&tv, 0);
time0 = tv.tv_sec;
fragtable = ( struct hostfrags **) calloc (n, sizeof ( struct hostfrags *));
if (!fragtable)
nids_params.no_mem( "ip_frag_init" );
hash_size = n;
}
|
简单到不能再简单——分片了一个主机的哈希表。分完手工。好吧,看重组逻辑:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
|
//先是判断是否为分片的函数
int
ip_defrag_stub( struct ip *iph, struct ip **defrag)
{
int offset, flags, tot_len;
struct sk_buff *skb;
numpack++;
//先处理超时事件
timenow = 0; //刷新时间
while (timer_head && timer_head->expires < jiffies()) {
this_host = (( struct ipq *) (timer_head->data))->hf;
timer_head->function(timer_head->data);
}
//然后计算分片的偏移
offset = ntohs(iph->ip_off);
flags = offset & ~IP_OFFSET;
offset &= IP_OFFSET;
//此包不是分片
if (((flags & IP_MF) == 0) && (offset == 0)) {
ip_defrag(iph, 0);
return IPF_NOTF;
}
//此包是分片,先申请一个sk_buff把分片的数据保存起来,然后交给defrag函数
tot_len = ntohs(iph->ip_len);
skb = ( struct sk_buff *) malloc (tot_len + sizeof ( struct sk_buff));
if (!skb)
nids_params.no_mem( "ip_defrag_stub" );
skb->data = ( char *) (skb + 1);
memcpy (skb->data, iph, tot_len);
skb->truesize = tot_len + 16 + nids_params.dev_addon;
skb->truesize = (skb->truesize + 15) & ~15;
skb->truesize += nids_params.sk_buff_size;
//如果集齐了一个ip包的所有分片ip_defrag将返回合并后的ip包,此时返回IPF_NEW,进行下一步的ip包处理
//否则,返回IPF_ISF,跳过ip包处理
if ((*defrag = ( struct ip *)ip_defrag(( struct ip *) (skb->data), skb)))
return IPF_NEW;
return IPF_ISF;
}
/* Process an incoming IP datagram fragment. */
//这里就是分片重组的主要逻辑了
static char *
ip_defrag( struct ip *iph, struct sk_buff *skb)
{
struct ipfrag *prev, *next, *tmp;
struct ipfrag *tfp;
struct ipq *qp;
char *skb2;
unsigned char *ptr;
int flags, offset;
int i, ihl, end;
//如果是分片,而且host哈希表里还没有对应的host项的话,果断新建一个
//此处还负责将this_host变量设为当前ip对应的host
if (!hostfrag_find(iph) && skb)
hostfrag_create(iph);
/* Start by cleaning up the memory. */
//内存用太多了,panic之,然后释放当前host分片所用的内存
if (this_host)
if (this_host->ip_frag_mem > IPFRAG_HIGH_THRESH)
ip_evictor();
/* Find the entry of this IP datagram in the "incomplete datagrams" queue. */
//这里,找到这个ip包对应的ip分片链表
if (this_host)
qp = ip_find(iph);
else
qp = 0;
/* Is this a non-fragmented datagram? */
offset = ntohs(iph->ip_off);
flags = offset & ~IP_OFFSET;
offset &= IP_OFFSET;
if (((flags & IP_MF) == 0) && (offset == 0)) {
if (qp != NULL)
ip_free(qp); /* Fragmented frame replaced by full
unfragmented copy */
return 0;
}
/* ip_evictor() could have removed all queues for the current host */
if (!this_host)
hostfrag_create(iph);
offset <<= 3; /* offset is in 8-byte chunks */
ihl = iph->ip_hl * 4;
/*
If the queue already existed, keep restarting its timer as long as
we still are receiving fragments. Otherwise, create a fresh queue
entry.
*/
//如果当前host下来过此包的碎片
if (qp != NULL) {
/* ANK. If the first fragment is received, we should remember the correct
IP header (with options) */
if (offset == 0) {
qp->ihlen = ihl;
memcpy (qp->iph, iph, ihl + 8);
}
del_timer(&qp->timer);
qp->timer.expires = jiffies() + IP_FRAG_TIME; /* about 30 seconds */
qp->timer.data = (unsigned long ) qp; /* pointer to queue */
qp->timer.function = ip_expire; /* expire function */
add_timer(&qp->timer);
}
//否则新建一个碎片队列
else {
/* If we failed to create it, then discard the frame. */
if ((qp = ip_create(iph)) == NULL) {
kfree_skb(skb, FREE_READ);
return NULL;
}
}
/* Attempt to construct an oversize packet. */
//再大的ip包也不能大过65535啊,一经发现,直接放弃
if (ntohs(iph->ip_len) + ( int ) offset > 65535) {
// NETDEBUG(printk("Oversized packet received from %s\n", int_ntoa(iph->ip_src.s_addr)));
nids_params.syslog(NIDS_WARN_IP, NIDS_WARN_IP_OVERSIZED, iph, 0);
kfree_skb(skb, FREE_READ);
return NULL;
}
//下面就开始在碎片队列里面找位置了,同时处理好重叠
//如果有重叠,把重叠的旧的部分去掉
/* Determine the position of this fragment. */
end = offset + ntohs(iph->ip_len) - ihl;
/* Point into the IP datagram 'data' part. */
ptr = (unsigned char *)(skb->data + ihl);
/* Is this the final fragment? */
if ((flags & IP_MF) == 0)
qp->len = end;
/*
Find out which fragments are in front and at the back of us in the
chain of fragments so far. We must know where to put this
fragment, right?
*/
prev = NULL;
for (next = qp->fragments; next != NULL; next = next->next) {
if (next->offset >= offset)
break ; /* bingo! */
prev = next;
}
/*
We found where to put this one. Check for overlap with preceding
fragment, and, if needed, align things so that any overlaps are
eliminated.
*/
if (prev != NULL && offset < prev->end) {
nids_params.syslog(NIDS_WARN_IP, NIDS_WARN_IP_OVERLAP, iph, 0);
i = prev->end - offset;
offset += i; /* ptr into datagram */
ptr += i; /* ptr into fragment data */
}
/*
Look for overlap with succeeding segments.
If we can merge fragments, do it.
*/
for (tmp = next; tmp != NULL; tmp = tfp) {
tfp = tmp->next;
if (tmp->offset >= end)
break ; /* no overlaps at all */
nids_params.syslog(NIDS_WARN_IP, NIDS_WARN_IP_OVERLAP, iph, 0);
i = end - next->offset; /* overlap is 'i' bytes */
tmp->len -= i; /* so reduce size of */
tmp->offset += i; /* next fragment */
tmp->ptr += i;
/*
If we get a frag size of <= 0, remove it and the packet that it
goes with. We never throw the new frag away, so the frag being
dumped has always been charged for.
*/
if (tmp->len <= 0) {
if (tmp->prev != NULL)
tmp->prev->next = tmp->next;
else
qp->fragments = tmp->next;
if (tmp->next != NULL)
tmp->next->prev = tmp->prev;
next = tfp; /* We have killed the original next frame */
frag_kfree_skb(tmp->skb, FREE_READ);
frag_kfree_s(tmp, sizeof ( struct ipfrag));
}
}
//下面往队列中插入当前碎片
/* Insert this fragment in the chain of fragments. */
tfp = NULL;
tfp = ip_frag_create(offset, end, skb, ptr);
/*
No memory to save the fragment - so throw the lot. If we failed
the frag_create we haven't charged the queue.
*/
if (!tfp) {
nids_params.no_mem( "ip_defrag" );
kfree_skb(skb, FREE_READ);
return NULL;
}
/* From now on our buffer is charged to the queues. */
tfp->prev = prev;
tfp->next = next;
if (prev != NULL)
prev->next = tfp;
else
qp->fragments = tfp;
if (next != NULL)
next->prev = tfp;
/*
OK, so we inserted this new fragment into the chain. Check if we
now have a full IP datagram which we can bump up to the IP
layer...
*/
//查看是不是碎片都搜集齐了,如果齐了,组合成一个大ip包返回
if (ip_done(qp)) {
skb2 = ip_glue(qp); /* glue together the fragments */
return (skb2);
}
return (NULL);
}
|
好了,打完收工!
libnids中TCP/IP栈实现细节分析——TCP会话重组相关推荐
- CISA 发布关于 Treck TCP/IP 栈中新漏洞的 ICS 安全公告
聚焦源代码安全,网罗国内外最新资讯! Treck TCP/IP 栈的安全更新,解决了两个可导致远程代码执行或拒绝服务的严重漏洞.美国网络安全和基础设施安全局 (CISA) 发布安全公告,警告组织机构 ...
- uIP 一个免费的TCP/IP栈
uIP 一个免费的TCP/IP栈 原文:Adam Dunkels adam@dunkels.com 2002年2月15日 翻译:张伟林 2003年5月17日 okelinchang@163.com ...
- 深入分析Linux操作系统对于TCP/IP栈的实现原理与具体过程
一.Linux内核与网络体系结构 在我们了解整个linux系统的网络体系结构之前,我们需要对整个网络体系调用,初始化和交互的位置,同时也是Linux操作系统中最为关键的一部分代码-------内核,有 ...
- 4个开源 TCP/IP 栈被曝33个漏洞,数百万智能和工业设备受影响
聚焦源代码安全,网罗国内外最新资讯! 编译:奇安信代码卫士团队 Forescout 公司的安全研究员披露了150多个厂商产品固件中当前所使用的四个开源 TCP/IP 库中的33个安全缺陷,被统称为 ...
- java堆和客栈_java中堆和栈的区别分析
堆和栈是java数据结构里非常重要的概念,本文较为详细的分析了二者之间的区别.供大家参考.具体如下: Java的堆是一个运行时数据区,类的(对象从中分配空间.这些对象通过new.newarray.an ...
- TCP/IP网络编程之基于TCP的服务端/客户端(二)
回声客户端问题 上一章TCP/IP网络编程之基于TCP的服务端/客户端(一)中,我们解释了回声客户端所存在的问题,那么单单是客户端的问题,服务端没有任何问题?是的,服务端没有问题,现在先让我们回顾下服 ...
- 单片机tcp ip协议c语言,单片机TCP IP协议栈实现的原理
对已TCP IP协议栈,我们已经说了很多关于它的原理相关的知识了.但是只有原理是不够的,在这方面我们将要举出一个实际操作实例为大家讲解,那么首先我们来看一下有关于单片机TCP/IP就是在单片机上运行的 ...
- TCP/IP协议(二)tcp/ip基础知识
转载:http://www.cnblogs.com/imyalost/p/6139191.html 一.TCP/IP的标准化 1.TCP/IP的含义 一般来说,TCP/IP是利用IP进行通信时所必须用 ...
- TCP/IP网络编程之基于TCP的服务端/客户端(一)
TCP/IP网络编程之基于TCP的服务端/客户端(一) 理解TCP和UDP 根据数据传输方式的不同,基于网络协议的套接字一般分为TCP套接字和UDP套接字.因为TCP套接字是面向连接的,因此又称为基于 ...
- 什么是TCP/IP?-四张图解释TCP/IP四层协议模型
什么是四层模型 第一层:应用层,主要有负责web浏览器的HTTP协议, 文件传输的FTP协议,负责电子邮件的SMTP协议,负责域名系统的DNS等. 第二层:传输层,主要是有可靠传输的TCP协议,特别高 ...
最新文章
- linux7挂载ntfs分区,刚安装centos7,请教大神如何挂载ntfs的分区
- 由各大企业移除MongoDB,回看关系模型与文档模型之争
- itk下FFTW的FFT和IFFT
- 这两天发现一个老外用JavaScript编写的好东西:dp.SyntaxHighlighter。
- oracle学习的方法:
- 天天用 Spring,bean 实例化原理你懂吗?
- 加载特征主数据自动删除重复关键值
- python asyncio和celery对比_如何将Celery与asyncio结合? - python
- 近期H5项目开发小结
- 5怎么选国外节点_外卖包装怎么选?这5个技巧要掌握
- udp文件服务器,UDP客户端服务器文件传输
- 回归即登顶社交应用排行榜,多牛传媒开启人人“第二春”
- QT开发pjsip的VOIP,A8平台运行
- VMware Workstation虚拟机无法运行
- cad转excel插件c2e_CAD表格互转EXCEL插件
- 用Python在word的指定位置插入图片(使用Python-docx包)
- R语言 使用getGEO()直接进行差异表达分析并显示Entrez_id和Symbol_id
- 英语听力软件测试自学,每日英语听力应该怎么学习?
- 计算机硬件未来发展前景,计算机硬件的未来发展趋势
- python和r语言哪个简单_python与r语言哪个简单
热门文章
- mysql dump 1449_关于mysqldump的ERROR 1449 问题 | 学步园
- 判断入射满射c语言编码,数学上可以分三类函数包括() 答案:单射双射满射...
- 怎样选择适合自己的视频加密软件方案?
- dtmf拨号原理matlab,matlab综合实验dtmf拨号器设计.doc
- 聊一聊阿里P8、P9及以上人的水平
- cout 声明与定义
- 环境和社会风险分类c类_A、B、C级风险隐患分类标准
- matlab emd imf波形,emd分解后画出IMF的波形
- node 加密解密模块_NodeJS加密解密及node-rsa加密解密用法详解
- python将数值存入excel指定单元格