Back to home page

OSCL-LXR

 
 

    


0001 /* SPDX-License-Identifier: GPL-2.0 */
0002 #include <linux/module.h>
0003 #include <linux/netfilter/nf_tables.h>
0004 #include <net/netfilter/nf_tables.h>
0005 #include <net/netfilter/nf_tables_core.h>
0006 #include <net/netfilter/nf_tproxy.h>
0007 #include <net/inet_sock.h>
0008 #include <net/tcp.h>
0009 #include <linux/if_ether.h>
0010 #include <net/netfilter/ipv4/nf_defrag_ipv4.h>
0011 #if IS_ENABLED(CONFIG_NF_TABLES_IPV6)
0012 #include <net/netfilter/ipv6/nf_defrag_ipv6.h>
0013 #endif
0014 
0015 struct nft_tproxy {
0016     u8  sreg_addr;
0017     u8  sreg_port;
0018     u8  family;
0019 };
0020 
0021 static void nft_tproxy_eval_v4(const struct nft_expr *expr,
0022                    struct nft_regs *regs,
0023                    const struct nft_pktinfo *pkt)
0024 {
0025     const struct nft_tproxy *priv = nft_expr_priv(expr);
0026     struct sk_buff *skb = pkt->skb;
0027     const struct iphdr *iph = ip_hdr(skb);
0028     struct udphdr _hdr, *hp;
0029     __be32 taddr = 0;
0030     __be16 tport = 0;
0031     struct sock *sk;
0032 
0033     if (pkt->tprot != IPPROTO_TCP &&
0034         pkt->tprot != IPPROTO_UDP) {
0035         regs->verdict.code = NFT_BREAK;
0036         return;
0037     }
0038 
0039     hp = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(_hdr), &_hdr);
0040     if (!hp) {
0041         regs->verdict.code = NFT_BREAK;
0042         return;
0043     }
0044 
0045     /* check if there's an ongoing connection on the packet addresses, this
0046      * happens if the redirect already happened and the current packet
0047      * belongs to an already established connection
0048      */
0049     sk = nf_tproxy_get_sock_v4(nft_net(pkt), skb, iph->protocol,
0050                    iph->saddr, iph->daddr,
0051                    hp->source, hp->dest,
0052                    skb->dev, NF_TPROXY_LOOKUP_ESTABLISHED);
0053 
0054     if (priv->sreg_addr)
0055         taddr = nft_reg_load_be32(&regs->data[priv->sreg_addr]);
0056     taddr = nf_tproxy_laddr4(skb, taddr, iph->daddr);
0057 
0058     if (priv->sreg_port)
0059         tport = nft_reg_load_be16(&regs->data[priv->sreg_port]);
0060     if (!tport)
0061         tport = hp->dest;
0062 
0063     /* UDP has no TCP_TIME_WAIT state, so we never enter here */
0064     if (sk && sk->sk_state == TCP_TIME_WAIT) {
0065         /* reopening a TIME_WAIT connection needs special handling */
0066         sk = nf_tproxy_handle_time_wait4(nft_net(pkt), skb, taddr, tport, sk);
0067     } else if (!sk) {
0068         /* no, there's no established connection, check if
0069          * there's a listener on the redirected addr/port
0070          */
0071         sk = nf_tproxy_get_sock_v4(nft_net(pkt), skb, iph->protocol,
0072                        iph->saddr, taddr,
0073                        hp->source, tport,
0074                        skb->dev, NF_TPROXY_LOOKUP_LISTENER);
0075     }
0076 
0077     if (sk && nf_tproxy_sk_is_transparent(sk))
0078         nf_tproxy_assign_sock(skb, sk);
0079     else
0080         regs->verdict.code = NFT_BREAK;
0081 }
0082 
0083 #if IS_ENABLED(CONFIG_NF_TABLES_IPV6)
0084 static void nft_tproxy_eval_v6(const struct nft_expr *expr,
0085                    struct nft_regs *regs,
0086                    const struct nft_pktinfo *pkt)
0087 {
0088     const struct nft_tproxy *priv = nft_expr_priv(expr);
0089     struct sk_buff *skb = pkt->skb;
0090     const struct ipv6hdr *iph = ipv6_hdr(skb);
0091     int thoff = nft_thoff(pkt);
0092     struct udphdr _hdr, *hp;
0093     struct in6_addr taddr;
0094     __be16 tport = 0;
0095     struct sock *sk;
0096     int l4proto;
0097 
0098     memset(&taddr, 0, sizeof(taddr));
0099 
0100     if (pkt->tprot != IPPROTO_TCP &&
0101         pkt->tprot != IPPROTO_UDP) {
0102         regs->verdict.code = NFT_BREAK;
0103         return;
0104     }
0105     l4proto = pkt->tprot;
0106 
0107     hp = skb_header_pointer(skb, thoff, sizeof(_hdr), &_hdr);
0108     if (hp == NULL) {
0109         regs->verdict.code = NFT_BREAK;
0110         return;
0111     }
0112 
0113     /* check if there's an ongoing connection on the packet addresses, this
0114      * happens if the redirect already happened and the current packet
0115      * belongs to an already established connection
0116      */
0117     sk = nf_tproxy_get_sock_v6(nft_net(pkt), skb, thoff, l4proto,
0118                    &iph->saddr, &iph->daddr,
0119                    hp->source, hp->dest,
0120                    nft_in(pkt), NF_TPROXY_LOOKUP_ESTABLISHED);
0121 
0122     if (priv->sreg_addr)
0123         memcpy(&taddr, &regs->data[priv->sreg_addr], sizeof(taddr));
0124     taddr = *nf_tproxy_laddr6(skb, &taddr, &iph->daddr);
0125 
0126     if (priv->sreg_port)
0127         tport = nft_reg_load_be16(&regs->data[priv->sreg_port]);
0128     if (!tport)
0129         tport = hp->dest;
0130 
0131     /* UDP has no TCP_TIME_WAIT state, so we never enter here */
0132     if (sk && sk->sk_state == TCP_TIME_WAIT) {
0133         /* reopening a TIME_WAIT connection needs special handling */
0134         sk = nf_tproxy_handle_time_wait6(skb, l4proto, thoff,
0135                          nft_net(pkt),
0136                          &taddr,
0137                          tport,
0138                          sk);
0139     } else if (!sk) {
0140         /* no there's no established connection, check if
0141          * there's a listener on the redirected addr/port
0142          */
0143         sk = nf_tproxy_get_sock_v6(nft_net(pkt), skb, thoff,
0144                        l4proto, &iph->saddr, &taddr,
0145                        hp->source, tport,
0146                        nft_in(pkt), NF_TPROXY_LOOKUP_LISTENER);
0147     }
0148 
0149     /* NOTE: assign_sock consumes our sk reference */
0150     if (sk && nf_tproxy_sk_is_transparent(sk))
0151         nf_tproxy_assign_sock(skb, sk);
0152     else
0153         regs->verdict.code = NFT_BREAK;
0154 }
0155 #endif
0156 
0157 static void nft_tproxy_eval(const struct nft_expr *expr,
0158                 struct nft_regs *regs,
0159                 const struct nft_pktinfo *pkt)
0160 {
0161     const struct nft_tproxy *priv = nft_expr_priv(expr);
0162 
0163     switch (nft_pf(pkt)) {
0164     case NFPROTO_IPV4:
0165         switch (priv->family) {
0166         case NFPROTO_IPV4:
0167         case NFPROTO_UNSPEC:
0168             nft_tproxy_eval_v4(expr, regs, pkt);
0169             return;
0170         }
0171         break;
0172 #if IS_ENABLED(CONFIG_NF_TABLES_IPV6)
0173     case NFPROTO_IPV6:
0174         switch (priv->family) {
0175         case NFPROTO_IPV6:
0176         case NFPROTO_UNSPEC:
0177             nft_tproxy_eval_v6(expr, regs, pkt);
0178             return;
0179         }
0180 #endif
0181     }
0182     regs->verdict.code = NFT_BREAK;
0183 }
0184 
0185 static const struct nla_policy nft_tproxy_policy[NFTA_TPROXY_MAX + 1] = {
0186     [NFTA_TPROXY_FAMILY]   = { .type = NLA_U32 },
0187     [NFTA_TPROXY_REG_ADDR] = { .type = NLA_U32 },
0188     [NFTA_TPROXY_REG_PORT] = { .type = NLA_U32 },
0189 };
0190 
0191 static int nft_tproxy_init(const struct nft_ctx *ctx,
0192                const struct nft_expr *expr,
0193                const struct nlattr * const tb[])
0194 {
0195     struct nft_tproxy *priv = nft_expr_priv(expr);
0196     unsigned int alen = 0;
0197     int err;
0198 
0199     if (!tb[NFTA_TPROXY_FAMILY] ||
0200         (!tb[NFTA_TPROXY_REG_ADDR] && !tb[NFTA_TPROXY_REG_PORT]))
0201         return -EINVAL;
0202 
0203     priv->family = ntohl(nla_get_be32(tb[NFTA_TPROXY_FAMILY]));
0204 
0205     switch (ctx->family) {
0206     case NFPROTO_IPV4:
0207         if (priv->family != NFPROTO_IPV4)
0208             return -EINVAL;
0209         break;
0210 #if IS_ENABLED(CONFIG_NF_TABLES_IPV6)
0211     case NFPROTO_IPV6:
0212         if (priv->family != NFPROTO_IPV6)
0213             return -EINVAL;
0214         break;
0215 #endif
0216     case NFPROTO_INET:
0217         break;
0218     default:
0219         return -EOPNOTSUPP;
0220     }
0221 
0222     /* Address is specified but the rule family is not set accordingly */
0223     if (priv->family == NFPROTO_UNSPEC && tb[NFTA_TPROXY_REG_ADDR])
0224         return -EINVAL;
0225 
0226     switch (priv->family) {
0227     case NFPROTO_IPV4:
0228         alen = sizeof_field(union nf_inet_addr, in);
0229         err = nf_defrag_ipv4_enable(ctx->net);
0230         if (err)
0231             return err;
0232         break;
0233 #if IS_ENABLED(CONFIG_NF_TABLES_IPV6)
0234     case NFPROTO_IPV6:
0235         alen = sizeof_field(union nf_inet_addr, in6);
0236         err = nf_defrag_ipv6_enable(ctx->net);
0237         if (err)
0238             return err;
0239         break;
0240 #endif
0241     case NFPROTO_UNSPEC:
0242         /* No address is specified here */
0243         err = nf_defrag_ipv4_enable(ctx->net);
0244         if (err)
0245             return err;
0246 #if IS_ENABLED(CONFIG_NF_TABLES_IPV6)
0247         err = nf_defrag_ipv6_enable(ctx->net);
0248         if (err)
0249             return err;
0250 #endif
0251         break;
0252     default:
0253         return -EOPNOTSUPP;
0254     }
0255 
0256     if (tb[NFTA_TPROXY_REG_ADDR]) {
0257         err = nft_parse_register_load(tb[NFTA_TPROXY_REG_ADDR],
0258                           &priv->sreg_addr, alen);
0259         if (err < 0)
0260             return err;
0261     }
0262 
0263     if (tb[NFTA_TPROXY_REG_PORT]) {
0264         err = nft_parse_register_load(tb[NFTA_TPROXY_REG_PORT],
0265                           &priv->sreg_port, sizeof(u16));
0266         if (err < 0)
0267             return err;
0268     }
0269 
0270     return 0;
0271 }
0272 
0273 static void nft_tproxy_destroy(const struct nft_ctx *ctx,
0274                    const struct nft_expr *expr)
0275 {
0276     const struct nft_tproxy *priv = nft_expr_priv(expr);
0277 
0278     switch (priv->family) {
0279     case NFPROTO_IPV4:
0280         nf_defrag_ipv4_disable(ctx->net);
0281         break;
0282 #if IS_ENABLED(CONFIG_NF_TABLES_IPV6)
0283     case NFPROTO_IPV6:
0284         nf_defrag_ipv6_disable(ctx->net);
0285         break;
0286 #endif
0287     case NFPROTO_UNSPEC:
0288         nf_defrag_ipv4_disable(ctx->net);
0289 #if IS_ENABLED(CONFIG_NF_TABLES_IPV6)
0290         nf_defrag_ipv6_disable(ctx->net);
0291 #endif
0292         break;
0293     }
0294 }
0295 
0296 static int nft_tproxy_dump(struct sk_buff *skb,
0297                const struct nft_expr *expr)
0298 {
0299     const struct nft_tproxy *priv = nft_expr_priv(expr);
0300 
0301     if (nla_put_be32(skb, NFTA_TPROXY_FAMILY, htonl(priv->family)))
0302         return -1;
0303 
0304     if (priv->sreg_addr &&
0305         nft_dump_register(skb, NFTA_TPROXY_REG_ADDR, priv->sreg_addr))
0306         return -1;
0307 
0308     if (priv->sreg_port &&
0309         nft_dump_register(skb, NFTA_TPROXY_REG_PORT, priv->sreg_port))
0310             return -1;
0311 
0312     return 0;
0313 }
0314 
0315 static int nft_tproxy_validate(const struct nft_ctx *ctx,
0316                    const struct nft_expr *expr,
0317                    const struct nft_data **data)
0318 {
0319     return nft_chain_validate_hooks(ctx->chain, 1 << NF_INET_PRE_ROUTING);
0320 }
0321 
0322 static struct nft_expr_type nft_tproxy_type;
0323 static const struct nft_expr_ops nft_tproxy_ops = {
0324     .type       = &nft_tproxy_type,
0325     .size       = NFT_EXPR_SIZE(sizeof(struct nft_tproxy)),
0326     .eval       = nft_tproxy_eval,
0327     .init       = nft_tproxy_init,
0328     .destroy    = nft_tproxy_destroy,
0329     .dump       = nft_tproxy_dump,
0330     .reduce     = NFT_REDUCE_READONLY,
0331     .validate   = nft_tproxy_validate,
0332 };
0333 
0334 static struct nft_expr_type nft_tproxy_type __read_mostly = {
0335     .name       = "tproxy",
0336     .ops        = &nft_tproxy_ops,
0337     .policy     = nft_tproxy_policy,
0338     .maxattr    = NFTA_TPROXY_MAX,
0339     .owner      = THIS_MODULE,
0340 };
0341 
0342 static int __init nft_tproxy_module_init(void)
0343 {
0344     return nft_register_expr(&nft_tproxy_type);
0345 }
0346 
0347 static void __exit nft_tproxy_module_exit(void)
0348 {
0349     nft_unregister_expr(&nft_tproxy_type);
0350 }
0351 
0352 module_init(nft_tproxy_module_init);
0353 module_exit(nft_tproxy_module_exit);
0354 
0355 MODULE_LICENSE("GPL");
0356 MODULE_AUTHOR("Máté Eckl");
0357 MODULE_DESCRIPTION("nf_tables tproxy support module");
0358 MODULE_ALIAS_NFT_EXPR("tproxy");