Loading include/net/netfilter/nf_conntrack.h +15 −0 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ #include <linux/bitops.h> #include <linux/compiler.h> #include <linux/atomic.h> #include <linux/list.h> #include <linux/netfilter/nf_conntrack_tcp.h> #include <linux/netfilter/nf_conntrack_dccp.h> Loading @@ -26,6 +27,14 @@ #include <net/netfilter/nf_conntrack_tuple.h> #define SIP_LIST_ELEMENTS 2 struct sip_length { int msg_length[SIP_LIST_ELEMENTS]; int skb_len[SIP_LIST_ELEMENTS]; int data_len[SIP_LIST_ELEMENTS]; }; /* per conntrack: protocol private data */ union nf_conntrack_proto { /* insert conntrack proto private data here */ Loading Loading @@ -121,6 +130,12 @@ struct nf_conn { unsigned long nattype_entry; #endif struct list_head sip_segment_list; const char *dptr_prev; struct sip_length segment; bool sip_original_dir; bool sip_reply_dir; /* Storage reserved for other modules, must be the last member */ union nf_conntrack_proto proto; }; Loading include/net/netfilter/nf_conntrack_core.h +5 −0 Original line number Diff line number Diff line Loading @@ -86,4 +86,9 @@ extern spinlock_t nf_conntrack_locks[CONNTRACK_LOCKS]; extern spinlock_t nf_conntrack_expect_lock; struct sip_list { struct nf_queue_entry *entry; struct list_head list; }; #endif /* _NF_CONNTRACK_CORE_H */ net/netfilter/nf_conntrack_core.c +44 −32 Original line number Diff line number Diff line Loading @@ -58,13 +58,13 @@ int (*nfnetlink_parse_nat_setup_hook)(struct nf_conn *ct, enum nf_nat_manip_type manip, const struct nlattr *attr) __read_mostly; EXPORT_SYMBOL_GPL(nfnetlink_parse_nat_setup_hook); EXPORT_SYMBOL(nfnetlink_parse_nat_setup_hook); __cacheline_aligned_in_smp spinlock_t nf_conntrack_locks[CONNTRACK_LOCKS]; EXPORT_SYMBOL_GPL(nf_conntrack_locks); EXPORT_SYMBOL(nf_conntrack_locks); __cacheline_aligned_in_smp DEFINE_SPINLOCK(nf_conntrack_expect_lock); EXPORT_SYMBOL_GPL(nf_conntrack_expect_lock); EXPORT_SYMBOL(nf_conntrack_expect_lock); static void nf_conntrack_double_unlock(unsigned int h1, unsigned int h2) { Loading Loading @@ -115,16 +115,16 @@ static void nf_conntrack_all_unlock(void) } unsigned int nf_conntrack_htable_size __read_mostly; EXPORT_SYMBOL_GPL(nf_conntrack_htable_size); EXPORT_SYMBOL(nf_conntrack_htable_size); unsigned int nf_conntrack_max __read_mostly; EXPORT_SYMBOL_GPL(nf_conntrack_max); EXPORT_SYMBOL(nf_conntrack_max); DEFINE_PER_CPU(struct nf_conn, nf_conntrack_untracked); EXPORT_PER_CPU_SYMBOL(nf_conntrack_untracked); unsigned int nf_conntrack_hash_rnd __read_mostly; EXPORT_SYMBOL_GPL(nf_conntrack_hash_rnd); EXPORT_SYMBOL(nf_conntrack_hash_rnd); static u32 hash_conntrack_raw(const struct nf_conntrack_tuple *tuple, u16 zone) { Loading Loading @@ -183,7 +183,7 @@ nf_ct_get_tuple(const struct sk_buff *skb, return l4proto->pkt_to_tuple(skb, dataoff, tuple); } EXPORT_SYMBOL_GPL(nf_ct_get_tuple); EXPORT_SYMBOL(nf_ct_get_tuple); bool nf_ct_get_tuplepr(const struct sk_buff *skb, unsigned int nhoff, u_int16_t l3num, struct nf_conntrack_tuple *tuple) Loading Loading @@ -211,7 +211,7 @@ bool nf_ct_get_tuplepr(const struct sk_buff *skb, unsigned int nhoff, rcu_read_unlock(); return ret; } EXPORT_SYMBOL_GPL(nf_ct_get_tuplepr); EXPORT_SYMBOL(nf_ct_get_tuplepr); bool nf_ct_invert_tuple(struct nf_conntrack_tuple *inverse, Loading @@ -230,7 +230,7 @@ nf_ct_invert_tuple(struct nf_conntrack_tuple *inverse, inverse->dst.protonum = orig->dst.protonum; return l4proto->invert_tuple(inverse, orig); } EXPORT_SYMBOL_GPL(nf_ct_invert_tuple); EXPORT_SYMBOL(nf_ct_invert_tuple); static void clean_from_lists(struct nf_conn *ct) Loading Loading @@ -293,6 +293,9 @@ destroy_conntrack(struct nf_conntrack *nfct) struct nf_conn *ct = (struct nf_conn *)nfct; struct net *net = nf_ct_net(ct); struct nf_conntrack_l4proto *l4proto; struct sip_list *sip_node = NULL; struct list_head *sip_node_list; struct list_head *sip_node_save_list; pr_debug("destroy_conntrack(%p)\n", ct); NF_CT_ASSERT(atomic_read(&nfct->use) == 0); Loading @@ -306,6 +309,14 @@ destroy_conntrack(struct nf_conntrack *nfct) rcu_read_unlock(); local_bh_disable(); pr_debug("freeing item in the SIP list\n"); list_for_each_safe(sip_node_list, sip_node_save_list, &ct->sip_segment_list) { sip_node = list_entry(sip_node_list, struct sip_list, list); list_del(&sip_node->list); kfree(sip_node); } /* Expectations will have been removed in clean_from_lists, * except TFTP can create an expectation on the first packet, * before connection is in the list, so we need to clean here, Loading Loading @@ -378,7 +389,7 @@ bool nf_ct_delete(struct nf_conn *ct, u32 portid, int report) nf_ct_put(ct); return true; } EXPORT_SYMBOL_GPL(nf_ct_delete); EXPORT_SYMBOL(nf_ct_delete); static void death_by_timeout(unsigned long ul_conntrack) { Loading Loading @@ -475,7 +486,7 @@ nf_conntrack_find_get(struct net *net, u16 zone, return __nf_conntrack_find_get(net, zone, tuple, hash_conntrack_raw(tuple, zone)); } EXPORT_SYMBOL_GPL(nf_conntrack_find_get); EXPORT_SYMBOL(nf_conntrack_find_get); static void __nf_conntrack_hash_insert(struct nf_conn *ct, unsigned int hash, Loading Loading @@ -538,7 +549,7 @@ out: local_bh_enable(); return -EEXIST; } EXPORT_SYMBOL_GPL(nf_conntrack_hash_check_insert); EXPORT_SYMBOL(nf_conntrack_hash_check_insert); /* deletion from this larval template list happens via nf_ct_put() */ void nf_conntrack_tmpl_insert(struct net *net, struct nf_conn *tmpl) Loading @@ -560,7 +571,7 @@ void nf_conntrack_tmpl_insert(struct net *net, struct nf_conn *tmpl) &pcpu->tmpl); spin_unlock_bh(&pcpu->lock); } EXPORT_SYMBOL_GPL(nf_conntrack_tmpl_insert); EXPORT_SYMBOL(nf_conntrack_tmpl_insert); /* Confirm a connection given skb; places it in hash table */ int Loading Loading @@ -676,7 +687,7 @@ out: local_bh_enable(); return NF_DROP; } EXPORT_SYMBOL_GPL(__nf_conntrack_confirm); EXPORT_SYMBOL(__nf_conntrack_confirm); /* Returns true if a connection correspondings to the tuple (required for NAT). */ Loading Loading @@ -710,7 +721,7 @@ nf_conntrack_tuple_taken(const struct nf_conntrack_tuple *tuple, return 0; } EXPORT_SYMBOL_GPL(nf_conntrack_tuple_taken); EXPORT_SYMBOL(nf_conntrack_tuple_taken); #define NF_CT_EVICTION_RANGE 8 Loading Loading @@ -872,7 +883,7 @@ struct nf_conn *nf_conntrack_alloc(struct net *net, u16 zone, { return __nf_conntrack_alloc(net, zone, orig, repl, gfp, 0); } EXPORT_SYMBOL_GPL(nf_conntrack_alloc); EXPORT_SYMBOL(nf_conntrack_alloc); void nf_conntrack_free(struct nf_conn *ct) { Loading @@ -889,7 +900,7 @@ void nf_conntrack_free(struct nf_conn *ct) smp_mb__before_atomic(); atomic_dec(&net->ct.count); } EXPORT_SYMBOL_GPL(nf_conntrack_free); EXPORT_SYMBOL(nf_conntrack_free); /* Allocate a new conntrack: we return -ENOMEM if classification Loading Loading @@ -951,6 +962,7 @@ init_conntrack(struct net *net, struct nf_conn *tmpl, GFP_ATOMIC); local_bh_disable(); INIT_LIST_HEAD(&ct->sip_segment_list); if (net->ct.expect_count) { spin_lock(&nf_conntrack_expect_lock); exp = nf_ct_find_expectation(net, zone, tuple); Loading Loading @@ -1170,7 +1182,7 @@ out: return ret; } EXPORT_SYMBOL_GPL(nf_conntrack_in); EXPORT_SYMBOL(nf_conntrack_in); bool nf_ct_invert_tuplepr(struct nf_conntrack_tuple *inverse, const struct nf_conntrack_tuple *orig) Loading @@ -1185,7 +1197,7 @@ bool nf_ct_invert_tuplepr(struct nf_conntrack_tuple *inverse, rcu_read_unlock(); return ret; } EXPORT_SYMBOL_GPL(nf_ct_invert_tuplepr); EXPORT_SYMBOL(nf_ct_invert_tuplepr); /* Alter reply tuple (maybe alter helper). This is for NAT, and is implicitly racy: see __nf_conntrack_confirm */ Loading @@ -1208,7 +1220,7 @@ void nf_conntrack_alter_reply(struct nf_conn *ct, __nf_ct_try_assign_helper(ct, NULL, GFP_ATOMIC); rcu_read_unlock(); } EXPORT_SYMBOL_GPL(nf_conntrack_alter_reply); EXPORT_SYMBOL(nf_conntrack_alter_reply); /* Refresh conntrack for this many jiffies and do accounting if do_acct is 1 */ void __nf_ct_refresh_acct(struct nf_conn *ct, Loading Loading @@ -1255,7 +1267,7 @@ acct: } } } EXPORT_SYMBOL_GPL(__nf_ct_refresh_acct); EXPORT_SYMBOL(__nf_ct_refresh_acct); bool __nf_ct_kill_acct(struct nf_conn *ct, enum ip_conntrack_info ctinfo, Loading @@ -1281,7 +1293,7 @@ bool __nf_ct_kill_acct(struct nf_conn *ct, } return false; } EXPORT_SYMBOL_GPL(__nf_ct_kill_acct); EXPORT_SYMBOL(__nf_ct_kill_acct); #ifdef CONFIG_NF_CONNTRACK_ZONES static struct nf_ct_ext_type nf_ct_zone_extend __read_mostly = { Loading Loading @@ -1311,13 +1323,13 @@ int nf_ct_port_tuple_to_nlattr(struct sk_buff *skb, nla_put_failure: return -1; } EXPORT_SYMBOL_GPL(nf_ct_port_tuple_to_nlattr); EXPORT_SYMBOL(nf_ct_port_tuple_to_nlattr); const struct nla_policy nf_ct_port_nla_policy[CTA_PROTO_MAX+1] = { [CTA_PROTO_SRC_PORT] = { .type = NLA_U16 }, [CTA_PROTO_DST_PORT] = { .type = NLA_U16 }, }; EXPORT_SYMBOL_GPL(nf_ct_port_nla_policy); EXPORT_SYMBOL(nf_ct_port_nla_policy); int nf_ct_port_nlattr_to_tuple(struct nlattr *tb[], struct nf_conntrack_tuple *t) Loading @@ -1330,13 +1342,13 @@ int nf_ct_port_nlattr_to_tuple(struct nlattr *tb[], return 0; } EXPORT_SYMBOL_GPL(nf_ct_port_nlattr_to_tuple); EXPORT_SYMBOL(nf_ct_port_nlattr_to_tuple); int nf_ct_port_nlattr_tuple_size(void) { return nla_policy_len(nf_ct_port_nla_policy, CTA_PROTO_MAX + 1); } EXPORT_SYMBOL_GPL(nf_ct_port_nlattr_tuple_size); EXPORT_SYMBOL(nf_ct_port_nlattr_tuple_size); #endif /* Used by ipt_REJECT and ip6t_REJECT. */ Loading Loading @@ -1422,7 +1434,7 @@ void nf_ct_iterate_cleanup(struct net *net, nf_ct_put(ct); } } EXPORT_SYMBOL_GPL(nf_ct_iterate_cleanup); EXPORT_SYMBOL(nf_ct_iterate_cleanup); static int kill_all(struct nf_conn *i, void *data) { Loading @@ -1437,13 +1449,13 @@ void nf_ct_free_hashtable(void *hash, unsigned int size) free_pages((unsigned long)hash, get_order(sizeof(struct hlist_head) * size)); } EXPORT_SYMBOL_GPL(nf_ct_free_hashtable); EXPORT_SYMBOL(nf_ct_free_hashtable); void nf_conntrack_flush_report(struct net *net, u32 portid, int report) { nf_ct_iterate_cleanup(net, kill_all, NULL, portid, report); } EXPORT_SYMBOL_GPL(nf_conntrack_flush_report); EXPORT_SYMBOL(nf_conntrack_flush_report); static int untrack_refs(void) { Loading Loading @@ -1554,7 +1566,7 @@ void *nf_ct_alloc_hashtable(unsigned int *sizep, int nulls) return hash; } EXPORT_SYMBOL_GPL(nf_ct_alloc_hashtable); EXPORT_SYMBOL(nf_ct_alloc_hashtable); int nf_conntrack_set_hashsize(const char *val, struct kernel_param *kp) { Loading Loading @@ -1615,7 +1627,7 @@ int nf_conntrack_set_hashsize(const char *val, struct kernel_param *kp) nf_ct_free_hashtable(old_hash, old_size); return 0; } EXPORT_SYMBOL_GPL(nf_conntrack_set_hashsize); EXPORT_SYMBOL(nf_conntrack_set_hashsize); module_param_call(hashsize, nf_conntrack_set_hashsize, param_get_uint, &nf_conntrack_htable_size, 0600); Loading @@ -1627,7 +1639,7 @@ void nf_ct_untracked_status_or(unsigned long bits) for_each_possible_cpu(cpu) per_cpu(nf_conntrack_untracked, cpu).status |= bits; } EXPORT_SYMBOL_GPL(nf_ct_untracked_status_or); EXPORT_SYMBOL(nf_ct_untracked_status_or); int nf_conntrack_init_start(void) { Loading net/netfilter/nf_conntrack_sip.c +383 −23 Original line number Diff line number Diff line /* SIP extension for IP connection tracking. * * Copyright (c) 2015, The Linux Foundation. All rights reserved. * (C) 2005 by Christian Hentschel <chentschel@arnet.com.ar> * based on RR's ip_conntrack_ftp.c and other modules. * (C) 2007 United Security Providers Loading @@ -18,13 +19,18 @@ #include <linux/udp.h> #include <linux/tcp.h> #include <linux/netfilter.h> #include <net/tcp.h> #include <net/netfilter/nf_conntrack.h> #include <net/netfilter/nf_conntrack_core.h> #include <net/netfilter/nf_conntrack_expect.h> #include <net/netfilter/nf_conntrack_helper.h> #include <net/netfilter/nf_conntrack_zones.h> #include <linux/netfilter/nf_conntrack_sip.h> #include <net/netfilter/nf_nat.h> #include <net/netfilter/nf_nat_l3proto.h> #include <net/netfilter/nf_nat_l4proto.h> #include <net/netfilter/nf_queue.h> MODULE_LICENSE("GPL"); MODULE_AUTHOR("Christian Hentschel <chentschel@arnet.com.ar>"); Loading @@ -48,10 +54,11 @@ MODULE_PARM_DESC(sip_direct_signalling, "expect incoming calls from registrar " "only (default 1)"); const struct nf_nat_sip_hooks *nf_nat_sip_hooks; EXPORT_SYMBOL_GPL(nf_nat_sip_hooks); EXPORT_SYMBOL(nf_nat_sip_hooks); static struct ctl_table_header *sip_sysctl_header; static unsigned nf_ct_disable_sip_alg; static int sip_direct_media = 1; static int packet_count; static struct ctl_table sip_sysctl_tbl[] = { { .procname = "nf_conntrack_disable_sip_alg", Loading @@ -70,6 +77,224 @@ static struct ctl_table sip_sysctl_tbl[] = { {} }; unsigned int (*nf_nat_sip_hook)(struct sk_buff *skb, unsigned int protoff, unsigned int dataoff, const char **dptr, unsigned int *datalen) __read_mostly; /* This function is to save all the information of the first segment * that will be needed for combining the two segments */ static bool sip_save_segment_info(struct nf_conn *ct, struct sk_buff *skb, unsigned int msglen, unsigned int datalen, const char *dptr, enum ip_conntrack_info ctinfo) { enum ip_conntrack_dir dir = IP_CT_DIR_MAX; bool skip = false; /* one set of information is saved per direction ,also only one segment * per direction is queued based on the assumption that after the first * complete message leaves the kernel, only then the next fragmented * segment will reach the kernel */ if (ct) dir = CTINFO2DIR(ctinfo); if (dir == IP_CT_DIR_ORIGINAL) { /* here we check if there is already an element queued for this * direction, in that case we do not queue the next element,we * make skip 1.ideally this scenario should never be hit */ if (ct->sip_original_dir == 1) { skip = true; } else { ct->segment.msg_length[0] = msglen; ct->segment.data_len[0] = datalen; ct->segment.skb_len[0] = skb->len; ct->dptr_prev = dptr; ct->sip_original_dir = 1; skip = false; } } else { if (ct->sip_reply_dir == 1) { skip = true; } else { ct->segment.msg_length[1] = msglen; ct->segment.data_len[1] = datalen; ct->segment.skb_len[1] = skb->len; ct->dptr_prev = dptr; ct->sip_reply_dir = 1; skip = false; } } return skip; } static struct sip_list *sip_coalesce_segments(struct nf_conn *ct, struct sk_buff **skb_ref, unsigned int dataoff, struct sk_buff **combined_skb_ref, bool *skip_sip_process, bool do_not_process, enum ip_conntrack_info ctinfo, bool *success) { struct list_head *list_trav_node; struct list_head *list_backup_node; struct nf_conn *ct_list; enum ip_conntrack_info ctinfo_list; enum ip_conntrack_dir dir_list; enum ip_conntrack_dir dir = IP_CT_DIR_MAX; const struct tcphdr *th_old; unsigned int prev_data_len; unsigned int seq_no, seq_old, exp_seq_no; const struct tcphdr *th_new; bool fragstolen = false; int delta_truesize = 0; struct sip_list *sip_entry = NULL; th_new = (struct tcphdr *)(skb_network_header(*skb_ref) + ip_hdrlen(*skb_ref)); seq_no = ntohl(th_new->seq); if (ct) dir = CTINFO2DIR(ctinfo); /* traverse the list it would have 1 or 2 elements. 1 element per * direction at max */ list_for_each_safe(list_trav_node, list_backup_node, &ct->sip_segment_list){ sip_entry = list_entry(list_trav_node, struct sip_list, list); ct_list = nf_ct_get(sip_entry->entry->skb, &ctinfo_list); dir_list = CTINFO2DIR(ctinfo_list); /* take an element and check if its direction matches with the * current one */ if (dir_list == dir) { /* once we have the two elements to be combined we do * another check. match the next expected seq no of the * packet in the list with the seq no of the current * packet.this is to be protected against out of order * fragments */ th_old = ((struct tcphdr *)(skb_network_header (sip_entry->entry->skb) + ip_hdrlen(sip_entry->entry->skb))); prev_data_len = (dir == IP_CT_DIR_ORIGINAL) ? ct->segment.data_len[0] : ct->segment.data_len[1]; seq_old = (ntohl(th_old->seq)); exp_seq_no = seq_old+prev_data_len; if (exp_seq_no == seq_no) { /* Found packets to be combined.Pull header from * second skb when preparing combined skb.This * shifts the second skb start pointer to its * data that was initially at the start of its * headers.This so that the combined skb has * the tcp ip headerof the first skb followed * by the data of first skb followed by the data * of second skb. */ skb_pull(*skb_ref, dataoff); if (skb_try_coalesce(sip_entry->entry->skb, *skb_ref, &fragstolen, &delta_truesize)) { *combined_skb_ref = sip_entry->entry->skb; *success = true; list_del(list_trav_node); } else{ skb_push(*skb_ref, dataoff); } } } else if (do_not_process) { *skip_sip_process = true; } } return sip_entry; } static void recalc_header(struct sk_buff *skb, unsigned int skblen, unsigned int oldlen, unsigned int protoff) { unsigned int datalen; struct tcphdr *tcph; const struct nf_nat_l3proto *l3proto; /* here we recalculate ip and tcp headers */ if (nf_ct_l3num((struct nf_conn *)skb->nfct) == NFPROTO_IPV4) { /* fix IP hdr checksum information */ ip_hdr(skb)->tot_len = htons(skblen); ip_send_check(ip_hdr(skb)); } else { ipv6_hdr(skb)->payload_len = htons(skblen - sizeof(struct ipv6hdr)); } datalen = skb->len - protoff; tcph = (struct tcphdr *)((void *)skb->data + protoff); l3proto = __nf_nat_l3proto_find(nf_ct_l3num ((struct nf_conn *)skb->nfct)); l3proto->csum_recalc(skb, IPPROTO_TCP, tcph, &tcph->check, datalen, oldlen); } EXPORT_SYMBOL(nf_nat_sip_hook); void (*nf_nat_sip_seq_adjust_hook)(struct sk_buff *skb, unsigned int protoff, s16 off) __read_mostly; EXPORT_SYMBOL(nf_nat_sip_seq_adjust_hook); unsigned int (*nf_nat_sip_expect_hook)(struct sk_buff *skb, unsigned int protoff, unsigned int dataoff, const char **dptr, unsigned int *datalen, struct nf_conntrack_expect *exp, unsigned int matchoff, unsigned int matchlen) __read_mostly; EXPORT_SYMBOL(nf_nat_sip_expect_hook); unsigned int (*nf_nat_sdp_addr_hook)(struct sk_buff *skb, unsigned int protoff, unsigned int dataoff, const char **dptr, unsigned int *datalen, unsigned int sdpoff, enum sdp_header_types type, enum sdp_header_types term, const union nf_inet_addr *addr) __read_mostly; EXPORT_SYMBOL(nf_nat_sdp_addr_hook); unsigned int (*nf_nat_sdp_port_hook)(struct sk_buff *skb, unsigned int protoff, unsigned int dataoff, const char **dptr, unsigned int *datalen, unsigned int matchoff, unsigned int matchlen, u_int16_t port) __read_mostly; EXPORT_SYMBOL(nf_nat_sdp_port_hook); unsigned int (*nf_nat_sdp_session_hook)(struct sk_buff *skb, unsigned int protoff, unsigned int dataoff, const char **dptr, unsigned int *datalen, unsigned int sdpoff, const union nf_inet_addr *addr) __read_mostly; EXPORT_SYMBOL(nf_nat_sdp_session_hook); unsigned int (*nf_nat_sdp_media_hook)(struct sk_buff *skb, unsigned int protoff, unsigned int dataoff, const char **dptr, unsigned int *datalen, struct nf_conntrack_expect *rtp_exp, struct nf_conntrack_expect *rtcp_exp, unsigned int mediaoff, unsigned int medialen, union nf_inet_addr *rtp_addr) __read_mostly; EXPORT_SYMBOL(nf_nat_sdp_media_hook); static int string_len(const struct nf_conn *ct, const char *dptr, const char *limit, int *shift) { Loading @@ -82,6 +307,26 @@ static int string_len(const struct nf_conn *ct, const char *dptr, return len; } static int nf_sip_enqueue_packet(struct nf_queue_entry *entry, unsigned int queuenum) { enum ip_conntrack_info ctinfo_list; struct nf_conn *ct_temp; struct sip_list *node = kzalloc(sizeof(*node), GFP_ATOMIC | __GFP_NOWARN); if (!node) return XT_CONTINUE; ct_temp = nf_ct_get(entry->skb, &ctinfo_list); node->entry = entry; list_add(&node->list, &ct_temp->sip_segment_list); return 0; } static const struct nf_queue_handler nf_sip_qh = { .outfn = &nf_sip_enqueue_packet, }; static int digits_len(const struct nf_conn *ct, const char *dptr, const char *limit, int *shift) { Loading Loading @@ -288,7 +533,7 @@ int ct_sip_parse_request(const struct nf_conn *ct, *matchlen = end - dptr; return 1; } EXPORT_SYMBOL_GPL(ct_sip_parse_request); EXPORT_SYMBOL(ct_sip_parse_request); /* SIP header parsing: SIP headers are located at the beginning of a line, but * may span several lines, in which case the continuation lines begin with a Loading Loading @@ -436,7 +681,7 @@ int ct_sip_get_header(const struct nf_conn *ct, const char *dptr, } return 0; } EXPORT_SYMBOL_GPL(ct_sip_get_header); EXPORT_SYMBOL(ct_sip_get_header); /* Get next header field in a list of comma separated values */ static int ct_sip_next_header(const struct nf_conn *ct, const char *dptr, Loading Loading @@ -540,7 +785,7 @@ int ct_sip_parse_header_uri(const struct nf_conn *ct, const char *dptr, *dataoff = c - dptr; return 1; } EXPORT_SYMBOL_GPL(ct_sip_parse_header_uri); EXPORT_SYMBOL(ct_sip_parse_header_uri); static int ct_sip_parse_param(const struct nf_conn *ct, const char *dptr, unsigned int dataoff, unsigned int datalen, Loading Loading @@ -594,7 +839,7 @@ int ct_sip_parse_address_param(const struct nf_conn *ct, const char *dptr, *matchlen = end - start; return 1; } EXPORT_SYMBOL_GPL(ct_sip_parse_address_param); EXPORT_SYMBOL(ct_sip_parse_address_param); /* Parse numerical header parameter and return value, offset and length */ int ct_sip_parse_numerical_param(const struct nf_conn *ct, const char *dptr, Loading Loading @@ -625,7 +870,7 @@ int ct_sip_parse_numerical_param(const struct nf_conn *ct, const char *dptr, } return 1; } EXPORT_SYMBOL_GPL(ct_sip_parse_numerical_param); EXPORT_SYMBOL(ct_sip_parse_numerical_param); static int ct_sip_parse_transport(struct nf_conn *ct, const char *dptr, unsigned int dataoff, unsigned int datalen, Loading Loading @@ -783,7 +1028,7 @@ int ct_sip_get_sdp_header(const struct nf_conn *ct, const char *dptr, } return 0; } EXPORT_SYMBOL_GPL(ct_sip_get_sdp_header); EXPORT_SYMBOL(ct_sip_get_sdp_header); static int ct_sip_parse_sdp_addr(const struct nf_conn *ct, const char *dptr, unsigned int dataoff, unsigned int datalen, Loading Loading @@ -1500,13 +1745,28 @@ static int sip_help_tcp(struct sk_buff *skb, unsigned int protoff, struct nf_conn *ct, enum ip_conntrack_info ctinfo) { struct tcphdr *th, _tcph; unsigned int dataoff, datalen; unsigned int dataoff; unsigned int matchoff, matchlen, clen; unsigned int msglen, origlen; const char *dptr, *end; s16 diff, tdiff = 0; int ret = NF_ACCEPT; bool term; unsigned int datalen = 0, msglen = 0, origlen = 0; unsigned int dataoff_orig = 0; unsigned int splitlen, oldlen, oldlen1; struct sip_list *sip_entry = NULL; bool skip_sip_process = false; bool do_not_process = false; bool skip = false; bool skb_is_combined = false; enum ip_conntrack_dir dir = IP_CT_DIR_MAX; struct sk_buff *combined_skb = NULL; packet_count++; pr_debug("packet count %d\n", packet_count); if (nf_ct_disable_sip_alg) return NF_ACCEPT; if (ctinfo != IP_CT_ESTABLISHED && ctinfo != IP_CT_ESTABLISHED_REPLY) Loading @@ -1530,15 +1790,25 @@ static int sip_help_tcp(struct sk_buff *skb, unsigned int protoff, if (datalen < strlen("SIP/2.0 200")) return NF_ACCEPT; /* here we save the original datalength and data offset of the skb, this * is needed later to split combined skbs */ oldlen1 = skb->len - protoff; dataoff_orig = dataoff; while (1) { if (ct_sip_get_header(ct, dptr, 0, datalen, SIP_HDR_CONTENT_LENGTH, &matchoff, &matchlen) <= 0) break; &matchoff, &matchlen) <= 0){ do_not_process = true; goto destination; } clen = simple_strtoul(dptr + matchoff, (char **)&end, 10); if (dptr + matchoff == end) break; if (dptr + matchoff == end) { do_not_process = true; goto destination; } term = false; for (; end + strlen("\r\n\r\n") <= dptr + datalen; end++) { Loading @@ -1548,27 +1818,114 @@ static int sip_help_tcp(struct sk_buff *skb, unsigned int protoff, break; } } if (!term) break; end += strlen("\r\n\r\n") + clen; if (!term) { do_not_process = true; goto destination; } end += strlen("\r\n\r\n") + clen; msglen = origlen = end - dptr; if (msglen > datalen) return NF_ACCEPT; ret = process_sip_msg(skb, ct, protoff, dataoff, destination: if (ct) dir = CTINFO2DIR(ctinfo); combined_skb = skb; /* Segmented Packet */ if (msglen > datalen) { skip = sip_save_segment_info(ct, skb, msglen, datalen, dptr, ctinfo); if (!skip) return NF_QUEUE; } /* Traverse list to find prev segment */ /*Traverse the list if list non empty */ if (((&ct->sip_segment_list)->next) != (&ct->sip_segment_list)) { /* Combine segments if they are fragments of the same * message. */ sip_entry = sip_coalesce_segments(ct, &skb, dataoff, &combined_skb, &skip_sip_process, do_not_process, ctinfo, &skb_is_combined); if (skb_is_combined) { /* The msglen of first skb has the total msg length of * the two fragments. hence after combining,we update * the msglen to that of the msglen of first skb */ msglen = (dir == IP_CT_DIR_ORIGINAL) ? ct->segment.msg_length[0] : ct->segment.msg_length[1]; origlen = msglen; dptr = ct->dptr_prev; datalen = msglen; } if (skip_sip_process) goto here; } else if (do_not_process) { goto here; } /* process the combined skb having the complete SIP message */ ret = process_sip_msg(combined_skb, ct, protoff, dataoff, &dptr, &msglen); /* process_sip_* functions report why this packet is dropped */ if (ret != NF_ACCEPT) break; diff = msglen - origlen; tdiff += diff; dataoff += msglen; dptr += msglen; datalen = datalen + diff - msglen; break; } if (skb_is_combined) { /* once combined skb is processed, split the skbs again The * length to split at is the same as length of first skb. Any * changes in the combined skb length because of SIP processing * will reflect in the second fragment */ splitlen = (dir == IP_CT_DIR_ORIGINAL) ? ct->segment.skb_len[0] : ct->segment.skb_len[1]; oldlen = combined_skb->len - protoff; skb_split(combined_skb, skb, splitlen); /* Headers need to be recalculated since during SIP processing * headers are calculated based on the change in length of the * combined message */ recalc_header(combined_skb, splitlen, oldlen, protoff); /* Reinject the first skb now that the processing is complete */ nf_reinject(sip_entry->entry, NF_ACCEPT); kfree(sip_entry); skb->len = (oldlen1 + protoff) + tdiff - dataoff_orig; /* After splitting, push the headers back to the first skb which * were removed before combining the skbs.This moves the skb * begin pointer back to the beginning of its headers */ skb_push(skb, dataoff_orig); /* Since the length of this second segment willbe affected * because of SIP processing,we need to recalculate its header * as well. */ recalc_header(skb, skb->len, oldlen1, protoff); /* Now that the processing is done and the first skb reinjected. * We allow addition of fragmented skbs to the list for this * direction */ if (dir == IP_CT_DIR_ORIGINAL) ct->sip_original_dir = 0; else ct->sip_reply_dir = 0; } here: if (ret == NF_ACCEPT && ct->status & IPS_NAT_MASK) { const struct nf_nat_sip_hooks *hooks; Loading Loading @@ -1700,6 +2057,9 @@ static int __init nf_conntrack_sip_init(void) } } } nf_register_queue_handler(&nf_sip_qh); return 0; } Loading Loading
include/net/netfilter/nf_conntrack.h +15 −0 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ #include <linux/bitops.h> #include <linux/compiler.h> #include <linux/atomic.h> #include <linux/list.h> #include <linux/netfilter/nf_conntrack_tcp.h> #include <linux/netfilter/nf_conntrack_dccp.h> Loading @@ -26,6 +27,14 @@ #include <net/netfilter/nf_conntrack_tuple.h> #define SIP_LIST_ELEMENTS 2 struct sip_length { int msg_length[SIP_LIST_ELEMENTS]; int skb_len[SIP_LIST_ELEMENTS]; int data_len[SIP_LIST_ELEMENTS]; }; /* per conntrack: protocol private data */ union nf_conntrack_proto { /* insert conntrack proto private data here */ Loading Loading @@ -121,6 +130,12 @@ struct nf_conn { unsigned long nattype_entry; #endif struct list_head sip_segment_list; const char *dptr_prev; struct sip_length segment; bool sip_original_dir; bool sip_reply_dir; /* Storage reserved for other modules, must be the last member */ union nf_conntrack_proto proto; }; Loading
include/net/netfilter/nf_conntrack_core.h +5 −0 Original line number Diff line number Diff line Loading @@ -86,4 +86,9 @@ extern spinlock_t nf_conntrack_locks[CONNTRACK_LOCKS]; extern spinlock_t nf_conntrack_expect_lock; struct sip_list { struct nf_queue_entry *entry; struct list_head list; }; #endif /* _NF_CONNTRACK_CORE_H */
net/netfilter/nf_conntrack_core.c +44 −32 Original line number Diff line number Diff line Loading @@ -58,13 +58,13 @@ int (*nfnetlink_parse_nat_setup_hook)(struct nf_conn *ct, enum nf_nat_manip_type manip, const struct nlattr *attr) __read_mostly; EXPORT_SYMBOL_GPL(nfnetlink_parse_nat_setup_hook); EXPORT_SYMBOL(nfnetlink_parse_nat_setup_hook); __cacheline_aligned_in_smp spinlock_t nf_conntrack_locks[CONNTRACK_LOCKS]; EXPORT_SYMBOL_GPL(nf_conntrack_locks); EXPORT_SYMBOL(nf_conntrack_locks); __cacheline_aligned_in_smp DEFINE_SPINLOCK(nf_conntrack_expect_lock); EXPORT_SYMBOL_GPL(nf_conntrack_expect_lock); EXPORT_SYMBOL(nf_conntrack_expect_lock); static void nf_conntrack_double_unlock(unsigned int h1, unsigned int h2) { Loading Loading @@ -115,16 +115,16 @@ static void nf_conntrack_all_unlock(void) } unsigned int nf_conntrack_htable_size __read_mostly; EXPORT_SYMBOL_GPL(nf_conntrack_htable_size); EXPORT_SYMBOL(nf_conntrack_htable_size); unsigned int nf_conntrack_max __read_mostly; EXPORT_SYMBOL_GPL(nf_conntrack_max); EXPORT_SYMBOL(nf_conntrack_max); DEFINE_PER_CPU(struct nf_conn, nf_conntrack_untracked); EXPORT_PER_CPU_SYMBOL(nf_conntrack_untracked); unsigned int nf_conntrack_hash_rnd __read_mostly; EXPORT_SYMBOL_GPL(nf_conntrack_hash_rnd); EXPORT_SYMBOL(nf_conntrack_hash_rnd); static u32 hash_conntrack_raw(const struct nf_conntrack_tuple *tuple, u16 zone) { Loading Loading @@ -183,7 +183,7 @@ nf_ct_get_tuple(const struct sk_buff *skb, return l4proto->pkt_to_tuple(skb, dataoff, tuple); } EXPORT_SYMBOL_GPL(nf_ct_get_tuple); EXPORT_SYMBOL(nf_ct_get_tuple); bool nf_ct_get_tuplepr(const struct sk_buff *skb, unsigned int nhoff, u_int16_t l3num, struct nf_conntrack_tuple *tuple) Loading Loading @@ -211,7 +211,7 @@ bool nf_ct_get_tuplepr(const struct sk_buff *skb, unsigned int nhoff, rcu_read_unlock(); return ret; } EXPORT_SYMBOL_GPL(nf_ct_get_tuplepr); EXPORT_SYMBOL(nf_ct_get_tuplepr); bool nf_ct_invert_tuple(struct nf_conntrack_tuple *inverse, Loading @@ -230,7 +230,7 @@ nf_ct_invert_tuple(struct nf_conntrack_tuple *inverse, inverse->dst.protonum = orig->dst.protonum; return l4proto->invert_tuple(inverse, orig); } EXPORT_SYMBOL_GPL(nf_ct_invert_tuple); EXPORT_SYMBOL(nf_ct_invert_tuple); static void clean_from_lists(struct nf_conn *ct) Loading Loading @@ -293,6 +293,9 @@ destroy_conntrack(struct nf_conntrack *nfct) struct nf_conn *ct = (struct nf_conn *)nfct; struct net *net = nf_ct_net(ct); struct nf_conntrack_l4proto *l4proto; struct sip_list *sip_node = NULL; struct list_head *sip_node_list; struct list_head *sip_node_save_list; pr_debug("destroy_conntrack(%p)\n", ct); NF_CT_ASSERT(atomic_read(&nfct->use) == 0); Loading @@ -306,6 +309,14 @@ destroy_conntrack(struct nf_conntrack *nfct) rcu_read_unlock(); local_bh_disable(); pr_debug("freeing item in the SIP list\n"); list_for_each_safe(sip_node_list, sip_node_save_list, &ct->sip_segment_list) { sip_node = list_entry(sip_node_list, struct sip_list, list); list_del(&sip_node->list); kfree(sip_node); } /* Expectations will have been removed in clean_from_lists, * except TFTP can create an expectation on the first packet, * before connection is in the list, so we need to clean here, Loading Loading @@ -378,7 +389,7 @@ bool nf_ct_delete(struct nf_conn *ct, u32 portid, int report) nf_ct_put(ct); return true; } EXPORT_SYMBOL_GPL(nf_ct_delete); EXPORT_SYMBOL(nf_ct_delete); static void death_by_timeout(unsigned long ul_conntrack) { Loading Loading @@ -475,7 +486,7 @@ nf_conntrack_find_get(struct net *net, u16 zone, return __nf_conntrack_find_get(net, zone, tuple, hash_conntrack_raw(tuple, zone)); } EXPORT_SYMBOL_GPL(nf_conntrack_find_get); EXPORT_SYMBOL(nf_conntrack_find_get); static void __nf_conntrack_hash_insert(struct nf_conn *ct, unsigned int hash, Loading Loading @@ -538,7 +549,7 @@ out: local_bh_enable(); return -EEXIST; } EXPORT_SYMBOL_GPL(nf_conntrack_hash_check_insert); EXPORT_SYMBOL(nf_conntrack_hash_check_insert); /* deletion from this larval template list happens via nf_ct_put() */ void nf_conntrack_tmpl_insert(struct net *net, struct nf_conn *tmpl) Loading @@ -560,7 +571,7 @@ void nf_conntrack_tmpl_insert(struct net *net, struct nf_conn *tmpl) &pcpu->tmpl); spin_unlock_bh(&pcpu->lock); } EXPORT_SYMBOL_GPL(nf_conntrack_tmpl_insert); EXPORT_SYMBOL(nf_conntrack_tmpl_insert); /* Confirm a connection given skb; places it in hash table */ int Loading Loading @@ -676,7 +687,7 @@ out: local_bh_enable(); return NF_DROP; } EXPORT_SYMBOL_GPL(__nf_conntrack_confirm); EXPORT_SYMBOL(__nf_conntrack_confirm); /* Returns true if a connection correspondings to the tuple (required for NAT). */ Loading Loading @@ -710,7 +721,7 @@ nf_conntrack_tuple_taken(const struct nf_conntrack_tuple *tuple, return 0; } EXPORT_SYMBOL_GPL(nf_conntrack_tuple_taken); EXPORT_SYMBOL(nf_conntrack_tuple_taken); #define NF_CT_EVICTION_RANGE 8 Loading Loading @@ -872,7 +883,7 @@ struct nf_conn *nf_conntrack_alloc(struct net *net, u16 zone, { return __nf_conntrack_alloc(net, zone, orig, repl, gfp, 0); } EXPORT_SYMBOL_GPL(nf_conntrack_alloc); EXPORT_SYMBOL(nf_conntrack_alloc); void nf_conntrack_free(struct nf_conn *ct) { Loading @@ -889,7 +900,7 @@ void nf_conntrack_free(struct nf_conn *ct) smp_mb__before_atomic(); atomic_dec(&net->ct.count); } EXPORT_SYMBOL_GPL(nf_conntrack_free); EXPORT_SYMBOL(nf_conntrack_free); /* Allocate a new conntrack: we return -ENOMEM if classification Loading Loading @@ -951,6 +962,7 @@ init_conntrack(struct net *net, struct nf_conn *tmpl, GFP_ATOMIC); local_bh_disable(); INIT_LIST_HEAD(&ct->sip_segment_list); if (net->ct.expect_count) { spin_lock(&nf_conntrack_expect_lock); exp = nf_ct_find_expectation(net, zone, tuple); Loading Loading @@ -1170,7 +1182,7 @@ out: return ret; } EXPORT_SYMBOL_GPL(nf_conntrack_in); EXPORT_SYMBOL(nf_conntrack_in); bool nf_ct_invert_tuplepr(struct nf_conntrack_tuple *inverse, const struct nf_conntrack_tuple *orig) Loading @@ -1185,7 +1197,7 @@ bool nf_ct_invert_tuplepr(struct nf_conntrack_tuple *inverse, rcu_read_unlock(); return ret; } EXPORT_SYMBOL_GPL(nf_ct_invert_tuplepr); EXPORT_SYMBOL(nf_ct_invert_tuplepr); /* Alter reply tuple (maybe alter helper). This is for NAT, and is implicitly racy: see __nf_conntrack_confirm */ Loading @@ -1208,7 +1220,7 @@ void nf_conntrack_alter_reply(struct nf_conn *ct, __nf_ct_try_assign_helper(ct, NULL, GFP_ATOMIC); rcu_read_unlock(); } EXPORT_SYMBOL_GPL(nf_conntrack_alter_reply); EXPORT_SYMBOL(nf_conntrack_alter_reply); /* Refresh conntrack for this many jiffies and do accounting if do_acct is 1 */ void __nf_ct_refresh_acct(struct nf_conn *ct, Loading Loading @@ -1255,7 +1267,7 @@ acct: } } } EXPORT_SYMBOL_GPL(__nf_ct_refresh_acct); EXPORT_SYMBOL(__nf_ct_refresh_acct); bool __nf_ct_kill_acct(struct nf_conn *ct, enum ip_conntrack_info ctinfo, Loading @@ -1281,7 +1293,7 @@ bool __nf_ct_kill_acct(struct nf_conn *ct, } return false; } EXPORT_SYMBOL_GPL(__nf_ct_kill_acct); EXPORT_SYMBOL(__nf_ct_kill_acct); #ifdef CONFIG_NF_CONNTRACK_ZONES static struct nf_ct_ext_type nf_ct_zone_extend __read_mostly = { Loading Loading @@ -1311,13 +1323,13 @@ int nf_ct_port_tuple_to_nlattr(struct sk_buff *skb, nla_put_failure: return -1; } EXPORT_SYMBOL_GPL(nf_ct_port_tuple_to_nlattr); EXPORT_SYMBOL(nf_ct_port_tuple_to_nlattr); const struct nla_policy nf_ct_port_nla_policy[CTA_PROTO_MAX+1] = { [CTA_PROTO_SRC_PORT] = { .type = NLA_U16 }, [CTA_PROTO_DST_PORT] = { .type = NLA_U16 }, }; EXPORT_SYMBOL_GPL(nf_ct_port_nla_policy); EXPORT_SYMBOL(nf_ct_port_nla_policy); int nf_ct_port_nlattr_to_tuple(struct nlattr *tb[], struct nf_conntrack_tuple *t) Loading @@ -1330,13 +1342,13 @@ int nf_ct_port_nlattr_to_tuple(struct nlattr *tb[], return 0; } EXPORT_SYMBOL_GPL(nf_ct_port_nlattr_to_tuple); EXPORT_SYMBOL(nf_ct_port_nlattr_to_tuple); int nf_ct_port_nlattr_tuple_size(void) { return nla_policy_len(nf_ct_port_nla_policy, CTA_PROTO_MAX + 1); } EXPORT_SYMBOL_GPL(nf_ct_port_nlattr_tuple_size); EXPORT_SYMBOL(nf_ct_port_nlattr_tuple_size); #endif /* Used by ipt_REJECT and ip6t_REJECT. */ Loading Loading @@ -1422,7 +1434,7 @@ void nf_ct_iterate_cleanup(struct net *net, nf_ct_put(ct); } } EXPORT_SYMBOL_GPL(nf_ct_iterate_cleanup); EXPORT_SYMBOL(nf_ct_iterate_cleanup); static int kill_all(struct nf_conn *i, void *data) { Loading @@ -1437,13 +1449,13 @@ void nf_ct_free_hashtable(void *hash, unsigned int size) free_pages((unsigned long)hash, get_order(sizeof(struct hlist_head) * size)); } EXPORT_SYMBOL_GPL(nf_ct_free_hashtable); EXPORT_SYMBOL(nf_ct_free_hashtable); void nf_conntrack_flush_report(struct net *net, u32 portid, int report) { nf_ct_iterate_cleanup(net, kill_all, NULL, portid, report); } EXPORT_SYMBOL_GPL(nf_conntrack_flush_report); EXPORT_SYMBOL(nf_conntrack_flush_report); static int untrack_refs(void) { Loading Loading @@ -1554,7 +1566,7 @@ void *nf_ct_alloc_hashtable(unsigned int *sizep, int nulls) return hash; } EXPORT_SYMBOL_GPL(nf_ct_alloc_hashtable); EXPORT_SYMBOL(nf_ct_alloc_hashtable); int nf_conntrack_set_hashsize(const char *val, struct kernel_param *kp) { Loading Loading @@ -1615,7 +1627,7 @@ int nf_conntrack_set_hashsize(const char *val, struct kernel_param *kp) nf_ct_free_hashtable(old_hash, old_size); return 0; } EXPORT_SYMBOL_GPL(nf_conntrack_set_hashsize); EXPORT_SYMBOL(nf_conntrack_set_hashsize); module_param_call(hashsize, nf_conntrack_set_hashsize, param_get_uint, &nf_conntrack_htable_size, 0600); Loading @@ -1627,7 +1639,7 @@ void nf_ct_untracked_status_or(unsigned long bits) for_each_possible_cpu(cpu) per_cpu(nf_conntrack_untracked, cpu).status |= bits; } EXPORT_SYMBOL_GPL(nf_ct_untracked_status_or); EXPORT_SYMBOL(nf_ct_untracked_status_or); int nf_conntrack_init_start(void) { Loading
net/netfilter/nf_conntrack_sip.c +383 −23 Original line number Diff line number Diff line /* SIP extension for IP connection tracking. * * Copyright (c) 2015, The Linux Foundation. All rights reserved. * (C) 2005 by Christian Hentschel <chentschel@arnet.com.ar> * based on RR's ip_conntrack_ftp.c and other modules. * (C) 2007 United Security Providers Loading @@ -18,13 +19,18 @@ #include <linux/udp.h> #include <linux/tcp.h> #include <linux/netfilter.h> #include <net/tcp.h> #include <net/netfilter/nf_conntrack.h> #include <net/netfilter/nf_conntrack_core.h> #include <net/netfilter/nf_conntrack_expect.h> #include <net/netfilter/nf_conntrack_helper.h> #include <net/netfilter/nf_conntrack_zones.h> #include <linux/netfilter/nf_conntrack_sip.h> #include <net/netfilter/nf_nat.h> #include <net/netfilter/nf_nat_l3proto.h> #include <net/netfilter/nf_nat_l4proto.h> #include <net/netfilter/nf_queue.h> MODULE_LICENSE("GPL"); MODULE_AUTHOR("Christian Hentschel <chentschel@arnet.com.ar>"); Loading @@ -48,10 +54,11 @@ MODULE_PARM_DESC(sip_direct_signalling, "expect incoming calls from registrar " "only (default 1)"); const struct nf_nat_sip_hooks *nf_nat_sip_hooks; EXPORT_SYMBOL_GPL(nf_nat_sip_hooks); EXPORT_SYMBOL(nf_nat_sip_hooks); static struct ctl_table_header *sip_sysctl_header; static unsigned nf_ct_disable_sip_alg; static int sip_direct_media = 1; static int packet_count; static struct ctl_table sip_sysctl_tbl[] = { { .procname = "nf_conntrack_disable_sip_alg", Loading @@ -70,6 +77,224 @@ static struct ctl_table sip_sysctl_tbl[] = { {} }; unsigned int (*nf_nat_sip_hook)(struct sk_buff *skb, unsigned int protoff, unsigned int dataoff, const char **dptr, unsigned int *datalen) __read_mostly; /* This function is to save all the information of the first segment * that will be needed for combining the two segments */ static bool sip_save_segment_info(struct nf_conn *ct, struct sk_buff *skb, unsigned int msglen, unsigned int datalen, const char *dptr, enum ip_conntrack_info ctinfo) { enum ip_conntrack_dir dir = IP_CT_DIR_MAX; bool skip = false; /* one set of information is saved per direction ,also only one segment * per direction is queued based on the assumption that after the first * complete message leaves the kernel, only then the next fragmented * segment will reach the kernel */ if (ct) dir = CTINFO2DIR(ctinfo); if (dir == IP_CT_DIR_ORIGINAL) { /* here we check if there is already an element queued for this * direction, in that case we do not queue the next element,we * make skip 1.ideally this scenario should never be hit */ if (ct->sip_original_dir == 1) { skip = true; } else { ct->segment.msg_length[0] = msglen; ct->segment.data_len[0] = datalen; ct->segment.skb_len[0] = skb->len; ct->dptr_prev = dptr; ct->sip_original_dir = 1; skip = false; } } else { if (ct->sip_reply_dir == 1) { skip = true; } else { ct->segment.msg_length[1] = msglen; ct->segment.data_len[1] = datalen; ct->segment.skb_len[1] = skb->len; ct->dptr_prev = dptr; ct->sip_reply_dir = 1; skip = false; } } return skip; } static struct sip_list *sip_coalesce_segments(struct nf_conn *ct, struct sk_buff **skb_ref, unsigned int dataoff, struct sk_buff **combined_skb_ref, bool *skip_sip_process, bool do_not_process, enum ip_conntrack_info ctinfo, bool *success) { struct list_head *list_trav_node; struct list_head *list_backup_node; struct nf_conn *ct_list; enum ip_conntrack_info ctinfo_list; enum ip_conntrack_dir dir_list; enum ip_conntrack_dir dir = IP_CT_DIR_MAX; const struct tcphdr *th_old; unsigned int prev_data_len; unsigned int seq_no, seq_old, exp_seq_no; const struct tcphdr *th_new; bool fragstolen = false; int delta_truesize = 0; struct sip_list *sip_entry = NULL; th_new = (struct tcphdr *)(skb_network_header(*skb_ref) + ip_hdrlen(*skb_ref)); seq_no = ntohl(th_new->seq); if (ct) dir = CTINFO2DIR(ctinfo); /* traverse the list it would have 1 or 2 elements. 1 element per * direction at max */ list_for_each_safe(list_trav_node, list_backup_node, &ct->sip_segment_list){ sip_entry = list_entry(list_trav_node, struct sip_list, list); ct_list = nf_ct_get(sip_entry->entry->skb, &ctinfo_list); dir_list = CTINFO2DIR(ctinfo_list); /* take an element and check if its direction matches with the * current one */ if (dir_list == dir) { /* once we have the two elements to be combined we do * another check. match the next expected seq no of the * packet in the list with the seq no of the current * packet.this is to be protected against out of order * fragments */ th_old = ((struct tcphdr *)(skb_network_header (sip_entry->entry->skb) + ip_hdrlen(sip_entry->entry->skb))); prev_data_len = (dir == IP_CT_DIR_ORIGINAL) ? ct->segment.data_len[0] : ct->segment.data_len[1]; seq_old = (ntohl(th_old->seq)); exp_seq_no = seq_old+prev_data_len; if (exp_seq_no == seq_no) { /* Found packets to be combined.Pull header from * second skb when preparing combined skb.This * shifts the second skb start pointer to its * data that was initially at the start of its * headers.This so that the combined skb has * the tcp ip headerof the first skb followed * by the data of first skb followed by the data * of second skb. */ skb_pull(*skb_ref, dataoff); if (skb_try_coalesce(sip_entry->entry->skb, *skb_ref, &fragstolen, &delta_truesize)) { *combined_skb_ref = sip_entry->entry->skb; *success = true; list_del(list_trav_node); } else{ skb_push(*skb_ref, dataoff); } } } else if (do_not_process) { *skip_sip_process = true; } } return sip_entry; } static void recalc_header(struct sk_buff *skb, unsigned int skblen, unsigned int oldlen, unsigned int protoff) { unsigned int datalen; struct tcphdr *tcph; const struct nf_nat_l3proto *l3proto; /* here we recalculate ip and tcp headers */ if (nf_ct_l3num((struct nf_conn *)skb->nfct) == NFPROTO_IPV4) { /* fix IP hdr checksum information */ ip_hdr(skb)->tot_len = htons(skblen); ip_send_check(ip_hdr(skb)); } else { ipv6_hdr(skb)->payload_len = htons(skblen - sizeof(struct ipv6hdr)); } datalen = skb->len - protoff; tcph = (struct tcphdr *)((void *)skb->data + protoff); l3proto = __nf_nat_l3proto_find(nf_ct_l3num ((struct nf_conn *)skb->nfct)); l3proto->csum_recalc(skb, IPPROTO_TCP, tcph, &tcph->check, datalen, oldlen); } EXPORT_SYMBOL(nf_nat_sip_hook); void (*nf_nat_sip_seq_adjust_hook)(struct sk_buff *skb, unsigned int protoff, s16 off) __read_mostly; EXPORT_SYMBOL(nf_nat_sip_seq_adjust_hook); unsigned int (*nf_nat_sip_expect_hook)(struct sk_buff *skb, unsigned int protoff, unsigned int dataoff, const char **dptr, unsigned int *datalen, struct nf_conntrack_expect *exp, unsigned int matchoff, unsigned int matchlen) __read_mostly; EXPORT_SYMBOL(nf_nat_sip_expect_hook); unsigned int (*nf_nat_sdp_addr_hook)(struct sk_buff *skb, unsigned int protoff, unsigned int dataoff, const char **dptr, unsigned int *datalen, unsigned int sdpoff, enum sdp_header_types type, enum sdp_header_types term, const union nf_inet_addr *addr) __read_mostly; EXPORT_SYMBOL(nf_nat_sdp_addr_hook); unsigned int (*nf_nat_sdp_port_hook)(struct sk_buff *skb, unsigned int protoff, unsigned int dataoff, const char **dptr, unsigned int *datalen, unsigned int matchoff, unsigned int matchlen, u_int16_t port) __read_mostly; EXPORT_SYMBOL(nf_nat_sdp_port_hook); unsigned int (*nf_nat_sdp_session_hook)(struct sk_buff *skb, unsigned int protoff, unsigned int dataoff, const char **dptr, unsigned int *datalen, unsigned int sdpoff, const union nf_inet_addr *addr) __read_mostly; EXPORT_SYMBOL(nf_nat_sdp_session_hook); unsigned int (*nf_nat_sdp_media_hook)(struct sk_buff *skb, unsigned int protoff, unsigned int dataoff, const char **dptr, unsigned int *datalen, struct nf_conntrack_expect *rtp_exp, struct nf_conntrack_expect *rtcp_exp, unsigned int mediaoff, unsigned int medialen, union nf_inet_addr *rtp_addr) __read_mostly; EXPORT_SYMBOL(nf_nat_sdp_media_hook); static int string_len(const struct nf_conn *ct, const char *dptr, const char *limit, int *shift) { Loading @@ -82,6 +307,26 @@ static int string_len(const struct nf_conn *ct, const char *dptr, return len; } static int nf_sip_enqueue_packet(struct nf_queue_entry *entry, unsigned int queuenum) { enum ip_conntrack_info ctinfo_list; struct nf_conn *ct_temp; struct sip_list *node = kzalloc(sizeof(*node), GFP_ATOMIC | __GFP_NOWARN); if (!node) return XT_CONTINUE; ct_temp = nf_ct_get(entry->skb, &ctinfo_list); node->entry = entry; list_add(&node->list, &ct_temp->sip_segment_list); return 0; } static const struct nf_queue_handler nf_sip_qh = { .outfn = &nf_sip_enqueue_packet, }; static int digits_len(const struct nf_conn *ct, const char *dptr, const char *limit, int *shift) { Loading Loading @@ -288,7 +533,7 @@ int ct_sip_parse_request(const struct nf_conn *ct, *matchlen = end - dptr; return 1; } EXPORT_SYMBOL_GPL(ct_sip_parse_request); EXPORT_SYMBOL(ct_sip_parse_request); /* SIP header parsing: SIP headers are located at the beginning of a line, but * may span several lines, in which case the continuation lines begin with a Loading Loading @@ -436,7 +681,7 @@ int ct_sip_get_header(const struct nf_conn *ct, const char *dptr, } return 0; } EXPORT_SYMBOL_GPL(ct_sip_get_header); EXPORT_SYMBOL(ct_sip_get_header); /* Get next header field in a list of comma separated values */ static int ct_sip_next_header(const struct nf_conn *ct, const char *dptr, Loading Loading @@ -540,7 +785,7 @@ int ct_sip_parse_header_uri(const struct nf_conn *ct, const char *dptr, *dataoff = c - dptr; return 1; } EXPORT_SYMBOL_GPL(ct_sip_parse_header_uri); EXPORT_SYMBOL(ct_sip_parse_header_uri); static int ct_sip_parse_param(const struct nf_conn *ct, const char *dptr, unsigned int dataoff, unsigned int datalen, Loading Loading @@ -594,7 +839,7 @@ int ct_sip_parse_address_param(const struct nf_conn *ct, const char *dptr, *matchlen = end - start; return 1; } EXPORT_SYMBOL_GPL(ct_sip_parse_address_param); EXPORT_SYMBOL(ct_sip_parse_address_param); /* Parse numerical header parameter and return value, offset and length */ int ct_sip_parse_numerical_param(const struct nf_conn *ct, const char *dptr, Loading Loading @@ -625,7 +870,7 @@ int ct_sip_parse_numerical_param(const struct nf_conn *ct, const char *dptr, } return 1; } EXPORT_SYMBOL_GPL(ct_sip_parse_numerical_param); EXPORT_SYMBOL(ct_sip_parse_numerical_param); static int ct_sip_parse_transport(struct nf_conn *ct, const char *dptr, unsigned int dataoff, unsigned int datalen, Loading Loading @@ -783,7 +1028,7 @@ int ct_sip_get_sdp_header(const struct nf_conn *ct, const char *dptr, } return 0; } EXPORT_SYMBOL_GPL(ct_sip_get_sdp_header); EXPORT_SYMBOL(ct_sip_get_sdp_header); static int ct_sip_parse_sdp_addr(const struct nf_conn *ct, const char *dptr, unsigned int dataoff, unsigned int datalen, Loading Loading @@ -1500,13 +1745,28 @@ static int sip_help_tcp(struct sk_buff *skb, unsigned int protoff, struct nf_conn *ct, enum ip_conntrack_info ctinfo) { struct tcphdr *th, _tcph; unsigned int dataoff, datalen; unsigned int dataoff; unsigned int matchoff, matchlen, clen; unsigned int msglen, origlen; const char *dptr, *end; s16 diff, tdiff = 0; int ret = NF_ACCEPT; bool term; unsigned int datalen = 0, msglen = 0, origlen = 0; unsigned int dataoff_orig = 0; unsigned int splitlen, oldlen, oldlen1; struct sip_list *sip_entry = NULL; bool skip_sip_process = false; bool do_not_process = false; bool skip = false; bool skb_is_combined = false; enum ip_conntrack_dir dir = IP_CT_DIR_MAX; struct sk_buff *combined_skb = NULL; packet_count++; pr_debug("packet count %d\n", packet_count); if (nf_ct_disable_sip_alg) return NF_ACCEPT; if (ctinfo != IP_CT_ESTABLISHED && ctinfo != IP_CT_ESTABLISHED_REPLY) Loading @@ -1530,15 +1790,25 @@ static int sip_help_tcp(struct sk_buff *skb, unsigned int protoff, if (datalen < strlen("SIP/2.0 200")) return NF_ACCEPT; /* here we save the original datalength and data offset of the skb, this * is needed later to split combined skbs */ oldlen1 = skb->len - protoff; dataoff_orig = dataoff; while (1) { if (ct_sip_get_header(ct, dptr, 0, datalen, SIP_HDR_CONTENT_LENGTH, &matchoff, &matchlen) <= 0) break; &matchoff, &matchlen) <= 0){ do_not_process = true; goto destination; } clen = simple_strtoul(dptr + matchoff, (char **)&end, 10); if (dptr + matchoff == end) break; if (dptr + matchoff == end) { do_not_process = true; goto destination; } term = false; for (; end + strlen("\r\n\r\n") <= dptr + datalen; end++) { Loading @@ -1548,27 +1818,114 @@ static int sip_help_tcp(struct sk_buff *skb, unsigned int protoff, break; } } if (!term) break; end += strlen("\r\n\r\n") + clen; if (!term) { do_not_process = true; goto destination; } end += strlen("\r\n\r\n") + clen; msglen = origlen = end - dptr; if (msglen > datalen) return NF_ACCEPT; ret = process_sip_msg(skb, ct, protoff, dataoff, destination: if (ct) dir = CTINFO2DIR(ctinfo); combined_skb = skb; /* Segmented Packet */ if (msglen > datalen) { skip = sip_save_segment_info(ct, skb, msglen, datalen, dptr, ctinfo); if (!skip) return NF_QUEUE; } /* Traverse list to find prev segment */ /*Traverse the list if list non empty */ if (((&ct->sip_segment_list)->next) != (&ct->sip_segment_list)) { /* Combine segments if they are fragments of the same * message. */ sip_entry = sip_coalesce_segments(ct, &skb, dataoff, &combined_skb, &skip_sip_process, do_not_process, ctinfo, &skb_is_combined); if (skb_is_combined) { /* The msglen of first skb has the total msg length of * the two fragments. hence after combining,we update * the msglen to that of the msglen of first skb */ msglen = (dir == IP_CT_DIR_ORIGINAL) ? ct->segment.msg_length[0] : ct->segment.msg_length[1]; origlen = msglen; dptr = ct->dptr_prev; datalen = msglen; } if (skip_sip_process) goto here; } else if (do_not_process) { goto here; } /* process the combined skb having the complete SIP message */ ret = process_sip_msg(combined_skb, ct, protoff, dataoff, &dptr, &msglen); /* process_sip_* functions report why this packet is dropped */ if (ret != NF_ACCEPT) break; diff = msglen - origlen; tdiff += diff; dataoff += msglen; dptr += msglen; datalen = datalen + diff - msglen; break; } if (skb_is_combined) { /* once combined skb is processed, split the skbs again The * length to split at is the same as length of first skb. Any * changes in the combined skb length because of SIP processing * will reflect in the second fragment */ splitlen = (dir == IP_CT_DIR_ORIGINAL) ? ct->segment.skb_len[0] : ct->segment.skb_len[1]; oldlen = combined_skb->len - protoff; skb_split(combined_skb, skb, splitlen); /* Headers need to be recalculated since during SIP processing * headers are calculated based on the change in length of the * combined message */ recalc_header(combined_skb, splitlen, oldlen, protoff); /* Reinject the first skb now that the processing is complete */ nf_reinject(sip_entry->entry, NF_ACCEPT); kfree(sip_entry); skb->len = (oldlen1 + protoff) + tdiff - dataoff_orig; /* After splitting, push the headers back to the first skb which * were removed before combining the skbs.This moves the skb * begin pointer back to the beginning of its headers */ skb_push(skb, dataoff_orig); /* Since the length of this second segment willbe affected * because of SIP processing,we need to recalculate its header * as well. */ recalc_header(skb, skb->len, oldlen1, protoff); /* Now that the processing is done and the first skb reinjected. * We allow addition of fragmented skbs to the list for this * direction */ if (dir == IP_CT_DIR_ORIGINAL) ct->sip_original_dir = 0; else ct->sip_reply_dir = 0; } here: if (ret == NF_ACCEPT && ct->status & IPS_NAT_MASK) { const struct nf_nat_sip_hooks *hooks; Loading Loading @@ -1700,6 +2057,9 @@ static int __init nf_conntrack_sip_init(void) } } } nf_register_queue_handler(&nf_sip_qh); return 0; } Loading