Loading...

当前位置:资讯中心主页 >Solaris >文章内容

  • SPARC/Solaris 8下快速终结TCP有限状态机的TIME_WAIT状态
  • 来源:ChinaUnix.net作者:ChinaUnix.net 发布时间:2008-04-07 15:23:56
    • 域名注册

    • 域名惊喜价格 cn域名1元注册
    • com域名39.9

      虚拟主机

    • 主机按月支付,低至19元/月
    • 超大流量,可开子站点

      VPS主机

    • 特惠VPS168元/月,4-8M独享带宽保证
    • 独立操作系统,无限开站点

    SPARC/Solaris 8下快速终结TCP有限状态机的TIME_WAIT状态

    发布日期:2002-09-17
    文章内容:
    --------------------------------------------------------------------------------
    作者:NSFocus Security Team <security@nsfocus.com>
    主页:http://www.nsfocus.com
    日期:2002-07-17

    我们这里讨论的是在程式不可控的情况下,比如别人的程式,又未使用SO_REUSEADDR选项,怎么快 速终结TCP有限状态机的TIME_WAIT状态。

    参看这个链接"closing half-open connections"

    http://www-mice.cs.ucl.ac.uk/multimedia/misc/tcp_ip/8707.mm.www/0009.html

    1987年10月1日(可真够早的)cdjohns@nswc-g.arpa提供了一个shell script,用于
    SunOS 4.x系统下快速终结TCP有限状态机的TIME_WAIT状态。非常奇怪,这个脚本并未
    得到人们的足够重视并广泛流传,直到SunOS 4.x退出历史舞台,他也就销声匿迹了。
    昨天被backend从故纸堆里翻了出来,我们就趁机移植到SPARC/Solaris 8下来。

    参看UNP 图2.4了解TCP有限状态机的变迁过程。

    /usr/include/netinet里的文件多是为用户空间编程准备的,/usr/include/inet里
    的文件多是为内核空间编程准备的。下面内容取自SPARC/Solaris 8

    --------------------------------------------------------------------------
    /*
    * /usr/include/inet/tcp.h
    */

    /*
    * TCP states
    */
    #define TCPS_CLOSED      -6
    #define TCPS_IDLE        -5  /* idle (opened, but not bound)      */
    #define TCPS_BOUND      -4  /* bound, ready to connect or accept */
    #define TCPS_LISTEN      -3  /* listening for connection          */
    #define TCPS_SYN_SENT    -2  /* active, have sent syn            */
    #define TCPS_SYN_RCVD    -1  /* have received syn (and sent ours) */
    /*
    * states < TCPS_ESTABLISHED are those where connections not established
    */
    #define TCPS_ESTABLISHED  0  /* established                      */
    #define TCPS_CLOSE_WAIT  1  /* rcvd fin, waiting for close      */
    /*
    * states > TCPS_CLOSE_WAIT are those where user has closed
    */
    #define TCPS_FIN_WAIT_1  2  /* have closed and sent fin          */
    #define TCPS_CLOSING      3  /* closed, xchd FIN, await FIN ACK  */
    #define TCPS_LAST_ACK    4  /* had fin and close; await FIN ACK  */
    /*
    * states > TCPS_CLOSE_WAIT && < TCPS_FIN_WAIT_2 await ACK of FIN
    */
    #define TCPS_FIN_WAIT_2  5  /* have closed, fin is acked        */
    #define TCPS_TIME_WAIT    6  /* in 2*msl quiet wait after close  */

    #if (defined(_KERNEL) || defined(_KMEMUSER))

    /*
    * If the information represented by the field is required even in the
    * TIME_WAIT state, it must be part of tcpb_t. Otherwise it must be part
    * of tcp_t. In other words, the tcp_t captures the information that is
    * not required, after a connection has entered the TIME_WAIT state.
    */
    typedef struct tcp_base_s
    {
        struct tcp_base_s  *tcpb_bind_hash;        /* Bind hash chain                    */
        struct tcp_base_s **tcpb_ptpbhn;          /* Pointer to previous bind hash next. */
        struct tcp_base_s *tcpb_conn_hash;        /* Connect hash chain                  */
        struct tcp_base_s **tcpb_ptpchn;          /* Pointer to previous conn hash next. */
        struct tcp_base_s *tcpb_time_wait_next;    /* Pointer to next T/W block          */
        struct tcp_base_s *tcpb_time_wait_prev;    /* Pointer to previous T/W next        */
        /*
        * /usr/include/sys/types.h
        * typedef long clock_t;
        * offset: 8 * 6 -> 48 -> 0x30,clock_t占8个字节(64-bit kernel mode)
        */
        clock_t            tcpb_time_wait_expire;  /* time in hz when t/w expires        */
        clock_t            tcpb_last_rcv_lbolt;    /* lbolt on last packet, used for PAWS */
        /*
        * offset: 0x40
        */
        int32_t            tcpb_state;
        int32_t            tcpb_rcv_ws;            /* My window scale power              */
        int32_t            tcpb_snd_ws;            /* Sender’s window scale power        */
        uint32_t          tcpb_ts_recent;        /* Timestamp of earliest unacked      */
        /*
        * data segment
        * offset: 0x50
        */
        clock_t            tcpb_rto;              /* Round trip timeout                  */
        uint32_t          tcpb_snd_ts_ok  : 1,
                          tcpb_snd_ws_ok  : 1,
                          tcpb_is_secure  : 1,
                          tcpb_reuseaddr  : 1,    /* SO_REUSEADDR "socket" option.      */
                          tcpb_exclbind  : 1,    /* ``exclusive’’ binding              */
                          tcpb_junk_fill_thru_bit_31 : 27;
        /*
        * offset: 0x5c
        */
        uint32_t          tcpb_snxt;              /* Senders next seq num                */
        uint32_t          tcpb_swnd;              /* Senders window (relative to suna)  */
        uint32_t          tcpb_mss;              /* Max segment size                    */
        uint32_t          tcpb_iss;              /* Initial send seq num                */
        /*
        * offset: 0x6c
        */
        uint32_t          tcpb_rnxt;              /* Seq we expect to recv next          */
        uint32_t          tcpb_rwnd;              /* Current receive window              */
        /*
        * /usr/include/sys/mutex.h
        * 无论64-bit还是32-bit kernel mode,kmutex_t都占8字节空间,这里需要对
        * 齐在数据类型自然连界上,0x6c + 8 -> 0x74不在kmutex_t自然边界上,继
        * 续后移4个字节
        *
        * offset: 0x78
        */
        kmutex_t          tcpb_reflock;          /* Protects tcp_refcnt                */
        /*
        * offset: 0x80
        */
        ushort_t          tcpb_refcnt;            /* Number of pending upstream msg      */
        union
        {
            struct
            {
                uchar_t v4_ttl;  /* Dup of tcp_ipha.iph_type_of_service */
                uchar_t v4_tos;  /* Dup of tcp_ipha.iph_ttl            */
            } v4_hdr_info;
            struct
            {
                /*
                * 对齐在uint_t的自然边界上
                * offset: 0x84
                */
                uint_t  v6_vcf;  /* Dup of tcp_ip6h.ip6h_vcf            */
                /*
                * offset: 0x88
                */
                uchar_t v6_hops;  /* Dup of tcp_ip6h.ip6h_hops          */
            } v6_hdr_info;
        } tcpb_hdr_info;

    #define tcpb_ttl      tcpb_hdr_info.v4_hdr_info.v4_ttl
    #define tcpb_tos      tcpb_hdr_info.v4_hdr_info.v4_tos
    #define tcpb_ip6_vcf  tcpb_hdr_info.v6_hdr_info.v6_vcf
    #define tcpb_ip6_hops tcpb_hdr_info.v6_hdr_info.v6_hops

        /*
        * offset: 0x8c,先是远端地址,后是本地地址
        * /usr/include/netinet/in.h
        * in6_addr_t长16字节,在内核空间里这个结构对齐在uint32_t的自然边界上
        */
        in6_addr_t        tcpb_remote_v6;        /* true remote address - needed for    */
                                                  /* source routing.                    */
        in6_addr_t        tcpb_bound_source_v6;  /* IP address in bind_req              */
        /*
        * offset: 0xac
        */
        in6_addr_t        tcpb_ip_src_v6;        /* same as tcp_iph.iph_src.            */

    #ifdef _KERNEL
    /*
    * Note: V4_PART_OF_V6 is meant to be used only for _KERNEL defined stuff
    */
    #define tcpb_remote      V4_PART_OF_V6(tcpb_remote_v6)
    #define tcpb_bound_source V4_PART_OF_V6(tcpb_bound_source_v6)
    #define tcpb_ip_src      V4_PART_OF_V6(tcpb_ip_src_v6)
    #endif  /* _KERNEL */

        /*
        * These fields contain the same information as tcp_tcph->th_*port.
        * However, the lookup functions can not use the header fields
        * since during IP option manipulation the tcp_tcph pointer
        * changes.
        */
        /*
        * offset: 0xbc,先是远端端口,后是本地端口
        */
        union
        {
            struct
            {
                in_port_t tcpu_fport;  /* Remote port          */
                in_port_t tcpu_lport;  /* Local port          */
            } tcpu_ports1;
            uint32_t      tcpu_ports2;  /* Rem port, local port */
            /*
            * Used for TCP_MATCH performance
            */
        } tcpb_tcpu;
    #define tcpb_fport tcpb_tcpu.tcpu_ports1.tcpu_fport
    #define tcpb_lport tcpb_tcpu.tcpu_ports1.tcpu_lport
    #define tcpb_ports tcpb_tcpu.tcpu_ports2
        /*
        * IP sends back 2 mblks with the unbind ACK for handling
        * IPSEC policy for detached connections. Following two fields
        * are initialized then.
        */
        mblk_t            *tcpb_ipsec_out;
        mblk_t            *tcpb_ipsec_req_in;
        /*
        * offset: 0xd0
        */
        tcp_t            *tcpb_tcp;
        /*
        * offset: 0xd8
        */
        /*
        * IP format that packets transmitted from this struct should use.
        * Value can be IPV4_VERSION or IPV6_VERSION.  Determines whether
        * IP+TCP header template above stores an IPv4 or IPv6 header.
        */
        ushort_t          tcpb_ipversion;
        uint_t            tcpb_bound_if;          /* IPV6_BOUND_IF                      */
        uid_t              tcpb_ownerid;          /* uid of process that did open        */
    #define tcp_bind_hash        tcp_base->tcpb_bind_hash
    #define tcp_ptpbhn          tcp_base->tcpb_ptpbhn
    #define tcp_conn_hash        tcp_base->tcpb_conn_hash
    #define tcp_ptpchn          tcp_base->tcpb_ptpchn
    #define tcp_time_wait_next  tcp_base->tcpb_time_wait_next
    #define tcp_time_wait_prev  tcp_base->tcpb_time_wait_prev
    #define tcp_time_wait_expire tcp_base->tcpb_time_wait_expire
    #define tcp_last_rcv_lbolt  tcp_base->tcpb_last_rcv_lbolt
    #define tcp_state            tcp_base->tcpb_state
    #define tcp_rcv_ws          tcp_base->tcpb_rcv_ws
    #define tcp_snd_ws          tcp_base->tcpb_snd_ws
    #define tcp_ts_recent        tcp_base->tcpb_ts_recent
    #define tcp_rto              tcp_base->tcpb_rto
    #define tcp_snd_ts_ok        tcp_base->tcpb_snd_ts_ok
    #define tcp_snd_ws_ok        tcp_base->tcpb_snd_ws_ok
    #define tcp_is_secure        tcp_base->tcpb_is_secure
    #define tcp_snxt            tcp_base->tcpb_snxt
    #define tcp_swnd            tcp_base->tcpb_swnd
    #define tcp_mss              tcp_base->tcpb_mss
    #define tcp_iss              tcp_base->tcpb_iss
    #define tcp_rnxt            tcp_base->tcpb_rnxt
    #define tcp_rwnd            tcp_base->tcpb_rwnd
    #define tcp_reflock          tcp_base->tcpb_reflock
    #define tcp_refcnt          tcp_base->tcpb_refcnt
    #define tcp_remote_v6        tcp_base->tcpb_remote_v6
    #define tcp_remote          tcp_base->tcpb_remote
    #define tcp_bound_source_v6  tcp_base->tcpb_bound_source_v6
    #define tcp_bound_source    tcp_base->tcpb_bound_source
    #define tcp_lport            tcp_base->tcpb_tcpu.tcpu_ports1.tcpu_lport
    #define tcp_fport            tcp_base->tcpb_tcpu.tcpu_ports1.tcpu_fport
    #define tcp_ports            tcp_base->tcpb_tcpu.tcpu_ports2
    #define tcp_ipsec_out        tcp_base->tcpb_ipsec_out
    #define tcp_ipsec_req_in    tcp_base->tcpb_ipsec_req_in
    #define tcp_ipversion        tcp_base->tcpb_ipversion
    #define tcp_bound_if        tcp_base->tcpb_bound_if
    #define tcp_reuseaddr        tcp_base->tcpb_reuseaddr
    #define tcp_exclbind        tcp_base->tcpb_exclbind
    #define tcp_ownerid          tcp_base->tcpb_ownerid
    } tcpb_t;
    #endif  /* (defined(_KERNEL) || defined(_KMEMUSER)) */
    --------------------------------------------------------------------------

    下面这两条命令其实是用分号连在一起执行的,否则前后数据不对应,我是telnet上
    去执行命令。

    # ndd /dev/tcp tcp_status | grep ESTABLISHED
    TCPB        dest              snxt    suna    swnd      rnxt    rack    rwnd      rto  mss  w sw rw t recent  [lport,fport] state
    30000cc21d0 ::ffff:192.168.5.8 8e0e46dc 8e0e46da 0000064010 3d49e751 3d49e751 0000024820 00583 01460 0 00 00 0 00000000 [23, 4613]    TCP_ESTABLISHED
    # skd64 0x30000cc21d0 256
    byteArray [ 256 bytes ] ---->
    0000000000000000  00 00 03 00 00 47 C1 80-00 00 00 00 10 48 18 88    .....G?.....H.?
    0000000000000010  00 00 00 00 00 00 00 00-00 00 03 00 00 3A 60 80    .............:`�
    0000000000000020  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00    ................
    0000000000000030  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00    ................
    0000000000000040  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00    ................
    0000000000000050  00 00 00 00 00 00 02 47-10 00 00 00 8E 0E 46 DC    .......G....?F?
    0000000000000060  00 00 FA 0A 00 00 05 B4-8E 0B 77 07 3D 49 E7 51    ..?...?.w.=I?
    0000000000000070  00 00 60 F4 00 00 00 00-00 00 00 00 00 00 00 00    ..`?...........
    0000000000000080  00 01 00 00 3C 00 00 00-3C 00 00 00 00 00 00 00    ....<...<.......
    0000000000000090  00 00 00 00 00 00 FF FF-C0 A8 05 08 00 00 00 00    ......��括......
    00000000000000A0  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00    ................
    00000000000000B0  00 00 00 00 00 00 FF FF-C0 A8 05 82 12 05 00 17    ......��括.?...
    00000000000000C0  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00    ................
    00000000000000D0  00 00 03 00 00 BB 6F D0-00 04 00 00 00 00 00 00    .....??.......
    00000000000000E0  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00    ................
    00000000000000F0  00 00 03 00 00 08 2B 68-00 00 00 00 00 00 00 00    ......+h........
    #

    显然0x30000cc21d0对应着一个tcpb_t结构。

    # adb -k /dev/ksyms /dev/mem
    physmem 3b68
    0x30000cc21d0$<tcpb

    30000cc21d0:    tcp_bind_hash  tcp_ptpbhn      tcp_conn_hash
                3000047c180      10481888        0
    30000cc21e8:    tcp_ptpchn      time_wait_next  time_wait_prev
                300003a6080      0                0
    30000cc2200:    time_wait_expir last_rcv_lbolt  tcp_state
                0                0                0
    30000cc2214:    tcp_rcv_ws      tcp_snd_ws      tcp_ts_recent
                    0              0              0
    30000cc2220:    tcp_rto
                24c
                    snd_ts_ok      0
                    snd_ws_ok      0
                    is_secure      0
                    reuseaddr      1
                    exclbind        0

    30000cc222c:    tcp_snxt        tcp_swnd        tcp_mss
                    8e176ce4        f8dc            5b4
    30000cc2238:    tcp_iss        tcp_rnxt        tcp_rwnd
                    8e0b7707        3d49ecf0        60f4
    30000cc2248:    tcpb_reflock
    30000cc2248:    owner/waiters
                0


    30000cc2250:    tcp_refcnt      tcpb_vcf        tcpb_hops
                    1              3c000000        3c
    30000cc225c:    tcpb_remote_v6

    30000cc225c:    0              0              ffff            c0a80508

    30000cc226c:    tcpb_bound_source_v6

    30000cc226c:    0              0              0              0

    30000cc227c:    tcpb_ip_src_v6

    30000cc227c:    0              0              ffff            c0a80582

    30000cc228c:    tcpu_fport      tcpu_lport      ipsec_out
                    1205            17              0
    30000cc2298:    ipsec_req_in    tcpb_tcp        tcpb_ipversion
                0                30000bb6fd0      4
    30000cc22ac:    bound_if        ownerid
                    0              0

    $q
    #

    于是我们能写这样一个脚本kill_timewait.sh

    --------------------------------------------------------------------------
    #! /sbin/sh
    #

    #
    # @(#)kill_timewait.sh 2002-07-07 NSFocus Copyleft 2002-2012
    #
    # Notice here is copyleft but not copyright, enjoy it by yourself.
    #
    # ------------------------------------------------------------------------
    # File    : kill_timewait.sh
    # Platform : SPARC/Solaris 8 64-bit kernel mode
    # Author  : NSFocus Security Team <security@nsfocus.com>
    #          : http://www.nsfocus.com
    # Date    : 2002-07-07 12:27
    # Modify  :
    # Thanks  : cdjohns@nswc-g.arpa for SunOS 4.x implementation
    #

    netstat -na -P tcp -f inet | grep TIME_WAIT

    echo
    echo ’TCPB dest [lport,fport] state’
    echo
    ndd /dev/tcp tcp_status | nawk ’{print $1 " " $2 " " $16 $17 " " $18}’ | egrep ’TIME_WAIT’

    echo
    /usr/bin/echo ’TCPB address to terminate: \c’
    read tcpb_addr
    echo

    adb -k /dev/ksyms /dev/mem << NSFOCUS_EOF
    $tcpb_addr\$</usr/lib/adb/sparcv9/tcpb
    \$q
    NSFOCUS_EOF

    #
    # Check to see if this was the correct address and TCPB. state should be 6
    #
    echo
    echo ’tcp_state = 6 = TCPS_TIME_WAIT’
    /usr/bin/echo ’Is this the correct TCPB (y/n)? \c’
    read answer
    echo
    case $answer in
      [Yy]*)
    ;;
      *)
    echo ’No Changes.’
    exit
    ;;
    esac

    #
    # Kernel Hacking, please. These value are expressed in hexadecimal.
    #
    TIME_WAIT_EXPIRE_OFFSET=0x30
    STATE_OFFSET=0x40

    #
    # This value is expressed in decimal and must be greater than zero.
    #
    TIME_WAIT_EXPIRE=0t06

    #
    # Use adb on kernel to set the tcpb_time_wait_expire=6 and
    # tcpb_state=TCPS_CLOSED (-6)
    #
    adb -kw /dev/ksyms /dev/mem << NSFOCUS_EOF
    $tcpb_addr+$TIME_WAIT_EXPIRE_OFFSET/Z $TIME_WAIT_EXPIRE
    $tcpb_addr+$STATE_OFFSET/W -6
    \$q
    NSFOCUS_EOF

    echo
    echo "TIME_WAIT state will disappear."
    echo

    netstat -na -P tcp -f inet | grep TIME_WAIT
    --------------------------------------------------------------------------

    不要设置tcpb_time_wait_expire成零,只要是个非常小的值就能了。这里必须同
    时设置tcpb_time_wait_expire和tcpb_state,只设置其中一个达不到效果。

    利用adb从TCPS_ESTABLISHED变为TCPS_CLOSE_WAIT,能使一条TCP连接不再工作,
    但这条连接并未销毁,tcpb_t结构也未删除。

    利用adb从TCPS_ESTABLISHED变为TCPS_CLOSED,会导致整个操作系统崩溃。可能是下
    层tcpb_t结构被删除,而上层socket并不了解,出现非法指针。

    简化一下kill_timewait.sh

    --------------------------------------------------------------------------
    #! /sbin/sh

    ndd /dev/tcp tcp_status | nawk ’{print $1 " " $2 " " $16 $17 " " $18}’ | egrep ’TIME_WAIT’

    echo
    /usr/bin/echo ’TCPB address to terminate: \c’
    read tcpb_addr
    echo

    adb -kw /dev/ksyms /dev/mem << NSFOCUS_EOF
    $tcpb_addr+0x30/Z 0t6
    $tcpb_addr+0x40/W -6
    \$q
    NSFOCUS_EOF
    --------------------------------------------------------------------------

    还能写一个脚本自动清除所有TIME_WAIT状态TCP连接

    --------------------------------------------------------------------------
    #! /sbin/sh

    ndd /dev/tcp tcp_status | nawk ’{print $1 " " $2 " " $16 $17 " " $18}’ | \
    egrep ’TIME_WAIT’ | cut -d’ ’ -f1 | while read tcpb_addr
    do
    adb -kw /dev/ksyms /dev/mem << NSFOCUS_EOF
    $tcpb_addr+0x30/Z 0t6
    $tcpb_addr+0x40/W -6
    \$q
    NSFOCUS_EOF
    done
    ----------------------------------------------------------------------


  • 以上内容由 华夏名网 搜集整理,如转载请注明原文出处,并保留这一部分内容。

  •  solaris小兵 回复于:2004-08-24 18:19:40
    顶!!
    非常不错啊!!非常有用!!

     solaris小兵 回复于:2004-08-24 18:26:42
    建议精华!!!

     yutian 回复于:2004-09-16 09:28:07
    顶起来

     anber45 回复于:2004-11-01 12:48:37
    原来公司的有个项目常常因TIME_WAIT过多,没有办法创建新的连接,让一班人苦脑死了。

    ^_^ 可惜目前我已离开了

     棉花糖 回复于:2004-11-03 10:24:21
    solaris8 32bit的能用吗?
    netstat -an |grep TIME_WAIT 有结果出来,不过
    ndd /dev/tcp tcp_status | nawk ’{print $1 " " $2 " " $16 $17 " " $18}’ | egrep ’TIME_WAIT’ 什么也没有

     HaHaDaDao 回复于:2004-11-03 13:12:55
    我在solaris 7上试过这个冬冬,结果crash了,solaris 8上一直没试过 :em11:

      “华夏名网” http://www.sudu.cn 和 http://www.bigwww.com 是成都飞数科技有限公司的网络服务品牌,专业经营虚拟主机,域名注册,VPS,服务器租用业务。公司创建于2002年,经过6年的高速发展,“华夏名网”已经成为我国一家知名的互联网服务提供商,被国外权威机构webhosting.info评价为25大IDC服务商之一。

    华夏名网网址导航: 虚拟主机 双线主机 主机 域名注册 cn域名 域名 服务器租用 酷睿服务器 vps vps主机

  • (阅读次数:41)
  • 上一篇: 关于如何修改hostid的问题    下一篇: 在Solaris8上安装SSH
  • [收藏] [推荐] [评论] [打印本页] [返回上一页][关闭窗口]
  • 昵称: (为空则显示guest)
  • 评论分数: ★ ★ ★★★ ★★★★ ★★★★★
  • 评论内容:(不能超过250字,需审核后才会公布,请自觉遵守互联网相关政策法规。