1515#include <linux/netfilter.h>
1616#include <linux/netfilter/nf_tables.h>
1717#include <net/netfilter/nf_tables.h>
18- // FIXME:
19- #include <net/ipv6.h>
18+ #include <net/tcp.h>
2019
2120struct nft_exthdr {
2221 u8 type ;
2322 u8 offset ;
2423 u8 len ;
24+ u8 op ;
2525 enum nft_registers dreg :8 ;
2626 u8 flags ;
2727};
2828
29- static void nft_exthdr_eval (const struct nft_expr * expr ,
30- struct nft_regs * regs ,
31- const struct nft_pktinfo * pkt )
29+ static unsigned int optlen (const u8 * opt , unsigned int offset )
30+ {
31+ /* Beware zero-length options: make finite progress */
32+ if (opt [offset ] <= TCPOPT_NOP || opt [offset + 1 ] == 0 )
33+ return 1 ;
34+ else
35+ return opt [offset + 1 ];
36+ }
37+
38+ static void nft_exthdr_ipv6_eval (const struct nft_expr * expr ,
39+ struct nft_regs * regs ,
40+ const struct nft_pktinfo * pkt )
3241{
3342 struct nft_exthdr * priv = nft_expr_priv (expr );
3443 u32 * dest = & regs -> data [priv -> dreg ];
@@ -52,6 +61,53 @@ static void nft_exthdr_eval(const struct nft_expr *expr,
5261 regs -> verdict .code = NFT_BREAK ;
5362}
5463
64+ static void nft_exthdr_tcp_eval (const struct nft_expr * expr ,
65+ struct nft_regs * regs ,
66+ const struct nft_pktinfo * pkt )
67+ {
68+ u8 buff [sizeof (struct tcphdr ) + MAX_TCP_OPTION_SPACE ];
69+ struct nft_exthdr * priv = nft_expr_priv (expr );
70+ unsigned int i , optl , tcphdr_len , offset ;
71+ u32 * dest = & regs -> data [priv -> dreg ];
72+ struct tcphdr * tcph ;
73+ u8 * opt ;
74+
75+ if (!pkt -> tprot_set || pkt -> tprot != IPPROTO_TCP )
76+ goto err ;
77+
78+ tcph = skb_header_pointer (pkt -> skb , pkt -> xt .thoff , sizeof (* tcph ), buff );
79+ if (!tcph )
80+ goto err ;
81+
82+ tcphdr_len = __tcp_hdrlen (tcph );
83+ if (tcphdr_len < sizeof (* tcph ))
84+ goto err ;
85+
86+ tcph = skb_header_pointer (pkt -> skb , pkt -> xt .thoff , tcphdr_len , buff );
87+ if (!tcph )
88+ goto err ;
89+
90+ opt = (u8 * )tcph ;
91+ for (i = sizeof (* tcph ); i < tcphdr_len - 1 ; i += optl ) {
92+ optl = optlen (opt , i );
93+
94+ if (priv -> type != opt [i ])
95+ continue ;
96+
97+ if (i + optl > tcphdr_len || priv -> len + priv -> offset > optl )
98+ goto err ;
99+
100+ offset = i + priv -> offset ;
101+ dest [priv -> len / NFT_REG32_SIZE ] = 0 ;
102+ memcpy (dest , opt + offset , priv -> len );
103+
104+ return ;
105+ }
106+
107+ err :
108+ regs -> verdict .code = NFT_BREAK ;
109+ }
110+
55111static const struct nla_policy nft_exthdr_policy [NFTA_EXTHDR_MAX + 1 ] = {
56112 [NFTA_EXTHDR_DREG ] = { .type = NLA_U32 },
57113 [NFTA_EXTHDR_TYPE ] = { .type = NLA_U8 },
@@ -65,13 +121,13 @@ static int nft_exthdr_init(const struct nft_ctx *ctx,
65121 const struct nlattr * const tb [])
66122{
67123 struct nft_exthdr * priv = nft_expr_priv (expr );
68- u32 offset , len , flags = 0 ;
124+ u32 offset , len , flags = 0 , op = NFT_EXTHDR_OP_IPV6 ;
69125 int err ;
70126
71- if (tb [NFTA_EXTHDR_DREG ] == NULL ||
72- tb [NFTA_EXTHDR_TYPE ] == NULL ||
73- tb [NFTA_EXTHDR_OFFSET ] == NULL ||
74- tb [NFTA_EXTHDR_LEN ] == NULL )
127+ if (! tb [NFTA_EXTHDR_DREG ] ||
128+ ! tb [NFTA_EXTHDR_TYPE ] ||
129+ ! tb [NFTA_EXTHDR_OFFSET ] ||
130+ ! tb [NFTA_EXTHDR_LEN ])
75131 return - EINVAL ;
76132
77133 err = nft_parse_u32_check (tb [NFTA_EXTHDR_OFFSET ], U8_MAX , & offset );
@@ -91,11 +147,18 @@ static int nft_exthdr_init(const struct nft_ctx *ctx,
91147 return - EINVAL ;
92148 }
93149
150+ if (tb [NFTA_EXTHDR_OP ]) {
151+ err = nft_parse_u32_check (tb [NFTA_EXTHDR_OP ], U8_MAX , & op );
152+ if (err < 0 )
153+ return err ;
154+ }
155+
94156 priv -> type = nla_get_u8 (tb [NFTA_EXTHDR_TYPE ]);
95157 priv -> offset = offset ;
96158 priv -> len = len ;
97159 priv -> dreg = nft_parse_register (tb [NFTA_EXTHDR_DREG ]);
98160 priv -> flags = flags ;
161+ priv -> op = op ;
99162
100163 return nft_validate_register_store (ctx , priv -> dreg , NULL ,
101164 NFT_DATA_VALUE , priv -> len );
@@ -115,24 +178,54 @@ static int nft_exthdr_dump(struct sk_buff *skb, const struct nft_expr *expr)
115178 goto nla_put_failure ;
116179 if (nla_put_be32 (skb , NFTA_EXTHDR_FLAGS , htonl (priv -> flags )))
117180 goto nla_put_failure ;
181+ if (nla_put_be32 (skb , NFTA_EXTHDR_OP , htonl (priv -> op )))
182+ goto nla_put_failure ;
118183 return 0 ;
119184
120185nla_put_failure :
121186 return -1 ;
122187}
123188
124189static struct nft_expr_type nft_exthdr_type ;
125- static const struct nft_expr_ops nft_exthdr_ops = {
190+ static const struct nft_expr_ops nft_exthdr_ipv6_ops = {
191+ .type = & nft_exthdr_type ,
192+ .size = NFT_EXPR_SIZE (sizeof (struct nft_exthdr )),
193+ .eval = nft_exthdr_ipv6_eval ,
194+ .init = nft_exthdr_init ,
195+ .dump = nft_exthdr_dump ,
196+ };
197+
198+ static const struct nft_expr_ops nft_exthdr_tcp_ops = {
126199 .type = & nft_exthdr_type ,
127200 .size = NFT_EXPR_SIZE (sizeof (struct nft_exthdr )),
128- .eval = nft_exthdr_eval ,
201+ .eval = nft_exthdr_tcp_eval ,
129202 .init = nft_exthdr_init ,
130203 .dump = nft_exthdr_dump ,
131204};
132205
206+ static const struct nft_expr_ops *
207+ nft_exthdr_select_ops (const struct nft_ctx * ctx ,
208+ const struct nlattr * const tb [])
209+ {
210+ u32 op ;
211+
212+ if (!tb [NFTA_EXTHDR_OP ])
213+ return & nft_exthdr_ipv6_ops ;
214+
215+ op = ntohl (nla_get_u32 (tb [NFTA_EXTHDR_OP ]));
216+ switch (op ) {
217+ case NFT_EXTHDR_OP_TCPOPT :
218+ return & nft_exthdr_tcp_ops ;
219+ case NFT_EXTHDR_OP_IPV6 :
220+ return & nft_exthdr_ipv6_ops ;
221+ }
222+
223+ return ERR_PTR (- EOPNOTSUPP );
224+ }
225+
133226static struct nft_expr_type nft_exthdr_type __read_mostly = {
134227 .name = "exthdr" ,
135- .ops = & nft_exthdr_ops ,
228+ .select_ops = & nft_exthdr_select_ops ,
136229 .policy = nft_exthdr_policy ,
137230 .maxattr = NFTA_EXTHDR_MAX ,
138231 .owner = THIS_MODULE ,
0 commit comments