#sysctl -a ...... net.ipv4.ip_always_defrag = 0 ......
2. 关键数据结构(2.2系列)
每一个分片用ipfrag结构表示:
/* 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; };
/* Describe an entry in the "incomplete datagrams" queue. */ struct ipq { struct iphdr *iph; /* pointer to IP header */ struct ipq *next; /* linked list pointers */ struct ipfrag *fragments; /* linked list of received fragments */ int len; /* total length of original datagram */ short ihlen; /* length of the IP header */ struct timer_list timer; /* when will this queue expire? */ struct ipq **pprev; struct device *dev; /* Device - for icmp replies */ };
(5)调节end值(数据的结尾位置),如果是最后一个包,则最终整个ip包的长度便可以知道了,为了组装时方便,将其记录到ipq中。 /* Determine the position of this fragment. */ end = offset + ntohs(iph-〉tot_len) - ihl;
/* Is this the final fragment? */ if ((flags & IP_MF) == 0) qp-〉len = end;
/* Insert this fragment in the chain of fragments. */ tfp-〉prev = prev; tfp-〉next = next; if (prev != NULL) prev-〉next = tfp; else qp-〉fragments = tfp;
/* 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)) { 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 */
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. */ 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;
/* We have killed the original next frame. */ next = tfp;
if(len〉65535) { printk("Oversized IP packet from %s.\n", in_ntoa(qp-〉iph-〉saddr)); ip_statistics.IpReasmFails++; ip_free(qp); return NULL; }
问题出现在printk上,如果对方一直用超大碎片(len〉65535),内核将会无节制的调用printk报警。而printk这种操作是相当耗费资源的,因此造成DOS。 在2.0.34版中改成了: NETDEBUG(printk("Oversized IP packet from %s.\n", in_ntoa(qp-〉iph-〉saddr))); 而/include/net/sock.h中: #if 1 #define NETDEBUG(x) do { } while (0) #else #define NETDEBUG(x) do { x; } while (0) #endif 即只有在调试是才打开此功能,正常时不作任何事。
而后来的版本中加入了net_ratelimit()函数,限制成最多5秒钟发出一次内核警告: if (net_ratelimit()) printk(KERN_INFO "Oversized IP packet from %d.%d.%d.%d.\n", NIPQUAD(qp-〉iph-〉saddr)); 这个问题不光在分片组装时用到,所有的网络部分的代码在打印调试信息时都要考虑大量日志造成拒绝服务的问题。目前的比较好且通用解决办法便是通过net_ratelimit()函数。
(4)bugtraq id 543 Linux IPChains Fragment Overlap Vulnerability
碎片攻击不光会攻击操作系统,由于许多网络工具,如防火墙,入侵检测系统(IDS)也在内部作了分片组装,如果处理不当,也同样会遭受攻击,如著名的checkpoint的防火墙FW-1某些版本(最新的已经改正了)便同样会受到碎片DOS攻击(可详见nsfocus第12期月刊《了解Check Point FW-1状态表》).