Extract IPv6 packet that triggered the sending of redirect message from ICMPv6 Redirected Header option and check if conntrack table contain such connection. Mark redirect packet as RELATED if a matching connection is found. Signed-off-by: Alin Nastac <alin.nastac@xxxxxxxxx> --- net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c | 87 ++++++++++++++++++++++++-- 1 file changed, 82 insertions(+), 5 deletions(-) diff --git a/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c b/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c index d2c2ccb..f7bae5c 100644 --- a/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c +++ b/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c @@ -144,7 +144,7 @@ static bool icmpv6_new(struct nf_conn *ct, const struct sk_buff *skb, static int icmpv6_error_message(struct net *net, struct nf_conn *tmpl, struct sk_buff *skb, - unsigned int icmp6off, + unsigned int inneripv6off, unsigned int hooknum) { struct nf_conntrack_tuple intuple, origtuple; @@ -157,9 +157,7 @@ static bool icmpv6_new(struct nf_conn *ct, const struct sk_buff *skb, /* Are they talking about one of our connections? */ if (!nf_ct_get_tuplepr(skb, - skb_network_offset(skb) - + sizeof(struct ipv6hdr) - + sizeof(struct icmp6hdr), + inneripv6off, PF_INET6, net, &origtuple)) { pr_debug("icmpv6_error: Can't get tuple\n"); return -NF_ACCEPT; @@ -194,6 +192,82 @@ static bool icmpv6_new(struct nf_conn *ct, const struct sk_buff *skb, } static int +icmpv6_redirect_message(struct net *net, struct nf_conn *tmpl, + struct sk_buff *skb, + unsigned int dataoff, + unsigned int hooknum) +{ + const struct in6_addr *dst; + struct in6_addr _dst; + const struct nd_opt_hdr *opt; + struct nd_opt_hdr _opt; + const struct ipv6hdr *iph; + struct ipv6hdr _iph; + + /* skip target address */ + dataoff += sizeof(_dst); + + /* read destination address */ + dst = skb_header_pointer(skb, dataoff, sizeof(_dst), &_dst); + if (dst == NULL) { + if (LOG_INVALID(net, IPPROTO_ICMPV6)) + nf_log_packet(net, PF_INET6, 0, skb, NULL, NULL, NULL, + "nf_ct_icmpv6: short redirect packet "); + return -NF_ACCEPT; + } + if (ipv6_addr_is_multicast(dst)) { + if (LOG_INVALID(net, IPPROTO_ICMPV6)) + nf_log_packet(net, PF_INET6, 0, skb, NULL, NULL, NULL, + "nf_ct_icmpv6: redirect destination address is multicast "); + return -NF_ACCEPT; + } + dataoff += sizeof(_dst); + + /* find redirected header */ + while (1) { + opt = skb_header_pointer(skb, dataoff, sizeof(_opt), &_opt); + if (opt == NULL) { + if (LOG_INVALID(net, IPPROTO_ICMPV6)) + nf_log_packet(net, PF_INET6, 0, skb, NULL, NULL, NULL, + "nf_ct_icmpv6: invalid redirect option "); + return -NF_ACCEPT; + } + if (opt->nd_opt_len == 0) { + if (LOG_INVALID(net, IPPROTO_ICMPV6)) + nf_log_packet(net, PF_INET6, 0, skb, NULL, NULL, NULL, + "nf_ct_icmpv6: invalid redirect option length "); + return -NF_ACCEPT; + } + + if (opt->nd_opt_type == ND_OPT_REDIRECT_HDR) { + dataoff += 8; + break; + } + + dataoff += opt->nd_opt_len << 3; + } + + /* read redirect header */ + iph = skb_header_pointer(skb, dataoff, sizeof(_iph), &_iph); + if (iph == NULL) { + if (LOG_INVALID(net, IPPROTO_ICMPV6)) + nf_log_packet(net, PF_INET6, 0, skb, NULL, NULL, NULL, + "nf_ct_icmpv6: short redirect header "); + return -NF_ACCEPT; + } + + /* validate destination address */ + if (!ipv6_addr_equal(&iph->daddr, dst)) { + if (LOG_INVALID(net, IPPROTO_ICMPV6)) + nf_log_packet(net, PF_INET6, 0, skb, NULL, NULL, NULL, + "nf_ct_icmpv6: redirect destination address not matching destination address of redirect header "); + return -NF_ACCEPT; + } + + return icmpv6_error_message(net, tmpl, skb, dataoff, hooknum); +} + +static int icmpv6_error(struct net *net, struct nf_conn *tmpl, struct sk_buff *skb, unsigned int dataoff, u8 pf, unsigned int hooknum) @@ -225,9 +299,12 @@ static bool icmpv6_new(struct nf_conn *ct, const struct sk_buff *skb, nf_conntrack_get(skb_nfct(skb)); return NF_ACCEPT; } + dataoff += sizeof(struct icmp6hdr); /* is not error message ? */ - if (icmp6h->icmp6_type >= 128) + if (icmp6h->icmp6_type == NDISC_REDIRECT) + return icmpv6_redirect_message(net, tmpl, skb, dataoff, hooknum); + else if (icmp6h->icmp6_type >= 128) return NF_ACCEPT; return icmpv6_error_message(net, tmpl, skb, dataoff, hooknum); -- 1.7.12.4 -- To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html