1/* 2 * Copyright (C)2003,2004 USAGI/WIDE Project 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 * Author: 9 * Yasuyuki Kozakai @USAGI <yasuyuki.kozakai@toshiba.co.jp> 10 */ 11 12#include <linux/types.h> 13#include <linux/timer.h> 14#include <linux/module.h> 15#include <linux/netfilter.h> 16#include <linux/in6.h> 17#include <linux/icmpv6.h> 18#include <linux/ipv6.h> 19#include <net/ipv6.h> 20#include <net/ip6_checksum.h> 21#include <linux/seq_file.h> 22#include <linux/netfilter_ipv6.h> 23#include <net/netfilter/nf_conntrack_tuple.h> 24#include <net/netfilter/nf_conntrack_l4proto.h> 25#include <net/netfilter/nf_conntrack_core.h> 26#include <net/netfilter/ipv6/nf_conntrack_icmpv6.h> 27 28static unsigned long nf_ct_icmpv6_timeout __read_mostly = 30*HZ; 29 30#define DEBUGP(format, args...) 31 32static int icmpv6_pkt_to_tuple(const struct sk_buff *skb, 33 unsigned int dataoff, 34 struct nf_conntrack_tuple *tuple) 35{ 36 struct icmp6hdr _hdr, *hp; 37 38 hp = skb_header_pointer(skb, dataoff, sizeof(_hdr), &_hdr); 39 if (hp == NULL) 40 return 0; 41 tuple->dst.u.icmp.type = hp->icmp6_type; 42 tuple->src.u.icmp.id = hp->icmp6_identifier; 43 tuple->dst.u.icmp.code = hp->icmp6_code; 44 45 return 1; 46} 47 48/* Add 1; spaces filled with 0. */ 49static u_int8_t invmap[] = { 50 [ICMPV6_ECHO_REQUEST - 128] = ICMPV6_ECHO_REPLY + 1, 51 [ICMPV6_ECHO_REPLY - 128] = ICMPV6_ECHO_REQUEST + 1, 52 [ICMPV6_NI_QUERY - 128] = ICMPV6_NI_QUERY + 1, 53 [ICMPV6_NI_REPLY - 128] = ICMPV6_NI_REPLY +1 54}; 55 56static int icmpv6_invert_tuple(struct nf_conntrack_tuple *tuple, 57 const struct nf_conntrack_tuple *orig) 58{ 59 int type = orig->dst.u.icmp.type - 128; 60 if (type < 0 || type >= sizeof(invmap) || !invmap[type]) 61 return 0; 62 63 tuple->src.u.icmp.id = orig->src.u.icmp.id; 64 tuple->dst.u.icmp.type = invmap[type] - 1; 65 tuple->dst.u.icmp.code = orig->dst.u.icmp.code; 66 return 1; 67} 68 69/* Print out the per-protocol part of the tuple. */ 70static int icmpv6_print_tuple(struct seq_file *s, 71 const struct nf_conntrack_tuple *tuple) 72{ 73 return seq_printf(s, "type=%u code=%u id=%u ", 74 tuple->dst.u.icmp.type, 75 tuple->dst.u.icmp.code, 76 ntohs(tuple->src.u.icmp.id)); 77} 78 79/* Print out the private part of the conntrack. */ 80static int icmpv6_print_conntrack(struct seq_file *s, 81 const struct nf_conn *conntrack) 82{ 83 return 0; 84} 85 86/* Returns verdict for packet, or -1 for invalid. */ 87static int icmpv6_packet(struct nf_conn *ct, 88 const struct sk_buff *skb, 89 unsigned int dataoff, 90 enum ip_conntrack_info ctinfo, 91 int pf, 92 unsigned int hooknum) 93{ 94 /* Try to delete connection immediately after all replies: 95 won't actually vanish as we still have skb, and del_timer 96 means this will only run once even if count hits zero twice 97 (theoretically possible with SMP) */ 98 if (CTINFO2DIR(ctinfo) == IP_CT_DIR_REPLY) { 99 if (atomic_dec_and_test(&ct->proto.icmp.count) 100 && del_timer(&ct->timeout)) 101 ct->timeout.function((unsigned long)ct); 102 } else { 103 atomic_inc(&ct->proto.icmp.count); 104 nf_conntrack_event_cache(IPCT_PROTOINFO_VOLATILE, skb); 105 nf_ct_refresh_acct(ct, ctinfo, skb, nf_ct_icmpv6_timeout); 106 } 107 108 return NF_ACCEPT; 109} 110 111/* Called when a new connection for this protocol found. */ 112static int icmpv6_new(struct nf_conn *conntrack, 113 const struct sk_buff *skb, 114 unsigned int dataoff) 115{ 116 static u_int8_t valid_new[] = { 117 [ICMPV6_ECHO_REQUEST - 128] = 1, 118 [ICMPV6_NI_QUERY - 128] = 1 119 }; 120 int type = conntrack->tuplehash[0].tuple.dst.u.icmp.type - 128; 121 122 if (type < 0 || type >= sizeof(valid_new) || !valid_new[type]) { 123 /* Can't create a new ICMPv6 `conn' with this. */ 124 DEBUGP("icmpv6: can't create new conn with type %u\n", 125 type + 128); 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 133static int 134icmpv6_error_message(struct sk_buff *skb, 135 unsigned int icmp6off, 136 enum ip_conntrack_info *ctinfo, 137 unsigned int hooknum) 138{ 139 struct nf_conntrack_tuple intuple, origtuple; 140 struct nf_conntrack_tuple_hash *h; 141 struct icmp6hdr _hdr, *hp; 142 unsigned int inip6off; 143 struct nf_conntrack_l4proto *inproto; 144 u_int8_t inprotonum; 145 unsigned int inprotoff; 146 147 NF_CT_ASSERT(skb->nfct == NULL); 148 149 hp = skb_header_pointer(skb, icmp6off, sizeof(_hdr), &_hdr); 150 if (hp == NULL) { 151 DEBUGP("icmpv6_error: Can't get ICMPv6 hdr.\n"); 152 return -NF_ACCEPT; 153 } 154 155 inip6off = icmp6off + sizeof(_hdr); 156 if (skb_copy_bits(skb, inip6off+offsetof(struct ipv6hdr, nexthdr), 157 &inprotonum, sizeof(inprotonum)) != 0) { 158 DEBUGP("icmpv6_error: Can't get nexthdr in inner IPv6 header.\n"); 159 return -NF_ACCEPT; 160 } 161 inprotoff = nf_ct_ipv6_skip_exthdr(skb, 162 inip6off + sizeof(struct ipv6hdr), 163 &inprotonum, 164 skb->len - inip6off 165 - sizeof(struct ipv6hdr)); 166 167 if ((inprotoff > skb->len) || (inprotonum == NEXTHDR_FRAGMENT)) { 168 DEBUGP("icmpv6_error: Can't get protocol header in ICMPv6 payload.\n"); 169 return -NF_ACCEPT; 170 } 171 172 /* rcu_read_lock()ed by nf_hook_slow */ 173 inproto = __nf_ct_l4proto_find(PF_INET6, inprotonum); 174 175 /* Are they talking about one of our connections? */ 176 if (!nf_ct_get_tuple(skb, inip6off, inprotoff, PF_INET6, inprotonum, 177 &origtuple, &nf_conntrack_l3proto_ipv6, inproto)) { 178 DEBUGP("icmpv6_error: Can't get tuple\n"); 179 return -NF_ACCEPT; 180 } 181 182 /* Ordinarily, we'd expect the inverted tupleproto, but it's 183 been preserved inside the ICMP. */ 184 if (!nf_ct_invert_tuple(&intuple, &origtuple, 185 &nf_conntrack_l3proto_ipv6, inproto)) { 186 DEBUGP("icmpv6_error: Can't invert tuple\n"); 187 return -NF_ACCEPT; 188 } 189 190 *ctinfo = IP_CT_RELATED; 191 192 h = nf_conntrack_find_get(&intuple, NULL); 193 if (!h) { 194 DEBUGP("icmpv6_error: no match\n"); 195 return -NF_ACCEPT; 196 } else { 197 if (NF_CT_DIRECTION(h) == IP_CT_DIR_REPLY) 198 *ctinfo += IP_CT_IS_REPLY; 199 } 200 201 /* Update skb to refer to this connection */ 202 skb->nfct = &nf_ct_tuplehash_to_ctrack(h)->ct_general; 203 skb->nfctinfo = *ctinfo; 204 return -NF_ACCEPT; 205} 206 207static int 208icmpv6_error(struct sk_buff *skb, unsigned int dataoff, 209 enum ip_conntrack_info *ctinfo, int pf, unsigned int hooknum) 210{ 211 struct icmp6hdr _ih, *icmp6h; 212 213 icmp6h = skb_header_pointer(skb, dataoff, sizeof(_ih), &_ih); 214 if (icmp6h == NULL) { 215 if (LOG_INVALID(IPPROTO_ICMPV6)) 216 nf_log_packet(PF_INET6, 0, skb, NULL, NULL, NULL, 217 "nf_ct_icmpv6: short packet "); 218 return -NF_ACCEPT; 219 } 220 221 if (nf_conntrack_checksum && hooknum == NF_IP6_PRE_ROUTING && 222 nf_ip6_checksum(skb, hooknum, dataoff, IPPROTO_ICMPV6)) { 223 nf_log_packet(PF_INET6, 0, skb, NULL, NULL, NULL, 224 "nf_ct_icmpv6: ICMPv6 checksum failed\n"); 225 return -NF_ACCEPT; 226 } 227 228 /* is not error message ? */ 229 if (icmp6h->icmp6_type >= 128) 230 return NF_ACCEPT; 231 232 return icmpv6_error_message(skb, dataoff, ctinfo, hooknum); 233} 234 235#if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE) 236 237#include <linux/netfilter/nfnetlink.h> 238#include <linux/netfilter/nfnetlink_conntrack.h> 239static int icmpv6_tuple_to_nfattr(struct sk_buff *skb, 240 const struct nf_conntrack_tuple *t) 241{ 242 NFA_PUT(skb, CTA_PROTO_ICMPV6_ID, sizeof(u_int16_t), 243 &t->src.u.icmp.id); 244 NFA_PUT(skb, CTA_PROTO_ICMPV6_TYPE, sizeof(u_int8_t), 245 &t->dst.u.icmp.type); 246 NFA_PUT(skb, CTA_PROTO_ICMPV6_CODE, sizeof(u_int8_t), 247 &t->dst.u.icmp.code); 248 249 return 0; 250 251nfattr_failure: 252 return -1; 253} 254 255static const size_t cta_min_proto[CTA_PROTO_MAX] = { 256 [CTA_PROTO_ICMPV6_TYPE-1] = sizeof(u_int8_t), 257 [CTA_PROTO_ICMPV6_CODE-1] = sizeof(u_int8_t), 258 [CTA_PROTO_ICMPV6_ID-1] = sizeof(u_int16_t) 259}; 260 261static int icmpv6_nfattr_to_tuple(struct nfattr *tb[], 262 struct nf_conntrack_tuple *tuple) 263{ 264 if (!tb[CTA_PROTO_ICMPV6_TYPE-1] 265 || !tb[CTA_PROTO_ICMPV6_CODE-1] 266 || !tb[CTA_PROTO_ICMPV6_ID-1]) 267 return -EINVAL; 268 269 if (nfattr_bad_size(tb, CTA_PROTO_MAX, cta_min_proto)) 270 return -EINVAL; 271 272 tuple->dst.u.icmp.type = 273 *(u_int8_t *)NFA_DATA(tb[CTA_PROTO_ICMPV6_TYPE-1]); 274 tuple->dst.u.icmp.code = 275 *(u_int8_t *)NFA_DATA(tb[CTA_PROTO_ICMPV6_CODE-1]); 276 tuple->src.u.icmp.id = 277 *(__be16 *)NFA_DATA(tb[CTA_PROTO_ICMPV6_ID-1]); 278 279 if (tuple->dst.u.icmp.type < 128 280 || tuple->dst.u.icmp.type - 128 >= sizeof(invmap) 281 || !invmap[tuple->dst.u.icmp.type - 128]) 282 return -EINVAL; 283 284 return 0; 285} 286#endif 287 288#ifdef CONFIG_SYSCTL 289static struct ctl_table_header *icmpv6_sysctl_header; 290static struct ctl_table icmpv6_sysctl_table[] = { 291 { 292 .ctl_name = NET_NF_CONNTRACK_ICMPV6_TIMEOUT, 293 .procname = "nf_conntrack_icmpv6_timeout", 294 .data = &nf_ct_icmpv6_timeout, 295 .maxlen = sizeof(unsigned int), 296 .mode = 0644, 297 .proc_handler = &proc_dointvec_jiffies, 298 }, 299 { 300 .ctl_name = 0 301 } 302}; 303#endif /* CONFIG_SYSCTL */ 304 305struct nf_conntrack_l4proto nf_conntrack_l4proto_icmpv6 = 306{ 307 .l3proto = PF_INET6, 308 .l4proto = IPPROTO_ICMPV6, 309 .name = "icmpv6", 310 .pkt_to_tuple = icmpv6_pkt_to_tuple, 311 .invert_tuple = icmpv6_invert_tuple, 312 .print_tuple = icmpv6_print_tuple, 313 .print_conntrack = icmpv6_print_conntrack, 314 .packet = icmpv6_packet, 315 .new = icmpv6_new, 316 .error = icmpv6_error, 317#if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE) 318 .tuple_to_nfattr = icmpv6_tuple_to_nfattr, 319 .nfattr_to_tuple = icmpv6_nfattr_to_tuple, 320#endif 321#ifdef CONFIG_SYSCTL 322 .ctl_table_header = &icmpv6_sysctl_header, 323 .ctl_table = icmpv6_sysctl_table, 324#endif 325}; 326 327EXPORT_SYMBOL(nf_conntrack_l4proto_icmpv6); 328