日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

ip 路由选项

發布時間:2024/3/24 编程问答 47 豆豆
生活随笔 收集整理的這篇文章主要介紹了 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 路由选项的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。