1/* (C) 1999-2001 Paul `Rusty' Russell 2 * (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org> 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License version 2 as 6 * published by the Free Software Foundation. 7 */ 8 9#include <linux/types.h> 10#include <linux/timer.h> 11#include <linux/netfilter.h> 12#include <linux/in.h> 13#include <linux/icmp.h> 14#include <linux/seq_file.h> 15#include <net/ip.h> 16#include <net/checksum.h> 17#include <linux/netfilter_ipv4.h> 18#include <net/netfilter/nf_conntrack_tuple.h> 19#include <net/netfilter/nf_conntrack_l4proto.h> 20#include <net/netfilter/nf_conntrack_core.h> 21 22static unsigned long nf_ct_icmp_timeout __read_mostly = 30*HZ; 23 24#define DEBUGP(format, args...) 25 26static int icmp_pkt_to_tuple(const struct sk_buff *skb, 27 unsigned int dataoff, 28 struct nf_conntrack_tuple *tuple) 29{ 30 struct icmphdr _hdr, *hp; 31 32 hp = skb_header_pointer(skb, dataoff, sizeof(_hdr), &_hdr); 33 if (hp == NULL) 34 return 0; 35 36 tuple->dst.u.icmp.type = hp->type; 37 tuple->src.u.icmp.id = hp->un.echo.id; 38 tuple->dst.u.icmp.code = hp->code; 39 40 return 1; 41} 42 43/* Add 1; spaces filled with 0. */ 44static const u_int8_t invmap[] = { 45 [ICMP_ECHO] = ICMP_ECHOREPLY + 1, 46 [ICMP_ECHOREPLY] = ICMP_ECHO + 1, 47 [ICMP_TIMESTAMP] = ICMP_TIMESTAMPREPLY + 1, 48 [ICMP_TIMESTAMPREPLY] = ICMP_TIMESTAMP + 1, 49 [ICMP_INFO_REQUEST] = ICMP_INFO_REPLY + 1, 50 [ICMP_INFO_REPLY] = ICMP_INFO_REQUEST + 1, 51 [ICMP_ADDRESS] = ICMP_ADDRESSREPLY + 1, 52 [ICMP_ADDRESSREPLY] = ICMP_ADDRESS + 1 53}; 54 55static int icmp_invert_tuple(struct nf_conntrack_tuple *tuple, 56 const struct nf_conntrack_tuple *orig) 57{ 58 if (orig->dst.u.icmp.type >= sizeof(invmap) 59 || !invmap[orig->dst.u.icmp.type]) 60 return 0; 61 62 tuple->src.u.icmp.id = orig->src.u.icmp.id; 63 tuple->dst.u.icmp.type = invmap[orig->dst.u.icmp.type] - 1; 64 tuple->dst.u.icmp.code = orig->dst.u.icmp.code; 65 return 1; 66} 67 68/* Print out the per-protocol part of the tuple. */ 69static int icmp_print_tuple(struct seq_file *s, 70 const struct nf_conntrack_tuple *tuple) 71{ 72 return seq_printf(s, "type=%u code=%u id=%u ", 73 tuple->dst.u.icmp.type, 74 tuple->dst.u.icmp.code, 75 ntohs(tuple->src.u.icmp.id)); 76} 77 78/* Print out the private part of the conntrack. */ 79static int icmp_print_conntrack(struct seq_file *s, 80 const struct nf_conn *conntrack) 81{ 82 return 0; 83} 84 85/* Returns verdict for packet, or -1 for invalid. */ 86static int icmp_packet(struct nf_conn *ct, 87 const struct sk_buff *skb, 88 unsigned int dataoff, 89 enum ip_conntrack_info ctinfo, 90 int pf, 91 unsigned int hooknum) 92{ 93 /* Try to delete connection immediately after all replies: 94 won't actually vanish as we still have skb, and del_timer 95 means this will only run once even if count hits zero twice 96 (theoretically possible with SMP) */ 97 if (CTINFO2DIR(ctinfo) == IP_CT_DIR_REPLY) { 98 if (atomic_dec_and_test(&ct->proto.icmp.count) 99 && del_timer(&ct->timeout)) 100 ct->timeout.function((unsigned long)ct); 101 } else { 102 atomic_inc(&ct->proto.icmp.count); 103 nf_conntrack_event_cache(IPCT_PROTOINFO_VOLATILE, skb); 104 nf_ct_refresh_acct(ct, ctinfo, skb, nf_ct_icmp_timeout); 105 } 106 107 return NF_ACCEPT; 108} 109 110/* Called when a new connection for this protocol found. */ 111static int icmp_new(struct nf_conn *conntrack, 112 const struct sk_buff *skb, unsigned int dataoff) 113{ 114 static const u_int8_t valid_new[] = { 115 [ICMP_ECHO] = 1, 116 [ICMP_TIMESTAMP] = 1, 117 [ICMP_INFO_REQUEST] = 1, 118 [ICMP_ADDRESS] = 1 119 }; 120 121 if (conntrack->tuplehash[0].tuple.dst.u.icmp.type >= sizeof(valid_new) 122 || !valid_new[conntrack->tuplehash[0].tuple.dst.u.icmp.type]) { 123 /* Can't create a new ICMP `conn' with this. */ 124 DEBUGP("icmp: can't create new conn with type %u\n", 125 conntrack->tuplehash[0].tuple.dst.u.icmp.type); 126 NF_CT_DUMP_TUPLE(&conntrack->tuplehash[0].tuple); 127 return 0; 128 } 129 atomic_set(&conntrack->proto.icmp.count, 0); 130 return 1; 131} 132 133extern struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv4; 134/* Returns conntrack if it dealt with ICMP, and filled in skb fields */ 135static int 136icmp_error_message(struct sk_buff *skb, 137 enum ip_conntrack_info *ctinfo, 138 unsigned int hooknum) 139{ 140 struct nf_conntrack_tuple innertuple, origtuple; 141 struct { 142 struct icmphdr icmp; 143 struct iphdr ip; 144 } _in, *inside; 145 struct nf_conntrack_l4proto *innerproto; 146 struct nf_conntrack_tuple_hash *h; 147 int dataoff; 148 149 NF_CT_ASSERT(skb->nfct == NULL); 150 151 /* Not enough header? */ 152 inside = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(_in), &_in); 153 if (inside == NULL) 154 return -NF_ACCEPT; 155 156 /* Ignore ICMP's containing fragments (shouldn't happen) */ 157 if (inside->ip.frag_off & htons(IP_OFFSET)) { 158 DEBUGP("icmp_error_message: fragment of proto %u\n", 159 inside->ip.protocol); 160 return -NF_ACCEPT; 161 } 162 163 /* rcu_read_lock()ed by nf_hook_slow */ 164 innerproto = __nf_ct_l4proto_find(PF_INET, inside->ip.protocol); 165 166 dataoff = ip_hdrlen(skb) + sizeof(inside->icmp); 167 /* Are they talking about one of our connections? */ 168 if (!nf_ct_get_tuple(skb, dataoff, dataoff + inside->ip.ihl*4, PF_INET, 169 inside->ip.protocol, &origtuple, 170 &nf_conntrack_l3proto_ipv4, innerproto)) { 171 DEBUGP("icmp_error_message: ! get_tuple p=%u", 172 inside->ip.protocol); 173 return -NF_ACCEPT; 174 } 175 176 /* Ordinarily, we'd expect the inverted tupleproto, but it's 177 been preserved inside the ICMP. */ 178 if (!nf_ct_invert_tuple(&innertuple, &origtuple, 179 &nf_conntrack_l3proto_ipv4, innerproto)) { 180 DEBUGP("icmp_error_message: no match\n"); 181 return -NF_ACCEPT; 182 } 183 184 *ctinfo = IP_CT_RELATED; 185 186 h = nf_conntrack_find_get(&innertuple, NULL); 187 if (!h) { 188 /* Locally generated ICMPs will match inverted if they 189 haven't been SNAT'ed yet */ 190 if (hooknum == NF_IP_LOCAL_OUT) 191 h = nf_conntrack_find_get(&origtuple, NULL); 192 193 if (!h) { 194 DEBUGP("icmp_error_message: no match\n"); 195 return -NF_ACCEPT; 196 } 197 198 /* Reverse direction from that found */ 199 if (NF_CT_DIRECTION(h) == IP_CT_DIR_REPLY) 200 *ctinfo += IP_CT_IS_REPLY; 201 } else { 202 if (NF_CT_DIRECTION(h) == IP_CT_DIR_REPLY) 203 *ctinfo += IP_CT_IS_REPLY; 204 } 205 206 /* Update skb to refer to this connection */ 207 skb->nfct = &nf_ct_tuplehash_to_ctrack(h)->ct_general; 208 skb->nfctinfo = *ctinfo; 209 return -NF_ACCEPT; 210} 211 212/* Small and modified version of icmp_rcv */ 213static int 214icmp_error(struct sk_buff *skb, unsigned int dataoff, 215 enum ip_conntrack_info *ctinfo, int pf, unsigned int hooknum) 216{ 217 struct icmphdr _ih, *icmph; 218 219 /* Not enough header? */ 220 icmph = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(_ih), &_ih); 221 if (icmph == NULL) { 222 if (LOG_INVALID(IPPROTO_ICMP)) 223 nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL, 224 "nf_ct_icmp: short packet "); 225 return -NF_ACCEPT; 226 } 227 228 /* See ip_conntrack_proto_tcp.c */ 229 if (nf_conntrack_checksum && hooknum == NF_IP_PRE_ROUTING && 230 nf_ip_checksum(skb, hooknum, dataoff, 0)) { 231 if (LOG_INVALID(IPPROTO_ICMP)) 232 nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL, 233 "nf_ct_icmp: bad HW ICMP checksum "); 234 return -NF_ACCEPT; 235 } 236 237 /* 238 * 18 is the highest 'known' ICMP type. Anything else is a mystery 239 * 240 * RFC 1122: 3.2.2 Unknown ICMP messages types MUST be silently 241 * discarded. 242 */ 243 if (icmph->type > NR_ICMP_TYPES) { 244 if (LOG_INVALID(IPPROTO_ICMP)) 245 nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL, 246 "nf_ct_icmp: invalid ICMP type "); 247 return -NF_ACCEPT; 248 } 249 250 /* Need to track icmp error message? */ 251 if (icmph->type != ICMP_DEST_UNREACH 252 && icmph->type != ICMP_SOURCE_QUENCH 253 && icmph->type != ICMP_TIME_EXCEEDED 254 && icmph->type != ICMP_PARAMETERPROB 255 && icmph->type != ICMP_REDIRECT) 256 return NF_ACCEPT; 257 258 return icmp_error_message(skb, ctinfo, hooknum); 259} 260 261#if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE) 262 263#include <linux/netfilter/nfnetlink.h> 264#include <linux/netfilter/nfnetlink_conntrack.h> 265 266static int icmp_tuple_to_nfattr(struct sk_buff *skb, 267 const struct nf_conntrack_tuple *t) 268{ 269 NFA_PUT(skb, CTA_PROTO_ICMP_ID, sizeof(u_int16_t), 270 &t->src.u.icmp.id); 271 NFA_PUT(skb, CTA_PROTO_ICMP_TYPE, sizeof(u_int8_t), 272 &t->dst.u.icmp.type); 273 NFA_PUT(skb, CTA_PROTO_ICMP_CODE, sizeof(u_int8_t), 274 &t->dst.u.icmp.code); 275 276 return 0; 277 278nfattr_failure: 279 return -1; 280} 281 282static const size_t cta_min_proto[CTA_PROTO_MAX] = { 283 [CTA_PROTO_ICMP_TYPE-1] = sizeof(u_int8_t), 284 [CTA_PROTO_ICMP_CODE-1] = sizeof(u_int8_t), 285 [CTA_PROTO_ICMP_ID-1] = sizeof(u_int16_t) 286}; 287 288static int icmp_nfattr_to_tuple(struct nfattr *tb[], 289 struct nf_conntrack_tuple *tuple) 290{ 291 if (!tb[CTA_PROTO_ICMP_TYPE-1] 292 || !tb[CTA_PROTO_ICMP_CODE-1] 293 || !tb[CTA_PROTO_ICMP_ID-1]) 294 return -EINVAL; 295 296 if (nfattr_bad_size(tb, CTA_PROTO_MAX, cta_min_proto)) 297 return -EINVAL; 298 299 tuple->dst.u.icmp.type = 300 *(u_int8_t *)NFA_DATA(tb[CTA_PROTO_ICMP_TYPE-1]); 301 tuple->dst.u.icmp.code = 302 *(u_int8_t *)NFA_DATA(tb[CTA_PROTO_ICMP_CODE-1]); 303 tuple->src.u.icmp.id = 304 *(__be16 *)NFA_DATA(tb[CTA_PROTO_ICMP_ID-1]); 305 306 if (tuple->dst.u.icmp.type >= sizeof(invmap) 307 || !invmap[tuple->dst.u.icmp.type]) 308 return -EINVAL; 309 310 return 0; 311} 312#endif 313 314#ifdef CONFIG_SYSCTL 315static struct ctl_table_header *icmp_sysctl_header; 316static struct ctl_table icmp_sysctl_table[] = { 317 { 318 .ctl_name = NET_NF_CONNTRACK_ICMP_TIMEOUT, 319 .procname = "nf_conntrack_icmp_timeout", 320 .data = &nf_ct_icmp_timeout, 321 .maxlen = sizeof(unsigned int), 322 .mode = 0644, 323 .proc_handler = &proc_dointvec_jiffies, 324 }, 325 { 326 .ctl_name = 0 327 } 328}; 329#ifdef CONFIG_NF_CONNTRACK_PROC_COMPAT 330static struct ctl_table icmp_compat_sysctl_table[] = { 331 { 332 .ctl_name = NET_IPV4_NF_CONNTRACK_ICMP_TIMEOUT, 333 .procname = "ip_conntrack_icmp_timeout", 334 .data = &nf_ct_icmp_timeout, 335 .maxlen = sizeof(unsigned int), 336 .mode = 0644, 337 .proc_handler = &proc_dointvec_jiffies, 338 }, 339 { 340 .ctl_name = 0 341 } 342}; 343#endif /* CONFIG_NF_CONNTRACK_PROC_COMPAT */ 344#endif /* CONFIG_SYSCTL */ 345 346struct nf_conntrack_l4proto nf_conntrack_l4proto_icmp = 347{ 348 .l3proto = PF_INET, 349 .l4proto = IPPROTO_ICMP, 350 .name = "icmp", 351 .pkt_to_tuple = icmp_pkt_to_tuple, 352 .invert_tuple = icmp_invert_tuple, 353 .print_tuple = icmp_print_tuple, 354 .print_conntrack = icmp_print_conntrack, 355 .packet = icmp_packet, 356 .new = icmp_new, 357 .error = icmp_error, 358 .destroy = NULL, 359 .me = NULL, 360#if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE) 361 .tuple_to_nfattr = icmp_tuple_to_nfattr, 362 .nfattr_to_tuple = icmp_nfattr_to_tuple, 363#endif 364#ifdef CONFIG_SYSCTL 365 .ctl_table_header = &icmp_sysctl_header, 366 .ctl_table = icmp_sysctl_table, 367#ifdef CONFIG_NF_CONNTRACK_PROC_COMPAT 368 .ctl_compat_table = icmp_compat_sysctl_table, 369#endif 370#endif 371}; 372EXPORT_SYMBOL_GPL(nf_conntrack_l4proto_icmp); 373