Add specifying an offset when calling nft_set_pktinfo_ipv4/6_validate()
for cases where the ip(v6) header is not located at skb_network_header().

When an offset is specified other then zero, do not set pkt->tprot and
the corresponding pkt->flags to not change rule processing. It does make
the offsets in pktinfo available for code that is not checking pkt->flags
to use the offsets, like nft_flow_offload_eval().

Existing behaviour for a rule like "tcp dport 22 accept" is not changed
when, for instance, a PPPoE packet is being matched inside a bridge.

Signed-off-by: Eric Woudstra <[email protected]>
---
 include/net/netfilter/nf_tables_ipv4.h | 21 +++++++++++++--------
 include/net/netfilter/nf_tables_ipv6.h | 21 +++++++++++++--------
 net/netfilter/nft_chain_filter.c       |  8 ++++----
 3 files changed, 30 insertions(+), 20 deletions(-)

diff --git a/include/net/netfilter/nf_tables_ipv4.h 
b/include/net/netfilter/nf_tables_ipv4.h
index fcf967286e37..bd354937134f 100644
--- a/include/net/netfilter/nf_tables_ipv4.h
+++ b/include/net/netfilter/nf_tables_ipv4.h
@@ -16,12 +16,12 @@ static inline void nft_set_pktinfo_ipv4(struct nft_pktinfo 
*pkt)
        pkt->fragoff = ntohs(ip->frag_off) & IP_OFFSET;
 }
 
-static inline int __nft_set_pktinfo_ipv4_validate(struct nft_pktinfo *pkt)
+static inline int __nft_set_pktinfo_ipv4_validate(struct nft_pktinfo *pkt, u32 
nhoff)
 {
        struct iphdr *iph, _iph;
        u32 len, thoff, skb_len;
 
-       iph = skb_header_pointer(pkt->skb, skb_network_offset(pkt->skb),
+       iph = skb_header_pointer(pkt->skb, skb_network_offset(pkt->skb) + nhoff,
                                 sizeof(*iph), &_iph);
        if (!iph)
                return -1;
@@ -31,7 +31,7 @@ static inline int __nft_set_pktinfo_ipv4_validate(struct 
nft_pktinfo *pkt)
 
        len = iph_totlen(pkt->skb, iph);
        thoff = iph->ihl * 4;
-       skb_len = pkt->skb->len - skb_network_offset(pkt->skb);
+       skb_len = pkt->skb->len - skb_network_offset(pkt->skb) - nhoff;
 
        if (skb_len < len)
                return -1;
@@ -40,17 +40,22 @@ static inline int __nft_set_pktinfo_ipv4_validate(struct 
nft_pktinfo *pkt)
        else if (thoff < sizeof(*iph))
                return -1;
 
-       pkt->flags = NFT_PKTINFO_L4PROTO;
-       pkt->tprot = iph->protocol;
-       pkt->thoff = skb_network_offset(pkt->skb) + thoff;
+       if (!nhoff) {
+               pkt->flags = NFT_PKTINFO_L4PROTO;
+               pkt->tprot = iph->protocol;
+       } else {
+               pkt->flags = 0;
+               pkt->tprot = 0;
+       }
+       pkt->thoff = skb_network_offset(pkt->skb) + nhoff + thoff;
        pkt->fragoff = ntohs(iph->frag_off) & IP_OFFSET;
 
        return 0;
 }
 
-static inline void nft_set_pktinfo_ipv4_validate(struct nft_pktinfo *pkt)
+static inline void nft_set_pktinfo_ipv4_validate(struct nft_pktinfo *pkt, u32 
nhoff)
 {
-       if (__nft_set_pktinfo_ipv4_validate(pkt) < 0)
+       if (__nft_set_pktinfo_ipv4_validate(pkt, nhoff) < 0)
                nft_set_pktinfo_unspec(pkt);
 }
 
diff --git a/include/net/netfilter/nf_tables_ipv6.h 
b/include/net/netfilter/nf_tables_ipv6.h
index a0633eeaec97..e7810c542856 100644
--- a/include/net/netfilter/nf_tables_ipv6.h
+++ b/include/net/netfilter/nf_tables_ipv6.h
@@ -24,17 +24,17 @@ static inline void nft_set_pktinfo_ipv6(struct nft_pktinfo 
*pkt)
        pkt->fragoff = frag_off;
 }
 
