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_ASSURED, 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, struct nf_conn *tmpl, 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 .procname = "nf_conntrack_udplite_timeout", 150 .data = &nf_ct_udplite_timeout, 151 .maxlen = sizeof(unsigned int), 152 .mode = 0644, 153 .proc_handler = proc_dointvec_jiffies, 154 }, 155 { 156 .procname = "nf_conntrack_udplite_timeout_stream", 157 .data = &nf_ct_udplite_timeout_stream, 158 .maxlen = sizeof(unsigned int), 159 .mode = 0644, 160 .proc_handler = proc_dointvec_jiffies, 161 }, 162 { } 163}; 164#endif /* CONFIG_SYSCTL */ 165 166static struct nf_conntrack_l4proto nf_conntrack_l4proto_udplite4 __read_mostly = 167{ 168 .l3proto = PF_INET, 169 .l4proto = IPPROTO_UDPLITE, 170 .name = "udplite", 171 .pkt_to_tuple = udplite_pkt_to_tuple, 172 .invert_tuple = udplite_invert_tuple, 173 .print_tuple = udplite_print_tuple, 174 .packet = udplite_packet, 175 .new = udplite_new, 176 .error = udplite_error, 177#if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE) 178 .tuple_to_nlattr = nf_ct_port_tuple_to_nlattr, 179 .nlattr_tuple_size = nf_ct_port_nlattr_tuple_size, 180 .nlattr_to_tuple = nf_ct_port_nlattr_to_tuple, 181 .nla_policy = nf_ct_port_nla_policy, 182#endif 183#ifdef CONFIG_SYSCTL 184 .ctl_table_users = &udplite_sysctl_table_users, 185 .ctl_table_header = &udplite_sysctl_header, 186 .ctl_table = udplite_sysctl_table, 187#endif 188}; 189 190static struct nf_conntrack_l4proto nf_conntrack_l4proto_udplite6 __read_mostly = 191{ 192 .l3proto = PF_INET6, 193 .l4proto = IPPROTO_UDPLITE, 194 .name = "udplite", 195 .pkt_to_tuple = udplite_pkt_to_tuple, 196 .invert_tuple = udplite_invert_tuple, 197 .print_tuple = udplite_print_tuple, 198 .packet = udplite_packet, 199 .new = udplite_new, 200 .error = udplite_error, 201#if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE) 202 .tuple_to_nlattr = nf_ct_port_tuple_to_nlattr, 203 .nlattr_tuple_size = nf_ct_port_nlattr_tuple_size, 204 .nlattr_to_tuple = nf_ct_port_nlattr_to_tuple, 205 .nla_policy = nf_ct_port_nla_policy, 206#endif 207#ifdef CONFIG_SYSCTL 208 .ctl_table_users = &udplite_sysctl_table_users, 209 .ctl_table_header = &udplite_sysctl_header, 210 .ctl_table = udplite_sysctl_table, 211#endif 212}; 213 214static int __init nf_conntrack_proto_udplite_init(void) 215{ 216 int err; 217 218 err = nf_conntrack_l4proto_register(&nf_conntrack_l4proto_udplite4); 219 if (err < 0) 220 goto err1; 221 err = nf_conntrack_l4proto_register(&nf_conntrack_l4proto_udplite6); 222 if (err < 0) 223 goto err2; 224 return 0; 225err2: 226 nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_udplite4); 227err1: 228 return err; 229} 230 231static void __exit nf_conntrack_proto_udplite_exit(void) 232{ 233 nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_udplite6); 234 nf_conntrack_l4proto_unregister(&nf_conntrack_l4proto_udplite4); 235} 236 237module_init(nf_conntrack_proto_udplite_init); 238module_exit(nf_conntrack_proto_udplite_exit); 239 240MODULE_LICENSE("GPL"); 241