网络子系统44_ip源路由选项处理
//调用路径ip_rcv->ip_rcv_finish->ip_options_rcv_srr//skb->pkt_type根据l2地址设置,在eth_type_trans(由驱动调用)中,如果数据帧的目的l2地址为接收接口的l2地址,设置为PACKET_HOST//skb->rt->rt_type根据l3地址设置,在ip_route_input(由ip协议调用)中,如果数据帧的目的l3地址为本机配置的l3地址,设置为RTN_LOCAL1.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;}