diff --git a/include/linux/netfilter/nf_conntrack_sip.h b/include/linux/netfilter/nf_conntrack_sip.h index c620521c42bc..4e9bf9905a88 100644 --- a/include/linux/netfilter/nf_conntrack_sip.h +++ b/include/linux/netfilter/nf_conntrack_sip.h @@ -166,6 +166,13 @@ struct nf_nat_sip_hooks { }; extern const struct nf_nat_sip_hooks *nf_nat_sip_hooks; +#ifdef CONFIG_NF_CONNTRACK_SIP_SEGMENTATION +extern void (*nf_nat_sip_seq_adjust_hook) + (struct sk_buff *skb, + unsigned int protoff, + s16 off); +#endif + int ct_sip_parse_request(const struct nf_conn *ct, const char *dptr, unsigned int datalen, unsigned int *matchoff, unsigned int *matchlen, union nf_inet_addr *addr, diff --git a/include/net/netfilter/nf_conntrack.h b/include/net/netfilter/nf_conntrack.h index ad3183d06878..452db7dfb406 100644 --- a/include/net/netfilter/nf_conntrack.h +++ b/include/net/netfilter/nf_conntrack.h @@ -17,6 +17,9 @@ #include #include #include +#ifdef CONFIG_NF_CONNTRACK_SIP_SEGMENTATION +#include +#endif #include #include @@ -26,10 +29,22 @@ #include +#ifdef CONFIG_NF_CONNTRACK_SIP_SEGMENTATION +#define SIP_LIST_ELEMENTS 2 +#endif + struct nf_ct_udp { unsigned long stream_ts; }; +#ifdef CONFIG_NF_CONNTRACK_SIP_SEGMENTATION +struct sip_length { + int msg_length[SIP_LIST_ELEMENTS]; + int skb_len[SIP_LIST_ELEMENTS]; + int data_len[SIP_LIST_ELEMENTS]; +}; +#endif + /* per conntrack: protocol private data */ union nf_conntrack_proto { /* insert conntrack proto private data here */ @@ -117,6 +132,13 @@ struct nf_conn { #ifdef CONFIG_ENABLE_SFE void *sfe_entry; #endif +#ifdef CONFIG_NF_CONNTRACK_SIP_SEGMENTATION + struct list_head sip_segment_list; + const char *dptr_prev; + struct sip_length segment; + bool sip_original_dir; + bool sip_reply_dir; +#endif /* Storage reserved for other modules, must be the last member */ union nf_conntrack_proto proto; diff --git a/include/net/netfilter/nf_conntrack_core.h b/include/net/netfilter/nf_conntrack_core.h index 7787b4774414..e799bea9dab7 100644 --- a/include/net/netfilter/nf_conntrack_core.h +++ b/include/net/netfilter/nf_conntrack_core.h @@ -22,6 +22,8 @@ standalone connection tracking module, and the compatibility layer's use of connection tracking. */ +extern unsigned int nf_conntrack_hash_rnd; + unsigned int nf_conntrack_in(struct sk_buff *skb, const struct nf_hook_state *state); @@ -90,4 +92,11 @@ void nf_conntrack_lock(spinlock_t *lock); extern spinlock_t nf_conntrack_expect_lock; +#ifdef CONFIG_NF_CONNTRACK_SIP_SEGMENTATION +struct sip_list { + struct nf_queue_entry *entry; + struct list_head list; +}; +#endif + #endif /* _NF_CONNTRACK_CORE_H */ diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index ea957f78a5a9..98922e00dd2c 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -342,6 +342,18 @@ config NF_CONNTRACK_SIP To compile it as a module, choose M here. If unsure, say N. +config NF_CONNTRACK_SIP_SEGMENTATION + tristate "SIP protocol segmentation support" + depends on NF_CONNTRACK_SIP + default m if NETFILTER_ADVANCED=n + help + Linux Kernel SIP ALG did not handle Segmented TCP Packets + because of which SIP communication could not be established + for some clients. This special type supports SIP segmentation + packets + + To compile it as a module, choose M here. If unsure, say N. + config NF_CONNTRACK_TFTP tristate "TFTP protocol support" depends on NETFILTER_ADVANCED diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index b8964418e66f..4f18c0e1e415 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -192,7 +192,8 @@ unsigned int nf_conntrack_pkt_threshold __read_mostly; EXPORT_SYMBOL(nf_conntrack_pkt_threshold); #endif -static unsigned int nf_conntrack_hash_rnd __read_mostly; +unsigned int nf_conntrack_hash_rnd __read_mostly; +EXPORT_SYMBOL(nf_conntrack_hash_rnd); static u32 hash_conntrack_raw(const struct nf_conntrack_tuple *tuple, const struct net *net) @@ -617,6 +618,11 @@ destroy_conntrack(struct nf_conntrack *nfct) #ifdef CONFIG_ENABLE_SFE void (*delete_entry)(struct nf_conn *ct); #endif +#ifdef CONFIG_NF_CONNTRACK_SIP_SEGMENTATION + struct sip_list *sip_node = NULL; + struct list_head *sip_node_list; + struct list_head *sip_node_save_list; +#endif pr_debug("destroy_conntrack(%p)\n", ct); WARN_ON(atomic_read(&nfct->use) != 0); @@ -638,6 +644,16 @@ destroy_conntrack(struct nf_conntrack *nfct) #endif local_bh_disable(); + +#ifdef CONFIG_NF_CONNTRACK_SIP_SEGMENTATION + 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); + } +#endif /* 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, @@ -1510,6 +1526,9 @@ init_conntrack(struct net *net, struct nf_conn *tmpl, GFP_ATOMIC); local_bh_disable(); +#ifdef CONFIG_NF_CONNTRACK_SIP_SEGMENTATION + INIT_LIST_HEAD(&ct->sip_segment_list); +#endif if (net->ct.expect_count) { spin_lock(&nf_conntrack_expect_lock); exp = nf_ct_find_expectation(net, zone, tuple); diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c index d8b842974a2f..21fa55704902 100644 --- a/net/netfilter/nf_conntrack_sip.c +++ b/net/netfilter/nf_conntrack_sip.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* SIP extension for IP connection tracking. * + * Copyright (c) 2015,2017,2018 The Linux Foundation. All rights reserved. * (C) 2005 by Christian Hentschel * based on RR's ip_conntrack_ftp.c and other modules. * (C) 2007 United Security Providers @@ -20,12 +21,19 @@ #include #include +#ifdef CONFIG_NF_CONNTRACK_SIP_SEGMENTATION +#include +#endif #include #include #include #include #include #include +#ifdef CONFIG_NF_CONNTRACK_SIP_SEGMENTATION +#include +#include +#endif #define HELPER_NAME "sip" @@ -60,6 +68,14 @@ EXPORT_SYMBOL_GPL(nf_nat_sip_hooks); static struct ctl_table_header *sip_sysctl_header; static unsigned int nf_ct_disable_sip_alg; static int sip_direct_media = 1; +#ifdef CONFIG_NF_CONNTRACK_SIP_SEGMENTATION +static unsigned int nf_ct_enable_sip_segmentation; +static int packet_count; +static +int proc_sip_segment(struct ctl_table *ctl, int write, + void __user *buffer, size_t *lenp, loff_t *ppos); +#endif + static struct ctl_table sip_sysctl_tbl[] = { { .procname = "nf_conntrack_disable_sip_alg", @@ -75,9 +91,289 @@ static struct ctl_table sip_sysctl_tbl[] = { .mode = 0644, .proc_handler = proc_dointvec, }, +#ifdef CONFIG_NF_CONNTRACK_SIP_SEGMENTATION + { + .procname = "nf_conntrack_enable_sip_segmentation", + .data = &nf_ct_enable_sip_segmentation, + .maxlen = sizeof(unsigned int), + .mode = 0644, + .proc_handler = proc_sip_segment, + }, +#endif {} }; +static unsigned int (*nf_nat_sip_hook) + (struct sk_buff *skb, + unsigned int protoff, + unsigned int dataoff, + const char **dptr, + unsigned int *datalen) + __read_mostly; +EXPORT_SYMBOL(nf_nat_sip_hook); +static void sip_calculate_parameters(s16 *diff, s16 *tdiff, + unsigned int *dataoff, const char **dptr, + unsigned int *datalen, + unsigned int msglen, unsigned int origlen) +{ + *diff = msglen - origlen; + *tdiff += *diff; + *dataoff += msglen; + *dptr += msglen; + *datalen = *datalen + *diff - msglen; +} + +#ifdef CONFIG_NF_CONNTRACK_SIP_SEGMENTATION +static void sip_update_params(enum ip_conntrack_dir dir, + unsigned int *msglen, unsigned int *origlen, + const char **dptr, unsigned int *datalen, + bool skb_is_combined, struct nf_conn *ct) +{ + 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; + } +} + +/* 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 + */ + 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 { + 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)) { + pr_debug(" Combining segments\n"); + *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; + + /* 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); + nf_nat_csum_recalc(skb, nf_ct_l3num((struct nf_conn *)skb->_nfct), + IPPROTO_TCP, tcph, &tcph->check, datalen, oldlen); +} + +void (*nf_nat_sip_seq_adjust_hook) + (struct sk_buff *skb, + unsigned int protoff, + s16 off); + +static 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); + +static 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); + +static 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); + +static 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); + +static 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); +#endif + static int string_len(const struct nf_conn *ct, const char *dptr, const char *limit, int *shift) { @@ -90,6 +386,45 @@ static int string_len(const struct nf_conn *ct, const char *dptr, return len; } +#ifdef CONFIG_NF_CONNTRACK_SIP_SEGMENTATION +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 proc_sip_segment(struct ctl_table *ctl, int write, + void __user *buffer, size_t *lenp, loff_t *ppos) +{ + int ret; + + ret = proc_dointvec(ctl, write, buffer, lenp, ppos); + if (nf_ct_enable_sip_segmentation) { + pr_debug("registering queue handler\n"); + nf_register_queue_handler(&init_net, &nf_sip_qh); + } else { + pr_debug("de-registering queue handler\n"); + nf_unregister_queue_handler(&init_net); + } + return ret; +} +#endif + static int digits_len(const struct nf_conn *ct, const char *dptr, const char *limit, int *shift) { @@ -1541,23 +1876,103 @@ static int process_sip_msg(struct sk_buff *skb, struct nf_conn *ct, return ret; } +static void sip_tcp_skip_process(int ret, struct nf_conn *ct, struct sk_buff *skb, + unsigned int protoff, s16 *tdiff) +{ + if (ret == NF_ACCEPT && ct && ct->status & IPS_NAT_MASK) { + const struct nf_nat_sip_hooks *hooks; + + hooks = rcu_dereference(nf_nat_sip_hooks); + if (hooks) + hooks->seq_adjust(skb, protoff, tdiff); + } +} + +#ifdef CONFIG_NF_CONNTRACK_SIP_SEGMENTATION +static void sip_tcp_skb_combined_processing(bool skb_is_combined, struct sk_buff *skb, + enum ip_conntrack_dir dir, struct sk_buff *combined_skb, + struct nf_conn *ct, unsigned int protoff, + struct sip_list *sip_entry, s16 tdiff, + unsigned int dataoff_orig) +{ + if (skb_is_combined) { + unsigned int splitlen, oldlen, oldlen1; + /* 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; + oldlen1 = 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 */ + if (sip_entry) { + 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; + } +} +#endif + 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; +#ifdef CONFIG_NF_CONNTRACK_SIP_SEGMENTATION + 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; + bool content_len_exists = true; - if (ctinfo != IP_CT_ESTABLISHED && - ctinfo != IP_CT_ESTABLISHED_REPLY) + packet_count++; + pr_debug("packet count %d\n", packet_count); + + if (nf_ct_disable_sip_alg) + return NF_ACCEPT; +#endif + + if (ctinfo != IP_CT_ESTABLISHED && ctinfo != IP_CT_ESTABLISHED_REPLY) return NF_ACCEPT; - /* No Data ? */ th = skb_header_pointer(skb, protoff, sizeof(_tcph), &_tcph); if (th == NULL) return NF_ACCEPT; @@ -1574,12 +1989,30 @@ static int sip_help_tcp(struct sk_buff *skb, unsigned int protoff, datalen = skb->len - dataoff; if (datalen < strlen("SIP/2.0 200")) return NF_ACCEPT; +#ifdef CONFIG_NF_CONNTRACK_SIP_SEGMENTATION + /* 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; + if (!ct) + return NF_DROP; +#endif while (1) { if (ct_sip_get_header(ct, dptr, 0, datalen, SIP_HDR_CONTENT_LENGTH, - &matchoff, &matchlen) <= 0) + &matchoff, &matchlen) <= 0){ +#ifdef CONFIG_NF_CONNTRACK_SIP_SEGMENTATION + if (!nf_ct_enable_sip_segmentation) + break; + do_not_process = true; + content_len_exists = false; + goto destination; +#else break; +#endif + } clen = simple_strtoul(dptr + matchoff, (char **)&end, 10); if (dptr + matchoff == end) @@ -1595,32 +2028,78 @@ static int sip_help_tcp(struct sk_buff *skb, unsigned int protoff, } if (!term) break; - end += strlen("\r\n\r\n") + clen; + end += strlen("\r\n\r\n") + clen; +#ifdef CONFIG_NF_CONNTRACK_SIP_SEGMENTATION +destination: + + origlen = datalen; + if (content_len_exists) + origlen = end - dptr; + msglen = origlen; + pr_debug("msglen %d datalen %d\n", msglen, datalen); + dir = CTINFO2DIR(ctinfo); + combined_skb = skb; + if (msglen > datalen) { + if (!nf_ct_enable_sip_segmentation) + return NF_ACCEPT; + /* Segmented Packet */ + skip = sip_save_segment_info(ct, skb, msglen, + datalen, dptr, ctinfo); + if (!skip) + return NF_QUEUE; + } + if (nf_ct_enable_sip_segmentation) { + /* Traverse list to find prev segment */ + /* Traverse the list if list non empty */ + /* Combine segments if they are fragments of + * the same message. + */ + if (ct->sip_segment_list.next != &ct->sip_segment_list) { + sip_entry = sip_coalesce_segments(ct, &skb, + dataoff, + &combined_skb, + &skip_sip_process, + do_not_process, + ctinfo, + &skb_is_combined); + sip_update_params(dir, &msglen, &origlen, &dptr, + &datalen, + skb_is_combined, ct); + + 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); +#else msglen = origlen = end - dptr; if (msglen > datalen) return NF_ACCEPT; - ret = process_sip_msg(skb, ct, protoff, dataoff, &dptr, &msglen); +#endif /* 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; + sip_calculate_parameters(&diff, &tdiff, &dataoff, &dptr, + &datalen, msglen, origlen); +#ifdef CONFIG_NF_CONNTRACK_SIP_SEGMENTATION + if (nf_ct_enable_sip_segmentation && skb_is_combined) + break; +#endif } +#ifdef CONFIG_NF_CONNTRACK_SIP_SEGMENTATION + sip_tcp_skb_combined_processing(skb_is_combined, skb, dir, combined_skb, + ct, protoff, sip_entry, tdiff, dataoff_orig); - if (ret == NF_ACCEPT && ct->status & IPS_NAT_MASK) { - const struct nf_nat_sip_hooks *hooks; - - hooks = rcu_dereference(nf_nat_sip_hooks); - if (hooks) - hooks->seq_adjust(skb, protoff, tdiff); - } +here: +#endif + sip_tcp_skip_process(ret, ct, skb, protoff, tdiff); return ret; }