パドヴァのコンピュータ ネットワーク実技試験 (「Reti di calcolatori」) に簡単に合格するためのヒントとコード。この Readme の内容はすべて、 man
およびさまざまな RFC から見つけることができます。簡単な参照のためにこれを実行しました。
イーサネット フレーム (データ リンク層) には、次の { tcp_segment (トランスポート層)、icmp_packet } のいずれかを含めることができる IP データグラム (ネットワーク層) が含まれています (この試験の目的のため)。それを実現する簡単な方法は次のとおりです。
eth = ( struct eth_frame * ) buffer ;
ip = ( struct ip_datagram * ) eth -> payload ;
tcp = ( struct tcp_segment * ) ip -> payload ;
// or
icmp = ( struct icmp_packet * ) ip -> payload ;
(アーキテクチャによって異なりますが、この試験では次のことが当てはまると想定してください)
unsigned char
:1バイトunsigned short
: 2バイトunsiged int
: 4バイトネットワーク上の転送にはビッグエンディアンが使用されます。 IntelのCPUのほとんどはリトルエンディアンです。変換するには、変換が必要かどうかを自動的に理解する次の 2 つの関数を使用します。
htonl(x)
またはhtons(x)
は、x をH ost からNネットワークのエンディアンに変換します。 lは 4 バイトの変数を変換する必要があり、 s は2 バイトの変数に変換します。ntohl(x)
またはntohs(x)
。 (htonx と ntohx の実装が同じであることに気づくかもしれません) // Frame Ethernet
struct eth_frame {
unsigned char dst [ 6 ]; // mac address
unsigned char src [ 6 ]; // mac address
unsigned short type ; // 0x0800 = ip, 0x0806 = arp
char payload [ 1500 ]; //ARP or IP
};
type
のおかげで、次のレベルのどこに転送するかを理解できます (2 つの例は ip または arp)
ヘッダーの長さ: ver_ihl
属性の後半を確認してください。例: 「5」の場合、ヘッダーの長さは4 * 5 = 20 バイトです。
//todo 画像を追加
// Datagramma IP
struct ip_datagram {
unsigned char ver_ihl ; // first 4 bits: version, second 4 bits: (lenght header)/8
unsigned char tos ; //type of service
unsigned short totlen ; // len header + payload
unsigned short id ; // useful in case of fragmentation
unsigned short flags_offs ; //offset/8 related to the original ip package
unsigned char ttl ;
unsigned char protocol ; // TCP = 6, ICMP = 1
unsigned short checksum ; // only header checksum (not of payload). Must be at 0 before the calculation.
unsigned int src ; // ip address
unsigned int dst ; // ip address
unsigned char payload [ 1500 ];
};
ヘッダー (ここで定義) 長さ: 20
struct tcp_segment {
unsigned short s_port ;
unsigned short d_port ;
unsigned int seq ; // offset in bytes from the start of the tcp segment in the stream (from initial sequance n)
unsigned int ack ; // useful only if ACK flag is 1. Next seq that sender expect
unsigned char d_offs_res ; // first 4 bits: (header len/8)
unsigned char flags ; // check rfc
unsigned short win ; // usually initially a 0 (?)
unsigned short checksum ; // use tcp_pseudo to calculate it. Must be at 0 before the calculation.
unsigned short urgp ;
unsigned char payload [ 1000 ];
};
TCP セグメントのチェックサムを計算すると、追加の構造を定義するのに役立ちます (相対 RFC を確認する)。 tcp_segment 部分を除いたサイズ
struct tcp_pseudo {
unsigned int ip_src , ip_dst ;
unsigned char zeroes ;
unsigned char proto ; // ip datagram protocol field (tcp = 6, ip = 1)
unsigned short entire_len ; // tcp length (header + data)
unsigned char tcp_segment [ 20 /*to set appropriatly */ ]; // entire tcp packet pointer
};
TCP セグメント全体 (または icmp) のサイズ、または一般的な IP ペイロードのサイズを計算するには、次のようにします。
unsigned short ip_total_len = ntohs ( ip -> totlen );
unsigned short ip_header_dim = ( ip -> ver_ihl & 0x0F ) * 4 ;
int ip_payload_len = ip_total_len - ip_header_dim ;
この関数は IP データグラムと TCP セグメントの両方に使用できますが、 len
パラメータに注意する必要があります。
unsigned short checksum ( unsigned char * buffer , int len ){
int i ;
unsigned short * p ;
unsigned int tot = 0 ;
p = ( unsigned short * ) buffer ;
for ( i = 0 ; i < len / 2 ; i ++ ){
tot = tot + htons ( p [ i ]);
if ( tot & 0x10000 ) tot = ( tot & 0xFFFF ) + 1 ;
}
return ( unsigned short ) 0xFFFF - tot ;
}
2 つのケースは次のとおりです。
ip->checksum=htons(checksum((unsigned char*) ip, 20));
` int TCP_TOTAL_LEN = 20 ;
struct tcp_pseudo pseudo ; // size of this: 12
memcpy ( pseudo . tcp_segment , tcp , TCP_TOTAL_LEN );
pseudo . zeroes = 0 ;
pseudo . ip_src = ip -> src ;
pseudo . ip_dst = ip -> dst ;
pseudo . proto = 6 ;
pseudo . entire_len = htons ( TCP_TOTAL_LEN ); // may vary
tcp -> checksum = htons ( checksum (( unsigned char * ) & pseudo , TCP_TOTAL_LEN + 12 ));
#include <arpa/inet.h>
void print_ip ( unsigned int ip ){
struct in_addr ip_addr ;
ip_addr . s_addr = ip ;
printf ( "%sn" , inet_ntoa ( ip_addr ));
}
VIMにアドバイスします。コードをインデントしてください。
:wq
保存して終了します。esc
2 回押してください/query
で「query」を検索、 n
およびN
で前/次の結果を検索 " auto reformat when you pres F7
map <F7> mzgg=G`z
" F8 to save and compile creating np executable
map <F8> :w <CR> :!gcc % -o np -g <CR>
" F9 to execute
map <F9> :!./np <CR>
" make your code look nicer
set tabstop=3
set shiftwidth=3
set softtabstop=0 expandtab
set incsearch
set cindent
" Ctrl+shift+up/down to swap the line up or doen
nnoremap <C-S-Up> <Up>"add"ap<Up>
nnoremap <C-S-Down> "add"ap
" ctrl+h to hilight the last search
nnoremap <C-h> :set hlsearch!<CR>
set number
set cursorline
set mouse=a
set foldmethod=indent
set foldlevelstart=99
let mapleader="<space>"
nnoremap <leader>b :make <CR> :cw <CR>
まず、次のように、コンパイルするファイルを含むディレクトリにmakefile
作成します。
np : ws18.c
gcc -o np ws18.c
「gcc」の前にスペースではなくタブを置くことに注意してください (vim で Expandtab を有効にしている場合は、 ctrl=v tab
使用してください)。ここで、 np
生成するもの (実行可能ファイル)、 ws18.c
コンパイルするファイルです。下の行には、vim で:make
記述するたびに呼び出すコマンドがあります。次に、上記で提供された.vimrc
使用して、 space
(リリース) とb
(ビルド) を押します。コマンドが実行され、コードの下部にエラーのリストが表示されます。各エントリで Enter キーを押すと、正しい行にすばやくジャンプできます。上部と下部の分割間を移動するには、 CTRL+W
W
押します。底面ビューを閉じるには (クイックフィックス) :q
または:cw
ます。
完全な試験声明は、この Readme の冒頭にあるサイトで見つけることができます。完全なコードはフォルダー内にあります。
TCP 3 ウェイ ハンドシェイク (ACK+SYN) を実装します。
ヒント: TCP チェックサムが正しいかどうかは Wireshark で確認できます。
特定のサイズの icmp リクエストに対してのみエコー応答を実装する
ヒント: 次の方法で icmp メッセージのサイズを計算できます。
unsigned short dimension = ntohs ( ip -> totlen );
unsigned short header_dim = ( ip -> ver_ihl & 0x0F ) * 4 ;
int icmp_dimension = dimension - header_dim ;
次のような HTTP サーバーを実装します。
ヒント: ヘッダーRetry-After
ほとんどの Web ブラウザーで無視されるため、リダイレクトは 10 秒後ではなく、すぐに行われます。このソリューションには、各 IP の接続状態を保持する配列があります。
ポートが利用できないことを示す ICMP「Destination unreachable」を実装する
ヒント: TCP 接続に応答してパッケージを送信する必要があります。 icmp->type = 3
、 icmp->code=3
。また、icmp の元のペイロードの内容をペイロードにコピーすることを忘れないでください。
最初に受信した接続をインターセプトし、それらのシーケンス番号と確認応答番号を出力します。次に、2 つのストリームを 2 つの異なるバッファーに再構築し、その内容を出力します。
ヒント: 接続の終わりをインターセプトするには、パッケージに 1 の FIN ビットが含まれているかどうかを確認します (すべてのパッケージをフィルタリングし、最初の接続に属するパッケージのみを維持した後)。 tcp シーケンス フィールドを使用して、2 つのバッファの右オフセットにあるコンテンツをコピーします。コードを複製しないでください。
IP アドレスのプールからのリクエストのみを許可し、テキストまたは HTML を含むファイルの転送のみを許可するようにプロキシを変更します。
ヒント: 最初にサーバーからの応答をバッファーで受信し、次にこのコンテンツを別のバッファーにコピーして、いつものようにヘッダーを抽出することをお勧めします。これは、ヘッダー抽出手順によってバッファーが変更されるためです。 Content-type の条件が満たされている場合は、初期バッファのコンテンツを転送するだけです。
チャンクボディを含む HTTP 応答を送信します。
ヒント: Content-Type: text/plainrnTransfer-Encoding: chunkedrn
を HTTP ヘッダーに追加します。次に、送信する各チャンクを構築するには、次のようなものを使用できます。
int build_chunk ( char * s , int len ){
sprintf ( chunk_buffer , "%xrn" , len ); // size in hex
// debug printf("%d in hex: %s",len,chunk_buffer);
int from = strlen ( chunk_buffer );
int i = 0 ;
for (; i < len ; i ++ )
chunk_buffer [ from + i ] = s [ i ];
chunk_buffer [ from + ( i ++ )] = 'r' ;
chunk_buffer [ from + ( i ++ )] = 'n' ;
chunk_buffer [ i + from ] = 0 ;
return i + from ;
}
HTTP/1.0のLast-Modified
ヘッダーを実装する
ヒント: その他セクションにいくつかの便利な時間変換関数があります。これらの変換を必要とせずに実行することもできます。 HTTP 日付形式は%a, %d %b %Y %H:%M:%S %Z
です。
1: コンテンツの長さ (既に実装されています) 2: トレース (??)
icmp echo を変更して、要求を 2 つの IP データグラムに分割します。1 つはペイロード サイズが 16 バイトで、もう 1 つは要求されたペイロード サイズです。
コース中にいくつかの宿題(必須ではありません)が課されます。試験でなくても、難易度はほぼ同じです。
トレースルート プログラムを実装し、パケットが宛先に到達する前に訪問する各ノードを追跡します。ヒント: 生存時間が 0 になると、ノードはパケットを破棄し、パケットの送信元 IP アドレスに「Time Exceeded メッセージ」を送信します (RFC793 を参照)。
HTTP ヘッダーWWW-Authenticate
使用して、クライアントへのアクセス資格情報を要求します。
チャンク化されたコンテンツを受け入れるクライアントを実装します。
HTTP 日付形式は%a, %d %b %Y %H:%M:%S %Z
です。
char date_buf [ 1000 ];
char * getNowHttpDate (){
time_t now = time ( 0 );
struct tm tm = * gmtime ( & now );
strftime ( date_buf , sizeof date_buf , "%a, %d %b %Y %H:%M:%S %Z" , & tm );
printf ( "Time is: [%s]n" , date_buf );
return date_buf ;
}
// parse time and convert it to millisecond from epoch
time_t httpTimeToEpoch ( char * time ){
struct tm tm ;
char buf [ 255 ];
memset ( & tm , 0 , sizeof ( struct tm ));
strptime ( time , "%a, %d %b %Y %H:%M:%S %Z" , & tm );
return mktime ( & tm );
}
// returns 1 if d1 < d2
unsigned char compareHttpDates ( char * d1 , char * d2 ){
return httpTimeToEpoch ( d1 ) < httpTimeToEpoch ( d2 );
}
unsigned char expired ( char * uri , char * last_modified ){
char * complete_name = uriToCachedFile ( uri );
FILE * fp = fopen ( complete_name , "r" );
if ( fp == NULL ) return 1 ;
//read the first line
char * line = 0 ; size_t len = 0 ;
getline ( & line , & len , fp );
if ( compareHttpDates ( last_modified , line )) return 0 ;
return 1 ;
//todo read First line and compare
}
rewind(FILE*)
カーソルを先頭に設定します
FILE * fin ;
if (( fin = fopen ( uri + 1 , "rt" )) == NULL ) { // the t is useless
printf ( "File %s non aperton" , uri + 1 );
sprintf ( response , "HTTP/1.1 404 File not foundrnrn<html>File non trovato</html>" );
t = write ( s2 , response , strlen ( response ));
if ( t == -1 ) {
perror ( "write fallita" );
return -1 ;
}
} else {
content_length = 0 ;
while (( c = fgetc ( fin )) != EOF ) content_length ++ ; // get file lenght
sprintf ( response , "HTTP/1.1 200 OKrnConnection: keep-alivernContent-Length: %drnrn" , content_length );
printf ( "Response: %sn" , response );
//send header
t = write ( s2 , response , strlen ( response ));
//rewind the file
rewind ( fin );
//re-read the file, char per char
while (( c = fgetc ( fin )) != EOF ) {
//printf("%c", c);
//sending the file, char per char
if ( write ( s2 , ( unsigned char * ) & c , 1 ) != 1 ) {
perror ( "Write fallita" );
}
}
fclose ( fin );
}
char car ;
while ( read ( s3 , & car , 1 )) {
write ( s2 , & car , 1 );
// printf("%c",car);
}
unsigned char targetip [ 4 ] = { 147 , 162 , 2 , 100 };
unsigned int netmask = 0x00FFFFFF ;
if (( * (( unsigned int * ) targetip ) & netmask ) == ( * (( unsigned int * ) myip ) & netmask ))
nexthop = targetip ;
else
nexthop = gateway ;
ホスト名 (www.google.it など) から IP アドレスまで
/**
struct hostent {
char *h_name; // official name of host
char **h_aliases; // alias list
int h_addrtype; // host address type
int h_length; // length of address
char **h_addr_list; // list of addresses
}
#define h_addr h_addr_list[0] // for backward compatibility
*/
struct hostent * he ;
he = gethostbyname ( hostname );
printf ( "Indirizzo di %s : %d.%d.%d.%dn" , hostname ,
( unsigned char )( he -> h_addr [ 0 ]), ( unsigned char )( he -> h_addr [ 1 ]),
( unsigned char )( he -> h_addr [ 2 ]), ( unsigned char )( he -> h_addr [ 3 ]));
リスニング用:
int s = socket ( AF_INET , // domain: ipv4
/*
SOCK_STREAM Provides sequenced, reliable, two-way, connection-based byte streams. An out-of-band data
transmission mechanism may be supported.
SOCK_DGRAM Supports datagrams (connectionless, unreliable messages of a fixed maximum length).
SOCK_RAW Provides raw network protocol access.
*/
SOCK_STREAM , // type: stream
0 ); // protocol (0=ip), check /etc/protocols
if ( s == -1 ) {
perror ( "Socket Fallita" );
return 1 ;
}
// https://stackoverflow.com/questions/3229860/what-is-the-meaning-of-so-reuseaddr-setsockopt-option-linux
// SO_REUSEADDR allows your server to bind to an address which is in a TIME_WAIT state.
int yes = 1 ;
if ( setsockopt ( s , SOL_SOCKET , SO_REUSEADDR , & yes , sizeof ( int )) == -1 ) {
perror ( "setsockopt" );
return 1 ;
}
struct sockaddr_in indirizzo ;
indirizzo . sin_family = AF_INET ;
indirizzo . sin_port = htons ( 8987 );
indirizzo . sin_addr . s_addr = 0 ;
t = bind ( s , ( struct sockaddr * ) & indirizzo , sizeof ( struct sockaddr_in ));
if ( t == -1 ) {
perror ( "Bind fallita" );
return 1 ;
}
t = listen ( s ,
// backlog defines the maximum length for the queue of pending connections.
10 );
if ( t == -1 ) {
perror ( "Listen Fallita" );
return 1 ;
}
int lunghezza = sizeof ( struct sockaddr_in );
// the remote address will be placed in indirizzo_remoto
s2 = accept ( s , ( struct sockaddr * ) & indirizzo_remoto , & lunghezza );
if ( s2 == -1 ) {
perror ( "Accept Fallita" );
return 1 ;
}
// now we can read in this way:
char buffer [ 10000 ];
int i ;
for ( i = 0 ; ( t = read ( s2 , buffer + i , 1 )) > 0 ; i ++ ); // ps. it's not a good way
// if the previous read returned -1
if ( t == -1 ) {
perror ( "Read Fallita" );
return 1 ;
}
最後に、忘れずにclose(s)
ですべてのソケットを閉じてください (s は閉じたいソケット内にあります)。
int s = socket (
//AF_PACKET Low level packet interface packet(7)
AF_PACKET ,
//SOCK_RAW Provides raw network protocol access.
SOCK_RAW ,
// When protocol is set to htons(ETH_P_ALL), then all protocols are received.
htons ( ETH_P_ALL ));
unsigned char buffer [ 1500 ];
bzero ( & sll , sizeof ( struct sockaddr_ll ));
struct sockaddr_ll sll ;
sll . sll_ifindex = if_nametoindex ( "eth0" );
len = sizeof ( sll );
int t = sendto ( s , //socket
buffer , //things to send
14 + 20 + 28 , // len datagram
0 , //flags
( struct sockaddr * ) & sll , // destination addr
len // dest addr len
);
// to receive
t = recvfrom ( s , buffer , 1500 , 0 , ( struct sockaddr * ) & sll , & len );
if ( t == -1 ) {
perror ( "recvfrom fallita" );
return 1 ;
}
void stampa_eth ( struct eth_frame * e ){
printf ( "nn ***** PACCHETTO Ethernet *****n" );
printf ( "Mac destinazione: %x:%x:%x:%x:%x:%xn" , e -> dst [ 0 ], e -> dst [ 1 ], e -> dst [ 2 ], e -> dst [ 3 ], e -> dst [ 4 ], e -> dst [ 5 ] );
printf ( "Mac sorgente: %x:%x:%x:%x:%x:%xn" , e -> src [ 0 ], e -> src [ 1 ], e -> src [ 2 ], e -> src [ 3 ], e -> src [ 4 ], e -> src [ 5 ] );
printf ( "EtherType: 0x%xn" , htons ( e -> type ) );
}
void stampa_ip ( struct ip_datagram * i ){
unsigned int ihl = ( i -> ver_ihl & 0x0F ) * 4 ; // Lunghezza header IP
unsigned int totlen = htons ( i -> totlen ); // Lunghezza totale pacchetto
unsigned int opt_len = ihl - 20 ; // Lunghezza campo opzioni
printf ( "nn ***** PACCHETTO IP *****n" );
printf ( "Version: %dn" , i -> ver_ihl & 0xF0 );
printf ( "IHL (bytes 60max): %dn" , ihl );
printf ( "TOS: %dn" , i -> tos );
printf ( "Lunghezza totale: %dn" , totlen );
printf ( "ID: %xn" , htons ( i -> id ) );
unsigned char flags = ( unsigned char )( htons ( i -> flag_offs ) >> 13 );
printf ( "Flags: %d | %d | %d n" , flags & 4 , flags & 2 , flags & 1 );
printf ( "Fragment Offset: %dn" , htons ( i -> flag_offs ) & 0x1FFF );
printf ( "TTL: %dn" , i -> ttl );
printf ( "Protocol: %dn" , i -> proto );
printf ( "Checksum: %xn" , htons ( i -> checksum ) );
unsigned char * saddr = ( unsigned char * ) & i -> saddr ;
unsigned char * daddr = ( unsigned char * ) & i -> daddr ;
printf ( "IP Source: %d.%d.%d.%dn" , saddr [ 0 ], saddr [ 1 ], saddr [ 2 ], saddr [ 3 ] );
printf ( "IP Destination: %d.%d.%d.%dn" , daddr [ 0 ], daddr [ 1 ], daddr [ 2 ], daddr [ 3 ] );
if ( ihl > 20 ){
// Stampa opzioni
printf ( "Options: " );
for ( int j = 0 ; j < opt_len ; j ++ ){
printf ( "%.3d(%.2x) " , i -> payload [ j ], i -> payload [ j ]);
}
printf ( "n" );
}
}
void stampa_arp ( struct arp_packet * a ){
printf ( "nn ***** PACCHETTO ARP *****n" );
printf ( "Hardware type: %dn" , htons ( a -> htype ) );
printf ( "Protocol type: %xn" , htons ( a -> ptype ) );
printf ( "Hardware Addr len: %dn" , a -> hlen );
printf ( "Protocol Addr len: %dn" , a -> plen );
printf ( "Operation: %dn" , htons ( a -> op ) );
printf ( "HW Addr sorgente: %x:%x:%x:%x:%x:%xn" , a -> hsrc [ 0 ], a -> hsrc [ 1 ], a -> hsrc [ 2 ], a -> hsrc [ 3 ], a -> hsrc [ 4 ], a -> hsrc [ 5 ] );
printf ( "IP Source: %d.%d.%d.%dn" , a -> psrc [ 0 ], a -> psrc [ 1 ], a -> psrc [ 2 ], a -> psrc [ 3 ] );
printf ( "HW Addr Destinazione: %x:%x:%x:%x:%x:%xn" , a -> hdst [ 0 ], a -> hdst [ 1 ], a -> hdst [ 2 ], a -> hdst [ 3 ], a -> hdst [ 4 ], a -> hdst [ 5 ] );
printf ( "IP Dest: %d.%d.%d.%dn" , a -> pdst [ 0 ], a -> pdst [ 1 ], a -> pdst [ 2 ], a -> pdst [ 3 ] );
}
void stampa_icmp ( struct icmp_packet * i ){
printf ( "nn ***** PACCHETTO ICMP *****n" );
printf ( "Type: %dn" , i -> type );
printf ( "Code: %dn" , i -> code );
printf ( "Code: 0x%xn" , htons ( i -> checksum ) );
printf ( "ID: %dn" , htons ( i -> id ) );
printf ( "Sequence: %dn" , htons ( i -> seq ) );
}
void stampa_tcp ( struct tcp_segment * t ){
printf ( "nn ***** PACCHETTO TCP *****n" );
printf ( "Source Port: %dn" , htons ( t -> s_port ) );
printf ( "Source Port: %dn" , htons ( t -> d_port ) );
printf ( "Sequence N: %dn" , ntohl ( t -> seq ) );
printf ( "ACK: %dn" , ntohl ( t -> ack ) );
printf ( "Data offset (bytes): %dn" , ( t -> d_offs_res >> 4 ) * 4 );
printf ( "Flags: " );
printf ( "CWR=%d | " , ( t -> flags & 0x80 ) >> 7 );
printf ( "ECE=%d | " , ( t -> flags & 0x40 ) >> 6 );
printf ( "URG=%d | " , ( t -> flags & 0x20 ) >> 5 );
printf ( "ACK=%d | " , ( t -> flags & 0x10 ) >> 4 );
printf ( "PSH=%d | " , ( t -> flags & 0x08 ) >> 3 );
printf ( "RST=%d | " , ( t -> flags & 0x04 ) >> 2 );
printf ( "SYN=%d | " , ( t -> flags & 0x02 ) >> 1 );
printf ( "FIN=%dn" , ( t -> flags & 0x01 ) );
printf ( "Windows size: %dn" , htons ( t -> win ) );
printf ( "Checksum: 0x%xn" , htons ( t -> checksum ) );
printf ( "Urgent pointer: %dn" , htons ( t -> urgp ) );
}
あまり役に立ちませんが...
// es. tcp.c
printf ( "%.4d. // delta_sec (unsigned int)
% .6d // delta_usec
% .5d -> % .5d // ports (unsigned short)
% .2 x // tcp flags (unsigned char) in hex: es: "12"
% .10u // seq (unsigned int)
% .10u // ack
% .5u //tcp win
% 4.2f n ", delta_sec , delta_usec , htons ( tcp -> s_port ), htons ( tcp -> d_port ), tcp -> flags , htonl ( tcp -> seq ) - seqzero , htonl ( tcp -> ack ) - ackzero , htons ( tcp -> win ), ( htonl ( tcp -> ack ) - ackzero ) / ( double )( delta_sec * 1000000 + delta_usec ));
/etc/services
: アプリケーション レベルで使用可能なすべての TCP ポートを確認します。/etc/protocols
nslookup <URL>
: 指定された URL の IP アドレスを検索します (例: www.google.com)netstat -rn
ルーティング テーブルを表示しますtraceroute
ルートは、低い TTL (生存時間、ホップごとに減分) カウントで偽造されたパケットをドロップすることを決定したすべてのゲートウェイの IP を出力することにより、IP パケットがどのパスを通過するかをルーティングします。