生活随笔
收集整理的這篇文章主要介紹了
ip 路由选项
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
1.ip頭中選項格式
由于IP首部中可以存在選項,且可以同時存在多個選項,因此IP首部的長度是可變的,IPv4允許選項最長可達40字節。選項的格式有單字節和多字節兩種,單字節的即只包括一個字節的選項類型,而多字節的則除一個字節的類型之外,還包括選項長度以及選項數據等。
多字節的選項格式如下所示:
所有選項都以1字節類型(type)字段開始。在多字節選項中,類型字段后面緊接著一個長度(len)字段,其他的字節是數據(data)。許多選項數據字段的第一個字節是1字節的位移(offset)字段,指向數據字段內的某個字節。長度字節的計算覆蓋了類型、長度和數據字段。類型被繼續分成三個子字段: 1 bit 備份(copied )標志、2 bit 類(class)字段和5 bit 數字(number)字段。
2.linux內核存儲ip選項的結構
struct ip_options { ????__u32????????faddr;//存在寬松路由或嚴格路由選項時,用來記錄嚇一跳的IP地址 ????unsigned char????optlen;//標識IP首部中選項所占的字節數 ????unsigned char????srr;//記錄寬松路由或嚴格路由選項在IP首部中的偏移量,即選項的第一個字節的地址 ?????????????????????????? 減去IP首部的第一個字節的地址 ????unsigned char????rr;//用于記錄記錄路徑選項在IP首部中的偏移量 ????unsigned char????ts; ????unsigned char????is_setbyuser: 1, ????????????is_data: 1, ????????????is_strictroute: 1, ????????????srr_is_hit: 1, ????????????is_changed: 1, ????????????rr_needaddr: 1, ????????????ts_needtime: 1, ????????????ts_needaddr: 1; ????unsigned char????router_alert; ????unsigned char????cipso; ????unsigned char????__pad2; ????unsigned char????__data[ 0] ;//若選項有數據則從該字段開始,使之緊跟在ip_options結構后面,最多不 ???????????????????????????????? 超過40字節 } ;
3.寬松路由選項(LSRR)和嚴格路由選項(SSRR)
LSRR在選項的ip地址列表中并不列出一條完備而嚴格的路徑,而是只給出路徑中的某些關鍵點。在關鍵的之間可以通過路由器的自動路由選擇功能進行路由,此選項在數據包分片的時候也必須被復制。
SSRR選項要求數據包必須嚴格按照發送方規定的路徑經過每一個路由器,這些路由器應該是一一相連的,每兩個指定的路由器之間不能有其他未指定的路由器,且路由器的順序是不能改變的。如果數據包在傳輸時無法直接到達下一跳指定的路由器,路由器就會丟棄該數據包,然后產生一個源路由失敗的目的不可達的ICMP差錯報文報告給發送方。
內核定義的ip選項類型值:
/ * IP options * / #define IPOPT_COPY????????0x80 #define IPOPT_CLASS_MASK????0x60 #define IPOPT_NUMBER_MASK????0x1f #define????IPOPT_COPIED( o) ????????( ( o) & IPOPT_COPY) #define????IPOPT_CLASS( o) ????????( ( o) & IPOPT_CLASS_MASK) #define????IPOPT_NUMBER( o) ????????( ( o) & IPOPT_NUMBER_MASK) #define????IPOPT_CONTROL????????0x00 #define????IPOPT_RESERVED1????????0x20 #define????IPOPT_MEASUREMENT????0x40 #define????IPOPT_RESERVED2????????0x60 #define IPOPT_END????( 0 | IPOPT_CONTROL) #define IPOPT_NOOP????( 1 | IPOPT_CONTROL) #define IPOPT_SEC????( 2 | IPOPT_CONTROL| IPOPT_COPY) #define IPOPT_LSRR????( 3 | IPOPT_CONTROL| IPOPT_COPY)//寬松路由的type值,即0x83 #define IPOPT_TIMESTAMP????( 4 | IPOPT_MEASUREMENT) #define IPOPT_CIPSO????( 6 | IPOPT_CONTROL| IPOPT_COPY) #define IPOPT_RR????( 7 | IPOPT_CONTROL) #define IPOPT_SID????( 8 | IPOPT_CONTROL| IPOPT_COPY) #define IPOPT_SSRR????( 9 | IPOPT_CONTROL| IPOPT_COPY)//嚴格路由的type值,即0x89 #define IPOPT_RA????( 20| IPOPT_CONTROL| IPOPT_COPY)
4.分析一個完整的ip選項處理流程(從setsockopt設置選項到發送syn、路由節點接收syn到轉發syn及目的端接收syn到發送synack的ip選項的處理過程)
(1)從setsockopt設置選項到發送syn
用戶層socket編程可以通過setsockopt來設置ip選項,在connect前設置選項值
setsockopt( sockfd, IPPROTO_IP, IP_OPTIONS, ( void* ) opt, optlen) ;
setsockopt系統調用在內核中的實現,之前已經介紹過了。現在我們直接跳到函數
int ip_setsockopt( struct sock * sk, int level, ????????int optname, char __user * optval, int optlen) { ??? ...... ????err = do_ip_setsockopt( sk, level, optname, optval, optlen) ;//IPPPROTO_IP的處理函數 ??? ...... }
static int do_ip_setsockopt( struct sock * sk, int level, ????????int optname, char __user * optval, int optlen) { ????struct inet_sock * inet = inet_sk( sk) ; ??? ...... ????lock_sock( sk) ; ????switch ( optname) { ????????case IP_OPTIONS://ip_options選項處理 ????????{ ????????????struct ip_options * opt = NULL ; ????????????if ( optlen > 40 | | optlen < 0) ????????????????goto e_inval; ????????????err = ip_options_get_from_user( & opt, optval, optlen) ; ????????????if ( err ) ????????????????break; ????????????if ( inet- > is_icsk) { ????????????????struct inet_connection_sock * icsk = inet_csk( sk) ; ????????????????...... ????????????????????if ( inet- > opt) ????????????????????????icsk- > icsk_ext_hdr_len - = inet- > opt- > optlen; ????????????????????if ( opt) ????????????????????????icsk- > icsk_ext_hdr_len + = opt- > optlen; ????????????????????icsk- > icsk_sync_mss( sk, icsk- > icsk_pmtu_cookie) ; ??????????????? ...... ????????????} ????????????opt = xchg( & inet- > opt, opt) ;//將opt與inet->opt交換;現在inet->opt中存儲了我們解 ????????????????????????????????????????? 析過的選項的值,inet->opt->faddr為下一跳的地址 ????????????kfree( opt) ; ????????????break; ????????} ????????...... }
int ip_options_get_from_user( struct ip_options * * optp, unsigned char __user * data, int optlen) { ????struct ip_options * opt = ip_options_get_alloc( optlen) ; ????if ( ! opt) ????????return - ENOMEM; ????if ( optlen & & copy_from_user( opt- > __data, data, optlen) ) {//?應用層數據拷貝到opt->__da ???????????????????????????????????????????????????????????????? ta指向的內存處 ????????kfree( opt) ; ????????return - EFAULT; ????} ????return ip_options_get_finish( optp, opt, optlen) ;//解析opt信息并用optp返回 }
static int ip_options_get_finish( struct ip_options * * optp, ???????????????? struct ip_options * opt, int optlen) { ????while ( optlen & 3) ????????opt- > __data[ optlen+ + ] = IPOPT_END;//如IP選項內容不是以四字節對齊的,則將未對齊部分用選 ??????????????????????????????????????????? 項列表結束符填充,使之對齊 ????opt- > optlen = optlen; ????opt- > is_data = 1; ????opt- > is_setbyuser = 1; ????if ( optlen & & ip_options_compile( opt, NULL ) ) {//解析IP選項信息塊各字段值 ????????kfree( opt) ; ????????return - EINVAL; ????} ????kfree( * optp) ; ????* optp = opt; ????return 0; }
int ip_options_compile( struct ip_options * opt, struct sk_buff * skb) { ??????? ...... ????????switch ( * optptr) { ???????? case IPOPT_SSRR: ???????? case IPOPT_LSRR: ????????????if ( optlen < 3) {//1字節type,1字節len,1字節offset ????????????????pp_ptr = optptr + 1; ????????????????goto error ; ????????????} ????????????if ( optptr[ 2] < 4) {//offset至少為4,指向第一個ip ????????????????pp_ptr = optptr + 2; ????????????????goto error ; ????????????} ????????????/ * NB: cf RFC- 1812 5. 2. 4. 1 * / ????????????if ( opt- > srr) { ????????????????pp_ptr = optptr; ????????????????goto error ; ????????????} ????????????if ( ! skb) { ????????????????if ( optptr[ 2] ! = 4 | | optlen < 7 | | ( ( optlen- 3) & 3) ) {//至少能容下一個ip ????????????????????pp_ptr = optptr + 1; ????????????????????goto error ; ????????????????} ????????????????memcpy( & opt- > faddr, & optptr[ 3] , 4) ;//取出第一個地址作為下一跳地址, ???????????????????????????????????????????????????? opt->faddr為下一跳的地址 ????????????????if ( optlen > 7) ????????????????????memmove( & optptr[ 3] , & optptr[ 7] , optlen- 7) ;//在路徑列表中多于一個地址 ???????????????????????????????????????????????????????時,將剩余的所有地址往前移動4個字節 ????????????} ????????????opt- > is_strictroute = ( optptr[ 0] = = IPOPT_SSRR) ;//記錄是否為嚴格路由選項 ????????????opt- > srr = optptr - iph;//記錄源路由選項在IP首部的偏移量 ????????????break; ??????????? ...... } options的offset項沒有修改,它指向了源地址路由的下一個節點,因為此時第一個路由節點已經從選項中刪除,其他路由節點向前移動了4個字節
應用層socket編程connect函數將執行系統調用sys_connect
asmlinkage long sys_connect( int fd, struct sockaddr __user * uservaddr, int addrlen) { ????struct socket * sock; ??? ...... ????err = sock- > ops- > connect( sock, ( struct sockaddr * ) address, addrlen, ???????????????? sock- > file- > f_flags) ; ????...... }
sock->ops指向注冊的proto_ops鉤子,此時為tcp_prot
struct proto tcp_prot = { ????. name????????????= "TCP" , ??? ...... ????. connect????????= tcp_v4_connect, ????...... } ;
int tcp_v4_connect( struct sock * sk, struct sockaddr * uaddr, int addr_len) { ????struct inet_sock * inet = inet_sk( sk) ; ????struct tcp_sock * tp = tcp_sk( sk) ; ????struct sockaddr_in * usin = ( struct sockaddr_in * ) uaddr; ??? ...... ????nexthop = daddr = usin- > sin_addr. s_addr; ????if ( inet- > opt & & inet- > opt- > srr) {//將臨時變量下一跳地址和目的地址值都暫時設置為connect參 ?????????????????????????? ?數中的地址,如果使用源地址路由,則將下一跳地址設置為IP選項中的faddr ????????if ( ! daddr) ????????????return - EINVAL; ????????nexthop = inet- > opt- > faddr; ????} ????tmp = ip_route_connect( & rt, nexthop, inet- > saddr,//查找路由緩存項 ???????????? RT_CONN_FLAGS( sk) , sk- > sk_bound_dev_if, ???????????? IPPROTO_TCP, ???????????? inet- > sport, usin- > sin_port, sk, 1) ;// ????if ( tmp < 0) { ????????if ( tmp = = - ENETUNREACH) ????????????IP_INC_STATS_BH( IPSTATS_MIB_OUTNOROUTES) ; ????????return tmp; ????} ??? ...... ????err = tcp_connect( sk) ;//發送syn包 ????rt = NULL ; ????if ( err ) ????????goto failure; ??? ...... }
int tcp_connect( struct sock * sk) { ??? ...... ????buff = alloc_skb_fclone( MAX_TCP_HEADER + 15, sk- > sk_allocation) ;//分配skbuff ??? ...... ????tcp_transmit_skb( sk, buff, 1, GFP_KERNEL) ;//構造tcp頭和ip頭并發送 ????...... }
static int tcp_transmit_skb( struct sock * sk, struct sk_buff * skb, int clone_it, gfp_t gfp_mask) { ??? ...... ????err = icsk- > icsk_af_ops- > queue_xmit( skb, 0) ;//構造ip頭并發送,queue_xmit注冊為 ????????????????????????????????????????????????? ip_queue_xmit函數 ??? ...... }
struct inet_connection_sock_af_ops ipv4_specific = { ????. queue_xmit???? = ip_queue_xmit, ??? ...... } ;
int ip_queue_xmit( struct sk_buff * skb, int ipfragok) { ????struct sock * sk = skb- > sk; ????struct inet_sock * inet = inet_sk( sk) ; ????struct ip_options * opt = inet- > opt; ????struct rtable * rt; ????struct iphdr * iph; ??? ...... ????????/ * Use correct destination address if we have options. * / ????????daddr = inet- > daddr; ????????if ( opt & & opt- > srr)//如果使用源地址路由,下一跳為inet->opt->faddr,即選項中的第一個地址 ????????????daddr = opt- > faddr; ????????{ ????????????struct flowi fl = { . oif = sk- > sk_bound_dev_if, ???????????????????? . nl_u = { . ip4_u = ???????????????????????? { . daddr = daddr, ????????????????????????????. saddr = inet- > saddr, ????????????????????????????. tos = RT_CONN_FLAGS( sk) } } , ???????????????????? . proto = sk- > sk_protocol, ???????????????????? . uli_u = { . ports = ???????????????????????? { . sport = inet- > sport, ???????????????????????????? . dport = inet- > dport } } } ; ????????????/ * If this fails, retransmit mechanism of transport layer will ???????????? * keep trying until route appears or the connection times ???????????? * itself out. ???????????? * / ????????????security_sk_classify_flow( sk, & fl) ; ????????????if ( ip_route_output_flow( & rt, & fl, sk, 0) ) ????????????????goto no_route; ????????} ????????sk_setup_caps( sk, & rt- > u. dst) ; ????} ????skb- > dst = dst_clone( & rt- > u. dst) ; packet_routed: ????if ( opt & & opt- > is_strictroute & & rt- > rt_dst ! = rt- > rt_gateway)//如果是嚴格路由選項,并且網關地址和路由的下一跳不一致則中斷處理流程,這也是嚴格路由所要求的:下一跳必須是選項中的順序的地址 ????????goto no_route; ??? ...... ????iph- > saddr = rt- > rt_src; ????iph- > daddr = rt- > rt_dst;//目的地址已經是路由的下一跳地址,即為選項中的第一個地址,即為 ??????????????????????????????inet->opt->faddr ????skb- > nh. iph = iph; ????/ * Transport layer set skb- > h. foo itself. * / ????if ( opt & & opt- > optlen) { ????????iph- > ihl + = opt- > optlen > > 2; ????????ip_options_build( skb, opt, inet- > daddr, rt, 0) ;//將目的地址到options中最后一個ip地 ???????????????????????????????????????????????????????? 址后面 ????} ??? ...... }
發送syn的處理告一段落,將通過一個實例來體現這一流程:
實驗一(LSRR):
客戶端:192.168.18.73 路由節點1:192.168.18.71 路由節點2:192.168.18.72 服務器端:192.168.18.76 18.71上18.0網段有個網關192.168.18.79
18.73抓包(發送syn包)結果如下:
一個NOP是為了對齊而設置的。從圖中可以看出目的地址改為了options中首個ip地址,偏移量指向了下一個路由節點地址,絕對目的地址被加入了options末尾。len為1個字節的type+1個字節len+1個字節的offset+2個四個字節的ip地址
如果在73上設定網關18.79后,syn包發送不受影響
實驗二(SSRR):
如果設定18.79網關,syn包就發不出去了,原因在ip_queue_xmit函數中分析過了
總結
以上是生活随笔 為你收集整理的ip 路由选项 的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網站內容還不錯,歡迎將生活随笔 推薦給好友。