看了tcpcopy的源码,php 和 python 都可以操作raw socket,因此,用php 和 python 实现了tcpcopy,代码比较简单(有些地方偷懒了~~)。
tcpcopy关键点,理认上来说,只要得到tcp请求报文中数据部分,在ip层转发到测试服务器,即可实现流量复制。
因此,只需要维护tcp会话即可维护一个假的tcp连接,骗过测试服务器即可。
代码实现,主要以TCP状态机为基础,同时考虑抓包的无序问题,结合tcpdump调试,用php实现tcpcopy,用python实现intercept。
以下代码,经测试,客户机发起5000个http请求(大约50个长连接),流量全部会复制到测试机上。
其实,tcpcopy与lvs、nat、运营商流量劫持工作原理类似,都是通过欺骗tcp协议栈达到目的; 同理,通过在应用层处理原始套接字,也可以实现lvs负载均衡功能(后续可能会尝试)。
Code:
1. tcpcopy.php
两个进程工作,一个进程负责抓包并放入queue中; 另一个进程负责消费抓到的数据包,因此对tcpcopy进程效率要求不高。
s = $s;
return $this->s;
}
// listen local ip
public function create_listen_socket( $local_ip, $remote_ip=null ){
$one = 1;
$raw_socket = socket_create( AF_INET, SOCK_RAW, getprotobyname("tcp") );
// $raw_socket = socket_create( AF_INET, SOCK_RAW, getprotobyname("icmp") ); /* no need bind,connect, 不能抓不属于自己的包 */
// $raw_socket = socket_create( AF_INET, SOCK_RAW, 1 );
socket_set_nonblock( $raw_socket );
// trick, 3 is stand for header control
$set_ret = socket_set_option( $raw_socket, getprotobyname("ip"), 3, $one);
$conn_ret = $bind_ret = true;
if( isset( $local_ip) ){
// 设置 dest ip
$bind_ret = socket_bind($raw_socket, $local_ip );
}
if( isset( $remote_ip) ){
// 不限制 source_ip
// $conn_ret = socket_connect( $raw_socket, $remote_ip, 0 );
}
// $bind_ret = socket_connect( $raw_socket, $remote_ip, 0 );
// $bind_ret = socket_bind($raw_socket, $local_ip );
if( $raw_socket === false
|| $set_ret === false
|| $conn_ret === false
|| $bind_ret === false ){
echo "socket_last_error_str:" . socket_strerror(socket_last_error()) . "\n";
return false;
}
$this->s = $raw_socket;
return $this->s;
}
// send raw socket to ip
public function create_send_socket( $remote_ip ){
$this->dest_ip = $remote_ip;
$dest_ip_fake = "127.0.0.1";
$dest_ip_fake = "192.168.56.101";
$dest_port_fake = 8080;
$one = 1;
$raw_socket = socket_create( AF_INET, SOCK_RAW, getprotobyname("tcp") );
// $raw_socket = socket_create( AF_INET, SOCK_RAW, getprotobyname("ip") );
socket_set_nonblock( $raw_socket );
// trick, 3 is stand for header control
$set_ret = socket_set_option( $raw_socket, getprotobyname("ip"), 3, $one);
// trick, connect is needed anyway. $dest_ip must by right.
$conn_ret = socket_connect( $raw_socket, $remote_ip, 0 );
// $conn_ret = socket_connect( $raw_socket, $dest_ip_fake, $dest_port );
// trick, for read
// socket_bind($raw_socket, $src_ip );
if( $raw_socket === false
|| $set_ret === false
|| $conn_ret === false ){
echo "socket_last_error_str:" . socket_strerror(socket_last_error()) . "\n";
return false;
}
$this->s = $raw_socket;
return $this->s;
}
public function create_socket( $src_ip, $src_port, $dest_ip=null, $dest_port=null ){
$this->dest_ip = $dest_ip;
$this->dest_port = $dest_port;
$dest_ip_fake = "127.0.0.1";
$dest_ip_fake = "192.168.56.101";
$dest_port_fake = 8080;
$one = 1;
$raw_socket = socket_create( AF_INET, SOCK_RAW, getprotobyname("tcp") );
socket_set_nonblock( $raw_socket );
// trick, 3 is stand for header control
$set_ret = socket_set_option( $raw_socket, getprotobyname("ip"), 3, $one);
// trick, connect is needed anyway. $dest_ip must by right.
// $conn_ret = socket_connect( $raw_socket, $dest_ip, $dest_port );
// $conn_ret = socket_connect( $raw_socket, $dest_ip_fake, $dest_port );
// trick, for read
socket_bind($raw_socket, $src_ip );
if( $raw_socket === false
|| $set_ret === false
|| $conn_ret === false ){
echo "socket_last_error_str:" . socket_strerror(socket_last_error()) . "\n";
return false;
}
$this->s = $raw_socket;
return $this->s;
}
public function socket_read_raw( ){
$read_ret = socket_read( $this->s, 65535 );
if( $read_ret === false ){
$error = socket_last_error();
if( 11 !== $error ){
echo "socket_last_error_str:" . socket_strerror(socket_last_error()) . "\n";
return null;
}
return false;
}
if( strlen($read_ret) > 20 ){
// echo "read_data...\n";
return self::IPUnpack( $read_ret );
}
return null;
}
public function socket_read_udp( ){
$read_ret = socket_recvfrom( $this->s, $buf, 65535, 0, $from='', $port=0 );
//echo $read_ret . "\n";
if( $read_ret === false ){
$error = socket_last_error();
if( 11 !== $error ){
echo "socket_last_error_str:" . socket_strerror(socket_last_error()) . "\n";
return null;
}
return false;
}
if( $read_ret > 20 ){
// echo "udp_read_data...\n";
return self::IPUnpack( $buf );
}
return null;
}
/*
public function socket_recv_raw( ){
$buf = '';
$read_ret = socket_recvfrom( $this->s, $buf, 65535 );
if( $read_ret === false ){
$error = socket_last_error();
if( 11 !== $error ){
echo "socket_last_error_str:" . socket_strerror(socket_last_error()) . "\n";
return null;
}
return false;
}
if( strlen($buf) > 20 ){
echo "read_data...\n";
return $this->IPUnpack( $buf );
}
return null;
}
*/
public function socket_send(
$src_ip, $src_port, $dest_ip, $dest_port,
$seq_num, $ack_num, $tcp_flag,
$tcp_data,$timestamp, $ts_echo ){
$ip_data = self::TCPPacket( ip2long($src_ip), ip2long($dest_ip), $src_port, $dest_port,
$seq_num, $ack_num, $tcp_flag,
$tcp_data, $timestamp, $ts_echo );
// echo "tcp_data_md5:" . md5( $ip_data ) . "\n";
$ip_data = self::IPPacket("tcp", ip2long($src_ip), ip2long($dest_ip), $ip_data );
$write_ret = socket_write( $this->s, $ip_data );
if( $write_ret === false || $write_ret !== strlen($ip_data)){
echo "socket_last_error_str:" . socket_strerror(socket_last_error()) . "\n";
return false;
}
// echo "socket_last_error_str:" . socket_strerror(socket_last_error()) . "\n";
// echo "write_ret:" . json_encode( $write_ret ) . "\n";
return $write_ret;
}
/* IP header */
/*
versionAndHeaderlen: 1Byte
service: 1Byte
totalLen: 2Bytes
PacketID: 2Bytes
SliceInfo: 2Bytes
TTL: 1Byte
type: 1Byte
checksum: 2Bytes ==> just ip header, 16bits fan ma sum
srcIP: 4Bytes
destIP: 4Bytes
*/
public static function IPUnpack( $packet ){
$arr = unpack("Cverlen/x/ntotal_len/x4/Cttl/Ctype/ncheck_sum/Nsrc_ip/Ndest_ip/x*", $packet);
$arr['version'] = $arr['verlen'] >> 4;
$arr['header_len'] = ($arr['verlen'] & 0x0f) << 2;
unset( $arr['verlen'] );
$arr['type'] = getprotobynumber( $arr['type'] );
$arr['src_ip'] = long2ip( $arr['src_ip'] );
$arr['dest_ip'] = long2ip( $arr['dest_ip'] );
if( $arr['type'] == 'tcp' ){
$arr['tcp'] = self::TCPUnPack( substr($packet, $arr['header_len'] ) );
$tcp_data_len = $arr['total_len'] - $arr['header_len'] - $arr['tcp']['header_len'];
$arr['tcp']['data_len'] = $tcp_data_len;
}
if( strlen($packet) < 20 ) return false;
return $arr;
}
public static function IPPacket( $proto, $src_ip, $dest_ip, $data ){
$ver_len = 4<<4 | 5;
$service = 0;
$total_len = 20+strlen($data);
$id_flag_offset = 0;
$ttl = 65;
$type = getprotobyname( $proto);
$chk_sum = 0;
$i = 2;
while( $i-- ){
$header = pack("CCn"."N"."CCn"."N"."N",
$ver_len, $service, $total_len,
$id_flag_offset,
$ttl, $type, str2int($chk_sum,2),
$src_ip, $dest_ip
);
$chk_sum= self::check_sum( $header, false );
}
//echo "check_sum_verify:";
//get_asc($this->check_sum( $header ));
return $header . $data;
}
/* TCP header */
/*
srcPort: 2Bytes
destPort: 2Bytes
seqNum: 4Bytes
ackNum: 4Bytes
headerLenAndFlag: 2Byte ==> 4Bits(Len)+6Bits(reserved)+(U,ACK,PSH,RST,SYN,FIN)
windowSize: 2Bytes
checkSum: 2Bytes ==> tcp header + tcp data
urgentPoint: 2Bytes
*/
/*
* opt: kind(8bit)+len(8bit)+content
*/
public static function TCPUnpack( $packet ){
$arr = unpack("nsrc_port/ndest_port/Nseq_num/Nack_num/nhdrlen_flag/nwindow_size/ncheck_sum/nurgent/xtcp_data", $packet."*" );
$arr['header_len'] = ($arr['hdrlen_flag'] >> 12 ) << 2;
$flag = $arr['hdrlen_flag'] & 0x3f;
unset( $arr['hdrlen_falg'] );
$arr['FIN'] = ($flag & 0x01) ? true : false;
$arr['SYN'] = (($flag>>1) & 0x01) ? true : false;
$arr['RST'] = (($flag>>2) & 0x01) ? true : false;
$arr['ACK'] = (($flag>>4) & 0x01) ? true : false;
$arr['tcp_data'] = strlen($packet) == $arr['header_len'] ? '': substr( $packet, $arr['header_len'] );
return $arr;
}
public static function TCPPacket( $src_ip, $dest_ip, $src_port, $dest_port, $seq_num, $ack_num, $flag,
$tcp_data, $timestamp=null, $ts_echo=null ){
$window_size = 6000;
$chk_sum = 0;
$header_len = 20 >> 2;
$header_option = "";
if( $timestamp !== null ){
$header_option = pack("CCNNn", 8, 10, $timestamp, $ts_echo, 0);
$header_len = (20+12) >> 2;
}
$i = 2;
while( $i -- ){
$tcp_header = pack("nn"."N"."N"."nn"."nn",
$src_port, $dest_port,
$seq_num,
$ack_num,
($header_len << 12) + $flag, $window_size,
str2int($chk_sum,2), 0
//$chk_sum, 0
)
. $header_option;
$ps_header = pack("NNCCn",
$src_ip, $dest_ip,
0, getprotobyname("tcp"),
strlen($tcp_header)+strlen($tcp_data)
);
$chk_sum = self::check_sum( $ps_header . $tcp_header . $tcp_data );
}
$packet = $tcp_header . $tcp_data;
return $packet;
}
private static function check_sum( $data, $need_pack=false ){
if( strlen($data)%2 ){
$data .= "\x00";
}
$bits = unpack("n*", $data );
$chk_sum = array_sum( $bits );
while( $chk_sum >> 16 ){
$chk_sum = ($chk_sum >> 16) + ($chk_sum & 0xffff);
}
$chk_sum = 0xffff & ~$chk_sum;
if( true || $need_pack ){
$chk_sum = pack("n*", $chk_sum );
return $chk_sum;
}
return $chk_sum;
}
}
$TCP_SYN = 1<<1;
$TCP_ACK = 1<<4;
$TCP_FIN = 1;
$tcp_data = "";
$tcp_flag = 0 | $TCP_SYN;
$timestamp = 100000000;
//$timestamp = null;
$ts_echo = 0;
/*
$raw_socket = socket_create( AF_INET, SOCK_RAW, getprotobyname("tcp") );
socket_set_nonblock( $raw_socket );
$one = 1;
$set_ret = socket_set_option( $raw_socket, getprotobyname("ip"), 3, $one);
$conn_ret = socket_connect( $raw_socket, $dest_ip_fake, $dest_port );
while( true ){
break;
$read_ret = socket_read( $raw_socket, 65535 );
if( $read_ret !== false ){
echo json_encode( IPUnpack( $read_ret ) ) . "\n";
}
}
*/
$g_send_socket = $raw_socket_1 = new RawSocket();
$s1 = $raw_socket_1->create_send_socket($dest_ip);
/*
$raw_socket_1->socket_send( $src_ip, $src_port, $dest_ip, $dest_port,
$seq_num, $ack_num, $tcp_flag,
$tcp_data, $timestamp, $ts_echo );
*/
$raw_socket_2 = new RawSocket( );
$s2 = $raw_socket_2->create_listen_socket( /*local_ip*/ $local_ip, /*$dest_ip*/ null );
/*
while( true ){
$read_ret = $raw_socket_2->socket_read_raw( );
if( $read_ret !== false && null !== $read_ret ){
echo json_encode( $read_ret ) . "\n";
}
}
*/
$raw_socket_3= new RawSocket( );
$s3 = $raw_socket_3->create_listen_udp( /*local_ip*/ $local_ip, /*$dest_ip*/ $local_udp_port );
/*
while( true ){
$read_ret = $raw_socket_3->socket_read_udp( );
if( $read_ret !== false && null !== $read_ret ){
echo json_encode( $read_ret ) . "\n";
}
}
*/
$socket_map = array(
(string)($s1) => array('type'=>'send_raw', 'obj'=>$raw_socket_1),
(string)($s2) => array('type'=>'read_raw', 'obj'=>$raw_socket_2),
(string)($s3) => array('type'=>'read_udp', 'obj'=>$raw_socket_3),
);
// var_dump( $socket_map );
/*
$raw_socket_4 = new RawSocket();
$raw_socket_4->create_listen_socket($local_ip);
while( true ){
$read_ret = $raw_socket_4->socket_recv_raw( );
if( $read_ret !== false && null !== $read_ret ){
echo json_encode( $read_ret ) . "\n";
}
}
*/
$g_real_map = array();
$g_fake_map = array();
$g_fake_request_info = array(
'real_ip'=>'',
'real_port'=>'',
'fake_ip'=>'',
'fake_port'=>'',
'send_data_len'=>0,
'receive_data_len'=>0,
'send_data_count'=>0,
'init_seq'=>0,
'next_seq'=>0,
'next_ack'=>0,
'need_deal'=>array(),
'need_deal_seq_min'=>0,
'need_deal_seq_next'=>0,
'update_time'=>0,
'state'=>'INIT',
'update_time'=>0,
'real_next_seq'=>0, // 与 next_seq 同步更新
'real_init_seq'=>0,
'test_init_seq'=>0, // 测试服务器开始序号
'latest_ack'=>0, // 测试服务器最后的确认
);
function add_need_deal_packet( & $fake_info, & $ip_packet, $real_time_request=true ){
global $g_cur_version;
$ip_packet['cur_version'] = $g_cur_version;
$ip_packet['enqueue_state'] = true;
isset( $ip_packet['enqueue_count'] )? $ip_packet['enqueue_count']++ : $ip_packet['enqueue_count'] = 1;
$fake_info['need_deal'][] = $ip_packet;
if( count($fake_info['need_deal']) == 1 ){
$fake_info['need_deal_seq_min'] = $fake_info['need_deal_seq_max'] = $ip_packet['tcp']['seq_num'];
$fake_info['need_deal_seq_next'] = $ip_packet['tcp']['seq_num'];
}else if( $real_time_request && $fake_info['need_deal_seq_next'] != $ip_packet['tcp']['seq_num'] ){
echo "ip_packet:" . json_encode( $ip_packet ) . "\n";
$tmp_fake_info = $fake_info;
$tmp_fake_info['need_deal_count'] = count( $tmp_fake_info['need_deal'] );
unset( $tmp_fake_info['need_deal'] );
echo "fake_info: " . json_encode( $tmp_fake_info ) . "\n";
echo ( "FATAL, may be lost packet\n" );
}
if( $real_time_request ){
$fake_info['need_deal_seq_next'] = get_next_seq( $fake_info['need_deal_seq_next'], $ip_packet['tcp']['data_len'] ) ;
}
}
$g_cur_version = 1;
function get_offset( $cur, $old ){
$offset = $cur - $old;
if( $offset < 0 && abs($offset) > (0xffffffff>>1) ){
$offset += (0xffffffff+1);
}
return $offset;
}
function get_next_seq( $cur, $inc ){
$next = $cur + $inc;
if( $next > 0xffffffff ){
$next %= (0xffffffff+1);
}
return $next;
}
function is_before( $cur, $old ){
$diff = $cur - $old;
if( $diff > 0 ) return true;
if( $diff < -(0xffffffff>>1) ) return true;
return false;
}
function is_after_or_equal( $cur, $old ){
$diff = $cur - $old;
if( $diff >= 0 ) return true;
if( $diff < -(0xffffffff>>1) ) return true;
return false;
}
function is_before_or_equal( $cur, $old ){
return is_after_or_equal( $old, $cur );
}
function is_after( $cur, $old ){
return is_before( $old, $cur );
}
function is_real_after( $fake_info, $cur ){
$old = $fake_info['real_next_seq'];
$diff = $cur - $old;
if( $diff > 0 ) return true;
//if( $diff < 0 && abs($diff) > (0xffffffff>>1) ) return true;
if( $diff < -(0xffffffff>>1) ) return true;
return false;
}
function is_real_before( $fake_info, $cur ){
return ! is_real_after($fake_info, $cur );
}
function is_real_equal( $fake_info, $cur ){
return $fake_info['real_next_seq'] == $cur;
}
function update_next_seq( & $fake_info, $inc ){
$fake_info['next_seq'] = get_next_seq( $fake_info['next_seq'] , $inc );
$fake_info['real_next_seq'] = get_next_seq( $fake_info['real_next_seq'] , $inc );
}
function update_next_ack( & $fake_info, $inc ){
$fake_info['next_ack'] = get_next_seq( $fake_info['next_ack'] , $inc );
}
/*
function get_fake_offset( $fake_info, $cur ){
return get_offset( $cur , $fake_info['next_seq'] );
}
function get_real_offset( $fake_info, $cur ){
return get_offset( $cur , $fake_info['real_next_seq'] );
}
*/
function set_update_time( & $fake_info ){
$cur = intval( microtime( true ) * 1000 );
$fake_info['update_time'] = $cur;
}
function deal_delay_data( & $fake_info, $not_crontab = true ){
global $g_cur_version;
if( $not_crontab ) {
set_update_time( $fake_info );
}
if( count($fake_info['need_deal']) < 1 ) return;
if( $fake_info['state'] !== 'ESTABLISHED' ) return;
// return;
echo "count_need_deal: " . count($fake_info['need_deal']) . "\n";
$tmp_fake_info = $fake_info;
unset( $tmp_fake_info['need_deal'] );
echo "fake_info: " . json_encode( $tmp_fake_info ) . "\n";
echo "need_deal_BEGIN\n";
$g_cur_version ++;
$latest_undeal_version = $g_cur_version;
$count = 0;
while( true ){
$count ++;
echo "deal_delay_data, packet count in buffer:" . count($fake_info['need_deal']) . "\n";
// echo "deal_delay_data: $count\n";
$ip_packet = array_shift($fake_info['need_deal']);
$ip_packet['enqueue_state'] = false;
echo "fake_info: " . json_encode( $tmp_fake_info ) . "\n";
echo sprintf("real_seq:[%d], real_data_len:[%d]\n" ,$ip_packet['tcp']['seq_num'], $ip_packet['tcp']['data_len']);
receive_real_request( $ip_packet, "need_deal" );
echo "ip_packet: " . json_encode( $ip_packet ) . "\n";
echo "ip_packet_enqueue_state:" . $ip_packet['enqueue_state'] . "\n";
echo "enqueue_count:" . $ip_packet['enqueue_count'] . "\n";
if( $ip_packet['cur_version'] >= $latest_undeal_version ){
break;
}
break;
if( 0 == count($fake_info['need_deal']) ){
break;
}
}
echo "need_deal_END\n";
}
function crontab_task( ){
global $g_fake_map, $g_real_map;
// echo "crontab_task..............\n";
$now = intval( microtime(true)*1000 );
if( count( $g_real_map ) == 0 ){
// echo "g_real_map is emtpy\n";
}else{
// echo "g_real_map count:" . count( $g_real_map ) . "\n";
}
foreach( $g_real_map as & $fake_info ){
/*
echo "crontab_task................................\n";
$update_time = $fake_info['update_time'];
echo "now[ $now ], update_time[ $update_time ]\n";
echo sprintf("now_update_time_diff:%d\n", $now - $update_time);
*/
$diff = $now - $fake_info['update_time'];
// echo "diff: " . $diff . "\n";
if( count($fake_info['need_deal']) > 0 ){
//&& ($now - $fake_info['update_time'] > -1) ){
deal_delay_data( $fake_info, false );
}
// echo $fake_info['state'] . "\n";
if( $fake_info['state'] == 'TIME_WAIT' && $diff > 1000*3 ){
$real_ip_port_key = $fake_info['real_ip'] .":" . $fake_info['real_port'];
$fake_ip_port_key = $fake_info['fake_ip'] .":" . $fake_info['fake_port'];
unset( $g_real_map[ $real_ip_port_key ] );
unset( $g_fake_map[ $fake_ip_port_key ] );
return;
}else if( $diff > 1000*3 ){
// var_dump( $fake_info );
// die( 0 );
}
}
}
function receive_test_server_request( $ip_packet ){
global $g_real_map, $g_fake_map, $g_real_request_info, $g_fake_request_info;
global $g_send_socket;
global $g_remote_test_ip;
global $g_cur_version;
$cur_time = microtime( true );
// echo "receive_test_server_response.....................test_server\n";
$TCP_SYN = 1<<1;
$TCP_ACK = 1<<4;
$TCP_FIN = 1;
$src_ip = $ip_packet['src_ip']; $src_port = $ip_packet['tcp']['src_port'];
$dest_ip = $ip_packet['dest_ip']; $dest_port = $ip_packet['tcp']['dest_port'];
$tcp_hdr = $ip_packet['tcp'];
$flag_syn = $tcp_hdr['SYN']; $flag_ack = $tcp_hdr['ACK'];
$flag_fin = $tcp_hdr['FIN']; $flag_rst = $tcp_hdr['RST'];
$seq_num = $tcp_hdr['seq_num']; $ack_num = $tcp_hdr['ack_num'];
$tcp_data = $tcp_hdr['tcp_data'];
$tcp_data_len = $tcp_hdr['data_len'];
if( $src_ip != $g_remote_test_ip ){
return;
}
$fake_ip_port_key = $dest_ip . ":" . $dest_port;
if( !isset( $g_fake_map[ $fake_ip_port_key ] ) ){
echo "NOTICE: not set g_fake_map[ fake_ip_port_key ]\n";
return;
}
$fake_info = & $g_fake_map[ $fake_ip_port_key ];
$status = $fake_info['state'];
// 建立连接
if( $flag_syn === true ){
if( $status == 'SYN_SEND' ){
$fake_info['next_ack'] = $seq_num;
$fake_info['latest_ack'] = $ack_num;
$fake_info['test_init_seq'] = $seq_num;
update_next_ack( $fake_info, 1 );
$fake_info['state'] = 'ESTABLISHED';
echo "--- receive test syn ----------------- SEND SYN ACK\n";
echo sprintf("seq:%d, ack:%d\n", $fake_info['next_seq'], $fake_info['next_ack'] );
$g_send_socket->socket_send( $fake_info['fake_ip'], $fake_info['fake_port'], $src_ip, $src_port,
$fake_info['next_seq'], $fake_info['next_ack'], $TCP_ACK,
'', null, null);
deal_delay_data( $fake_info );
return;
}else if( $status == 'ESTABLISHED' ){
echo "WARNING: receive dumplicated syn from test server.\n";
deal_delay_data( $fake_info );
return;
}
/*
log2( sprintf("line[%d],%s", __LINE__ , "WARNING: syn from test server, status not match:\nfake_info:" . json_encode( $fake_info )
. ";\nip_packet:" . json_encode($ip_packet)) );
packet_debug( $ip_packet );
*/
return;
}
if( is_after_or_equal( $ack_num, $fake_info['latest_ack'] ) ){
$fake_info['latest_ack'] = $ack_num;
}
// $fake_info['latest_ack'] = $ack_num;
//服务端返回数据.
if( $flag_ack
&& ! $flag_fin
&& ! $flag_rst
&& $tcp_data_len > 0 ){
// && is_before_or_equal( $ack_num, $fake_info['next_seq'] ) // 过时的ack
// && is_before_or_equal( $seq_num, $fake_info['next_ack'] ) ){ // 过时的seq
if( $seq_num == $fake_info['next_ack'] ){
update_next_ack( $fake_info, $tcp_data_len );
}
$g_send_socket->socket_send( $fake_info['fake_ip'], $fake_info['fake_port'], $src_ip, $src_port,
$fake_info['next_seq'], $fake_info['next_ack'], $TCP_ACK,
'', null, null);
deal_delay_data( $fake_info );
return;
}
// 测试服务器返回数据包无序, 返回数据包的ack_num可以比fake_info的seq小,因为fake_client 可以在没有收到ack的情况下继续发数据包.
if( $seq_num != $fake_info['next_ack'] ){
// echo ( sprintf("line[%d],%s", __LINE__ , "WARNING: receive unsorted packet from test server :\nfake_info:" . json_encode( $fake_info )
// . ";\nip_packet:" . json_encode($ip_packet)) );
deal_delay_data( $fake_info );
return;
}
// 收到FIN
if( $flag_fin ){
// fake client 被动关闭
if( $fake_info['state'] == 'ESTABLISHED' ){
$fake_info['state'] = 'CLOSE_WAIT';
//$fake_info['next_ack'] += (1 + $tcp_data_len);
update_next_ack( $fake_info, 1+$tcp_data_len );
$fake_info['state'] = 'LAST_ACK';
$g_send_socket->socket_send( $fake_info['fake_ip'], $fake_info['fake_port'], $src_ip, $src_port,
$fake_info['next_seq'], $fake_info['next_ack'], $TCP_FIN | $TCP_ACK,
'', null, null);
//$fake_info['next_seq']++;
update_next_seq( $fake_info, 1 );
return;
// fake client 主动关闭
} else if( $fake_info['state'] == 'TIME_WAIT' ) {
echo ("WARNING: receive FIN from test server, fake_info state is TIME_WAIT, " . json_encode( $fake_info )
. ";\nip_packet:" . json_encode($ip_packet) );
return;
// 同时关闭
}else if( $fake_info['state'] == 'FIN_WAIT_1'){
// $fake_info['next_ack'] += 1 + $tcp_data_len;
update_next_ack( $fake_info, 1+$tcp_data_len );
$g_send_socket->socket_send( $fake_info['fake_ip'], $fake_info['fake_port'], $src_ip, $src_port,
$fake_info['next_seq'], $fake_info['next_ack'], $TCP_ACK,
'', null, null);
$fake_info['state'] = 'CLOSING';
return;
// 主动关闭对方ACK 先于FIN到达
}else if( $fake_info['state'] == 'FIN_WAIT_2'){
//$fake_info['next_ack'] += 1 + $tcp_data_len;
update_next_ack( $fake_info, 1+$tcp_data_len );
$g_send_socket->socket_send( $fake_info['fake_ip'], $fake_info['fake_port'], $src_ip, $src_port,
$fake_info['next_seq'], $fake_info['next_ack'], $TCP_ACK,
'', null, null);
$fake_info['state'] = 'TIME_WAIT';
return;
}
log2( "WARNING: fin from test server, status not match, fake_info:" . json_encode( $fake_info ) );
packet_debug( $ip_packet );
return;
}
if( $flag_rst ){
$msg = "recv RST from test server";
log2( $msg );
return;
}
if( $flag_ack
&& ! $flag_fin
&& ! $flag_rst
&& in_array($status, array('FIN_WAIT_1', 'LAST_ACK', 'CLOSING'))
&& $tcp_data_len == 0 ){
// 主动关闭,收到FIN的ack.
if( $status == 'FIN_WAIT_1' ){
// echo "receive FIN_WAIT_1 ACK !!!!!!!!!!!!!!!!!!!!!!!!\n";
$fake_info['state'] = 'FIN_WAIT_2';
return;
}
// 被动关闭,收到FIN的ack.
if( $status == 'LAST_ACK' ){
$fake_info['state'] = 'CLOSED';
return;
}
// 同时关闭.
if( $status == 'CLOSING' ){
$fake_info['state'] = 'TIME_WAIT';
return;
}
}
if( $flag_ack
&& ! $flag_fin
&& ! $flag_rst
&& in_array($status , array( 'ESTABLISHED', 'TIME_WAIT', 'FIN_WAIT_2') )
&& $tcp_data_len == 0 ){
if( $ack_num <= $fake_info['next_seq'] ){
}else{
echo "Notice, receive test server ack num bigger than next_seq:\n" . "fake_info:" . json_encode( $fake_info )
. ";\nip_packet:" . json_encode($ip_packet) . "\n";
}
deal_delay_data( $fake_info );
return;
}
if( is_before( $seq_num, $fake_info['next_ack'] )
&& $tcp_data_len == 0
&& ! $flag_fin
&& ! $flag_rst ){
deal_delay_data( $fake_info );
return;
}
log2( sprintf("line[%d],%s", __LINE__ , "WARNING: can't deal packet from test server, status not match:\nfake_info:" . json_encode( $fake_info )
. ";\nip_packet:" . json_encode($ip_packet)) );
return;
}
function receive_real_request( & $ip_packet, $type = "" ){
global $g_real_map, $g_fake_map, $g_real_request_info, $g_fake_request_info;
global $g_remote_test_ip;
global $g_send_socket;
global $g_cur_version;
global $g_fake_port_indx;
$deal_real_time_request = true;
$deal_retry = false;
$TCP_SYN = 1<<1;
$TCP_ACK = 1<<4;
$TCP_PSH = 1<<3;
$TCP_FIN = 1;
if( strlen($type) > 0 ){
echo "Notice: " . $type . "\n";
$deal_real_time_request = false;
$deal_retry = true;
}
echo sprintf("DEBUG: %d : %d\n",
$deal_real_time_request, isset($ip_packet['cur_version']) ? $ip_packet['cur_version'] : -1);
$src_ip = $ip_packet['src_ip']; $src_port = $ip_packet['tcp']['src_port'];
$dest_ip = $ip_packet['dest_ip']; $dest_port = $ip_packet['tcp']['dest_port'];
$tcp_hdr = $ip_packet['tcp'];
$flag_syn = $tcp_hdr['SYN']; $flag_ack = $tcp_hdr['ACK'];
$flag_fin = $tcp_hdr['FIN']; $flag_rst = $tcp_hdr['RST'];
$seq_num = $tcp_hdr['seq_num']; $ack_num = $tcp_hdr['ack_num'];
$tcp_data = $tcp_hdr['tcp_data'];
$tcp_data_len = strlen( $tcp_data );
$flag_str = '';
if( $flag_ack ){ $flag_str .= "ACK"; }
if( $flag_syn ){ $flag_str .= "SYN"; }
if( $flag_fin ){ $flag_str .= "FIN"; }
if( $flag_rst ){ $flag_str .= "RST"; }
$ip_port_key = $src_ip . ":" . $src_port;
$cur = intval( microtime(true)*1000 );
// 建立连接
if( $flag_syn === true && $flag_ack === false ){
if( isset($g_real_map[ $ip_port_key ]) ){
$fake_info = & $g_real_map[ $ip_port_key ];
if( $fake_info['real_init_seq'] != $seq_num ){
$fake_ip_port_key = $fake_info['fake_ip'] .":" . $fake_info['fake_port'];
unset( $g_real_map[ $ip_port_key ] );
unset( $g_fake_map[ $fake_ip_port_key ] );
}else{
return;
}
}
echo "receive REAL_SYN ,ip_packet:" . json_encode($ip_packet) . "\n";
//$fake_ip = $src_ip; $fake_port = $src_port;
//$fake_ip = "11.11.11.11";
$fake_ip = "193.168.56.121";
$g_fake_port_indx ++;
if( $g_fake_port_indx > 60000 ){
$g_fake_port_indx = 20000;
}
$fake_port = $g_fake_port_indx;
$real_ip_port_key = $src_ip .":" . $src_port;
$fake_ip_port_key = $fake_ip .":" . $fake_port;
$g_fake_map[ $fake_ip_port_key ] = $g_fake_request_info;
$g_real_map[ $real_ip_port_key ] = & $g_fake_map[ $fake_ip_port_key ];
$fake_info = & $g_fake_map[ $fake_ip_port_key ];
$fake_info['real_ip'] = $src_ip;
$fake_info['real_port'] = $src_port;
$fake_info['fake_ip'] = $fake_ip;
$fake_info['fake_port'] = $fake_port;
$fake_info['real_next_seq'] = $seq_num + 1;
$fake_info['relative_seq'] = $fake_info['next_seq'] = $fake_info['init_seq'] = 0xffffffff;
$fake_info['relative_seq'] = $fake_info['next_seq'] = $fake_info['init_seq'] = $seq_num;
$fake_info['real_relative_seq'] = $fake_info['real_next_seq'] = $fake_info['real_init_seq'] = $seq_num;
$fake_info['state'] = 'INIT';
$fake_info['update_time'] = $cur;
// 发送syn数据包,建立连接.
/*
RawSocket::socket_send( $src_ip, $src_port, $dest_ip, $dest_port,
$seq_num, $ack_num, $tcp_flag,
$tcp_data,$timestamp, $ts_echo );
*/
$g_send_socket->socket_send( $fake_ip, $fake_port, $g_remote_test_ip, $dest_port,
$fake_info['next_seq'], 0, $TCP_SYN,
'', null, null);
$fake_info['state'] = 'SYN_SEND';
/*
$fake_info['next_seq'] += 1;
$fake_info['real_next_seq'] += 1;
*/
update_next_seq( $fake_info, 1 );
echo "### receive real syn ################## SEND SYN\n";
/*
var_dump( $fake_info );
var_dump( $g_remote_test_ip );
die( 0 );
*/
return;
}
$real_ip_port_key = $src_ip .":" . $src_port;
if( ! isset( $g_real_map[ $real_ip_port_key ] ) ){
echo "WARNING: real_ip_port_key, no fake_info, " . ", ip_packet:" . json_encode($ip_packet);
return;
}
$fake_info = & $g_real_map[ $real_ip_port_key ];
$fake_ip = $fake_info['fake_ip'];
$fake_port = $fake_info['fake_port'];
$real_offset =get_offset( $seq_num, $fake_info['real_next_seq'] );
if( $tcp_data_len > 0 || true ){
echo sprintf("DEBUG: type[$type], real_offset, data_len, flags, [ %d ][ %d ][ %s ]\n",
$real_offset, $tcp_data_len, $flag_str);
}
// fake client 处理较慢
//if( is_real_after($fake_info, $seq_num) && $tcp_data_len > 0 ){
if( $real_offset > 0 && $tcp_data_len > 0 ){
echo sprintf("NOTICE: test is slow, real_offset[%d], tcp_data_len[%d]\n", $real_offset, $tcp_data_len);
echo sprintf("line[%d], %s", __LINE__ , "ADD NEED DEAL DATA\n");
/*
$ip_packet['cur_version'] = $g_cur_version;
$fake_info['need_deal'][] = $ip_packet;
*/
add_need_deal_packet( $fake_info, $ip_packet, $deal_real_time_request );
if( $deal_real_time_request ){
echo "will deal_delay_data\n";
deal_delay_data( $fake_info );
}
return;
}
if( $real_offset < 0 ){
echo "REAL_OFFSET: real_offset: $real_offset\n";
echo sprintf("WARNING: real_offset < 0, ignore, tcp_data_len[%d]\n", $tcp_data_len);
echo "FAKE_INFO:" . json_encode( $fake_info ) . "\n";
echo "IP_PACKET:" . json_encode( $ip_packet ) . "\n";
// $fake_info['need_deal'][] = $ip_packet;
return;
}
if( $flag_fin ){
// return;
if( $tcp_data_len > 0 ){
echo "FATAL: fin packet with tcp_data_len > 0, ip_packet: " . json_encode( $ip_packet ) . "\n";
die( 255 );
}
if( $fake_info['state'] == 'ESTABLISHED'
&& $real_offset == 0 && is_after_or_equal($fake_info['latest_ack'], $fake_info['next_seq'])
&& $cur - $fake_info['update_time'] > 30 * 1000 // 30s
){
$g_send_socket->socket_send( $fake_ip, $fake_port, $g_remote_test_ip, $dest_port,
$fake_info['next_seq'], $fake_info['next_ack'], $TCP_FIN | $TCP_ACK,
'', null, null);
$fake_info['state'] = 'FIN_WAIT_1';
// 更新next_seq, real_next_seq
update_next_seq( $fake_info, 1 );
echo "### receive real fin ################## SEND FIN\n";
return;
}else{
// return;
/*
$tmp = $fake_info;
$tmp['need_deal'] = count( $tmp['need_deal'] );
echo sprintf( "line[%d],%s", __LINE__ , "WARNING: receive FIN from real client, fake info state is not ESTABLISHED, fake_info:"
. json_encode( $tmp )
. ", ip_packet:" . json_encode($ip_packet) );
*/
add_need_deal_packet( $fake_info, $ip_packet, $deal_real_time_request );
if( $deal_real_time_request ){
echo "will deal_delay_data\n";
deal_delay_data( $fake_info );
}
return;
}
return;
}
if( $flag_rst ){
$msg = "WARNING, recv RST from real client";
log2( $msg );
return;
}
if( $flag_ack && $tcp_data_len > 0 ){
//if( $fake_info['state'] == 'ESTABLISHED' && is_after_or_equal($fake_info['latest_ack'], $fake_info['next_seq']) ){
if( $fake_info['state'] == 'ESTABLISHED' ){
echo "### receive real data ################## SEND DATA\n";
$g_send_socket->socket_send( $fake_ip, $fake_port, $g_remote_test_ip, $dest_port,
$fake_info['next_seq'], $fake_info['next_ack'], $TCP_ACK | $TCP_PSH,
$tcp_data, null, null);
$fake_info['send_data_len'] += $tcp_data_len;
$fake_info['send_data_count'] ++;
// 更新next_seq, real_next_seq
update_next_seq( $fake_info, $tcp_data_len );
if( isset($ip_packet['cur_version']) ){
echo "NOTICE: deal backup data success\n";
}
}else{
echo sprintf("line[%d], %s", __LINE__ , "ADD NEED DEAL DATA\n");
add_need_deal_packet( $fake_info, $ip_packet, $deal_real_time_request );
}
if( $deal_real_time_request ){
echo "will deal_delay_data 2\n";
deal_delay_data( $fake_info );
}
return;
}
if( $flag_ack
&& ! $flag_fin
&& ! $flag_rst
&& 0 == $tcp_data_len ){
echo "### receive real ack #################### IGNORE ACK\n";
return;
}
/*
log2( sprintf("line[%d],%s", __LINE__ , "WARNING: no default, receive packet from client, status not match, fake_info:"
. json_encode( $fake_info ) )
. ", ip_packet:" . json_encode($ip_packet) );
packet_debug( $ip_packet );
echo "\n";
*/
$tmp = $fake_info;
$tmp['need_deal'] = count( $tmp['need_deal'] );
echo sprintf( "WARNING, need die, line[%d],%s", __LINE__ , ", fake_info:"
. json_encode( $tmp )
. ", ip_packet:" . json_encode($ip_packet) . "\n" );
return;
return;
}
function log2( $msg ){
echo $msg . "\n";
echo "back_trace: " . json_encode( debug_backtrace() ) . "\n";
}
function packet_debug( $ip_packet ){
$src_ip = $ip_packet['src_ip']; $src_port = $ip_packet['tcp']['src_port'];
$dest_ip = $ip_packet['dest_ip']; $dest_port = $ip_packet['tcp']['dest_port'];
$tcp_hdr = $ip_packet['tcp'];
$flag_syn = $tcp_hdr['SYN']; $flag_ack = $tcp_hdr['ACK'];
$flag_fin = $tcp_hdr['FIN']; $flag_rst = $tcp_hdr['RST'];
$seq_num = $tcp_hdr['seq_num']; $ack_num = $tcp_hdr['ack_num'];
$tcp_data = $tcp_hdr['tcp_data'];
$ip_port_key = $src_ip . ":" . $src_port; $cur = time();
echo sprintf("read_raw_tcp: %s:%s --> %s:%s\n", $src_ip, $src_port, $dest_ip, $dest_port );
echo sprintf("\t: seq_num[%d], ack_num[%d], SYN:%d, ACK:%d, FIN:%d, RST:%d\n",
$tcp_hdr['seq_num'], $tcp_hdr['ack_num'],
$tcp_hdr['SYN'], $tcp_hdr['ACK'], $tcp_hdr['FIN'], $tcp_hdr['RST']);
}
$select_read = array( $s2, $s3 );
// $select_read = array( $s3 );
$mq_key = ftok( dirname(__FILE__), 'a');
$mq = msg_get_queue($mq_key, 0666);
msg_remove_queue( $mq );
$mq = msg_get_queue($mq_key, 0666);
$pid = pcntl_fork();
if( $pid == 0 ){
// tcpcopy进程,从mq里拿数据包,发送伪造请求报文 或 回复test server报文。
while( true ){
$ret = msg_receive($mq, 0, $message_type=1, 10240, $m, true, MSG_IPC_NOWAIT);
if( $ret == false ){
crontab_task( );
continue;
}
$ip_packet = json_decode( $m, true );
switch( $ip_packet['from'] ){
case 'read_raw':
receive_real_request( $ip_packet );
break;
case 'send_raw':
// echo "need send_raw\n";
break;
case 'read_udp':
// receive_test_server_packet
receive_test_server_request( $ip_packet );
break;
default:
echo "NOTICE:error\n";
break;
}
}
}else{
// 抓包进程,单纯从socket接收IP报文,防止进程过慢而丢包.
$need_send = array();
while( true ){
$need_read = $select_read;
$select_ret = socket_select( $need_read, $need_write = null, $expect, 1, 0 );
if( false === $select_ret ){
echo "socket_last_error_str:" . socket_strerror(socket_last_error()) . "\n";
continue;
}
if( $select_ret < 1 ){
// echo "no need read\n";
continue;
}
// echo "after select\n";
// echo "need_read:\n";
if( is_array($need_read) && count($need_read) > 0 ){
foreach( $need_read as $s ){
$type = $socket_map[ (string)$s ]['type'];
$obj = $socket_map[ (string)$s ]['obj'];
switch( $type ){
case 'read_raw':
// echo "need_read_raw\n";
$read_ret = $obj->socket_read_raw( );
if( $read_ret === false || null === $read_ret ){
continue;
// echo json_encode( $read_ret ) . "\n\n";
}else{
// echo json_encode( $read_ret ) . "\n\n";
}
// receive_real_packet
// receive_real_request( $read_ret );
$ip_packet = & $read_ret;
$ip_packet['from'] = 'read_raw';
// $ret = msg_send($mq, 1, json_encode( $ip_packet), true, false, $msg_err );
$need_send[] = $ip_packet;
break;
case 'send_raw':
// echo "need send_raw\n";
break;
case 'read_udp':
// echo "need read udp --------------------- read udp packet\n";
$read_ret = $obj->socket_read_udp( );
if( $read_ret === false || null === $read_ret ){
continue;
}else{
// echo json_encode( $read_ret ) . "\n\n";
}
// receive_test_server_packet
// receive_test_server_request( $read_ret );
$ip_packet = & $read_ret;
$ip_packet['from'] = 'read_udp';
// $ret = msg_send($mq, 1, json_encode( $ip_packet), true, false, $msg_err );
$need_send[] = $ip_packet;
break;
default:
echo "NOTICE:error\n";
break;
}
if( false && $ret == false ){
$mq_stat = msg_stat_queue( $mq );
echo "NOTICE:" . $mq_stat['msg_qnum'] . "\n";
echo "FATAL: msg_send_ret false\n";
var_dump( $msg_err );
//die( "FATAL: msg_send_error" );
$need_send[] = $ip_packet;
}
}//foreach
}//if
$mq_stat = msg_stat_queue( $mq );
if( $mq_stat['msg_qnum'] > 100 ){
continue;
}
if( count($need_send) > 0 ){
foreach( $need_send as $k => $ip_packet ){
$ret = msg_send($mq, 1, json_encode( $ip_packet), true, false, $msg_err );
if( $ret ==true ){
unset( $need_send[$k] );
$mq_stat = msg_stat_queue( $mq );
if( $mq_stat['msg_qnum'] > 100 ){
break;
}
}else{
break;
}
}
}
}//while
die( 0 );
}
while( true ){
$need_read = $select_read;
$select_ret = socket_select( $need_read, $need_write = null, $expect, 1, 0 );
if( false === $select_ret ){
echo "socket_last_error_str:" . socket_strerror(socket_last_error()) . "\n";
continue;
}
if( $select_ret < 1 ){
// echo "no need read\n";
continue;
}
// echo "after select\n";
// echo "need_read:\n";
if( is_array($need_read) && count($need_read) > 0 ){
foreach( $need_read as $s ){
$type = $socket_map[ (string)$s ]['type'];
$obj = $socket_map[ (string)$s ]['obj'];
switch( $type ){
case 'read_raw':
// echo "need_read_raw\n";
$read_ret = $obj->socket_read_raw( );
if( $read_ret === false || null === $read_ret ){
continue;
// echo json_encode( $read_ret ) . "\n\n";
}else{
// echo json_encode( $read_ret ) . "\n\n";
}
// receive_real_packet
receive_real_request( $read_ret );
break;
case 'send_raw':
// echo "need send_raw\n";
break;
case 'read_udp':
// echo "need read udp --------------------- read udp packet\n";
$read_ret = $obj->socket_read_udp( );
if( $read_ret === false || null === $read_ret ){
continue;
}else{
// echo json_encode( $read_ret ) . "\n\n";
}
// receive_test_server_packet
receive_test_server_request( $read_ret );
break;
default:
echo "NOTICE:error\n";
break;
}
}
}
}
die( 0 );
function get_asc( $str ){
echo "get_asc:\n";
$arr = str_split( $str,1 );
foreach( $arr as $v ){
echo ord( $v ) . "," . bin2hex($v) . "\n";
}
echo "\n";
}
function str2int( $str, $size=4 ){
$ret = 0;
for( $i=0; $i<$size; $i++){
$ret = ($ret << 8) + ord($str[$i ]);
}
return $ret;
}
2. intercept.py
抓到测试服务器返回的数据,将其发送到tcpcopy进程。
#!/usr/bin/python
import socket
import struct
import binascii
remote_addr = ("192.168.56.101", 20000)
address=('localhost',20000)
udp_s=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
#udp_s.bind( address )
#print udp_s.getsockname()
s=socket.socket(socket.PF_PACKET,socket.SOCK_RAW,socket.htons(0x0800))
while True:
pkt = s.recvfrom(65532)
#print pkt[1]
ethernetHeader=pkt[0][0:14]
eth_hdr = struct.unpack("!6s6s2s",ethernetHeader)
#print eth_hdr
eth_hdr2 = struct.unpack("!6s6sH",ethernetHeader)
if( eth_hdr2[2] != 0x0800 ):
continue
print 'IP:'
print len(pkt[0])
ipHeader = pkt[0][14:34]
ip_hdr = struct.unpack("!12s4s4s",ipHeader)
#ip_header_len = 4* (ord(ip_hdr[0][0]) & 0xf)
ip_header_len = (struct.unpack("!1B11x", ip_hdr[0])[0] & 0x0f) << 2;
print ip_header_len
ip_packet_len = (struct.unpack("!2xH8x", ip_hdr[0])[0] & 0xffff);
print ip_packet_len;
proto = struct.unpack("!9x1B2x", ip_hdr[0])[0]
if proto != 0x06:
continue;
print proto
proto_map={1:"ICMP", 2:"IGMP", 6:"TCP", 17:"UDP", 89:"OSPF" }
print proto_map[proto]
print ip_header_len
print ip_packet_len
tcp_packet = pkt[0][14+ip_header_len:14+ip_packet_len]
print len(tcp_packet)
tcp_header = tcp_packet[0:20]
tcp_hdr_len = 4*(struct.unpack("!12x1H6x", tcp_header)[0] >> 12)
print tcp_hdr_len
ip_tcp_header = pkt[0][14:14+ip_header_len+tcp_hdr_len]
print len(ip_tcp_header)
print "real_len:%d, ip:%d, tcp:%d" % ((ip_header_len + tcp_hdr_len), ip_header_len, tcp_hdr_len )
print remote_addr
ret = udp_s.sendto( ip_tcp_header, remote_addr )
print ret
print "Source IP address:"+socket.inet_ntoa(ip_hdr[1])
print "Destination IP address:"+socket.inet_ntoa(ip_hdr[2])
# tcpHeader = pkt[0][34:54]
# tcp_hdr = struct.unpack("!HH16s",tcpHeader)
3. 环境设置:
1). 环境介绍:
101: centos4, online server.
104: centos5, fake router.
102: centos4-1, test server.
103: centos4-2, real client.
2). 102设置路由:
route add -host 193.168.56.121 gw 192.168.56.104
4. 注意:
104 上禁用 ICMP redirects。
原本想用PHP写raw socket玩玩的,原来看过tcpcopy代码,用php写了个试试,没想还跑通了...