linux/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c
<<
>>
Prefs
   1
   2/* (C) 1999-2001 Paul `Rusty' Russell
   3 * (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org>
   4 *
   5 * This program is free software; you can redistribute it and/or modify
   6 * it under the terms of the GNU General Public License version 2 as
   7 * published by the Free Software Foundation.
   8 */
   9
  10#include <linux/types.h>
  11#include <linux/ip.h>
  12#include <linux/netfilter.h>
  13#include <linux/module.h>
  14#include <linux/skbuff.h>
  15#include <linux/icmp.h>
  16#include <linux/sysctl.h>
  17#include <net/route.h>
  18#include <net/ip.h>
  19
  20#include <linux/netfilter_ipv4.h>
  21#include <net/netfilter/nf_conntrack.h>
  22#include <net/netfilter/nf_conntrack_helper.h>
  23#include <net/netfilter/nf_conntrack_l4proto.h>
  24#include <net/netfilter/nf_conntrack_l3proto.h>
  25#include <net/netfilter/nf_conntrack_core.h>
  26#include <net/netfilter/ipv4/nf_conntrack_ipv4.h>
  27#include <net/netfilter/nf_nat_helper.h>
  28#include <net/netfilter/ipv4/nf_defrag_ipv4.h>
  29#include <net/netfilter/nf_log.h>
  30
  31int (*nf_nat_seq_adjust_hook)(struct sk_buff *skb,
  32                              struct nf_conn *ct,
  33                              enum ip_conntrack_info ctinfo);
  34EXPORT_SYMBOL_GPL(nf_nat_seq_adjust_hook);
  35
  36static bool ipv4_pkt_to_tuple(const struct sk_buff *skb, unsigned int nhoff,
  37                              struct nf_conntrack_tuple *tuple)
  38{
  39        const __be32 *ap;
  40        __be32 _addrs[2];
  41        ap = skb_header_pointer(skb, nhoff + offsetof(struct iphdr, saddr),
  42                                sizeof(u_int32_t) * 2, _addrs);
  43        if (ap == NULL)
  44                return false;
  45
  46        tuple->src.u3.ip = ap[0];
  47        tuple->dst.u3.ip = ap[1];
  48
  49        return true;
  50}
  51
  52static bool ipv4_invert_tuple(struct nf_conntrack_tuple *tuple,
  53                              const struct nf_conntrack_tuple *orig)
  54{
  55        tuple->src.u3.ip = orig->dst.u3.ip;
  56        tuple->dst.u3.ip = orig->src.u3.ip;
  57
  58        return true;
  59}
  60
  61static int ipv4_print_tuple(struct seq_file *s,
  62                            const struct nf_conntrack_tuple *tuple)
  63{
  64        return seq_printf(s, "src=%pI4 dst=%pI4 ",
  65                          &tuple->src.u3.ip, &tuple->dst.u3.ip);
  66}
  67
  68static int ipv4_get_l4proto(const struct sk_buff *skb, unsigned int nhoff,
  69                            unsigned int *dataoff, u_int8_t *protonum)
  70{
  71        const struct iphdr *iph;
  72        struct iphdr _iph;
  73
  74        iph = skb_header_pointer(skb, nhoff, sizeof(_iph), &_iph);
  75        if (iph == NULL)
  76                return -NF_DROP;
  77
  78        /* Conntrack defragments packets, we might still see fragments
  79         * inside ICMP packets though. */
  80        if (iph->frag_off & htons(IP_OFFSET))
  81                return -NF_DROP;
  82
  83        *dataoff = nhoff + (iph->ihl << 2);
  84        *protonum = iph->protocol;
  85
  86        return NF_ACCEPT;
  87}
  88
  89static unsigned int ipv4_confirm(unsigned int hooknum,
  90                                 struct sk_buff *skb,
  91                                 const struct net_device *in,
  92                                 const struct net_device *out,
  93                                 int (*okfn)(struct sk_buff *))
  94{
  95        struct nf_conn *ct;
  96        enum ip_conntrack_info ctinfo;
  97        const struct nf_conn_help *help;
  98        const struct nf_conntrack_helper *helper;
  99        unsigned int ret;
 100
 101        /* This is where we call the helper: as the packet goes out. */
 102        ct = nf_ct_get(skb, &ctinfo);
 103        if (!ct || ctinfo == IP_CT_RELATED + IP_CT_IS_REPLY)
 104                goto out;
 105
 106        help = nfct_help(ct);
 107        if (!help)
 108                goto out;
 109
 110        /* rcu_read_lock()ed by nf_hook_slow */
 111        helper = rcu_dereference(help->helper);
 112        if (!helper)
 113                goto out;
 114
 115        ret = helper->help(skb, skb_network_offset(skb) + ip_hdrlen(skb),
 116                           ct, ctinfo);
 117        if (ret != NF_ACCEPT) {
 118                nf_log_packet(NFPROTO_IPV4, hooknum, skb, in, out, NULL,
 119                              "nf_ct_%s: dropping packet", helper->name);
 120                return ret;
 121        }
 122
 123        if (test_bit(IPS_SEQ_ADJUST_BIT, &ct->status)) {
 124                typeof(nf_nat_seq_adjust_hook) seq_adjust;
 125
 126                seq_adjust = rcu_dereference(nf_nat_seq_adjust_hook);
 127                if (!seq_adjust || !seq_adjust(skb, ct, ctinfo)) {
 128                        NF_CT_STAT_INC_ATOMIC(nf_ct_net(ct), drop);
 129                        return NF_DROP;
 130                }
 131        }
 132out:
 133        /* We've seen it coming out the other side: confirm it */
 134        return nf_conntrack_confirm(skb);
 135}
 136
 137static unsigned int ipv4_conntrack_in(unsigned int hooknum,
 138                                      struct sk_buff *skb,
 139                                      const struct net_device *in,
 140                                      const struct net_device *out,
 141                                      int (*okfn)(struct sk_buff *))
 142{
 143        return nf_conntrack_in(dev_net(in), PF_INET, hooknum, skb);
 144}
 145
 146static unsigned int ipv4_conntrack_local(unsigned int hooknum,
 147                                         struct sk_buff *skb,
 148                                         const struct net_device *in,
 149                                         const struct net_device *out,
 150                                         int (*okfn)(struct sk_buff *))
 151{
 152        /* root is playing with raw sockets. */
 153        if (skb->len < sizeof(struct iphdr) ||
 154            ip_hdrlen(skb) < sizeof(struct iphdr))
 155                return NF_ACCEPT;
 156        return nf_conntrack_in(dev_net(out), PF_INET, hooknum, skb);
 157}
 158
 159/* Connection tracking may drop packets, but never alters them, so
 160   make it the first hook. */
 161static struct nf_hook_ops ipv4_conntrack_ops[] __read_mostly = {
 162        {
 163                .hook           = ipv4_conntrack_in,
 164                .owner          = THIS_MODULE,
 165                .pf             = NFPROTO_IPV4,
 166                .hooknum        = NF_INET_PRE_ROUTING,
 167                .priority       = NF_IP_PRI_CONNTRACK,
 168        },
 169        {
 170                .hook           = ipv4_conntrack_local,
 171                .owner          = THIS_MODULE,
 172                .pf             = NFPROTO_IPV4,
 173                .hooknum        = NF_INET_LOCAL_OUT,
 174                .priority       = NF_IP_PRI_CONNTRACK,
 175        },
 176        {
 177                .hook           = ipv4_confirm,
 178                .owner          = THIS_MODULE,
 179                .pf             = NFPROTO_IPV4,
 180                .hooknum        = NF_INET_POST_ROUTING,
 181                .priority       = NF_IP_PRI_CONNTRACK_CONFIRM,
 182        },
 183        {
 184                .hook           = ipv4_confirm,
 185                .owner          = THIS_MODULE,
 186                .pf             = NFPROTO_IPV4,
 187                .hooknum        = NF_INET_LOCAL_IN,
 188                .priority       = NF_IP_PRI_CONNTRACK_CONFIRM,
 189        },
 190};
 191
 192#if defined(CONFIG_SYSCTL) && defined(CONFIG_NF_CONNTRACK_PROC_COMPAT)
 193static int log_invalid_proto_min = 0;
 194static int log_invalid_proto_max = 255;
 195
 196static ctl_table ip_ct_sysctl_table[] = {
 197        {
 198                .ctl_name       = NET_IPV4_NF_CONNTRACK_MAX,
 199                .procname       = "ip_conntrack_max",
 200                .data           = &nf_conntrack_max,
 201                .maxlen         = sizeof(int),
 202                .mode           = 0644,
 203                .proc_handler   = proc_dointvec,
 204        },
 205        {
 206                .ctl_name       = NET_IPV4_NF_CONNTRACK_COUNT,
 207                .procname       = "ip_conntrack_count",
 208                .data           = &init_net.ct.count,
 209                .maxlen         = sizeof(int),
 210                .mode           = 0444,
 211                .proc_handler   = proc_dointvec,
 212        },
 213        {
 214                .ctl_name       = NET_IPV4_NF_CONNTRACK_BUCKETS,
 215                .procname       = "ip_conntrack_buckets",
 216                .data           = &nf_conntrack_htable_size,
 217                .maxlen         = sizeof(unsigned int),
 218                .mode           = 0444,
 219                .proc_handler   = proc_dointvec,
 220        },
 221        {
 222                .ctl_name       = NET_IPV4_NF_CONNTRACK_CHECKSUM,
 223                .procname       = "ip_conntrack_checksum",
 224                .data           = &init_net.ct.sysctl_checksum,
 225                .maxlen         = sizeof(int),
 226                .mode           = 0644,
 227                .proc_handler   = proc_dointvec,
 228        },
 229        {
 230                .ctl_name       = NET_IPV4_NF_CONNTRACK_LOG_INVALID,
 231                .procname       = "ip_conntrack_log_invalid",
 232                .data           = &init_net.ct.sysctl_log_invalid,
 233                .maxlen         = sizeof(unsigned int),
 234                .mode           = 0644,
 235                .proc_handler   = proc_dointvec_minmax,
 236                .strategy       = sysctl_intvec,
 237                .extra1         = &log_invalid_proto_min,
 238                .extra2         = &log_invalid_proto_max,
 239        },
 240        {
 241                .ctl_name       = 0
 242        }
 243};
 244#endif /* CONFIG_SYSCTL && CONFIG_NF_CONNTRACK_PROC_COMPAT */
 245
 246/* Fast function for those who don't want to parse /proc (and I don't
 247   blame them). */
 248/* Reversing the socket's dst/src point of view gives us the reply
 249   mapping. */
 250static int
 251getorigdst(struct sock *sk, int optval, void __user *user, int *len)
 252{
 253        const struct inet_sock *inet = inet_sk(sk);
 254        const struct nf_conntrack_tuple_hash *h;
 255        struct nf_conntrack_tuple tuple;
 256
 257        memset(&tuple, 0, sizeof(tuple));
 258        tuple.src.u3.ip = inet->rcv_saddr;
 259        tuple.src.u.tcp.port = inet->sport;
 260        tuple.dst.u3.ip = inet->daddr;
 261        tuple.dst.u.tcp.port = inet->dport;
 262        tuple.src.l3num = PF_INET;
 263        tuple.dst.protonum = sk->sk_protocol;
 264
 265        /* We only do TCP and SCTP at the moment: is there a better way? */
 266        if (sk->sk_protocol != IPPROTO_TCP && sk->sk_protocol != IPPROTO_SCTP) {
 267                pr_debug("SO_ORIGINAL_DST: Not a TCP/SCTP socket\n");
 268                return -ENOPROTOOPT;
 269        }
 270
 271        if ((unsigned int) *len < sizeof(struct sockaddr_in)) {
 272                pr_debug("SO_ORIGINAL_DST: len %d not %Zu\n",
 273                         *len, sizeof(struct sockaddr_in));
 274                return -EINVAL;
 275        }
 276
 277        h = nf_conntrack_find_get(sock_net(sk), &tuple);
 278        if (h) {
 279                struct sockaddr_in sin;
 280                struct nf_conn *ct = nf_ct_tuplehash_to_ctrack(h);
 281
 282                sin.sin_family = AF_INET;
 283                sin.sin_port = ct->tuplehash[IP_CT_DIR_ORIGINAL]
 284                        .tuple.dst.u.tcp.port;
 285                sin.sin_addr.s_addr = ct->tuplehash[IP_CT_DIR_ORIGINAL]
 286                        .tuple.dst.u3.ip;
 287                memset(sin.sin_zero, 0, sizeof(sin.sin_zero));
 288
 289                pr_debug("SO_ORIGINAL_DST: %pI4 %u\n",
 290                         &sin.sin_addr.s_addr, ntohs(sin.sin_port));
 291                nf_ct_put(ct);
 292                if (copy_to_user(user, &sin, sizeof(sin)) != 0)
 293                        return -EFAULT;
 294                else
 295                        return 0;
 296        }
 297        pr_debug("SO_ORIGINAL_DST: Can't find %pI4/%u-%pI4/%u.\n",
 298                 &tuple.src.u3.ip, ntohs(tuple.src.u.tcp.port),
 299                 &tuple.dst.u3.ip, ntohs(tuple.dst.u.tcp.port));
 300        return -ENOENT;
 301}
 302
 303#if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE)
 304
 305#include <linux/netfilter/nfnetlink.h>
 306#include <linux/netfilter/nfnetlink_conntrack.h>
 307
 308static int ipv4_tuple_to_nlattr(struct sk_buff *skb,
 309                                const struct nf_conntrack_tuple *tuple)
 310{
 311        NLA_PUT_BE32(skb, CTA_IP_V4_SRC, tuple->src.u3.ip);
 312        NLA_PUT_BE32(skb, CTA_IP_V4_DST, tuple->dst.u3.ip);
 313        return 0;
 314
 315nla_put_failure:
 316        return -1;
 317}
 318
 319static const struct nla_policy ipv4_nla_policy[CTA_IP_MAX+1] = {
 320        [CTA_IP_V4_SRC] = { .type = NLA_U32 },
 321        [CTA_IP_V4_DST] = { .type = NLA_U32 },
 322};
 323
 324static int ipv4_nlattr_to_tuple(struct nlattr *tb[],
 325                                struct nf_conntrack_tuple *t)
 326{
 327        if (!tb[CTA_IP_V4_SRC] || !tb[CTA_IP_V4_DST])
 328                return -EINVAL;
 329
 330        t->src.u3.ip = nla_get_be32(tb[CTA_IP_V4_SRC]);
 331        t->dst.u3.ip = nla_get_be32(tb[CTA_IP_V4_DST]);
 332
 333        return 0;
 334}
 335
 336static int ipv4_nlattr_tuple_size(void)
 337{
 338        return nla_policy_len(ipv4_nla_policy, CTA_IP_MAX + 1);
 339}
 340#endif
 341
 342static struct nf_sockopt_ops so_getorigdst = {
 343        .pf             = PF_INET,
 344        .get_optmin     = SO_ORIGINAL_DST,
 345        .get_optmax     = SO_ORIGINAL_DST+1,
 346        .get            = &getorigdst,
 347        .owner          = THIS_MODULE,
 348};
 349
 350struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv4 __read_mostly = {
 351        .l3proto         = PF_INET,
 352        .name            = "ipv4",
 353        .pkt_to_tuple    = ipv4_pkt_to_tuple,
 354        .invert_tuple    = ipv4_invert_tuple,
 355        .print_tuple     = ipv4_print_tuple,
 356        .get_l4proto     = ipv4_get_l4proto,
 357#if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE)
 358        .tuple_to_nlattr = ipv4_tuple_to_nlattr,
 359        .nlattr_tuple_size = ipv4_nlattr_tuple_size,
 360        .nlattr_to_tuple = ipv4_nlattr_to_tuple,
 361        .nla_policy      = ipv4_nla_policy,
 362#endif
 363#if defined(CONFIG_SYSCTL) && defined(CONFIG_NF_CONNTRACK_PROC_COMPAT)
 364        .ctl_table_path  = nf_net_ipv4_netfilter_sysctl_path,
 365        .ctl_table       = ip_ct_sysctl_table,
 366#endif
 367        .me              = THIS_MODULE,
 368};
 369
 370module_param_call(hashsize, nf_conntrack_set_hashsize, param_get_uint,
 371                  &nf_conntrack_htable_size, 0600);
 372
 373MODULE_ALIAS("nf_conntrack-" __stringify(AF_INET));
 374MODULE_ALIAS("ip_conntrack");
 375MODULE_LICENSE("GPL");
 376
 377static int __init nf_conntrack_l3proto_ipv4_init(void)
 378{
 379        int ret = 0;
 380
 381        need_conntrack();
 382        nf_defrag_ipv4_enable();
 383
 384        ret = nf_register_sockopt(&so_getorigdst);
 385        if (ret < 0) {
 386                printk(KERN_ERR "Unable to register netfilter socket option\n");
 387                return ret;
 388        }
 389
 390        ret = nf_conntrack_l4proto_register(&nf_conntrack_l4proto_tcp4);
 391        if (ret < 0) {
 392                printk("nf_conntrack_ipv4: can't register tcp.\n");
 393                goto cleanup_sockopt;
 394        }
 395
 396        ret = nf_conntrack_l4proto_register(&nf_conntrack_l4proto_udp4);
 397        if (ret < 0) {
 398                printk("nf_conntrack_ipv4: can't register udp.\n");
 399                goto cleanup_tcp;
 400        }
 401
 402        ret = nf_conntrack_l4proto_register(&nf_conntrack_l4proto_icmp);
 403        if (ret < 0) {
 404                printk("nf_conntrack_ipv4: can't register icmp.\n");
 405                goto cleanup_udp;
 406        }
 407
 408        ret = nf_conntrack_l3proto_register(&nf_conntrack_l3proto_ipv4);
 409        if (ret < 0) {
 410                printk("nf_conntrack_ipv4: can't register ipv4\n");
 411                goto cleanup_icmp;
 412        }
 413
 414        ret = nf_register_hooks(ipv4_conntrack_ops,
 415                                ARRAY_SIZE(ipv4_conntrack_ops));
 416        if (ret < 0) {
 417                printk("nf_conntrack_ipv4: can't register hooks.\n");
 418                goto cleanup_ipv4;
 419        }
 420#if defined(CONFIG_PROC_FS) && defined(CONFIG_NF_CONNTRACK_PROC_COMPAT)
 421        ret = nf_conntrack_ipv4_compat_init();
 422        if (ret < 0)
 423                goto cleanup_hooks;
 424#endif
 425        return ret;
 426#if defined(CONFIG_PROC_FS) && defined(CONFIG_NF_CONNTRACK_PROC_COMPAT)
 427 cleanup_hooks:
 428        nf_unregister_hooks(ipv4_conntrack_ops, ARRAY_SIZE(ipv4_conntrack_ops));
 429#endif
 430 cleanup_ipv4:
 431        nf_conntrack_l3proto_unregister(&nf_conntrack_l3proto_ipv4);
 432 cleanup_icmp:
 433        nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_icmp);
 434 cleanup_udp:
 435        nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_udp4);
 436 cleanup_tcp:
 437        nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_tcp4);
 438 cleanup_sockopt:
 439        nf_unregister_sockopt(&so_getorigdst);
 440        return ret;
 441}
 442
 443static void __exit nf_conntrack_l3proto_ipv4_fini(void)
 444{
 445        synchronize_net();
 446#if defined(CONFIG_PROC_FS) && defined(CONFIG_NF_CONNTRACK_PROC_COMPAT)
 447        nf_conntrack_ipv4_compat_fini();
 448#endif
 449        nf_unregister_hooks(ipv4_conntrack_ops, ARRAY_SIZE(ipv4_conntrack_ops));
 450        nf_conntrack_l3proto_unregister(&nf_conntrack_l3proto_ipv4);
 451        nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_icmp);
 452        nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_udp4);
 453        nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_tcp4);
 454        nf_unregister_sockopt(&so_getorigdst);
 455}
 456
 457module_init(nf_conntrack_l3proto_ipv4_init);
 458module_exit(nf_conntrack_l3proto_ipv4_fini);
 459
 460void need_ipv4_conntrack(void)
 461{
 462        return;
 463}
 464EXPORT_SYMBOL_GPL(need_ipv4_conntrack);
 465
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.