linux/net/netfilter/xt_socket.c
<<
>>
Prefs
   1/*
   2 * Transparent proxy support for Linux/iptables
   3 *
   4 * Copyright (C) 2007-2008 BalaBit IT Ltd.
   5 * Author: Krisztian Kovacs
   6 *
   7 * This program is free software; you can redistribute it and/or modify
   8 * it under the terms of the GNU General Public License version 2 as
   9 * published by the Free Software Foundation.
  10 *
  11 */
  12#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  13#include <linux/module.h>
  14#include <linux/skbuff.h>
  15#include <linux/netfilter/x_tables.h>
  16#include <linux/netfilter_ipv4/ip_tables.h>
  17#include <net/tcp.h>
  18#include <net/udp.h>
  19#include <net/icmp.h>
  20#include <net/sock.h>
  21#include <net/inet_sock.h>
  22#include <net/netfilter/ipv4/nf_defrag_ipv4.h>
  23
  24#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)
  25#define XT_SOCKET_HAVE_IPV6 1
  26#include <linux/netfilter_ipv6/ip6_tables.h>
  27#include <net/inet6_hashtables.h>
  28#include <net/netfilter/ipv6/nf_defrag_ipv6.h>
  29#endif
  30
  31#include <linux/netfilter/xt_socket.h>
  32
  33#if IS_ENABLED(CONFIG_NF_CONNTRACK)
  34#define XT_SOCKET_HAVE_CONNTRACK 1
  35#include <net/netfilter/nf_conntrack.h>
  36#endif
  37
  38static int
  39extract_icmp4_fields(const struct sk_buff *skb,
  40                    u8 *protocol,
  41                    __be32 *raddr,
  42                    __be32 *laddr,
  43                    __be16 *rport,
  44                    __be16 *lport)
  45{
  46        unsigned int outside_hdrlen = ip_hdrlen(skb);
  47        struct iphdr *inside_iph, _inside_iph;
  48        struct icmphdr *icmph, _icmph;
  49        __be16 *ports, _ports[2];
  50
  51        icmph = skb_header_pointer(skb, outside_hdrlen,
  52                                   sizeof(_icmph), &_icmph);
  53        if (icmph == NULL)
  54                return 1;
  55
  56        switch (icmph->type) {
  57        case ICMP_DEST_UNREACH:
  58        case ICMP_SOURCE_QUENCH:
  59        case ICMP_REDIRECT:
  60        case ICMP_TIME_EXCEEDED:
  61        case ICMP_PARAMETERPROB:
  62                break;
  63        default:
  64                return 1;
  65        }
  66
  67        inside_iph = skb_header_pointer(skb, outside_hdrlen +
  68                                        sizeof(struct icmphdr),
  69                                        sizeof(_inside_iph), &_inside_iph);
  70        if (inside_iph == NULL)
  71                return 1;
  72
  73        if (inside_iph->protocol != IPPROTO_TCP &&
  74            inside_iph->protocol != IPPROTO_UDP)
  75                return 1;
  76
  77        ports = skb_header_pointer(skb, outside_hdrlen +
  78                                   sizeof(struct icmphdr) +
  79                                   (inside_iph->ihl << 2),
  80                                   sizeof(_ports), &_ports);
  81        if (ports == NULL)
  82                return 1;
  83
  84        /* the inside IP packet is the one quoted from our side, thus
  85         * its saddr is the local address */
  86        *protocol = inside_iph->protocol;
  87        *laddr = inside_iph->saddr;
  88        *lport = ports[0];
  89        *raddr = inside_iph->daddr;
  90        *rport = ports[1];
  91
  92        return 0;
  93}
  94
  95/* "socket" match based redirection (no specific rule)
  96 * ===================================================
  97 *
  98 * There are connections with dynamic endpoints (e.g. FTP data
  99 * connection) that the user is unable to add explicit rules
 100 * for. These are taken care of by a generic "socket" rule. It is
 101 * assumed that the proxy application is trusted to open such
 102 * connections without explicit iptables rule (except of course the
 103 * generic 'socket' rule). In this case the following sockets are
 104 * matched in preference order:
 105 *
 106 *   - match: if there's a fully established connection matching the
 107 *     _packet_ tuple
 108 *
 109 *   - match: if there's a non-zero bound listener (possibly with a
 110 *     non-local address) We don't accept zero-bound listeners, since
 111 *     then local services could intercept traffic going through the
 112 *     box.
 113 */
 114static struct sock *
 115xt_socket_get_sock_v4(struct net *net, const u8 protocol,
 116                      const __be32 saddr, const __be32 daddr,
 117                      const __be16 sport, const __be16 dport,
 118                      const struct net_device *in)
 119{
 120        switch (protocol) {
 121        case IPPROTO_TCP:
 122                return __inet_lookup(net, &tcp_hashinfo,
 123                                     saddr, sport, daddr, dport,
 124                                     in->ifindex);
 125        case IPPROTO_UDP:
 126                return udp4_lib_lookup(net, saddr, sport, daddr, dport,
 127                                       in->ifindex);
 128        }
 129        return NULL;
 130}
 131
 132static bool
 133socket_match(const struct sk_buff *skb, struct xt_action_param *par,
 134             const struct xt_socket_mtinfo1 *info)
 135{
 136        const struct iphdr *iph = ip_hdr(skb);
 137        struct udphdr _hdr, *hp = NULL;
 138        struct sock *sk = skb->sk;
 139        __be32 uninitialized_var(daddr), uninitialized_var(saddr);
 140        __be16 uninitialized_var(dport), uninitialized_var(sport);
 141        u8 uninitialized_var(protocol);
 142#ifdef XT_SOCKET_HAVE_CONNTRACK
 143        struct nf_conn const *ct;
 144        enum ip_conntrack_info ctinfo;
 145#endif
 146
 147        if (iph->protocol == IPPROTO_UDP || iph->protocol == IPPROTO_TCP) {
 148                hp = skb_header_pointer(skb, ip_hdrlen(skb),
 149                                        sizeof(_hdr), &_hdr);
 150                if (hp == NULL)
 151                        return false;
 152
 153                protocol = iph->protocol;
 154                saddr = iph->saddr;
 155                sport = hp->source;
 156                daddr = iph->daddr;
 157                dport = hp->dest;
 158
 159        } else if (iph->protocol == IPPROTO_ICMP) {
 160                if (extract_icmp4_fields(skb, &protocol, &saddr, &daddr,
 161                                        &sport, &dport))
 162                        return false;
 163        } else {
 164                return false;
 165        }
 166
 167#ifdef XT_SOCKET_HAVE_CONNTRACK
 168        /* Do the lookup with the original socket address in case this is a
 169         * reply packet of an established SNAT-ted connection. */
 170
 171        ct = nf_ct_get(skb, &ctinfo);
 172        if (ct && !nf_ct_is_untracked(ct) &&
 173            ((iph->protocol != IPPROTO_ICMP &&
 174              ctinfo == IP_CT_ESTABLISHED_REPLY) ||
 175             (iph->protocol == IPPROTO_ICMP &&
 176              ctinfo == IP_CT_RELATED_REPLY)) &&
 177            (ct->status & IPS_SRC_NAT_DONE)) {
 178
 179                daddr = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip;
 180                dport = (iph->protocol == IPPROTO_TCP) ?
 181                        ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.tcp.port :
 182                        ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.udp.port;
 183        }
 184#endif
 185
 186        if (!sk)
 187                sk = xt_socket_get_sock_v4(dev_net(skb->dev), protocol,
 188                                           saddr, daddr, sport, dport,
 189                                           par->in);
 190        if (sk) {
 191                bool wildcard;
 192                bool transparent = true;
 193
 194                /* Ignore sockets listening on INADDR_ANY,
 195                 * unless XT_SOCKET_NOWILDCARD is set
 196                 */
 197                wildcard = (!(info->flags & XT_SOCKET_NOWILDCARD) &&
 198                            sk->sk_state != TCP_TIME_WAIT &&
 199                            inet_sk(sk)->inet_rcv_saddr == 0);
 200
 201                /* Ignore non-transparent sockets,
 202                   if XT_SOCKET_TRANSPARENT is used */
 203                if (info->flags & XT_SOCKET_TRANSPARENT)
 204                        transparent = ((sk->sk_state != TCP_TIME_WAIT &&
 205                                        inet_sk(sk)->transparent) ||
 206                                       (sk->sk_state == TCP_TIME_WAIT &&
 207                                        inet_twsk(sk)->tw_transparent));
 208
 209                if (sk != skb->sk)
 210                        sock_gen_put(sk);
 211
 212                if (wildcard || !transparent)
 213                        sk = NULL;
 214        }
 215
 216        pr_debug("proto %hhu %pI4:%hu -> %pI4:%hu (orig %pI4:%hu) sock %p\n",
 217                 protocol, &saddr, ntohs(sport),
 218                 &daddr, ntohs(dport),
 219                 &iph->daddr, hp ? ntohs(hp->dest) : 0, sk);
 220
 221        return (sk != NULL);
 222}
 223
 224static bool
 225socket_mt4_v0(const struct sk_buff *skb, struct xt_action_param *par)
 226{
 227        static struct xt_socket_mtinfo1 xt_info_v0 = {
 228                .flags = 0,
 229        };
 230
 231        return socket_match(skb, par, &xt_info_v0);
 232}
 233
 234static bool
 235socket_mt4_v1_v2(const struct sk_buff *skb, struct xt_action_param *par)
 236{
 237        return socket_match(skb, par, par->matchinfo);
 238}
 239
 240#ifdef XT_SOCKET_HAVE_IPV6
 241
 242static int
 243extract_icmp6_fields(const struct sk_buff *skb,
 244                     unsigned int outside_hdrlen,
 245                     int *protocol,
 246                     const struct in6_addr **raddr,
 247                     const struct in6_addr **laddr,
 248                     __be16 *rport,
 249                     __be16 *lport,
 250                     struct ipv6hdr *ipv6_var)
 251{
 252        const struct ipv6hdr *inside_iph;
 253        struct icmp6hdr *icmph, _icmph;
 254        __be16 *ports, _ports[2];
 255        u8 inside_nexthdr;
 256        __be16 inside_fragoff;
 257        int inside_hdrlen;
 258
 259        icmph = skb_header_pointer(skb, outside_hdrlen,
 260                                   sizeof(_icmph), &_icmph);
 261        if (icmph == NULL)
 262                return 1;
 263
 264        if (icmph->icmp6_type & ICMPV6_INFOMSG_MASK)
 265                return 1;
 266
 267        inside_iph = skb_header_pointer(skb, outside_hdrlen + sizeof(_icmph),
 268                                        sizeof(*ipv6_var), ipv6_var);
 269        if (inside_iph == NULL)
 270                return 1;
 271        inside_nexthdr = inside_iph->nexthdr;
 272
 273        inside_hdrlen = ipv6_skip_exthdr(skb, outside_hdrlen + sizeof(_icmph) +
 274                                              sizeof(*ipv6_var),
 275                                         &inside_nexthdr, &inside_fragoff);
 276        if (inside_hdrlen < 0)
 277                return 1; /* hjm: Packet has no/incomplete transport layer headers. */
 278
 279        if (inside_nexthdr != IPPROTO_TCP &&
 280            inside_nexthdr != IPPROTO_UDP)
 281                return 1;
 282
 283        ports = skb_header_pointer(skb, inside_hdrlen,
 284                                   sizeof(_ports), &_ports);
 285        if (ports == NULL)
 286                return 1;
 287
 288        /* the inside IP packet is the one quoted from our side, thus
 289         * its saddr is the local address */
 290        *protocol = inside_nexthdr;
 291        *laddr = &inside_iph->saddr;
 292        *lport = ports[0];
 293        *raddr = &inside_iph->daddr;
 294        *rport = ports[1];
 295
 296        return 0;
 297}
 298
 299static struct sock *
 300xt_socket_get_sock_v6(struct net *net, const u8 protocol,
 301                      const struct in6_addr *saddr, const struct in6_addr *daddr,
 302                      const __be16 sport, const __be16 dport,
 303                      const struct net_device *in)
 304{
 305        switch (protocol) {
 306        case IPPROTO_TCP:
 307                return inet6_lookup(net, &tcp_hashinfo,
 308                                    saddr, sport, daddr, dport,
 309                                    in->ifindex);
 310        case IPPROTO_UDP:
 311                return udp6_lib_lookup(net, saddr, sport, daddr, dport,
 312                                       in->ifindex);
 313        }
 314
 315        return NULL;
 316}
 317
 318static bool
 319socket_mt6_v1_v2(const struct sk_buff *skb, struct xt_action_param *par)
 320{
 321        struct ipv6hdr ipv6_var, *iph = ipv6_hdr(skb);
 322        struct udphdr _hdr, *hp = NULL;
 323        struct sock *sk = skb->sk;
 324        const struct in6_addr *daddr = NULL, *saddr = NULL;
 325        __be16 uninitialized_var(dport), uninitialized_var(sport);
 326        int thoff = 0, uninitialized_var(tproto);
 327        const struct xt_socket_mtinfo1 *info = (struct xt_socket_mtinfo1 *) par->matchinfo;
 328
 329        tproto = ipv6_find_hdr(skb, &thoff, -1, NULL, NULL);
 330        if (tproto < 0) {
 331                pr_debug("unable to find transport header in IPv6 packet, dropping\n");
 332                return NF_DROP;
 333        }
 334
 335        if (tproto == IPPROTO_UDP || tproto == IPPROTO_TCP) {
 336                hp = skb_header_pointer(skb, thoff,
 337                                        sizeof(_hdr), &_hdr);
 338                if (hp == NULL)
 339                        return false;
 340
 341                saddr = &iph->saddr;
 342                sport = hp->source;
 343                daddr = &iph->daddr;
 344                dport = hp->dest;
 345
 346        } else if (tproto == IPPROTO_ICMPV6) {
 347                if (extract_icmp6_fields(skb, thoff, &tproto, &saddr, &daddr,
 348                                         &sport, &dport, &ipv6_var))
 349                        return false;
 350        } else {
 351                return false;
 352        }
 353
 354        if (!sk)
 355                sk = xt_socket_get_sock_v6(dev_net(skb->dev), tproto,
 356                                           saddr, daddr, sport, dport,
 357                                           par->in);
 358        if (sk) {
 359                bool wildcard;
 360                bool transparent = true;
 361
 362                /* Ignore sockets listening on INADDR_ANY
 363                 * unless XT_SOCKET_NOWILDCARD is set
 364                 */
 365                wildcard = (!(info->flags & XT_SOCKET_NOWILDCARD) &&
 366                            sk->sk_state != TCP_TIME_WAIT &&
 367                            ipv6_addr_any(&sk->sk_v6_rcv_saddr));
 368
 369                /* Ignore non-transparent sockets,
 370                   if XT_SOCKET_TRANSPARENT is used */
 371                if (info->flags & XT_SOCKET_TRANSPARENT)
 372                        transparent = ((sk->sk_state != TCP_TIME_WAIT &&
 373                                        inet_sk(sk)->transparent) ||
 374                                       (sk->sk_state == TCP_TIME_WAIT &&
 375                                        inet_twsk(sk)->tw_transparent));
 376
 377                if (sk != skb->sk)
 378                        sock_gen_put(sk);
 379
 380                if (wildcard || !transparent)
 381                        sk = NULL;
 382        }
 383
 384        pr_debug("proto %hhd %pI6:%hu -> %pI6:%hu "
 385                 "(orig %pI6:%hu) sock %p\n",
 386                 tproto, saddr, ntohs(sport),
 387                 daddr, ntohs(dport),
 388                 &iph->daddr, hp ? ntohs(hp->dest) : 0, sk);
 389
 390        return (sk != NULL);
 391}
 392#endif
 393
 394static int socket_mt_v1_check(const struct xt_mtchk_param *par)
 395{
 396        const struct xt_socket_mtinfo1 *info = (struct xt_socket_mtinfo1 *) par->matchinfo;
 397
 398        if (info->flags & ~XT_SOCKET_FLAGS_V1) {
 399                pr_info("unknown flags 0x%x\n", info->flags & ~XT_SOCKET_FLAGS_V1);
 400                return -EINVAL;
 401        }
 402        return 0;
 403}
 404
 405static int socket_mt_v2_check(const struct xt_mtchk_param *par)
 406{
 407        const struct xt_socket_mtinfo2 *info = (struct xt_socket_mtinfo2 *) par->matchinfo;
 408
 409        if (info->flags & ~XT_SOCKET_FLAGS_V2) {
 410                pr_info("unknown flags 0x%x\n", info->flags & ~XT_SOCKET_FLAGS_V2);
 411                return -EINVAL;
 412        }
 413        return 0;
 414}
 415
 416static struct xt_match socket_mt_reg[] __read_mostly = {
 417        {
 418                .name           = "socket",
 419                .revision       = 0,
 420                .family         = NFPROTO_IPV4,
 421                .match          = socket_mt4_v0,
 422                .hooks          = (1 << NF_INET_PRE_ROUTING) |
 423                                  (1 << NF_INET_LOCAL_IN),
 424                .me             = THIS_MODULE,
 425        },
 426        {
 427                .name           = "socket",
 428                .revision       = 1,
 429                .family         = NFPROTO_IPV4,
 430                .match          = socket_mt4_v1_v2,
 431                .checkentry     = socket_mt_v1_check,
 432                .matchsize      = sizeof(struct xt_socket_mtinfo1),
 433                .hooks          = (1 << NF_INET_PRE_ROUTING) |
 434                                  (1 << NF_INET_LOCAL_IN),
 435                .me             = THIS_MODULE,
 436        },
 437#ifdef XT_SOCKET_HAVE_IPV6
 438        {
 439                .name           = "socket",
 440                .revision       = 1,
 441                .family         = NFPROTO_IPV6,
 442                .match          = socket_mt6_v1_v2,
 443                .checkentry     = socket_mt_v1_check,
 444                .matchsize      = sizeof(struct xt_socket_mtinfo1),
 445                .hooks          = (1 << NF_INET_PRE_ROUTING) |
 446                                  (1 << NF_INET_LOCAL_IN),
 447                .me             = THIS_MODULE,
 448        },
 449#endif
 450        {
 451                .name           = "socket",
 452                .revision       = 2,
 453                .family         = NFPROTO_IPV4,
 454                .match          = socket_mt4_v1_v2,
 455                .checkentry     = socket_mt_v2_check,
 456                .matchsize      = sizeof(struct xt_socket_mtinfo1),
 457                .hooks          = (1 << NF_INET_PRE_ROUTING) |
 458                                  (1 << NF_INET_LOCAL_IN),
 459                .me             = THIS_MODULE,
 460        },
 461#ifdef XT_SOCKET_HAVE_IPV6
 462        {
 463                .name           = "socket",
 464                .revision       = 2,
 465                .family         = NFPROTO_IPV6,
 466                .match          = socket_mt6_v1_v2,
 467                .checkentry     = socket_mt_v2_check,
 468                .matchsize      = sizeof(struct xt_socket_mtinfo1),
 469                .hooks          = (1 << NF_INET_PRE_ROUTING) |
 470                                  (1 << NF_INET_LOCAL_IN),
 471                .me             = THIS_MODULE,
 472        },
 473#endif
 474};
 475
 476static int __init socket_mt_init(void)
 477{
 478        nf_defrag_ipv4_enable();
 479#ifdef XT_SOCKET_HAVE_IPV6
 480        nf_defrag_ipv6_enable();
 481#endif
 482
 483        return xt_register_matches(socket_mt_reg, ARRAY_SIZE(socket_mt_reg));
 484}
 485
 486static void __exit socket_mt_exit(void)
 487{
 488        xt_unregister_matches(socket_mt_reg, ARRAY_SIZE(socket_mt_reg));
 489}
 490
 491module_init(socket_mt_init);
 492module_exit(socket_mt_exit);
 493
 494MODULE_LICENSE("GPL");
 495MODULE_AUTHOR("Krisztian Kovacs, Balazs Scheidler");
 496MODULE_DESCRIPTION("x_tables socket match module");
 497MODULE_ALIAS("ipt_socket");
 498MODULE_ALIAS("ip6t_socket");
 499