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