linux/net/netfilter/nf_conntrack_proto_udplite.c
<<
>>
Prefs
   1/* (C) 1999-2001 Paul `Rusty' Russell
   2 * (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org>
   3 * (C) 2007 Patrick McHardy <kaber@trash.net>
   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/timer.h>
  12#include <linux/module.h>
  13#include <linux/udp.h>
  14#include <linux/seq_file.h>
  15#include <linux/skbuff.h>
  16#include <linux/ipv6.h>
  17#include <net/ip6_checksum.h>
  18#include <net/checksum.h>
  19
  20#include <linux/netfilter.h>
  21#include <linux/netfilter_ipv4.h>
  22#include <linux/netfilter_ipv6.h>
  23#include <net/netfilter/nf_conntrack_l4proto.h>
  24#include <net/netfilter/nf_conntrack_ecache.h>
  25#include <net/netfilter/nf_log.h>
  26
  27static unsigned int nf_ct_udplite_timeout __read_mostly = 30*HZ;
  28static unsigned int nf_ct_udplite_timeout_stream __read_mostly = 180*HZ;
  29
  30static bool udplite_pkt_to_tuple(const struct sk_buff *skb,
  31                                 unsigned int dataoff,
  32                                 struct nf_conntrack_tuple *tuple)
  33{
  34        const struct udphdr *hp;
  35        struct udphdr _hdr;
  36
  37        hp = skb_header_pointer(skb, dataoff, sizeof(_hdr), &_hdr);
  38        if (hp == NULL)
  39                return false;
  40
  41        tuple->src.u.udp.port = hp->source;
  42        tuple->dst.u.udp.port = hp->dest;
  43        return true;
  44}
  45
  46static bool udplite_invert_tuple(struct nf_conntrack_tuple *tuple,
  47                                 const struct nf_conntrack_tuple *orig)
  48{
  49        tuple->src.u.udp.port = orig->dst.u.udp.port;
  50        tuple->dst.u.udp.port = orig->src.u.udp.port;
  51        return true;
  52}
  53
  54/* Print out the per-protocol part of the tuple. */
  55static int udplite_print_tuple(struct seq_file *s,
  56                               const struct nf_conntrack_tuple *tuple)
  57{
  58        return seq_printf(s, "sport=%hu dport=%hu ",
  59                          ntohs(tuple->src.u.udp.port),
  60                          ntohs(tuple->dst.u.udp.port));
  61}
  62
  63/* Returns verdict for packet, and may modify conntracktype */
  64static int udplite_packet(struct nf_conn *ct,
  65                          const struct sk_buff *skb,
  66                          unsigned int dataoff,
  67                          enum ip_conntrack_info ctinfo,
  68                          u_int8_t pf,
  69                          unsigned int hooknum)
  70{
  71        /* If we've seen traffic both ways, this is some kind of UDP
  72           stream.  Extend timeout. */
  73        if (test_bit(IPS_SEEN_REPLY_BIT, &ct->status)) {
  74                nf_ct_refresh_acct(ct, ctinfo, skb,
  75                                   nf_ct_udplite_timeout_stream);
  76                /* Also, more likely to be important, and not a probe */
  77                if (!test_and_set_bit(IPS_ASSURED_BIT, &ct->status))
  78                        nf_conntrack_event_cache(IPCT_STATUS, ct);
  79        } else
  80                nf_ct_refresh_acct(ct, ctinfo, skb, nf_ct_udplite_timeout);
  81
  82        return NF_ACCEPT;
  83}
  84
  85/* Called when a new connection for this protocol found. */
  86static bool udplite_new(struct nf_conn *ct, const struct sk_buff *skb,
  87                        unsigned int dataoff)
  88{
  89        return true;
  90}
  91
  92static int udplite_error(struct net *net,
  93                         struct sk_buff *skb,
  94                         unsigned int dataoff,
  95                         enum ip_conntrack_info *ctinfo,
  96                         u_int8_t pf,
  97                         unsigned int hooknum)
  98{
  99        unsigned int udplen = skb->len - dataoff;
 100        const struct udphdr *hdr;
 101        struct udphdr _hdr;
 102        unsigned int cscov;
 103
 104        /* Header is too small? */
 105        hdr = skb_header_pointer(skb, dataoff, sizeof(_hdr), &_hdr);
 106        if (hdr == NULL) {
 107                if (LOG_INVALID(net, IPPROTO_UDPLITE))
 108                        nf_log_packet(pf, 0, skb, NULL, NULL, NULL,
 109                                      "nf_ct_udplite: short packet ");
 110                return -NF_ACCEPT;
 111        }
 112
 113        cscov = ntohs(hdr->len);
 114        if (cscov == 0)
 115                cscov = udplen;
 116        else if (cscov < sizeof(*hdr) || cscov > udplen) {
 117                if (LOG_INVALID(net, IPPROTO_UDPLITE))
 118                        nf_log_packet(pf, 0, skb, NULL, NULL, NULL,
 119                                "nf_ct_udplite: invalid checksum coverage ");
 120                return -NF_ACCEPT;
 121        }
 122
 123        /* UDPLITE mandates checksums */
 124        if (!hdr->check) {
 125                if (LOG_INVALID(net, IPPROTO_UDPLITE))
 126                        nf_log_packet(pf, 0, skb, NULL, NULL, NULL,
 127                                      "nf_ct_udplite: checksum missing ");
 128                return -NF_ACCEPT;
 129        }
 130
 131        /* Checksum invalid? Ignore. */
 132        if (net->ct.sysctl_checksum && hooknum == NF_INET_PRE_ROUTING &&
 133            nf_checksum_partial(skb, hooknum, dataoff, cscov, IPPROTO_UDP,
 134                                pf)) {
 135                if (LOG_INVALID(net, IPPROTO_UDPLITE))
 136                        nf_log_packet(pf, 0, skb, NULL, NULL, NULL,
 137                                      "nf_ct_udplite: bad UDPLite checksum ");
 138                return -NF_ACCEPT;
 139        }
 140
 141        return NF_ACCEPT;
 142}
 143
 144#ifdef CONFIG_SYSCTL
 145static unsigned int udplite_sysctl_table_users;
 146static struct ctl_table_header *udplite_sysctl_header;
 147static struct ctl_table udplite_sysctl_table[] = {
 148        {
 149                .ctl_name       = CTL_UNNUMBERED,
 150                .procname       = "nf_conntrack_udplite_timeout",
 151                .data           = &nf_ct_udplite_timeout,
 152                .maxlen         = sizeof(unsigned int),
 153                .mode           = 0644,
 154                .proc_handler   = &proc_dointvec_jiffies,
 155        },
 156        {
 157                .ctl_name       = CTL_UNNUMBERED,
 158                .procname       = "nf_conntrack_udplite_timeout_stream",
 159                .data           = &nf_ct_udplite_timeout_stream,
 160                .maxlen         = sizeof(unsigned int),
 161                .mode           = 0644,
 162                .proc_handler   = &proc_dointvec_jiffies,
 163        },
 164        {
 165                .ctl_name       = 0
 166        }
 167};
 168#endif /* CONFIG_SYSCTL */
 169
 170static struct nf_conntrack_l4proto nf_conntrack_l4proto_udplite4 __read_mostly =
 171{
 172        .l3proto                = PF_INET,
 173        .l4proto                = IPPROTO_UDPLITE,
 174        .name                   = "udplite",
 175        .pkt_to_tuple           = udplite_pkt_to_tuple,
 176        .invert_tuple           = udplite_invert_tuple,
 177        .print_tuple            = udplite_print_tuple,
 178        .packet                 = udplite_packet,
 179        .new                    = udplite_new,
 180        .error                  = udplite_error,
 181#if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE)
 182        .tuple_to_nlattr        = nf_ct_port_tuple_to_nlattr,
 183        .nlattr_to_tuple        = nf_ct_port_nlattr_to_tuple,
 184        .nla_policy             = nf_ct_port_nla_policy,
 185#endif
 186#ifdef CONFIG_SYSCTL
 187        .ctl_table_users        = &udplite_sysctl_table_users,
 188        .ctl_table_header       = &udplite_sysctl_header,
 189        .ctl_table              = udplite_sysctl_table,
 190#endif
 191};
 192
 193static struct nf_conntrack_l4proto nf_conntrack_l4proto_udplite6 __read_mostly =
 194{
 195        .l3proto                = PF_INET6,
 196        .l4proto                = IPPROTO_UDPLITE,
 197        .name                   = "udplite",
 198        .pkt_to_tuple           = udplite_pkt_to_tuple,
 199        .invert_tuple           = udplite_invert_tuple,
 200        .print_tuple            = udplite_print_tuple,
 201        .packet                 = udplite_packet,
 202        .new                    = udplite_new,
 203        .error                  = udplite_error,
 204#if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE)
 205        .tuple_to_nlattr        = nf_ct_port_tuple_to_nlattr,
 206        .nlattr_to_tuple        = nf_ct_port_nlattr_to_tuple,
 207        .nla_policy             = nf_ct_port_nla_policy,
 208#endif
 209#ifdef CONFIG_SYSCTL
 210        .ctl_table_users        = &udplite_sysctl_table_users,
 211        .ctl_table_header       = &udplite_sysctl_header,
 212        .ctl_table              = udplite_sysctl_table,
 213#endif
 214};
 215
 216static int __init nf_conntrack_proto_udplite_init(void)
 217{
 218        int err;
 219
 220        err = nf_conntrack_l4proto_register(&nf_conntrack_l4proto_udplite4);
 221        if (err < 0)
 222                goto err1;
 223        err = nf_conntrack_l4proto_register(&nf_conntrack_l4proto_udplite6);
 224        if (err < 0)
 225                goto err2;
 226        return 0;
 227err2:
 228        nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_udplite4);
 229err1:
 230        return err;
 231}
 232
 233static void __exit nf_conntrack_proto_udplite_exit(void)
 234{
 235        nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_udplite6);
 236        nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_udplite4);
 237}
 238
 239module_init(nf_conntrack_proto_udplite_init);
 240module_exit(nf_conntrack_proto_udplite_exit);
 241
 242MODULE_LICENSE("GPL");
 243