Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
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 = &regs->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");