/* this is guaranteed to always return a valid protocol helper, since * it falls back to generic_protocol */ struct nf_conntrack_l4proto * nf_ct_l4proto_find_get(u_int16_t l3proto, u_int8_t l4proto) { struct nf_conntrack_l4proto *p; rcu_read_lock(); p = __nf_ct_l4proto_find(l3proto, l4proto); if (!try_module_get(p->me)) p = &nf_conntrack_l4proto_generic; rcu_read_unlock(); return p; }
/* this is guaranteed to always return a valid protocol helper, since * it falls back to generic_protocol */ struct nf_conntrack_l4proto * nf_ct_l4proto_find_get(u_int16_t l3proto, u_int8_t l4proto) { struct nf_conntrack_l4proto *p; preempt_disable(); p = __nf_ct_l4proto_find(l3proto, l4proto); if (!try_module_get(p->me)) p = &nf_conntrack_l4proto_generic; preempt_enable(); return p; }
static int icmpv6_error_message(struct net *net, struct nf_conn *tmpl, struct sk_buff *skb, unsigned int icmp6off, enum ip_conntrack_info *ctinfo, unsigned int hooknum) { struct nf_conntrack_tuple intuple, origtuple; const struct nf_conntrack_tuple_hash *h; const struct nf_conntrack_l4proto *inproto; u16 zone = tmpl ? nf_ct_zone(tmpl) : NF_CT_DEFAULT_ZONE; NF_CT_ASSERT(skb->nfct == NULL); /* Are they talking about one of our connections? */ if (!nf_ct_get_tuplepr(skb, skb_network_offset(skb) + sizeof(struct ipv6hdr) + sizeof(struct icmp6hdr), PF_INET6, &origtuple)) { pr_debug("icmpv6_error: Can't get tuple\n"); return -NF_ACCEPT; } /* rcu_read_lock()ed by nf_hook_slow */ inproto = __nf_ct_l4proto_find(PF_INET6, origtuple.dst.protonum); /* Ordinarily, we'd expect the inverted tupleproto, but it's been preserved inside the ICMP. */ if (!nf_ct_invert_tuple(&intuple, &origtuple, &nf_conntrack_l3proto_ipv6, inproto)) { pr_debug("icmpv6_error: Can't invert tuple\n"); return -NF_ACCEPT; } *ctinfo = IP_CT_RELATED; h = nf_conntrack_find_get(net, zone, &intuple); if (!h) { pr_debug("icmpv6_error: no match\n"); return -NF_ACCEPT; } else { if (NF_CT_DIRECTION(h) == IP_CT_DIR_REPLY) *ctinfo += IP_CT_IS_REPLY; } /* Update skb to refer to this connection */ skb->nfct = &nf_ct_tuplehash_to_ctrack(h)->ct_general; skb->nfctinfo = *ctinfo; return -NF_ACCEPT; }
static int icmpv6_error_message(struct net *net, struct nf_conn *tmpl, struct sk_buff *skb, unsigned int icmp6off) { struct nf_conntrack_tuple intuple, origtuple; const struct nf_conntrack_tuple_hash *h; const struct nf_conntrack_l4proto *inproto; enum ip_conntrack_info ctinfo; struct nf_conntrack_zone tmp; WARN_ON(skb_nfct(skb)); /* Are they talking about one of our connections? */ if (!nf_ct_get_tuplepr(skb, skb_network_offset(skb) + sizeof(struct ipv6hdr) + sizeof(struct icmp6hdr), PF_INET6, net, &origtuple)) { pr_debug("icmpv6_error: Can't get tuple\n"); return -NF_ACCEPT; } /* rcu_read_lock()ed by nf_hook_thresh */ inproto = __nf_ct_l4proto_find(PF_INET6, origtuple.dst.protonum); /* Ordinarily, we'd expect the inverted tupleproto, but it's been preserved inside the ICMP. */ if (!nf_ct_invert_tuple(&intuple, &origtuple, &nf_conntrack_l3proto_ipv6, inproto)) { pr_debug("icmpv6_error: Can't invert tuple\n"); return -NF_ACCEPT; } ctinfo = IP_CT_RELATED; h = nf_conntrack_find_get(net, nf_ct_zone_tmpl(tmpl, skb, &tmp), &intuple); if (!h) { pr_debug("icmpv6_error: no match\n"); return -NF_ACCEPT; } else { if (NF_CT_DIRECTION(h) == IP_CT_DIR_REPLY) ctinfo += IP_CT_IS_REPLY; } /* Update skb to refer to this connection */ nf_ct_set(skb, nf_ct_tuplehash_to_ctrack(h), ctinfo); return NF_ACCEPT; }
static int icmpv6_error_message(struct net *net, struct nf_conn *tmpl, struct sk_buff *skb, unsigned int icmp6off, enum ip_conntrack_info *ctinfo, unsigned int hooknum) { struct nf_conntrack_tuple intuple, origtuple; const struct nf_conntrack_tuple_hash *h; const struct nf_conntrack_l4proto *inproto; u16 zone = tmpl ? nf_ct_zone(tmpl) : NF_CT_DEFAULT_ZONE; NF_CT_ASSERT(skb->nfct == NULL); if (!nf_ct_get_tuplepr(skb, skb_network_offset(skb) + sizeof(struct ipv6hdr) + sizeof(struct icmp6hdr), PF_INET6, &origtuple)) { pr_debug("icmpv6_error: Can't get tuple\n"); return -NF_ACCEPT; } inproto = __nf_ct_l4proto_find(PF_INET6, origtuple.dst.protonum); if (!nf_ct_invert_tuple(&intuple, &origtuple, &nf_conntrack_l3proto_ipv6, inproto)) { pr_debug("icmpv6_error: Can't invert tuple\n"); return -NF_ACCEPT; } *ctinfo = IP_CT_RELATED; h = nf_conntrack_find_get(net, zone, &intuple); if (!h) { pr_debug("icmpv6_error: no match\n"); return -NF_ACCEPT; } else { if (NF_CT_DIRECTION(h) == IP_CT_DIR_REPLY) *ctinfo += IP_CT_IS_REPLY; } skb->nfct = &nf_ct_tuplehash_to_ctrack(h)->ct_general; skb->nfctinfo = *ctinfo; return NF_ACCEPT; }
/* Returns conntrack if it dealt with ICMP, and filled in skb fields */ static int icmp_error_message(struct sk_buff *skb, enum ip_conntrack_info *ctinfo, unsigned int hooknum) { struct nf_conntrack_tuple innertuple, origtuple; struct nf_conntrack_l4proto *innerproto; struct nf_conntrack_tuple_hash *h; NF_CT_ASSERT(skb->nfct == NULL); /* Are they talking about one of our connections? */ if (!nf_ct_get_tuplepr(skb, skb_network_offset(skb) + ip_hdrlen(skb) + sizeof(struct icmphdr), PF_INET, &origtuple)) { pr_debug("icmp_error_message: failed to get tuple\n"); return -NF_ACCEPT; } /* rcu_read_lock()ed by nf_hook_slow */ innerproto = __nf_ct_l4proto_find(PF_INET, origtuple.dst.protonum); /* Ordinarily, we'd expect the inverted tupleproto, but it's been preserved inside the ICMP. */ if (!nf_ct_invert_tuple(&innertuple, &origtuple, &nf_conntrack_l3proto_ipv4, innerproto)) { pr_debug("icmp_error_message: no match\n"); return -NF_ACCEPT; } *ctinfo = IP_CT_RELATED; h = nf_conntrack_find_get(&innertuple); if (!h) { pr_debug("icmp_error_message: no match\n"); return -NF_ACCEPT; } if (NF_CT_DIRECTION(h) == IP_CT_DIR_REPLY) *ctinfo += IP_CT_IS_REPLY; /* Update skb to refer to this connection */ skb->nfct = &nf_ct_tuplehash_to_ctrack(h)->ct_general; skb->nfctinfo = *ctinfo; return -NF_ACCEPT; }
/* * Function to get the tuple out of a given struct sk_buff. */ bool nat64_get_tuple(u_int8_t l3protocol, u_int8_t l4protocol, struct sk_buff *skb, struct nf_conntrack_tuple * inner) { const struct nf_conntrack_l4proto *l4proto; struct nf_conntrack_l3proto *l3proto; int l3_hdrlen, ret; unsigned int protoff = 0; u_int8_t protonum = 0; pr_debug("NAT64: Getting the protocol and header length"); /* * Get L3 header length */ l3_hdrlen = nat64_get_l3hdrlen(skb, l3protocol); if (l3_hdrlen == -1) { pr_debug("NAT64: Something went wrong getting the" " l3 header length"); return false; } /* * Get L3 struct to access it's functions. */ if (!(nat64_get_l3struct(l3protocol, &l3proto))) return false; if (l3proto == NULL) { pr_info("NAT64: nat64_get_tuple - the l3proto pointer is null"); return false; } rcu_read_lock(); pr_debug("NAT64: l3_hdrlen = %d", l3_hdrlen); /* * Gets the structure with the respective L4 protocol functions. */ ret = l3proto->get_l4proto(skb, skb_network_offset(skb), &protoff, &protonum); if (ret != NF_ACCEPT) { pr_info("NAT64: nat64_get_tuple - error getting the L4 offset"); pr_debug("NAT64: ret = %d", ret); pr_debug("NAT64: protoff = %u", protoff); rcu_read_unlock(); return false; } else if (protonum != l4protocol) { pr_info("NAT64: nat64_get_tuple - protocols don't match"); pr_debug("NAT64: protonum = %u", protonum); pr_debug("NAT64: l4protocol = %u", l4protocol); rcu_read_unlock(); return false; } l4proto = __nf_ct_l4proto_find(l3protocol, l4protocol); pr_debug("l4proto name = %s %d %d", l4proto->name, (u_int32_t)l4proto->l3proto, (u_int32_t)l4proto->l4proto); /* * Get the tuple out of the sk_buff. */ if (!nf_ct_get_tuple(skb, skb_network_offset(skb), l3_hdrlen, (u_int16_t)l3protocol, l4protocol, inner, l3proto, l4proto)) { pr_debug("NAT64: couldn't get the tuple"); rcu_read_unlock(); return false; } pr_debug("\nPRINTED TUPLE"); nat64_print_tuple(inner); pr_debug("\n"); rcu_read_unlock(); return true; }
/* Returns conntrack if it dealt with ICMP, and filled in skb fields */ static int icmp_error_message(struct net *net, struct sk_buff *skb, enum ip_conntrack_info *ctinfo, unsigned int hooknum) { struct nf_conntrack_tuple innertuple, origtuple; const struct nf_conntrack_l4proto *innerproto; const struct nf_conntrack_tuple_hash *h; #ifdef CFG_LINUX_NET_PACKED struct { struct icmphdr icmp; struct iphdr ip; } _in, *inside; #endif NF_CT_ASSERT(skb->nfct == NULL); #ifdef CFG_LINUX_NET_PACKED /* Not enough header? */ inside = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(_in), &_in); if (inside == NULL) return -NF_ACCEPT; #endif /* Are they talking about one of our connections? */ if (!nf_ct_get_tuplepr(skb, skb_network_offset(skb) + ip_hdrlen(skb) + sizeof(struct icmphdr), PF_INET, &origtuple)) { pr_debug("icmp_error_message: failed to get tuple\n"); return -NF_ACCEPT; } /* rcu_read_lock()ed by nf_hook_slow */ #ifdef CFG_LINUX_NET_PACKED innerproto = __nf_ct_l4proto_find(PF_INET, inside->ip.protocol); origtuple.src.u3.ip = inside->ip.saddr; origtuple.dst.u3.ip = inside->ip.daddr; #else innerproto = __nf_ct_l4proto_find(PF_INET, origtuple.dst.protonum); #endif /* Ordinarily, we'd expect the inverted tupleproto, but it's been preserved inside the ICMP. */ if (!nf_ct_invert_tuple(&innertuple, &origtuple, &nf_conntrack_l3proto_ipv4, innerproto)) { pr_debug("icmp_error_message: no match\n"); return -NF_ACCEPT; } *ctinfo = IP_CT_RELATED; h = nf_conntrack_find_get(net, &innertuple); if (!h) { pr_debug("icmp_error_message: no match\n"); return -NF_ACCEPT; } if (NF_CT_DIRECTION(h) == IP_CT_DIR_REPLY) *ctinfo += IP_CT_IS_REPLY; /* Update skb to refer to this connection */ skb->nfct = &nf_ct_tuplehash_to_ctrack(h)->ct_general; skb->nfctinfo = *ctinfo; return -NF_ACCEPT; }
/* Returns conntrack if it dealt with ICMP, and filled in skb fields */ static int icmp_error_message(struct sk_buff *skb, enum ip_conntrack_info *ctinfo, unsigned int hooknum) { struct nf_conntrack_tuple innertuple, origtuple; struct { struct icmphdr icmp; struct iphdr ip; } _in, *inside; struct nf_conntrack_l4proto *innerproto; struct nf_conntrack_tuple_hash *h; int dataoff; NF_CT_ASSERT(skb->nfct == NULL); /* Not enough header? */ inside = skb_header_pointer(skb, skb->nh.iph->ihl*4, sizeof(_in), &_in); if (inside == NULL) return -NF_ACCEPT; /* Ignore ICMP's containing fragments (shouldn't happen) */ if (inside->ip.frag_off & htons(IP_OFFSET)) { DEBUGP("icmp_error_message: fragment of proto %u\n", inside->ip.protocol); return -NF_ACCEPT; } innerproto = __nf_ct_l4proto_find(PF_INET, inside->ip.protocol); dataoff = skb->nh.iph->ihl*4 + sizeof(inside->icmp); /* Are they talking about one of our connections? */ if (!nf_ct_get_tuple(skb, dataoff, dataoff + inside->ip.ihl*4, PF_INET, inside->ip.protocol, &origtuple, &nf_conntrack_l3proto_ipv4, innerproto)) { DEBUGP("icmp_error_message: ! get_tuple p=%u", inside->ip.protocol); return -NF_ACCEPT; } /* Ordinarily, we'd expect the inverted tupleproto, but it's been preserved inside the ICMP. */ if (!nf_ct_invert_tuple(&innertuple, &origtuple, &nf_conntrack_l3proto_ipv4, innerproto)) { DEBUGP("icmp_error_message: no match\n"); return -NF_ACCEPT; } *ctinfo = IP_CT_RELATED; h = nf_conntrack_find_get(&innertuple, NULL); if (!h) { /* Locally generated ICMPs will match inverted if they haven't been SNAT'ed yet */ /* FIXME: NAT code has to handle half-done double NAT --RR */ if (hooknum == NF_IP_LOCAL_OUT) h = nf_conntrack_find_get(&origtuple, NULL); if (!h) { DEBUGP("icmp_error_message: no match\n"); return -NF_ACCEPT; } /* Reverse direction from that found */ if (NF_CT_DIRECTION(h) == IP_CT_DIR_REPLY) *ctinfo += IP_CT_IS_REPLY; } else { if (NF_CT_DIRECTION(h) == IP_CT_DIR_REPLY) *ctinfo += IP_CT_IS_REPLY; } /* Update skb to refer to this connection */ skb->nfct = &nf_ct_tuplehash_to_ctrack(h)->ct_general; skb->nfctinfo = *ctinfo; return -NF_ACCEPT; }
static int xt_ct_set_timeout(struct nf_conn *ct, const struct xt_tgchk_param *par, const char *timeout_name) { #ifdef CONFIG_NF_CONNTRACK_TIMEOUT typeof(nf_ct_timeout_find_get_hook) timeout_find_get; struct ctnl_timeout *timeout; struct nf_conn_timeout *timeout_ext; struct nf_conntrack_l4proto *l4proto; int ret = 0; u8 proto; rcu_read_lock(); timeout_find_get = rcu_dereference(nf_ct_timeout_find_get_hook); if (timeout_find_get == NULL) { ret = -ENOENT; pr_info("Timeout policy base is empty\n"); goto out; } proto = xt_ct_find_proto(par); if (!proto) { ret = -EINVAL; pr_info("You must specify a L4 protocol, and not use " "inversions on it.\n"); goto out; } timeout = timeout_find_get(timeout_name); if (timeout == NULL) { ret = -ENOENT; pr_info("No such timeout policy \"%s\"\n", timeout_name); goto out; } if (timeout->l3num != par->family) { ret = -EINVAL; pr_info("Timeout policy `%s' can only be used by L3 protocol " "number %d\n", timeout_name, timeout->l3num); goto err_put_timeout; } /* Make sure the timeout policy matches any existing protocol tracker, * otherwise default to generic. */ l4proto = __nf_ct_l4proto_find(par->family, proto); if (timeout->l4proto->l4proto != l4proto->l4proto) { ret = -EINVAL; pr_info("Timeout policy `%s' can only be used by L4 protocol " "number %d\n", timeout_name, timeout->l4proto->l4proto); goto err_put_timeout; } timeout_ext = nf_ct_timeout_ext_add(ct, timeout, GFP_ATOMIC); if (timeout_ext == NULL) ret = -ENOMEM; err_put_timeout: __xt_ct_tg_timeout_put(timeout); out: rcu_read_unlock(); return ret; #else return -EOPNOTSUPP; #endif }
static int xt_ct_tg_check_v1(const struct xt_tgchk_param *par) { struct xt_ct_target_info_v1 *info = par->targinfo; struct nf_conntrack_tuple t; struct nf_conn_help *help; struct nf_conn *ct; int ret = 0; u8 proto; #ifdef CONFIG_NF_CONNTRACK_TIMEOUT struct ctnl_timeout *timeout; #endif if (info->flags & ~XT_CT_NOTRACK) return -EINVAL; if (info->flags & XT_CT_NOTRACK) { ct = nf_ct_untracked_get(); atomic_inc(&ct->ct_general.use); goto out; } #ifndef CONFIG_NF_CONNTRACK_ZONES if (info->zone) goto err1; #endif ret = nf_ct_l3proto_try_module_get(par->family); if (ret < 0) goto err1; memset(&t, 0, sizeof(t)); ct = nf_conntrack_alloc(par->net, info->zone, &t, &t, GFP_KERNEL); ret = PTR_ERR(ct); if (IS_ERR(ct)) goto err2; ret = 0; if ((info->ct_events || info->exp_events) && !nf_ct_ecache_ext_add(ct, info->ct_events, info->exp_events, GFP_KERNEL)) goto err3; if (info->helper[0]) { ret = -ENOENT; proto = xt_ct_find_proto(par); if (!proto) { pr_info("You must specify a L4 protocol, " "and not use inversions on it.\n"); goto err3; } ret = -ENOMEM; help = nf_ct_helper_ext_add(ct, GFP_KERNEL); if (help == NULL) goto err3; ret = -ENOENT; help->helper = nf_conntrack_helper_try_module_get(info->helper, par->family, proto); if (help->helper == NULL) { pr_info("No such helper \"%s\"\n", info->helper); goto err3; } } #ifdef CONFIG_NF_CONNTRACK_TIMEOUT if (info->timeout[0]) { typeof(nf_ct_timeout_find_get_hook) timeout_find_get; struct nf_conn_timeout *timeout_ext; rcu_read_lock(); timeout_find_get = rcu_dereference(nf_ct_timeout_find_get_hook); if (timeout_find_get) { const struct ipt_entry *e = par->entryinfo; struct nf_conntrack_l4proto *l4proto; if (e->ip.invflags & IPT_INV_PROTO) { ret = -EINVAL; pr_info("You cannot use inversion on " "L4 protocol\n"); goto err4; } timeout = timeout_find_get(info->timeout); if (timeout == NULL) { ret = -ENOENT; pr_info("No such timeout policy \"%s\"\n", info->timeout); goto err4; } if (timeout->l3num != par->family) { ret = -EINVAL; pr_info("Timeout policy `%s' can only be " "used by L3 protocol number %d\n", info->timeout, timeout->l3num); goto err5; } /* Make sure the timeout policy matches any existing * protocol tracker, otherwise default to generic. */ l4proto = __nf_ct_l4proto_find(par->family, e->ip.proto); if (timeout->l4proto->l4proto != l4proto->l4proto) { ret = -EINVAL; pr_info("Timeout policy `%s' can only be " "used by L4 protocol number %d\n", info->timeout, timeout->l4proto->l4proto); goto err5; } timeout_ext = nf_ct_timeout_ext_add(ct, timeout, GFP_ATOMIC); if (timeout_ext == NULL) { ret = -ENOMEM; goto err5; } } else { ret = -ENOENT; pr_info("Timeout policy base is empty\n"); goto err4; } rcu_read_unlock(); } #endif __set_bit(IPS_TEMPLATE_BIT, &ct->status); __set_bit(IPS_CONFIRMED_BIT, &ct->status); out: info->ct = ct; return 0; #ifdef CONFIG_NF_CONNTRACK_TIMEOUT err5: __xt_ct_tg_timeout_put(timeout); err4: rcu_read_unlock(); #endif err3: nf_conntrack_free(ct); err2: nf_ct_l3proto_module_put(par->family); err1: return ret; }