1 // SPDX-License-Identifier: GPL-2.0-only
5 #include <net/netfilter/nf_tables.h>
6 #include <linux/netfilter/nfnetlink_osf.h>
14 static const struct nla_policy nft_osf_policy
[NFTA_OSF_MAX
+ 1] = {
15 [NFTA_OSF_DREG
] = { .type
= NLA_U32
},
16 [NFTA_OSF_TTL
] = { .type
= NLA_U8
},
17 [NFTA_OSF_FLAGS
] = { .type
= NLA_U32
},
20 static void nft_osf_eval(const struct nft_expr
*expr
, struct nft_regs
*regs
,
21 const struct nft_pktinfo
*pkt
)
23 struct nft_osf
*priv
= nft_expr_priv(expr
);
24 u32
*dest
= ®s
->data
[priv
->dreg
];
25 struct sk_buff
*skb
= pkt
->skb
;
26 char os_match
[NFT_OSF_MAXGENRELEN
];
27 const struct tcphdr
*tcp
;
28 struct nf_osf_data data
;
31 if (pkt
->tprot
!= IPPROTO_TCP
) {
32 regs
->verdict
.code
= NFT_BREAK
;
36 tcp
= skb_header_pointer(skb
, ip_hdrlen(skb
),
37 sizeof(struct tcphdr
), &_tcph
);
39 regs
->verdict
.code
= NFT_BREAK
;
43 regs
->verdict
.code
= NFT_BREAK
;
47 if (!nf_osf_find(skb
, nf_osf_fingers
, priv
->ttl
, &data
)) {
48 strscpy_pad((char *)dest
, "unknown", NFT_OSF_MAXGENRELEN
);
50 if (priv
->flags
& NFT_OSF_F_VERSION
)
51 snprintf(os_match
, NFT_OSF_MAXGENRELEN
, "%s:%s",
52 data
.genre
, data
.version
);
54 strscpy(os_match
, data
.genre
, NFT_OSF_MAXGENRELEN
);
56 strscpy_pad((char *)dest
, os_match
, NFT_OSF_MAXGENRELEN
);
60 static int nft_osf_init(const struct nft_ctx
*ctx
,
61 const struct nft_expr
*expr
,
62 const struct nlattr
* const tb
[])
64 struct nft_osf
*priv
= nft_expr_priv(expr
);
69 if (!tb
[NFTA_OSF_DREG
])
72 if (tb
[NFTA_OSF_TTL
]) {
73 ttl
= nla_get_u8(tb
[NFTA_OSF_TTL
]);
79 if (tb
[NFTA_OSF_FLAGS
]) {
80 flags
= ntohl(nla_get_be32(tb
[NFTA_OSF_FLAGS
]));
81 if (flags
!= NFT_OSF_F_VERSION
)
86 err
= nft_parse_register_store(ctx
, tb
[NFTA_OSF_DREG
], &priv
->dreg
,
95 static int nft_osf_dump(struct sk_buff
*skb
,
96 const struct nft_expr
*expr
, bool reset
)
98 const struct nft_osf
*priv
= nft_expr_priv(expr
);
100 if (nla_put_u8(skb
, NFTA_OSF_TTL
, priv
->ttl
))
101 goto nla_put_failure
;
103 if (nla_put_u32(skb
, NFTA_OSF_FLAGS
, ntohl((__force __be32
)priv
->flags
)))
104 goto nla_put_failure
;
106 if (nft_dump_register(skb
, NFTA_OSF_DREG
, priv
->dreg
))
107 goto nla_put_failure
;
115 static int nft_osf_validate(const struct nft_ctx
*ctx
,
116 const struct nft_expr
*expr
,
117 const struct nft_data
**data
)
121 switch (ctx
->family
) {
125 hooks
= (1 << NF_INET_LOCAL_IN
) |
126 (1 << NF_INET_PRE_ROUTING
) |
127 (1 << NF_INET_FORWARD
);
133 return nft_chain_validate_hooks(ctx
->chain
, hooks
);
136 static bool nft_osf_reduce(struct nft_regs_track
*track
,
137 const struct nft_expr
*expr
)
139 struct nft_osf
*priv
= nft_expr_priv(expr
);
142 if (!nft_reg_track_cmp(track
, expr
, priv
->dreg
)) {
143 nft_reg_track_update(track
, expr
, priv
->dreg
, NFT_OSF_MAXGENRELEN
);
147 osf
= nft_expr_priv(track
->regs
[priv
->dreg
].selector
);
148 if (priv
->flags
!= osf
->flags
||
149 priv
->ttl
!= osf
->ttl
) {
150 nft_reg_track_update(track
, expr
, priv
->dreg
, NFT_OSF_MAXGENRELEN
);
154 if (!track
->regs
[priv
->dreg
].bitwise
)
160 static struct nft_expr_type nft_osf_type
;
161 static const struct nft_expr_ops nft_osf_op
= {
162 .eval
= nft_osf_eval
,
163 .size
= NFT_EXPR_SIZE(sizeof(struct nft_osf
)),
164 .init
= nft_osf_init
,
165 .dump
= nft_osf_dump
,
166 .type
= &nft_osf_type
,
167 .validate
= nft_osf_validate
,
168 .reduce
= nft_osf_reduce
,
171 static struct nft_expr_type nft_osf_type __read_mostly
= {
174 .owner
= THIS_MODULE
,
175 .policy
= nft_osf_policy
,
176 .maxattr
= NFTA_OSF_MAX
,
179 static int __init
nft_osf_module_init(void)
181 return nft_register_expr(&nft_osf_type
);
184 static void __exit
nft_osf_module_exit(void)
186 return nft_unregister_expr(&nft_osf_type
);
189 module_init(nft_osf_module_init
);
190 module_exit(nft_osf_module_exit
);
192 MODULE_LICENSE("GPL");
193 MODULE_AUTHOR("Fernando Fernandez <ffmancera@riseup.net>");
194 MODULE_ALIAS_NFT_EXPR("osf");
195 MODULE_DESCRIPTION("nftables passive OS fingerprint support");