0001
0002 #include <net/ip.h>
0003 #include <net/tcp.h>
0004
0005 #include <net/netfilter/nf_tables.h>
0006 #include <linux/netfilter/nfnetlink_osf.h>
0007
0008 struct nft_osf {
0009 u8 dreg;
0010 u8 ttl;
0011 u32 flags;
0012 };
0013
0014 static const struct nla_policy nft_osf_policy[NFTA_OSF_MAX + 1] = {
0015 [NFTA_OSF_DREG] = { .type = NLA_U32 },
0016 [NFTA_OSF_TTL] = { .type = NLA_U8 },
0017 [NFTA_OSF_FLAGS] = { .type = NLA_U32 },
0018 };
0019
0020 static void nft_osf_eval(const struct nft_expr *expr, struct nft_regs *regs,
0021 const struct nft_pktinfo *pkt)
0022 {
0023 struct nft_osf *priv = nft_expr_priv(expr);
0024 u32 *dest = ®s->data[priv->dreg];
0025 struct sk_buff *skb = pkt->skb;
0026 char os_match[NFT_OSF_MAXGENRELEN + 1];
0027 const struct tcphdr *tcp;
0028 struct nf_osf_data data;
0029 struct tcphdr _tcph;
0030
0031 if (pkt->tprot != IPPROTO_TCP) {
0032 regs->verdict.code = NFT_BREAK;
0033 return;
0034 }
0035
0036 tcp = skb_header_pointer(skb, ip_hdrlen(skb),
0037 sizeof(struct tcphdr), &_tcph);
0038 if (!tcp) {
0039 regs->verdict.code = NFT_BREAK;
0040 return;
0041 }
0042 if (!tcp->syn) {
0043 regs->verdict.code = NFT_BREAK;
0044 return;
0045 }
0046
0047 if (!nf_osf_find(skb, nf_osf_fingers, priv->ttl, &data)) {
0048 strncpy((char *)dest, "unknown", NFT_OSF_MAXGENRELEN);
0049 } else {
0050 if (priv->flags & NFT_OSF_F_VERSION)
0051 snprintf(os_match, NFT_OSF_MAXGENRELEN, "%s:%s",
0052 data.genre, data.version);
0053 else
0054 strlcpy(os_match, data.genre, NFT_OSF_MAXGENRELEN);
0055
0056 strncpy((char *)dest, os_match, NFT_OSF_MAXGENRELEN);
0057 }
0058 }
0059
0060 static int nft_osf_init(const struct nft_ctx *ctx,
0061 const struct nft_expr *expr,
0062 const struct nlattr * const tb[])
0063 {
0064 struct nft_osf *priv = nft_expr_priv(expr);
0065 u32 flags;
0066 int err;
0067 u8 ttl;
0068
0069 if (!tb[NFTA_OSF_DREG])
0070 return -EINVAL;
0071
0072 if (tb[NFTA_OSF_TTL]) {
0073 ttl = nla_get_u8(tb[NFTA_OSF_TTL]);
0074 if (ttl > 2)
0075 return -EINVAL;
0076 priv->ttl = ttl;
0077 }
0078
0079 if (tb[NFTA_OSF_FLAGS]) {
0080 flags = ntohl(nla_get_be32(tb[NFTA_OSF_FLAGS]));
0081 if (flags != NFT_OSF_F_VERSION)
0082 return -EINVAL;
0083 priv->flags = flags;
0084 }
0085
0086 err = nft_parse_register_store(ctx, tb[NFTA_OSF_DREG], &priv->dreg,
0087 NULL, NFT_DATA_VALUE,
0088 NFT_OSF_MAXGENRELEN);
0089 if (err < 0)
0090 return err;
0091
0092 return 0;
0093 }
0094
0095 static int nft_osf_dump(struct sk_buff *skb, const struct nft_expr *expr)
0096 {
0097 const struct nft_osf *priv = nft_expr_priv(expr);
0098
0099 if (nla_put_u8(skb, NFTA_OSF_TTL, priv->ttl))
0100 goto nla_put_failure;
0101
0102 if (nla_put_u32(skb, NFTA_OSF_FLAGS, ntohl((__force __be32)priv->flags)))
0103 goto nla_put_failure;
0104
0105 if (nft_dump_register(skb, NFTA_OSF_DREG, priv->dreg))
0106 goto nla_put_failure;
0107
0108 return 0;
0109
0110 nla_put_failure:
0111 return -1;
0112 }
0113
0114 static int nft_osf_validate(const struct nft_ctx *ctx,
0115 const struct nft_expr *expr,
0116 const struct nft_data **data)
0117 {
0118 unsigned int hooks;
0119
0120 switch (ctx->family) {
0121 case NFPROTO_IPV4:
0122 case NFPROTO_IPV6:
0123 case NFPROTO_INET:
0124 hooks = (1 << NF_INET_LOCAL_IN) |
0125 (1 << NF_INET_PRE_ROUTING) |
0126 (1 << NF_INET_FORWARD);
0127 break;
0128 default:
0129 return -EOPNOTSUPP;
0130 }
0131
0132 return nft_chain_validate_hooks(ctx->chain, hooks);
0133 }
0134
0135 static bool nft_osf_reduce(struct nft_regs_track *track,
0136 const struct nft_expr *expr)
0137 {
0138 struct nft_osf *priv = nft_expr_priv(expr);
0139 struct nft_osf *osf;
0140
0141 if (!nft_reg_track_cmp(track, expr, priv->dreg)) {
0142 nft_reg_track_update(track, expr, priv->dreg, NFT_OSF_MAXGENRELEN);
0143 return false;
0144 }
0145
0146 osf = nft_expr_priv(track->regs[priv->dreg].selector);
0147 if (priv->flags != osf->flags ||
0148 priv->ttl != osf->ttl) {
0149 nft_reg_track_update(track, expr, priv->dreg, NFT_OSF_MAXGENRELEN);
0150 return false;
0151 }
0152
0153 if (!track->regs[priv->dreg].bitwise)
0154 return true;
0155
0156 return false;
0157 }
0158
0159 static struct nft_expr_type nft_osf_type;
0160 static const struct nft_expr_ops nft_osf_op = {
0161 .eval = nft_osf_eval,
0162 .size = NFT_EXPR_SIZE(sizeof(struct nft_osf)),
0163 .init = nft_osf_init,
0164 .dump = nft_osf_dump,
0165 .type = &nft_osf_type,
0166 .validate = nft_osf_validate,
0167 .reduce = nft_osf_reduce,
0168 };
0169
0170 static struct nft_expr_type nft_osf_type __read_mostly = {
0171 .ops = &nft_osf_op,
0172 .name = "osf",
0173 .owner = THIS_MODULE,
0174 .policy = nft_osf_policy,
0175 .maxattr = NFTA_OSF_MAX,
0176 };
0177
0178 static int __init nft_osf_module_init(void)
0179 {
0180 return nft_register_expr(&nft_osf_type);
0181 }
0182
0183 static void __exit nft_osf_module_exit(void)
0184 {
0185 return nft_unregister_expr(&nft_osf_type);
0186 }
0187
0188 module_init(nft_osf_module_init);
0189 module_exit(nft_osf_module_exit);
0190
0191 MODULE_LICENSE("GPL");
0192 MODULE_AUTHOR("Fernando Fernandez <ffmancera@riseup.net>");
0193 MODULE_ALIAS_NFT_EXPR("osf");
0194 MODULE_DESCRIPTION("nftables passive OS fingerprint support");