741
技術社區[雲棲]
網絡子係統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