-static inline int __nft_set_pktinfo_ipv6_validate(struct nft_pktinfo *pkt)
+static inline int __nft_set_pktinfo_ipv6_validate(struct nft_pktinfo *pkt, u32 
nhoff)
 {
 #if IS_ENABLED(CONFIG_IPV6)
        unsigned int flags = IP6_FH_F_AUTH;
        struct ipv6hdr *ip6h, _ip6h;
-       unsigned int thoff = 0;
+       unsigned int thoff = nhoff;
        unsigned short frag_off;
        u32 pkt_len, skb_len;
        int protohdr;
 
-       ip6h = skb_header_pointer(pkt->skb, skb_network_offset(pkt->skb),
+       ip6h = skb_header_pointer(pkt->skb, skb_network_offset(pkt->skb) + 
nhoff,
                                  sizeof(*ip6h), &_ip6h);
        if (!ip6h)
                return -1;
@@ -43,7 +43,7 @@ static inline int __nft_set_pktinfo_ipv6_validate(struct 
nft_pktinfo *pkt)
                return -1;
 
        pkt_len = ntohs(ip6h->payload_len);
-       skb_len = pkt->skb->len - skb_network_offset(pkt->skb);
+       skb_len = pkt->skb->len - skb_network_offset(pkt->skb) - nhoff;
        if (pkt_len + sizeof(*ip6h) > skb_len)
                return -1;
 
@@ -51,8 +51,13 @@ static inline int __nft_set_pktinfo_ipv6_validate(struct 
nft_pktinfo *pkt)
        if (protohdr < 0 || thoff > U16_MAX)
                return -1;
 
-       pkt->flags = NFT_PKTINFO_L4PROTO;
-       pkt->tprot = protohdr;
+       if (!nhoff) {
+               pkt->flags = NFT_PKTINFO_L4PROTO;
+               pkt->tprot = protohdr;
+       } else {
+               pkt->flags = 0;
+               pkt->tprot = 0;
+       }
        pkt->thoff = thoff;
        pkt->fragoff = frag_off;
 
@@ -62,9 +67,9 @@ static inline int __nft_set_pktinfo_ipv6_validate(struct 
nft_pktinfo *pkt)
 #endif
 }
 
-static inline void nft_set_pktinfo_ipv6_validate(struct nft_pktinfo *pkt)
+static inline void nft_set_pktinfo_ipv6_validate(struct nft_pktinfo *pkt, u32 
nhoff)
 {
-       if (__nft_set_pktinfo_ipv6_validate(pkt) < 0)
+       if (__nft_set_pktinfo_ipv6_validate(pkt, nhoff) < 0)
                nft_set_pktinfo_unspec(pkt);
 }
 
diff --git a/net/netfilter/nft_chain_filter.c b/net/netfilter/nft_chain_filter.c
index b16185e9a6dd..d4d5eadaba9c 100644
--- a/net/netfilter/nft_chain_filter.c
+++ b/net/netfilter/nft_chain_filter.c
@@ -238,10 +238,10 @@ nft_do_chain_bridge(void *priv,
 
        switch (eth_hdr(skb)->h_proto) {
        case htons(ETH_P_IP):
-               nft_set_pktinfo_ipv4_validate(&pkt);
+               nft_set_pktinfo_ipv4_validate(&pkt, 0);
                break;
        case htons(ETH_P_IPV6):
-               nft_set_pktinfo_ipv6_validate(&pkt);
+               nft_set_pktinfo_ipv6_validate(&pkt, 0);
                break;
        default:
                nft_set_pktinfo_unspec(&pkt);
@@ -293,10 +293,10 @@ static unsigned int nft_do_chain_netdev(void *priv, 
struct sk_buff *skb,
 
        switch (skb->protocol) {
        case htons(ETH_P_IP):
-               nft_set_pktinfo_ipv4_validate(&pkt);
+               nft_set_pktinfo_ipv4_validate(&pkt, 0);
                break;
        case htons(ETH_P_IPV6):
-               nft_set_pktinfo_ipv6_validate(&pkt);
+               nft_set_pktinfo_ipv6_validate(&pkt, 0);
                break;
        default:
                nft_set_pktinfo_unspec(&pkt);
-- 
2.50.0


Reply via email to