linux/net/netfilter/nft_tproxy.c
<<
>>
Prefs
   1/* SPDX-License-Identifier: GPL-2.0 */
   2#include <linux/module.h>
   3#include <linux/netfilter/nf_tables.h>
   4#include <net/netfilter/nf_tables.h>
   5#include <net/netfilter/nf_tables_core.h>
   6#include <net/netfilter/nf_tproxy.h>
   7#include <net/inet_sock.h>
   8#include <net/tcp.h>
   9#include <linux/if_ether.h>
  10#include <net/netfilter/ipv4/nf_defrag_ipv4.h>
  11#if IS_ENABLED(CONFIG_NF_TABLES_IPV6)
  12#include <net/netfilter/ipv6/nf_defrag_ipv6.h>
  13#endif
  14
  15struct nft_tproxy {
  16        u8      sreg_addr;
  17        u8      sreg_port;
  18        u8      family;
  19};
  20
  21static void nft_tproxy_eval_v4(const struct nft_expr *expr,
  22                               struct nft_regs *regs,
  23                               const struct nft_pktinfo *pkt)
  24{
  25        const struct nft_tproxy *priv = nft_expr_priv(expr);
  26        struct sk_buff *skb = pkt->skb;
  27        const struct iphdr *iph = ip_hdr(skb);
  28        struct udphdr _hdr, *hp;
  29        __be32 taddr = 0;
  30        __be16 tport = 0;
  31        struct sock *sk;
  32
  33        hp = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(_hdr), &_hdr);
  34        if (!hp) {
  35                regs->verdict.code = NFT_BREAK;
  36                return;
  37        }
  38
  39        /* check if there's an ongoing connection on the packet addresses, this
  40         * happens if the redirect already happened and the current packet
  41         * belongs to an already established connection
  42         */
  43        sk = nf_tproxy_get_sock_v4(nft_net(pkt), skb, iph->protocol,
  44                                   iph->saddr, iph->daddr,
  45                                   hp->source, hp->dest,
  46                                   skb->dev, NF_TPROXY_LOOKUP_ESTABLISHED);
  47
  48        if (priv->sreg_addr)
  49                taddr = regs->data[priv->sreg_addr];
  50        taddr = nf_tproxy_laddr4(skb, taddr, iph->daddr);
  51
  52        if (priv->sreg_port)
  53                tport = nft_reg_load16(&regs->data[priv->sreg_port]);
  54        if (!tport)
  55                tport = hp->dest;
  56
  57        /* UDP has no TCP_TIME_WAIT state, so we never enter here */
  58        if (sk && sk->sk_state == TCP_TIME_WAIT) {
  59                /* reopening a TIME_WAIT connection needs special handling */
  60                sk = nf_tproxy_handle_time_wait4(nft_net(pkt), skb, taddr, tport, sk);
  61        } else if (!sk) {
  62                /* no, there's no established connection, check if
  63                 * there's a listener on the redirected addr/port
  64                 */
  65                sk = nf_tproxy_get_sock_v4(nft_net(pkt), skb, iph->protocol,
  66                                           iph->saddr, taddr,
  67                                           hp->source, tport,
  68                                           skb->dev, NF_TPROXY_LOOKUP_LISTENER);
  69        }
  70
  71        if (sk && nf_tproxy_sk_is_transparent(sk))
  72                nf_tproxy_assign_sock(skb, sk);
  73        else
  74                regs->verdict.code = NFT_BREAK;
  75}
  76
  77#if IS_ENABLED(CONFIG_NF_TABLES_IPV6)
  78static void nft_tproxy_eval_v6(const struct nft_expr *expr,
  79                               struct nft_regs *regs,
  80                               const struct nft_pktinfo *pkt)
  81{
  82        const struct nft_tproxy *priv = nft_expr_priv(expr);
  83        struct sk_buff *skb = pkt->skb;
  84        const struct ipv6hdr *iph = ipv6_hdr(skb);
  85        struct in6_addr taddr;
  86        int thoff = pkt->xt.thoff;
  87        struct udphdr _hdr, *hp;
  88        __be16 tport = 0;
  89        struct sock *sk;
  90        int l4proto;
  91
  92        memset(&taddr, 0, sizeof(taddr));
  93
  94        if (!pkt->tprot_set) {
  95                regs->verdict.code = NFT_BREAK;
  96                return;
  97        }
  98        l4proto = pkt->tprot;
  99
 100        hp = skb_header_pointer(skb, thoff, sizeof(_hdr), &_hdr);
 101        if (hp == NULL) {
 102                regs->verdict.code = NFT_BREAK;
 103                return;
 104        }
 105
 106        /* check if there's an ongoing connection on the packet addresses, this
 107         * happens if the redirect already happened and the current packet
 108         * belongs to an already established connection
 109         */
 110        sk = nf_tproxy_get_sock_v6(nft_net(pkt), skb, thoff, l4proto,
 111                                   &iph->saddr, &iph->daddr,
 112                                   hp->source, hp->dest,
 113                                   nft_in(pkt), NF_TPROXY_LOOKUP_ESTABLISHED);
 114
 115        if (priv->sreg_addr)
 116                memcpy(&taddr, &regs->data[priv->sreg_addr], sizeof(taddr));
 117        taddr = *nf_tproxy_laddr6(skb, &taddr, &iph->daddr);
 118
 119        if (priv->sreg_port)
 120                tport = nft_reg_load16(&regs->data[priv->sreg_port]);
 121        if (!tport)
 122                tport = hp->dest;
 123
 124        /* UDP has no TCP_TIME_WAIT state, so we never enter here */
 125        if (sk && sk->sk_state == TCP_TIME_WAIT) {
 126                /* reopening a TIME_WAIT connection needs special handling */
 127                sk = nf_tproxy_handle_time_wait6(skb, l4proto, thoff,
 128                                                 nft_net(pkt),
 129                                                 &taddr,
 130                                                 tport,
 131                                                 sk);
 132        } else if (!sk) {
 133                /* no there's no established connection, check if
 134                 * there's a listener on the redirected addr/port
 135                 */
 136                sk = nf_tproxy_get_sock_v6(nft_net(pkt), skb, thoff,
 137                                           l4proto, &iph->saddr, &taddr,
 138                                           hp->source, tport,
 139                                           nft_in(pkt), NF_TPROXY_LOOKUP_LISTENER);
 140        }
 141
 142        /* NOTE: assign_sock consumes our sk reference */
 143        if (sk && nf_tproxy_sk_is_transparent(sk))
 144                nf_tproxy_assign_sock(skb, sk);
 145        else
 146                regs->verdict.code = NFT_BREAK;
 147}
 148#endif
 149
 150static void nft_tproxy_eval(const struct nft_expr *expr,
 151                            struct nft_regs *regs,
 152                            const struct nft_pktinfo *pkt)
 153{
 154        const struct nft_tproxy *priv = nft_expr_priv(expr);
 155
 156        switch (nft_pf(pkt)) {
 157        case NFPROTO_IPV4:
 158                switch (priv->family) {
 159                case NFPROTO_IPV4:
 160                case NFPROTO_UNSPEC:
 161                        nft_tproxy_eval_v4(expr, regs, pkt);
 162                        return;
 163                }
 164                break;
 165#if IS_ENABLED(CONFIG_NF_TABLES_IPV6)
 166        case NFPROTO_IPV6:
 167                switch (priv->family) {
 168                case NFPROTO_IPV6:
 169                case NFPROTO_UNSPEC:
 170                        nft_tproxy_eval_v6(expr, regs, pkt);
 171                        return;
 172                }
 173#endif
 174        }
 175        regs->verdict.code = NFT_BREAK;
 176}
 177
 178static const struct nla_policy nft_tproxy_policy[NFTA_TPROXY_MAX + 1] = {
 179        [NFTA_TPROXY_FAMILY]   = { .type = NLA_U32 },
 180        [NFTA_TPROXY_REG_ADDR] = { .type = NLA_U32 },
 181        [NFTA_TPROXY_REG_PORT] = { .type = NLA_U32 },
 182};
 183
 184static int nft_tproxy_init(const struct nft_ctx *ctx,
 185                           const struct nft_expr *expr,
 186                           const struct nlattr * const tb[])
 187{
 188        struct nft_tproxy *priv = nft_expr_priv(expr);
 189        unsigned int alen = 0;
 190        int err;
 191
 192        if (!tb[NFTA_TPROXY_FAMILY] ||
 193            (!tb[NFTA_TPROXY_REG_ADDR] && !tb[NFTA_TPROXY_REG_PORT]))
 194                return -EINVAL;
 195
 196        priv->family = ntohl(nla_get_be32(tb[NFTA_TPROXY_FAMILY]));
 197
 198        switch (ctx->family) {
 199        case NFPROTO_IPV4:
 200                if (priv->family != NFPROTO_IPV4)
 201                        return -EINVAL;
 202                break;
 203#if IS_ENABLED(CONFIG_NF_TABLES_IPV6)
 204        case NFPROTO_IPV6:
 205                if (priv->family != NFPROTO_IPV6)
 206                        return -EINVAL;
 207                break;
 208#endif
 209        case NFPROTO_INET:
 210                break;
 211        default:
 212                return -EOPNOTSUPP;
 213        }
 214
 215        /* Address is specified but the rule family is not set accordingly */
 216        if (priv->family == NFPROTO_UNSPEC && tb[NFTA_TPROXY_REG_ADDR])
 217                return -EINVAL;
 218
 219        switch (priv->family) {
 220        case NFPROTO_IPV4:
 221                alen = sizeof_field(union nf_inet_addr, in);
 222                err = nf_defrag_ipv4_enable(ctx->net);
 223                if (err)
 224                        return err;
 225                break;
 226#if IS_ENABLED(CONFIG_NF_TABLES_IPV6)
 227        case NFPROTO_IPV6:
 228                alen = sizeof_field(union nf_inet_addr, in6);
 229                err = nf_defrag_ipv6_enable(ctx->net);
 230                if (err)
 231                        return err;
 232                break;
 233#endif
 234        case NFPROTO_UNSPEC:
 235                /* No address is specified here */
 236                err = nf_defrag_ipv4_enable(ctx->net);
 237                if (err)
 238                        return err;
 239#if IS_ENABLED(CONFIG_NF_TABLES_IPV6)
 240                err = nf_defrag_ipv6_enable(ctx->net);
 241                if (err)
 242                        return err;
 243#endif
 244                break;
 245        default:
 246                return -EOPNOTSUPP;
 247        }
 248
 249        if (tb[NFTA_TPROXY_REG_ADDR]) {
 250                err = nft_parse_register_load(tb[NFTA_TPROXY_REG_ADDR],
 251                                              &priv->sreg_addr, alen);
 252                if (err < 0)
 253                        return err;
 254        }
 255
 256        if (tb[NFTA_TPROXY_REG_PORT]) {
 257                err = nft_parse_register_load(tb[NFTA_TPROXY_REG_PORT],
 258                                              &priv->sreg_port, sizeof(u16));
 259                if (err < 0)
 260                        return err;
 261        }
 262
 263        return 0;
 264}
 265
 266static int nft_tproxy_dump(struct sk_buff *skb,
 267                           const struct nft_expr *expr)
 268{
 269        const struct nft_tproxy *priv = nft_expr_priv(expr);
 270
 271        if (nla_put_be32(skb, NFTA_TPROXY_FAMILY, htonl(priv->family)))
 272                return -1;
 273
 274        if (priv->sreg_addr &&
 275            nft_dump_register(skb, NFTA_TPROXY_REG_ADDR, priv->sreg_addr))
 276                return -1;
 277
 278        if (priv->sreg_port &&
 279            nft_dump_register(skb, NFTA_TPROXY_REG_PORT, priv->sreg_port))
 280                        return -1;
 281
 282        return 0;
 283}
 284
 285static struct nft_expr_type nft_tproxy_type;
 286static const struct nft_expr_ops nft_tproxy_ops = {
 287        .type           = &nft_tproxy_type,
 288        .size           = NFT_EXPR_SIZE(sizeof(struct nft_tproxy)),
 289        .eval           = nft_tproxy_eval,
 290        .init           = nft_tproxy_init,
 291        .dump           = nft_tproxy_dump,
 292};
 293
 294static struct nft_expr_type nft_tproxy_type __read_mostly = {
 295        .name           = "tproxy",
 296        .ops            = &nft_tproxy_ops,
 297        .policy         = nft_tproxy_policy,
 298        .maxattr        = NFTA_TPROXY_MAX,
 299        .owner          = THIS_MODULE,
 300};
 301
 302static int __init nft_tproxy_module_init(void)
 303{
 304        return nft_register_expr(&nft_tproxy_type);
 305}
 306
 307static void __exit nft_tproxy_module_exit(void)
 308{
 309        nft_unregister_expr(&nft_tproxy_type);
 310}
 311
 312module_init(nft_tproxy_module_init);
 313module_exit(nft_tproxy_module_exit);
 314
 315MODULE_LICENSE("GPL");
 316MODULE_AUTHOR("Máté Eckl");
 317MODULE_DESCRIPTION("nf_tables tproxy support module");
 318MODULE_ALIAS_NFT_EXPR("tproxy");
 319