mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-11 15:11:48 -04:00
Merge "netfilter: Changes to handle segmentation in SIP ALG"
This commit is contained in:
commit
e71fa8be05
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