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                     struct in6_addr **raddr,
 247                     struct in6_addr **laddr,
 248                     __be16 *rport,
 249                     __be16 *lport)
 250{
 251        struct ipv6hdr *inside_iph, _inside_iph;
 252        struct icmp6hdr *icmph, _icmph;
 253        __be16 *ports, _ports[2];
 254        u8 inside_nexthdr;
 255        __be16 inside_fragoff;
 256        int inside_hdrlen;
 257
 258        icmph = skb_header_pointer(skb, outside_hdrlen,
 259                                   sizeof(_icmph), &_icmph);
 260        if (icmph == NULL)
 261                return 1;
 262
 263        if (icmph->icmp6_type & ICMPV6_INFOMSG_MASK)
 264                return 1;
 265
 266        inside_iph = skb_header_pointer(skb, outside_hdrlen + sizeof(_icmph), sizeof(_inside_iph), &_inside_iph);
 267        if (inside_iph == NULL)
 268                return 1;
 269        inside_nexthdr = inside_iph->nexthdr;
 270
 271        inside_hdrlen = ipv6_skip_exthdr(skb, outside_hdrlen + sizeof(_icmph) + sizeof(_inside_iph),
 272                                         &inside_nexthdr, &inside_fragoff);
 273        if (inside_hdrlen < 0)
 274                return 1; /* hjm: Packet has no/incomplete transport layer headers. */
 275
 276        if (inside_nexthdr != IPPROTO_TCP &&
 277            inside_nexthdr != IPPROTO_UDP)
 278                return 1;
 279
 280        ports = skb_header_pointer(skb, inside_hdrlen,
 281                                   sizeof(_ports), &_ports);
 282        if (ports == NULL)
 283                return 1;
 284
 285        /* the inside IP packet is the one quoted from our side, thus
 286         * its saddr is the local address */
 287        *protocol = inside_nexthdr;
 288        *laddr = &inside_iph->saddr;
 289        *lport = ports[0];
 290        *raddr = &inside_iph->daddr;
 291        *rport = ports[1];
 292
 293        return 0;
 294}
 295
 296static struct sock *
 297xt_socket_get_sock_v6(struct net *net, const u8 protocol,
 298                      const struct in6_addr *saddr, const struct in6_addr *daddr,
 299                      const __be16 sport, const __be16 dport,
 300                      const struct net_device *in)
 301{
 302        switch (protocol) {
 303        case IPPROTO_TCP:
 304                return inet6_lookup(net, &tcp_hashinfo,
 305                                    saddr, sport, daddr, dport,
 306                                    in->ifindex);
 307        case IPPROTO_UDP:
 308                return udp6_lib_lookup(net, saddr, sport, daddr, dport,
 309                                       in->ifindex);
 310        }
 311
 312        return NULL;
 313}
 314
 315static bool
 316socket_mt6_v1_v2(const struct sk_buff *skb, struct xt_action_param *par)
 317{
 318        struct ipv6hdr *iph = ipv6_hdr(skb);
 319        struct udphdr _hdr, *hp = NULL;
 320        struct sock *sk = skb->sk;
 321        struct in6_addr *daddr = NULL, *saddr = NULL;
 322        __be16 uninitialized_var(dport), uninitialized_var(sport);
 323        int thoff = 0, uninitialized_var(tproto);
 324        const struct xt_socket_mtinfo1 *info = (struct xt_socket_mtinfo1 *) par->matchinfo;
 325
 326        tproto = ipv6_find_hdr(skb, &thoff, -1, NULL, NULL);
 327        if (tproto < 0) {
 328                pr_debug("unable to find transport header in IPv6 packet, dropping\n");
 329                return NF_DROP;
 330        }
 331
 332        if (tproto == IPPROTO_UDP || tproto == IPPROTO_TCP) {
 333                hp = skb_header_pointer(skb, thoff,
 334                                        sizeof(_hdr), &_hdr);
 335                if (hp == NULL)
 336                        return false;
 337
 338                saddr = &iph->saddr;
 339                sport = hp->source;
 340                daddr = &iph->daddr;
 341                dport = hp->dest;
 342
 343        } else if (tproto == IPPROTO_ICMPV6) {
 344                if (extract_icmp6_fields(skb, thoff, &tproto, &saddr, &daddr,
 345                                         &sport, &dport))
 346                        return false;
 347        } else {
 348                return false;
 349        }
 350
 351        if (!sk)
 352                sk = xt_socket_get_sock_v6(dev_net(skb->dev), tproto,
 353                                           saddr, daddr, sport, dport,
 354                                           par->in);
 355        if (sk) {
 356                bool wildcard;
 357                bool transparent = true;
 358
 359                /* Ignore sockets listening on INADDR_ANY
 360                 * unless XT_SOCKET_NOWILDCARD is set
 361                 */
 362                wildcard = (!(info->flags & XT_SOCKET_NOWILDCARD) &&
 363                            sk->sk_state != TCP_TIME_WAIT &&
 364                            ipv6_addr_any(&sk->sk_v6_rcv_saddr));
 365
 366                /* Ignore non-transparent sockets,
 367                   if XT_SOCKET_TRANSPARENT is used */
 368                if (info->flags & XT_SOCKET_TRANSPARENT)
 369                        transparent = ((sk->sk_state != TCP_TIME_WAIT &&
 370                                        inet_sk(sk)->transparent) ||
 371                                       (sk->sk_state == TCP_TIME_WAIT &&
 372                                        inet_twsk(sk)->tw_transparent));
 373
 374                if (sk != skb->sk)
 375                        sock_gen_put(sk);
 376
 377                if (wildcard || !transparent)
 378                        sk = NULL;
 379        }
 380
 381        pr_debug("proto %hhd %pI6:%hu -> %pI6:%hu "
 382                 "(orig %pI6:%hu) sock %p\n",
 383                 tproto, saddr, ntohs(sport),
 384                 daddr, ntohs(dport),
 385                 &iph->daddr, hp ? ntohs(hp->dest) : 0, sk);
 386
 387        return (sk != NULL);
 388}
 389#endif
 390
 391static int socket_mt_v1_check(const struct xt_mtchk_param *par)
 392{
 393        const struct xt_socket_mtinfo1 *info = (struct xt_socket_mtinfo1 *) par->matchinfo;
 394
 395        if (info->flags & ~XT_SOCKET_FLAGS_V1) {
 396                pr_info("unknown flags 0x%x\n", info->flags & ~XT_SOCKET_FLAGS_V1);
 397                return -EINVAL;
 398        }
 399        return 0;
 400}
 401
 402static int socket_mt_v2_check(const struct xt_mtchk_param *par)
 403{
 404        const struct xt_socket_mtinfo2 *info = (struct xt_socket_mtinfo2 *) par->matchinfo;
 405
 406        if (info->flags & ~XT_SOCKET_FLAGS_V2) {
 407                pr_info("unknown flags 0x%x\n", info->flags & ~XT_SOCKET_FLAGS_V2);
 408                return -EINVAL;
 409        }
 410        return 0;
 411}
 412
 413static struct xt_match socket_mt_reg[] __read_mostly = {
 414        {
 415                .name           = "socket",
 416                .revision       = 0,
 417                .family         = NFPROTO_IPV4,
 418                .match          = socket_mt4_v0,
 419                .hooks          = (1 << NF_INET_PRE_ROUTING) |
 420                                  (1 << NF_INET_LOCAL_IN),
 421                .me             = THIS_MODULE,
 422        },
 423        {
 424                .name           = "socket",
 425                .revision       = 1,
 426                .family         = NFPROTO_IPV4,
 427                .match          = socket_mt4_v1_v2,
 428                .checkentry     = socket_mt_v1_check,
 429                .matchsize      = sizeof(struct xt_socket_mtinfo1),
 430                .hooks          = (1 << NF_INET_PRE_ROUTING) |
 431                                  (1 << NF_INET_LOCAL_IN),
 432                .me             = THIS_MODULE,
 433        },
 434#ifdef XT_SOCKET_HAVE_IPV6
 435        {
 436                .name           = "socket",
 437                .revision       = 1,
 438                .family         = NFPROTO_IPV6,
 439                .match          = socket_mt6_v1_v2,
 440                .checkentry     = socket_mt_v1_check,
 441                .matchsize      = sizeof(struct xt_socket_mtinfo1),
 442                .hooks          = (1 << NF_INET_PRE_ROUTING) |
 443                                  (1 << NF_INET_LOCAL_IN),
 444                .me             = THIS_MODULE,
 445        },
 446#endif
 447        {
 448                .name           = "socket",
 449                .revision       = 2,
 450                .family         = NFPROTO_IPV4,
 451                .match          = socket_mt4_v1_v2,
 452                .checkentry     = socket_mt_v2_check,
 453                .matchsize      = sizeof(struct xt_socket_mtinfo1),
 454                .hooks          = (1 << NF_INET_PRE_ROUTING) |
 455                                  (1 << NF_INET_LOCAL_IN),
 456                .me             = THIS_MODULE,
 457        },
 458#ifdef XT_SOCKET_HAVE_IPV6
 459        {
 460                .name           = "socket",
 461                .revision       = 2,
 462                .family         = NFPROTO_IPV6,
 463                .match          = socket_mt6_v1_v2,
 464                .checkentry     = socket_mt_v2_check,
 465                .matchsize      = sizeof(struct xt_socket_mtinfo1),
 466                .hooks          = (1 << NF_INET_PRE_ROUTING) |
 467                                  (1 << NF_INET_LOCAL_IN),
 468                .me             = THIS_MODULE,
 469        },
 470#endif
 471};
 472
 473static int __init socket_mt_init(void)
 474{
 475        nf_defrag_ipv4_enable();
 476#ifdef XT_SOCKET_HAVE_IPV6
 477        nf_defrag_ipv6_enable();
 478#endif
 479
 480        return xt_register_matches(socket_mt_reg, ARRAY_SIZE(socket_mt_reg));
 481}
 482
 483static void __exit socket_mt_exit(void)
 484{
 485        xt_unregister_matches(socket_mt_reg, ARRAY_SIZE(socket_mt_reg));
 486}
 487
 488module_init(socket_mt_init);
 489module_exit(socket_mt_exit);
 490
 491MODULE_LICENSE("GPL");
 492MODULE_AUTHOR("Krisztian Kovacs, Balazs Scheidler");
 493MODULE_DESCRIPTION("x_tables socket match module");
 494MODULE_ALIAS("ipt_socket");
 495MODULE_ALIAS("ip6t_socket");
 496