Commit | Line | Data |
---|---|---|
09c434b8 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
b96af92d FFM |
2 | #include <net/ip.h> |
3 | #include <net/tcp.h> | |
4 | ||
5 | #include <net/netfilter/nf_tables.h> | |
ddba40be | 6 | #include <linux/netfilter/nfnetlink_osf.h> |
b96af92d | 7 | |
b96af92d | 8 | struct nft_osf { |
345023b0 | 9 | u8 dreg; |
a218dc82 | 10 | u8 ttl; |
22c7652c | 11 | u32 flags; |
b96af92d FFM |
12 | }; |
13 | ||
14 | static const struct nla_policy nft_osf_policy[NFTA_OSF_MAX + 1] = { | |
15 | [NFTA_OSF_DREG] = { .type = NLA_U32 }, | |
a218dc82 | 16 | [NFTA_OSF_TTL] = { .type = NLA_U8 }, |
22c7652c | 17 | [NFTA_OSF_FLAGS] = { .type = NLA_U32 }, |
b96af92d FFM |
18 | }; |
19 | ||
20 | static void nft_osf_eval(const struct nft_expr *expr, struct nft_regs *regs, | |
21 | const struct nft_pktinfo *pkt) | |
22 | { | |
23 | struct nft_osf *priv = nft_expr_priv(expr); | |
24 | u32 *dest = ®s->data[priv->dreg]; | |
25 | struct sk_buff *skb = pkt->skb; | |
22c7652c | 26 | char os_match[NFT_OSF_MAXGENRELEN + 1]; |
b96af92d | 27 | const struct tcphdr *tcp; |
22c7652c | 28 | struct nf_osf_data data; |
b96af92d | 29 | struct tcphdr _tcph; |
b96af92d | 30 | |
8f518d43 PNA |
31 | if (pkt->tprot != IPPROTO_TCP) { |
32 | regs->verdict.code = NFT_BREAK; | |
33 | return; | |
34 | } | |
35 | ||
b96af92d FFM |
36 | tcp = skb_header_pointer(skb, ip_hdrlen(skb), |
37 | sizeof(struct tcphdr), &_tcph); | |
38 | if (!tcp) { | |
39 | regs->verdict.code = NFT_BREAK; | |
40 | return; | |
41 | } | |
42 | if (!tcp->syn) { | |
43 | regs->verdict.code = NFT_BREAK; | |
44 | return; | |
45 | } | |
46 | ||
22c7652c | 47 | if (!nf_osf_find(skb, nf_osf_fingers, priv->ttl, &data)) { |
35a8a3bd | 48 | strncpy((char *)dest, "unknown", NFT_OSF_MAXGENRELEN); |
22c7652c FFM |
49 | } else { |
50 | if (priv->flags & NFT_OSF_F_VERSION) | |
51 | snprintf(os_match, NFT_OSF_MAXGENRELEN, "%s:%s", | |
52 | data.genre, data.version); | |
53 | else | |
54 | strlcpy(os_match, data.genre, NFT_OSF_MAXGENRELEN); | |
55 | ||
56 | strncpy((char *)dest, os_match, NFT_OSF_MAXGENRELEN); | |
57 | } | |
b96af92d FFM |
58 | } |
59 | ||
60 | static int nft_osf_init(const struct nft_ctx *ctx, | |
61 | const struct nft_expr *expr, | |
62 | const struct nlattr * const tb[]) | |
63 | { | |
64 | struct nft_osf *priv = nft_expr_priv(expr); | |
22c7652c | 65 | u32 flags; |
b96af92d | 66 | int err; |
a218dc82 FFM |
67 | u8 ttl; |
68 | ||
7eaecf79 FW |
69 | if (!tb[NFTA_OSF_DREG]) |
70 | return -EINVAL; | |
71 | ||
5e91c9d9 | 72 | if (tb[NFTA_OSF_TTL]) { |
a218dc82 FFM |
73 | ttl = nla_get_u8(tb[NFTA_OSF_TTL]); |
74 | if (ttl > 2) | |
75 | return -EINVAL; | |
76 | priv->ttl = ttl; | |
77 | } | |
b96af92d | 78 | |
22c7652c FFM |
79 | if (tb[NFTA_OSF_FLAGS]) { |
80 | flags = ntohl(nla_get_be32(tb[NFTA_OSF_FLAGS])); | |
81 | if (flags != NFT_OSF_F_VERSION) | |
82 | return -EINVAL; | |
83 | priv->flags = flags; | |
84 | } | |
85 | ||
345023b0 PNA |
86 | err = nft_parse_register_store(ctx, tb[NFTA_OSF_DREG], &priv->dreg, |
87 | NULL, NFT_DATA_VALUE, | |
88 | NFT_OSF_MAXGENRELEN); | |
b96af92d FFM |
89 | if (err < 0) |
90 | return err; | |
91 | ||
92 | return 0; | |
93 | } | |
94 | ||
95 | static int nft_osf_dump(struct sk_buff *skb, const struct nft_expr *expr) | |
96 | { | |
97 | const struct nft_osf *priv = nft_expr_priv(expr); | |
98 | ||
a218dc82 FFM |
99 | if (nla_put_u8(skb, NFTA_OSF_TTL, priv->ttl)) |
100 | goto nla_put_failure; | |
101 | ||
22c7652c FFM |
102 | if (nla_put_be32(skb, NFTA_OSF_FLAGS, ntohl(priv->flags))) |
103 | goto nla_put_failure; | |
104 | ||
b96af92d FFM |
105 | if (nft_dump_register(skb, NFTA_OSF_DREG, priv->dreg)) |
106 | goto nla_put_failure; | |
107 | ||
108 | return 0; | |
109 | ||
110 | nla_put_failure: | |
111 | return -1; | |
112 | } | |
113 | ||
4a3e71b7 FFM |
114 | static int nft_osf_validate(const struct nft_ctx *ctx, |
115 | const struct nft_expr *expr, | |
116 | const struct nft_data **data) | |
117 | { | |
118 | return nft_chain_validate_hooks(ctx->chain, (1 << NF_INET_LOCAL_IN) | | |
119 | (1 << NF_INET_PRE_ROUTING) | | |
120 | (1 << NF_INET_FORWARD)); | |
121 | } | |
122 | ||
ffe6488e PNA |
123 | static bool nft_osf_reduce(struct nft_regs_track *track, |
124 | const struct nft_expr *expr) | |
125 | { | |
126 | struct nft_osf *priv = nft_expr_priv(expr); | |
127 | struct nft_osf *osf; | |
128 | ||
129 | if (!nft_reg_track_cmp(track, expr, priv->dreg)) { | |
130 | nft_reg_track_update(track, expr, priv->dreg, NFT_OSF_MAXGENRELEN); | |
131 | return false; | |
132 | } | |
133 | ||
134 | osf = nft_expr_priv(track->regs[priv->dreg].selector); | |
135 | if (priv->flags != osf->flags || | |
136 | priv->ttl != osf->ttl) { | |
137 | nft_reg_track_update(track, expr, priv->dreg, NFT_OSF_MAXGENRELEN); | |
138 | return false; | |
139 | } | |
140 | ||
141 | if (!track->regs[priv->dreg].bitwise) | |
142 | return true; | |
143 | ||
144 | return false; | |
145 | } | |
146 | ||
b96af92d FFM |
147 | static struct nft_expr_type nft_osf_type; |
148 | static const struct nft_expr_ops nft_osf_op = { | |
149 | .eval = nft_osf_eval, | |
150 | .size = NFT_EXPR_SIZE(sizeof(struct nft_osf)), | |
151 | .init = nft_osf_init, | |
152 | .dump = nft_osf_dump, | |
153 | .type = &nft_osf_type, | |
4a3e71b7 | 154 | .validate = nft_osf_validate, |
ffe6488e | 155 | .reduce = nft_osf_reduce, |
b96af92d FFM |
156 | }; |
157 | ||
158 | static struct nft_expr_type nft_osf_type __read_mostly = { | |
159 | .ops = &nft_osf_op, | |
160 | .name = "osf", | |
161 | .owner = THIS_MODULE, | |
162 | .policy = nft_osf_policy, | |
163 | .maxattr = NFTA_OSF_MAX, | |
164 | }; | |
165 | ||
166 | static int __init nft_osf_module_init(void) | |
167 | { | |
168 | return nft_register_expr(&nft_osf_type); | |
169 | } | |
170 | ||
171 | static void __exit nft_osf_module_exit(void) | |
172 | { | |
173 | return nft_unregister_expr(&nft_osf_type); | |
174 | } | |
175 | ||
176 | module_init(nft_osf_module_init); | |
177 | module_exit(nft_osf_module_exit); | |
178 | ||
179 | MODULE_LICENSE("GPL"); | |
180 | MODULE_AUTHOR("Fernando Fernandez <ffmancera@riseup.net>"); | |
181 | MODULE_ALIAS_NFT_EXPR("osf"); | |
4cacc395 | 182 | MODULE_DESCRIPTION("nftables passive OS fingerprint support"); |