Message ID | 84b12a8d761ac804794f6a0e08011eff4c2c0a3a.1677888566.git.lucien.xin@gmail.com (mailing list archive) |
---|---|
State | Superseded |
Delegated to: | Netdev Maintainers |
Headers | show |
Series | netfilter: handle ipv6 jumbo packets properly for bridge ovs and tc | expand |
On Fri, Mar 03, 2023 at 07:12:40PM -0500, Xin Long wrote: > Rename br_nf_check_hbh_len() to nf_ip6_check_hbh_len() and move it > to netfilter utils, so that it can be used by other modules, like > ovs and tc. > > Signed-off-by: Xin Long <lucien.xin@gmail.com> Reviewed-by: Simon Horman <simon.horman@corigine.com>
On 04/03/2023 02:12, Xin Long wrote: > Rename br_nf_check_hbh_len() to nf_ip6_check_hbh_len() and move it > to netfilter utils, so that it can be used by other modules, like > ovs and tc. > > Signed-off-by: Xin Long <lucien.xin@gmail.com> > --- > include/linux/netfilter_ipv6.h | 2 ++ > net/bridge/br_netfilter_ipv6.c | 57 +--------------------------------- > net/netfilter/utils.c | 54 ++++++++++++++++++++++++++++++++ > 3 files changed, 57 insertions(+), 56 deletions(-) > Reviewed-by: Nikolay Aleksandrov <razor@blackwall.org>
Xin Long <lucien.xin@gmail.com> writes: > Rename br_nf_check_hbh_len() to nf_ip6_check_hbh_len() and move it > to netfilter utils, so that it can be used by other modules, like > ovs and tc. > > Signed-off-by: Xin Long <lucien.xin@gmail.com> > --- Reviewed-by: Aaron Conole <aconole@redhat.com> > include/linux/netfilter_ipv6.h | 2 ++ > net/bridge/br_netfilter_ipv6.c | 57 +--------------------------------- > net/netfilter/utils.c | 54 ++++++++++++++++++++++++++++++++ > 3 files changed, 57 insertions(+), 56 deletions(-) > > diff --git a/include/linux/netfilter_ipv6.h b/include/linux/netfilter_ipv6.h > index 48314ade1506..7834c0be2831 100644 > --- a/include/linux/netfilter_ipv6.h > +++ b/include/linux/netfilter_ipv6.h > @@ -197,6 +197,8 @@ static inline int nf_cookie_v6_check(const struct ipv6hdr *iph, > __sum16 nf_ip6_checksum(struct sk_buff *skb, unsigned int hook, > unsigned int dataoff, u_int8_t protocol); > > +int nf_ip6_check_hbh_len(struct sk_buff *skb, u32 *plen); > + > int ipv6_netfilter_init(void); > void ipv6_netfilter_fini(void); > > diff --git a/net/bridge/br_netfilter_ipv6.c b/net/bridge/br_netfilter_ipv6.c > index 07289e4f3213..550039dfc31a 100644 > --- a/net/bridge/br_netfilter_ipv6.c > +++ b/net/bridge/br_netfilter_ipv6.c > @@ -40,61 +40,6 @@ > #include <linux/sysctl.h> > #endif > > -/* We only check the length. A bridge shouldn't do any hop-by-hop stuff > - * anyway > - */ > -static int br_nf_check_hbh_len(struct sk_buff *skb, u32 *plen) > -{ > - int len, off = sizeof(struct ipv6hdr); > - unsigned char *nh; > - u32 pkt_len = 0; > - > - if (!pskb_may_pull(skb, off + 8)) > - return -1; > - nh = (u8 *)(ipv6_hdr(skb) + 1); > - len = (nh[1] + 1) << 3; > - > - if (!pskb_may_pull(skb, off + len)) > - return -1; > - nh = skb_network_header(skb); > - > - off += 2; > - len -= 2; > - while (len > 0) { > - int optlen; > - > - if (nh[off] == IPV6_TLV_PAD1) { > - off++; > - len--; > - continue; > - } > - if (len < 2) > - return -1; > - optlen = nh[off + 1] + 2; > - if (optlen > len) > - return -1; > - > - if (nh[off] == IPV6_TLV_JUMBO) { > - if (nh[off + 1] != 4 || (off & 3) != 2) > - return -1; > - pkt_len = ntohl(*(__be32 *)(nh + off + 2)); > - if (pkt_len <= IPV6_MAXPLEN || > - ipv6_hdr(skb)->payload_len) > - return -1; > - if (pkt_len > skb->len - sizeof(struct ipv6hdr)) > - return -1; > - } > - off += optlen; > - len -= optlen; > - } > - if (len) > - return -1; > - > - if (pkt_len) > - *plen = pkt_len; > - return 0; > -} > - > int br_validate_ipv6(struct net *net, struct sk_buff *skb) > { > const struct ipv6hdr *hdr; > @@ -114,7 +59,7 @@ int br_validate_ipv6(struct net *net, struct sk_buff *skb) > goto inhdr_error; > > pkt_len = ntohs(hdr->payload_len); > - if (hdr->nexthdr == NEXTHDR_HOP && br_nf_check_hbh_len(skb, &pkt_len)) > + if (hdr->nexthdr == NEXTHDR_HOP && nf_ip6_check_hbh_len(skb, &pkt_len)) > goto drop; > > if (pkt_len + ip6h_len > skb->len) { > diff --git a/net/netfilter/utils.c b/net/netfilter/utils.c > index 2182d361e273..04f4bd661774 100644 > --- a/net/netfilter/utils.c > +++ b/net/netfilter/utils.c > @@ -215,3 +215,57 @@ int nf_reroute(struct sk_buff *skb, struct nf_queue_entry *entry) > } > return ret; > } > + > +/* Only get and check the lengths, not do any hop-by-hop stuff. */ > +int nf_ip6_check_hbh_len(struct sk_buff *skb, u32 *plen) > +{ > + int len, off = sizeof(struct ipv6hdr); > + unsigned char *nh; > + u32 pkt_len = 0; > + > + if (!pskb_may_pull(skb, off + 8)) > + return -ENOMEM; > + nh = (u8 *)(ipv6_hdr(skb) + 1); > + len = (nh[1] + 1) << 3; > + > + if (!pskb_may_pull(skb, off + len)) > + return -ENOMEM; > + nh = skb_network_header(skb); > + > + off += 2; > + len -= 2; > + while (len > 0) { > + int optlen; > + > + if (nh[off] == IPV6_TLV_PAD1) { > + off++; > + len--; > + continue; > + } > + if (len < 2) > + return -EBADMSG; > + optlen = nh[off + 1] + 2; > + if (optlen > len) > + return -EBADMSG; > + > + if (nh[off] == IPV6_TLV_JUMBO) { > + if (nh[off + 1] != 4 || (off & 3) != 2) > + return -EBADMSG; > + pkt_len = ntohl(*(__be32 *)(nh + off + 2)); > + if (pkt_len <= IPV6_MAXPLEN || > + ipv6_hdr(skb)->payload_len) > + return -EBADMSG; > + if (pkt_len > skb->len - sizeof(struct ipv6hdr)) > + return -EBADMSG; > + } > + off += optlen; > + len -= optlen; > + } > + if (len) > + return -EBADMSG; > + > + if (pkt_len) > + *plen = pkt_len; > + return 0; > +} > +EXPORT_SYMBOL_GPL(nf_ip6_check_hbh_len);
diff --git a/include/linux/netfilter_ipv6.h b/include/linux/netfilter_ipv6.h index 48314ade1506..7834c0be2831 100644 --- a/include/linux/netfilter_ipv6.h +++ b/include/linux/netfilter_ipv6.h @@ -197,6 +197,8 @@ static inline int nf_cookie_v6_check(const struct ipv6hdr *iph, __sum16 nf_ip6_checksum(struct sk_buff *skb, unsigned int hook, unsigned int dataoff, u_int8_t protocol); +int nf_ip6_check_hbh_len(struct sk_buff *skb, u32 *plen); + int ipv6_netfilter_init(void); void ipv6_netfilter_fini(void); diff --git a/net/bridge/br_netfilter_ipv6.c b/net/bridge/br_netfilter_ipv6.c index 07289e4f3213..550039dfc31a 100644 --- a/net/bridge/br_netfilter_ipv6.c +++ b/net/bridge/br_netfilter_ipv6.c @@ -40,61 +40,6 @@ #include <linux/sysctl.h> #endif -/* We only check the length. A bridge shouldn't do any hop-by-hop stuff - * anyway - */ -static int br_nf_check_hbh_len(struct sk_buff *skb, u32 *plen) -{ - int len, off = sizeof(struct ipv6hdr); - unsigned char *nh; - u32 pkt_len = 0; - - if (!pskb_may_pull(skb, off + 8)) - return -1; - nh = (u8 *)(ipv6_hdr(skb) + 1); - len = (nh[1] + 1) << 3; - - if (!pskb_may_pull(skb, off + len)) - return -1; - nh = skb_network_header(skb); - - off += 2; - len -= 2; - while (len > 0) { - int optlen; - - if (nh[off] == IPV6_TLV_PAD1) { - off++; - len--; - continue; - } - if (len < 2) - return -1; - optlen = nh[off + 1] + 2; - if (optlen > len) - return -1; - - if (nh[off] == IPV6_TLV_JUMBO) { - if (nh[off + 1] != 4 || (off & 3) != 2) - return -1; - pkt_len = ntohl(*(__be32 *)(nh + off + 2)); - if (pkt_len <= IPV6_MAXPLEN || - ipv6_hdr(skb)->payload_len) - return -1; - if (pkt_len > skb->len - sizeof(struct ipv6hdr)) - return -1; - } - off += optlen; - len -= optlen; - } - if (len) - return -1; - - if (pkt_len) - *plen = pkt_len; - return 0; -} - int br_validate_ipv6(struct net *net, struct sk_buff *skb) { const struct ipv6hdr *hdr; @@ -114,7 +59,7 @@ int br_validate_ipv6(struct net *net, struct sk_buff *skb) goto inhdr_error; pkt_len = ntohs(hdr->payload_len); - if (hdr->nexthdr == NEXTHDR_HOP && br_nf_check_hbh_len(skb, &pkt_len)) + if (hdr->nexthdr == NEXTHDR_HOP && nf_ip6_check_hbh_len(skb, &pkt_len)) goto drop; if (pkt_len + ip6h_len > skb->len) { diff --git a/net/netfilter/utils.c b/net/netfilter/utils.c index 2182d361e273..04f4bd661774 100644 --- a/net/netfilter/utils.c +++ b/net/netfilter/utils.c @@ -215,3 +215,57 @@ int nf_reroute(struct sk_buff *skb, struct nf_queue_entry *entry) } return ret; } + +/* Only get and check the lengths, not do any hop-by-hop stuff. */ +int nf_ip6_check_hbh_len(struct sk_buff *skb, u32 *plen) +{ + int len, off = sizeof(struct ipv6hdr); + unsigned char *nh; + u32 pkt_len = 0; + + if (!pskb_may_pull(skb, off + 8)) + return -ENOMEM; + nh = (u8 *)(ipv6_hdr(skb) + 1); + len = (nh[1] + 1) << 3; + + if (!pskb_may_pull(skb, off + len)) + return -ENOMEM; + nh = skb_network_header(skb); + + off += 2; + len -= 2; + while (len > 0) { + int optlen; + + if (nh[off] == IPV6_TLV_PAD1) { + off++; + len--; + continue; + } + if (len < 2) + return -EBADMSG; + optlen = nh[off + 1] + 2; + if (optlen > len) + return -EBADMSG; + + if (nh[off] == IPV6_TLV_JUMBO) { + if (nh[off + 1] != 4 || (off & 3) != 2) + return -EBADMSG; + pkt_len = ntohl(*(__be32 *)(nh + off + 2)); + if (pkt_len <= IPV6_MAXPLEN || + ipv6_hdr(skb)->payload_len) + return -EBADMSG; + if (pkt_len > skb->len - sizeof(struct ipv6hdr)) + return -EBADMSG; + } + off += optlen; + len -= optlen; + } + if (len) + return -EBADMSG; + + if (pkt_len) + *plen = pkt_len; + return 0; +} +EXPORT_SYMBOL_GPL(nf_ip6_check_hbh_len);
Rename br_nf_check_hbh_len() to nf_ip6_check_hbh_len() and move it to netfilter utils, so that it can be used by other modules, like ovs and tc. Signed-off-by: Xin Long <lucien.xin@gmail.com> --- include/linux/netfilter_ipv6.h | 2 ++ net/bridge/br_netfilter_ipv6.c | 57 +--------------------------------- net/netfilter/utils.c | 54 ++++++++++++++++++++++++++++++++ 3 files changed, 57 insertions(+), 56 deletions(-)