網絡子係統44_ip協議源路由選項處理
//skb->pkt_type根據l2地址設置,在eth_type_trans(由驅動調用)中,如果數據幀的目的l2地址為接收接口的l2地址,設置為PACKET_HOST //skb->rt->rt_type根據l3地址設置,在ip_route_input(由ip協議調用)中,如果數據幀的目的l3地址為本機配置的l3地址,設置為RTN_LOCAL //調用路徑ip_rcv->ip_rcv_finish->ip_options_rcv_srr // 1.skb->dst非本機ip, // 1.1 嚴路由選項,向發送方返回錯誤 // 1.2 寬路由選項,直接返回,繼續ip_rcv_finish後續處理 // 2.skb->dst本機ip, // 尋找源路由選項ptr指示的下一個,非本機ip的單播地址,複製到ip報頭,設置ip_changed使ip校驗和失效。 //根據源路由選項,選擇下一跳地址,並更新skb->dst路由緩存 1.1 int ip_options_rcv_srr(struct sk_buff *skb) { struct ip_options *opt = &(IPCB(skb)->opt);//ip控製塊,在ip_options_compile中被初始化 int srrspace, srrptr; u32 nexthop; struct iphdr *iph = skb->nh.iph; unsigned char * optptr = skb->nh.raw + opt->srr; struct rtable *rt = (struct rtable*)skb->dst; struct rtable *rt2; int err; if (!opt->srr) return 0; if (skb->pkt_type != PACKET_HOST)//l2地址非本機 return -EINVAL; if (rt->rt_type == RTN_UNICAST) {//l3地址非本機,但是可以到達 if (!opt->is_strictroute)//非嚴源路由選項,可以跳過,不處理 return 0; icmp_send(skb, ICMP_PARAMETERPROB, 0, htonl(16<<24));//否則通過icmp,報告發送主機,本機不在嚴路由ip列表中 return -EINVAL; } if (rt->rt_type != RTN_LOCAL)//l3地址非主機,則返回錯誤 return -EINVAL; //從ptr指示的起始位置,逐個遍曆後續的路由ip,直到下一跳為非本機單播路由 for (srrptr=optptr[2], srrspace = optptr[1]; srrptr <= srrspace; srrptr += 4) { if (srrptr + 3 > srrspace) {//不足4字節 icmp_send(skb, ICMP_PARAMETERPROB, 0, htonl((opt->srr+2)<<24));//報告錯誤 return -EINVAL; } memcpy(&nexthop, &optptr[srrptr-1], 4);//從源路由選項中,複製下一跳的ip地址 rt = (struct rtable*)skb->dst; skb->dst = NULL; err = ip_route_input(skb, nexthop, iph->saddr, iph->tos, skb->dev);//通過源路由選項提供的下一跳地址,路由查詢 rt2 = (struct rtable*)skb->dst; if (err || (rt2->rt_type != RTN_UNICAST && rt2->rt_type != RTN_LOCAL)) {//查詢出現錯誤,或者下一跳地址非單播路由,並且非指向本地的路由 ip_rt_put(rt2); skb->dst = &rt->u.dst;//使用之前的路由選項 return -EINVAL;//返回錯誤 } ip_rt_put(rt); if (rt2->rt_type != RTN_LOCAL)//找到非本機單播地址 break; memcpy(&iph->daddr, &optptr[srrptr-1], 4);//修改ip報頭的目的地址 opt->is_changed = 1; } if (srrptr <= srrspace) { opt->srr_is_hit = 1; opt->is_changed = 1; } return 0; }
最後更新:2017-04-03 14:53:37