mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-10 06:09:23 -04:00
netfilter: Changes to handle segmentation in SIP ALG
Linux Kernel SIP ALG did not handle Segmented TCP Packets
because of which SIP communication could not be established
for some clients. This change fixes that issue.
Also,This change handle porting of below changes
c39f2db28d61ec4 ("Additional fixes for SIP Segmentation Support")
07c07c942ec37f0 ("Changes to handle MT call issue in SIP ALG")
18f40547250d8b6 ("Enable/Disable SIP Segmentation Support").
Change-Id: I8c77322f69cf4d9ad4c7b4971da924ffd585dea0
Acked-by: Vinisha Varre <vvarre@qti.qualcomm.com>
Signed-off-by: Paras Singh Jain <parassin@codeaurora.org>
This commit is contained in:
parent
e72d8c95da
commit
d30948cecd
6 changed files with 570 additions and 22 deletions
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -17,6 +17,9 @@
|
|||
#include <linux/compiler.h>
|
||||
#include <linux/android_kabi.h>
|
||||
#include <linux/android_vendor.h>
|
||||
#ifdef CONFIG_NF_CONNTRACK_SIP_SEGMENTATION
|
||||
#include <linux/list.h>
|
||||
#endif
|
||||
|
||||
#include <linux/netfilter/nf_conntrack_common.h>
|
||||
#include <linux/netfilter/nf_conntrack_tcp.h>
|
||||
|
|
@ -26,10 +29,22 @@
|
|||
|
||||
#include <net/netfilter/nf_conntrack_tuple.h>
|
||||
|
||||
#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;
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 <chentschel@arnet.com.ar>
|
||||
* based on RR's ip_conntrack_ftp.c and other modules.
|
||||
* (C) 2007 United Security Providers
|
||||
|
|
@ -20,12 +21,19 @@
|
|||
#include <linux/netfilter_ipv4.h>
|
||||
#include <linux/netfilter_ipv6.h>
|
||||
|
||||
#ifdef CONFIG_NF_CONNTRACK_SIP_SEGMENTATION
|
||||
#include <net/tcp.h>
|
||||
#endif
|
||||
#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>
|
||||
#ifdef CONFIG_NF_CONNTRACK_SIP_SEGMENTATION
|
||||
#include <net/netfilter/nf_nat.h>
|
||||
#include <net/netfilter/nf_queue.h>
|
||||
#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;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue