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
  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/nf_tproxy_core.h>
  23#include <net/netfilter/ipv4/nf_defrag_ipv4.h>
  24
  25#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
  26#define XT_SOCKET_HAVE_CONNTRACK 1
  27#include <net/netfilter/nf_conntrack.h>
  28#endif
  29
  30static int
  31extract_icmp_fields(const struct sk_buff *skb,
  32                    u8 *protocol,
  33                    __be32 *raddr,
  34                    __be32 *laddr,
  35                    __be16 *rport,
  36                    __be16 *lport)
  37{
  38        unsigned int outside_hdrlen = ip_hdrlen(skb);
  39        struct iphdr *inside_iph, _inside_iph;
  40        struct icmphdr *icmph, _icmph;
  41        __be16 *ports, _ports[2];
  42
  43        icmph = skb_header_pointer(skb, outside_hdrlen,
  44                                   sizeof(_icmph), &_icmph);
  45        if (icmph == NULL)
  46                return 1;
  47
  48        switch (icmph->type) {
  49        case ICMP_DEST_UNREACH:
  50        case ICMP_SOURCE_QUENCH:
  51        case ICMP_REDIRECT:
  52        case ICMP_TIME_EXCEEDED:
  53        case ICMP_PARAMETERPROB:
  54                break;
  55        default:
  56                return 1;
  57        }
  58
  59        inside_iph = skb_header_pointer(skb, outside_hdrlen +
  60                                        sizeof(struct icmphdr),
  61                                        sizeof(_inside_iph), &_inside_iph);
  62        if (inside_iph == NULL)
  63                return 1;
  64
  65        if (inside_iph->protocol != IPPROTO_TCP &&
  66            inside_iph->protocol != IPPROTO_UDP)
  67                return 1;
  68
  69        ports = skb_header_pointer(skb, outside_hdrlen +
  70                                   sizeof(struct icmphdr) +
  71                                   (inside_iph->ihl << 2),
  72                                   sizeof(_ports), &_ports);
  73        if (ports == NULL)
  74                return 1;
  75
  76        /* the inside IP packet is the one quoted from our side, thus
  77         * its saddr is the local address */
  78        *protocol = inside_iph->protocol;
  79        *laddr = inside_iph->saddr;
  80        *lport = ports[0];
  81        *raddr = inside_iph->daddr;
  82        *rport = ports[1];
  83
  84        return 0;
  85}
  86
  87
  88static bool
  89socket_mt(const struct sk_buff *skb, const struct xt_match_param *par)
  90{
  91        const struct iphdr *iph = ip_hdr(skb);
  92        struct udphdr _hdr, *hp = NULL;
  93        struct sock *sk;
  94        __be32 daddr, saddr;
  95        __be16 dport, sport;
  96        u8 protocol;
  97#ifdef XT_SOCKET_HAVE_CONNTRACK
  98        struct nf_conn const *ct;
  99        enum ip_conntrack_info ctinfo;
 100#endif
 101
 102        if (iph->protocol == IPPROTO_UDP || iph->protocol == IPPROTO_TCP) {
 103                hp = skb_header_pointer(skb, ip_hdrlen(skb),
 104                                        sizeof(_hdr), &_hdr);
 105                if (hp == NULL)
 106                        return false;
 107
 108                protocol = iph->protocol;
 109                saddr = iph->saddr;
 110                sport = hp->source;
 111                daddr = iph->daddr;
 112                dport = hp->dest;
 113
 114        } else if (iph->protocol == IPPROTO_ICMP) {
 115                if (extract_icmp_fields(skb, &protocol, &saddr, &daddr,
 116                                        &sport, &dport))
 117                        return false;
 118        } else {
 119                return false;
 120        }
 121
 122#ifdef XT_SOCKET_HAVE_CONNTRACK
 123        /* Do the lookup with the original socket address in case this is a
 124         * reply packet of an established SNAT-ted connection. */
 125
 126        ct = nf_ct_get(skb, &ctinfo);
 127        if (ct && (ct != &nf_conntrack_untracked) &&
 128            ((iph->protocol != IPPROTO_ICMP &&
 129              ctinfo == IP_CT_IS_REPLY + IP_CT_ESTABLISHED) ||
 130             (iph->protocol == IPPROTO_ICMP &&
 131              ctinfo == IP_CT_IS_REPLY + IP_CT_RELATED)) &&
 132            (ct->status & IPS_SRC_NAT_DONE)) {
 133
 134                daddr = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip;
 135                dport = (iph->protocol == IPPROTO_TCP) ?
 136                        ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.tcp.port :
 137                        ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.udp.port;
 138        }
 139#endif
 140
 141        sk = nf_tproxy_get_sock_v4(dev_net(skb->dev), protocol,
 142                                   saddr, daddr, sport, dport, par->in, false);
 143        if (sk != NULL) {
 144                bool wildcard = (sk->sk_state != TCP_TIME_WAIT && inet_sk(sk)->rcv_saddr == 0);
 145
 146                nf_tproxy_put_sock(sk);
 147                if (wildcard)
 148                        sk = NULL;
 149        }
 150
 151        pr_debug("socket match: proto %u %08x:%u -> %08x:%u "
 152                 "(orig %08x:%u) sock %p\n",
 153                 protocol, ntohl(saddr), ntohs(sport),
 154                 ntohl(daddr), ntohs(dport),
 155                 ntohl(iph->daddr), hp ? ntohs(hp->dest) : 0, sk);
 156
 157        return (sk != NULL);
 158}
 159
 160static struct xt_match socket_mt_reg __read_mostly = {
 161        .name           = "socket",
 162        .family         = AF_INET,
 163        .match          = socket_mt,
 164        .hooks          = 1 << NF_INET_PRE_ROUTING,
 165        .me             = THIS_MODULE,
 166};
 167
 168static int __init socket_mt_init(void)
 169{
 170        nf_defrag_ipv4_enable();
 171        return xt_register_match(&socket_mt_reg);
 172}
 173
 174static void __exit socket_mt_exit(void)
 175{
 176        xt_unregister_match(&socket_mt_reg);
 177}
 178
 179module_init(socket_mt_init);
 180module_exit(socket_mt_exit);
 181
 182MODULE_LICENSE("GPL");
 183MODULE_AUTHOR("Krisztian Kovacs, Balazs Scheidler");
 184MODULE_DESCRIPTION("x_tables socket match module");
 185MODULE_ALIAS("ipt_socket");
 186