1 /* SPDX-License-Identifier: GPL-2.0 */
2 #ifndef _NF_TABLES_IPV6_H_
3 #define _NF_TABLES_IPV6_H_
4
5 #include <linux/netfilter_ipv6/ip6_tables.h>
6 #include <net/ipv6.h>
7 #include <net/netfilter/nf_tables.h>
8
nft_set_pktinfo_ipv6(struct nft_pktinfo * pkt)9 static inline void nft_set_pktinfo_ipv6(struct nft_pktinfo *pkt)
10 {
11 unsigned int flags = IP6_FH_F_AUTH;
12 int protohdr, thoff = 0;
13 unsigned short frag_off;
14
15 protohdr = ipv6_find_hdr(pkt->skb, &thoff, -1, &frag_off, &flags);
16 if (protohdr < 0 || thoff > U16_MAX) {
17 nft_set_pktinfo_unspec(pkt);
18 return;
19 }
20
21 pkt->flags = NFT_PKTINFO_L4PROTO;
22 pkt->tprot = protohdr;
23 pkt->thoff = thoff;
24 pkt->fragoff = frag_off;
25 }
26
__nft_set_pktinfo_ipv6_validate(struct nft_pktinfo * pkt)27 static inline int __nft_set_pktinfo_ipv6_validate(struct nft_pktinfo *pkt)
28 {
29 #if IS_ENABLED(CONFIG_IPV6)
30 unsigned int flags = IP6_FH_F_AUTH;
31 struct ipv6hdr *ip6h, _ip6h;
32 unsigned int thoff = 0;
33 unsigned short frag_off;
34 u32 pkt_len, skb_len;
35 int protohdr;
36
37 ip6h = skb_header_pointer(pkt->skb, skb_network_offset(pkt->skb),
38 sizeof(*ip6h), &_ip6h);
39 if (!ip6h)
40 return -1;
41
42 if (ip6h->version != 6)
43 return -1;
44
45 pkt_len = ntohs(ip6h->payload_len);
46 skb_len = pkt->skb->len - skb_network_offset(pkt->skb);
47 if (pkt_len + sizeof(*ip6h) > skb_len)
48 return -1;
49
50 protohdr = ipv6_find_hdr(pkt->skb, &thoff, -1, &frag_off, &flags);
51 if (protohdr < 0 || thoff > U16_MAX)
52 return -1;
53
54 pkt->flags = NFT_PKTINFO_L4PROTO;
55 pkt->tprot = protohdr;
56 pkt->thoff = thoff;
57 pkt->fragoff = frag_off;
58
59 return 0;
60 #else
61 return -1;
62 #endif
63 }
64
nft_set_pktinfo_ipv6_validate(struct nft_pktinfo * pkt)65 static inline void nft_set_pktinfo_ipv6_validate(struct nft_pktinfo *pkt)
66 {
67 if (__nft_set_pktinfo_ipv6_validate(pkt) < 0)
68 nft_set_pktinfo_unspec(pkt);
69 }
70
nft_set_pktinfo_ipv6_ingress(struct nft_pktinfo * pkt)71 static inline int nft_set_pktinfo_ipv6_ingress(struct nft_pktinfo *pkt)
72 {
73 #if IS_ENABLED(CONFIG_IPV6)
74 unsigned int flags = IP6_FH_F_AUTH;
75 unsigned short frag_off;
76 unsigned int thoff = 0;
77 struct inet6_dev *idev;
78 struct ipv6hdr *ip6h;
79 int protohdr;
80 u32 pkt_len;
81
82 if (!pskb_may_pull(pkt->skb, sizeof(*ip6h)))
83 return -1;
84
85 ip6h = ipv6_hdr(pkt->skb);
86 if (ip6h->version != 6)
87 goto inhdr_error;
88
89 pkt_len = ntohs(ip6h->payload_len);
90 if (pkt_len + sizeof(*ip6h) > pkt->skb->len) {
91 idev = __in6_dev_get(nft_in(pkt));
92 __IP6_INC_STATS(nft_net(pkt), idev, IPSTATS_MIB_INTRUNCATEDPKTS);
93 return -1;
94 }
95
96 protohdr = ipv6_find_hdr(pkt->skb, &thoff, -1, &frag_off, &flags);
97 if (protohdr < 0 || thoff > U16_MAX)
98 goto inhdr_error;
99
100 pkt->flags = NFT_PKTINFO_L4PROTO;
101 pkt->tprot = protohdr;
102 pkt->thoff = thoff;
103 pkt->fragoff = frag_off;
104
105 return 0;
106
107 inhdr_error:
108 idev = __in6_dev_get(nft_in(pkt));
109 __IP6_INC_STATS(nft_net(pkt), idev, IPSTATS_MIB_INHDRERRORS);
110 return -1;
111 #else
112 return -1;
113 #endif
114 }
115
116 #endif
117