├── README └── initcwnd_check.pl /README: -------------------------------------------------------------------------------- 1 | Get initial congestion window (initcwnd) and initial receive window (initrwnd) of remote server. 2 | 3 | Usage: initcwnd_check.pl () 4 | 5 | Example: 6 | 7 | # ./initcwnd_check.pl eth0 http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js 8 | + connected from x.x.x.x:1777 to 74.125.71.95:80 9 | * ajax.googleapis.com (74.125.71.95) - init_cwnd: 10 (14300 byte), init_rwnd: 4 (5720 byte) 10 | 11 | # ./initcwnd_check.pl eth0 http://kr.yahoo.com/ 111.67.226.84 12 | + connected from x.x.x.x:2652 to 111.67.226.84:80 13 | * kr.yahoo.com (111.67.226.84) - init_cwnd: 2 (2875 byte), init_rwnd: 4 (5840 byte) 14 | 15 | Requirements: 16 | 17 | * root 18 | * /sbin/iptables, /sbin/ip 19 | * perl module 20 | - Net::RawIP 21 | - Net::Pcap 22 | - NetPacket::Ethernet 23 | - NetPacket::IP 24 | - NetPacket::TCP 25 | - Socket 26 | - POSIX 27 | -------------------------------------------------------------------------------- /initcwnd_check.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use warnings; 4 | use strict; 5 | use Net::RawIP; 6 | use Net::Pcap; 7 | use NetPacket::Ethernet; 8 | use NetPacket::IP; 9 | use NetPacket::TCP; 10 | use Socket qw(inet_ntoa); 11 | use POSIX qw(ceil); 12 | 13 | $SIG{INT} = \&finish; 14 | 15 | # args 16 | my $dev = $ARGV[0] or &usage(); 17 | my $uri = $ARGV[1] or &usage(); 18 | my $dst_ip = $ARGV[2] or undef; 19 | my $src_ip = undef; 20 | 21 | # find local ip 22 | foreach my $ip (`/sbin/ip addr show dev $dev`) { 23 | if($ip =~ m/inet ([0-9\.]+)\/.+$dev$/) { 24 | $src_ip = $1; 25 | } 26 | } 27 | if(!defined $src_ip) { 28 | die "! can't find local ip\n"; 29 | } 30 | 31 | # default values 32 | my $now = time; 33 | my $src_port = 1024 + int(rand(6000)); 34 | my $dst_port = 80; 35 | 36 | my $src_seq = int(rand(2**32) + 1); 37 | my $dst_seq = undef ; 38 | my $dst_seq_last = undef; 39 | 40 | my $mss = 1460; 41 | my $pack_mss = pack('n', $mss); 42 | my $win_size = 65535; 43 | 44 | my $rwnd_size = 0; 45 | my $cwnd_size = 0; 46 | 47 | my $uri_host = undef; 48 | my $uri_path = "/"; 49 | 50 | # url parsing 51 | if($uri =~ /^http:\/\/([a-z0-9\.]+)(:([0-9]+)|)(\/.*|)$/i) { 52 | $uri_host = $1; 53 | if($3) { $dst_port = $3; } 54 | if($4) { $uri_path = $4; } 55 | } else { 56 | print STDERR "! invalid uri: $uri\n"; 57 | &usage(); 58 | } 59 | 60 | if($uri_host && !defined $dst_ip) { 61 | my $pack_ip = gethostbyname($uri_host); 62 | if(defined $pack_ip) { 63 | $dst_ip = inet_ntoa($pack_ip); 64 | } else { 65 | print STDERR "! can't find domain: $uri_host\n"; 66 | &usage(); 67 | } 68 | } 69 | 70 | my $http_req; 71 | $http_req = "GET $uri_path HTTP/1.1\r\n"; 72 | $http_req .= "Host: $uri_host\r\n"; 73 | $http_req .= "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_2) AppleWebKit/534.52.7 (KHTML, like Gecko) Version/5.1.2 Safari/534.52.7\r\n"; 74 | $http_req .= "Accept-Encoding: compress, gzip\r\n"; 75 | $http_req .= "Accept: */*\r\n\r\n"; 76 | my $http_req_len = length($http_req); 77 | 78 | # pcap 79 | my $pcap = ''; 80 | my $err = ''; 81 | my $filter_str = "ip and tcp"; 82 | my $filter = ''; 83 | my $snaplen = 1500 ; 84 | my $timeout = 0; 85 | my ($net,$mask) = 0; 86 | 87 | # RST packet drop for raw socket 88 | my $iptables = "OUTPUT -p tcp --tcp-flags RST RST -s $src_ip --sport $src_port -d $dst_ip --dport $dst_port -j DROP"; 89 | system("/sbin/iptables -A $iptables"); 90 | 91 | # init pcap 92 | Net::Pcap::pcap_lookupnet($dev, \$net, \$mask, \$err); 93 | $pcap = &Net::Pcap::pcap_open_live($dev, $snaplen, 1, $timeout, \$err); 94 | if (Net::Pcap::compile($pcap, \$filter, $filter_str, 0, $mask)) { 95 | print STDERR "! error compiling capture filter!\n"; 96 | exit 1; 97 | } 98 | Net::Pcap::pcap_setnonblock($pcap, 1, \$err); 99 | 100 | my $syn_pkt = make_packet($src_seq, undef, 1, 0, 0, 0, 65535, undef); 101 | 102 | $syn_pkt->send(); 103 | $src_seq++; 104 | 105 | $now = time; 106 | while (!defined $dst_seq) { 107 | Net::Pcap::pcap_dispatch($pcap, 1, \&receive_synack, ''); 108 | if($now <= time-3) { 109 | print STDERR "! connection timeout\n"; 110 | finish(); 111 | } 112 | } 113 | print STDERR "+ connected from $src_ip:$src_port to $dst_ip:$dst_port\n"; 114 | 115 | my $ack_pkt = make_packet($src_seq, $dst_seq, 0, 1, 0, 0); 116 | $ack_pkt->send(); 117 | 118 | my $data_pkt = make_packet($src_seq, $dst_seq, 0, 1, 1, 0, $http_req); 119 | $data_pkt->send(); 120 | 121 | $now = time; 122 | Net::Pcap::pcap_loop($pcap, -1, \&check_cwnd, ''); 123 | 124 | finish(); 125 | 126 | sub usage( ) { 127 | die "usage: $0 ()\n"; 128 | } 129 | 130 | sub finish { 131 | system("/sbin/iptables -D $iptables"); 132 | my $rst_pkt = make_packet($src_seq + $http_req_len, undef, 0, 0, 0, 1); 133 | $rst_pkt->send(); 134 | if($pcap) { 135 | Net::Pcap::close ($pcap); 136 | } 137 | if($cwnd_size > 0 && $rwnd_size > 0) { 138 | print "* $uri_host ($dst_ip) - init_cwnd: ".ceil($cwnd_size/$mss)." (".$cwnd_size." byte), init_rwnd: ".ceil($rwnd_size/$mss)." (".$rwnd_size." byte)\n"; 139 | } 140 | exit 0; 141 | } 142 | 143 | sub make_packet { 144 | my ($src_seq, $dst_seq, $syn, $ack, $psh, $rst, $data) = @_; 145 | 146 | my $pkt = Net::RawIP->new({ 147 | ip => { 148 | saddr => $src_ip, 149 | daddr => $dst_ip 150 | }, 151 | tcp => { 152 | source => $src_port, 153 | dest => $dst_port, 154 | seq => $src_seq, 155 | ack_seq => $dst_seq, 156 | syn => $syn, 157 | ack => $ack, 158 | psh => $psh, 159 | rst => $rst, 160 | window => $win_size, 161 | data => $data 162 | } 163 | }); 164 | $pkt->optset(tcp => {type => [2], data => [$pack_mss]}); 165 | 166 | return $pkt; 167 | } 168 | 169 | # receive packet parser 170 | sub receive_packet { 171 | my ($data, $hdr, $pkt) = @_ ; 172 | 173 | if (!$pkt || !defined($hdr) || !defined($pkt)) { 174 | print STDERR "! invalid packet!\n"; 175 | return undef; 176 | } 177 | 178 | my $eth = NetPacket::Ethernet->decode($pkt); 179 | my $ip = NetPacket::IP->decode($eth->{data}); 180 | my $tcp = NetPacket::TCP->decode($ip->{data}); 181 | if ($ip->{proto} != NetPacket::IP::IP_PROTO_TCP) { 182 | return undef; 183 | } 184 | 185 | my $seq = $tcp->{seqnum}; 186 | my $ack = $tcp->{acknum}; 187 | my $win = $tcp->{winsize}; 188 | my $len = length($tcp->{data}); 189 | 190 | return ($seq, $ack, $len, $win); 191 | } 192 | 193 | sub check_cwnd { 194 | my ($seq, $ack, $len, $win) = receive_packet(@_); 195 | 196 | if ((defined $seq && $seq) && ($ack == ($src_seq + $http_req_len))) { 197 | if(!defined $dst_seq_last || $dst_seq_last <= $seq) { 198 | $cwnd_size += $len; 199 | $dst_seq_last = $seq; 200 | } else { 201 | finish(); 202 | } 203 | } 204 | if($now <= time-3) { 205 | finish(); 206 | } 207 | } 208 | 209 | # get ack no of synack packet 210 | sub receive_synack { 211 | my ($seq, $ack, $len, $win) = receive_packet(@_); 212 | $rwnd_size = $win; 213 | 214 | if ((defined $seq && $seq) && ($ack == $src_seq)) { 215 | $dst_seq = $seq + 1; 216 | } 217 | } 218 | --------------------------------------------------------------------------------