ipv6Ready测试遇到如下问题:测试仪将一个报文分为两个分片,第一个分片报文未包含原报文的所有报头,发送两个分片报文,设备应该丢弃两个分片报文,并回复ICMPv6 error报文,在测试过程中测试仪未收到该错误报文。
具体case号是IPv6 Specification 的64-67。
修改 net\ipv6\ip6_input.c 文件中的 ip6_input_finish 函数:
static int ip6_input_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
{
const struct inet6_protocol *ipprot;
struct inet6_dev *idev;
unsigned int nhoff;
int nexthdr = -1;
int preNextHdr;
bool raw;
bool have_final = false;
/*
* Parse extension headers
*/
rcu_read_lock();
resubmit:
idev = ip6_dst_idev(skb_dst(skb));
if (!pskb_pull(skb, skb_transport_offset(skb)))
goto discard;
nhoff = IP6CB(skb)->nhoff;
nexthdr = skb_network_header(skb)[nhoff];
resubmit_final:
raw = raw6_local_deliver(skb, nexthdr);
ipprot = rcu_dereference(inet6_protos[nexthdr]);
if (ipprot) {
int ret;
if (have_final) {
if (!(ipprot->flags & INET6_PROTO_FINAL)) {
/* Once we've seen a final protocol don't
* allow encapsulation on any non-final
* ones. This allows foo in UDP encapsulation
* to work.
*/
goto discard;
}
} else if (ipprot->flags & INET6_PROTO_FINAL) {
const struct ipv6hdr *hdr;
/* Only do this once for first final protocol */
have_final = true;
/* Free reference early: we don't need it any more,
and it may hold ip_conntrack module loaded
indefinitely. */
nf_reset(skb);
skb_postpull_rcsum(skb, skb_network_header(skb),
skb_network_header_len(skb));
hdr = ipv6_hdr(skb);
if (ipv6_addr_is_multicast(&hdr->daddr) &&
!ipv6_chk_mcast_addr(skb->dev, &hdr->daddr,
&hdr->saddr) &&
!ipv6_is_mld(skb, nexthdr, skb_network_header_len(skb)))
goto discard;
}
if (!(ipprot->flags & INET6_PROTO_NOPOLICY) &&
!xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb))
goto discard;
if ( 44 == preNextHdr && (6 != nexthdr && 17 != nexthdr && 58 != nexthdr && 59 != nexthdr) )
{
printk("fragpacket5, %d, %d, %p\n", preNextHdr, nexthdr, skb);
icmpv6_send(skb, ICMPV6_PARAMPROB, 3, 0);
}
ret = ipprot->handler(skb);
if (ret > 0) {
if (ipprot->flags & INET6_PROTO_FINAL) {
/* Not an extension header, most likely UDP
* encapsulation. Use return value as nexthdr
* protocol not nhoff (which presumably is
* not set by handler).
*/
nexthdr = ret;
goto resubmit_final;
} else {
preNextHdr = nexthdr;
goto resubmit;
}
} else if (ret == 0) {
__IP6_INC_STATS(net, idev, IPSTATS_MIB_INDELIVERS);
}
} else {
if (!raw) {
if (xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb)) {
__IP6_INC_STATS(net, idev,
IPSTATS_MIB_INUNKNOWNPROTOS);
icmpv6_send(skb, ICMPV6_PARAMPROB,
ICMPV6_UNK_NEXTHDR, nhoff);
}
kfree_skb(skb);
} else {
__IP6_INC_STATS(net, idev, IPSTATS_MIB_INDELIVERS);
consume_skb(skb);
}
}
rcu_read_unlock();
return 0;
discard:
__IP6_INC_STATS(net, idev, IPSTATS_MIB_INDISCARDS);
rcu_read_unlock();
kfree_skb(skb);
return 0;
}
修改后的函数与源函数对比:
增加preNextHdr记录前一个包的nexthdr;
检查第一个分片报文中的各个报头,当最后一个报头不是上层报头如icmp、udp、等或no next header时,判断要素为上一个报头中的next header域,给原节点反馈一个icmp parameter problem(code=3,pointer=0)。
注:我测试用的内核版本是 linux-4.19.155,其它内核版本可能需要做相应的调整。