Loading include/net/netfilter/nf_conntrack.h +2 −0 Original line number Diff line number Diff line Loading @@ -33,6 +33,7 @@ struct sip_length { int msg_length[SIP_LIST_ELEMENTS]; int skb_len[SIP_LIST_ELEMENTS]; int data_len[SIP_LIST_ELEMENTS]; int orig_data_len[SIP_LIST_ELEMENTS]; }; /* per conntrack: protocol private data */ Loading Loading @@ -132,6 +133,7 @@ struct nf_conn { struct list_head sip_segment_list; const char *dptr_prev; bool dir_prev; struct sip_length segment; bool sip_original_dir; bool sip_reply_dir; Loading net/netfilter/nf_conntrack_sip.c +150 −18 Original line number Diff line number Diff line Loading @@ -60,6 +60,8 @@ static unsigned nf_ct_disable_sip_alg; static int sip_direct_media = 1; static unsigned nf_ct_enable_sip_segmentation; static int packet_count; static bool split_skb; static int proc_sip_segment(struct ctl_table *ctl, int write, void __user *buffer, size_t *lenp, loff_t *ppos); Loading Loading @@ -128,7 +130,7 @@ static void sip_update_params(enum ip_conntrack_dir dir, */ static bool sip_save_segment_info(struct nf_conn *ct, struct sk_buff *skb, unsigned int msglen, unsigned int datalen, const char *dptr, unsigned int datalen_orig, const char *dptr, enum ip_conntrack_info ctinfo) { enum ip_conntrack_dir dir = IP_CT_DIR_MAX; Loading @@ -151,7 +153,9 @@ static bool sip_save_segment_info(struct nf_conn *ct, struct sk_buff *skb, ct->segment.msg_length[0] = msglen; ct->segment.data_len[0] = datalen; ct->segment.skb_len[0] = skb->len; ct->segment.orig_data_len[0] = datalen_orig; ct->dptr_prev = dptr; ct->dir_prev = dir; ct->sip_original_dir = 1; skip = false; } Loading @@ -162,7 +166,9 @@ static bool sip_save_segment_info(struct nf_conn *ct, struct sk_buff *skb, ct->segment.msg_length[1] = msglen; ct->segment.data_len[1] = datalen; ct->segment.skb_len[1] = skb->len; ct->segment.orig_data_len[1] = datalen_orig; ct->dptr_prev = dptr; ct->dir_prev = dir; ct->sip_reply_dir = 1; skip = false; } Loading @@ -170,6 +176,66 @@ static bool sip_save_segment_info(struct nf_conn *ct, struct sk_buff *skb, return skip; } static bool sip_calculate_sequence_num(struct nf_conn *ct, struct sk_buff **skb_ref, enum ip_conntrack_info ctinfo) { 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; struct sip_list *sip_entry = NULL; int orig_data_len = 0; int reply_data_len = 0; th_new = (struct tcphdr *)(skb_network_header(*skb_ref) + ip_hdrlen(*skb_ref)); seq_no = ntohl(th_new->seq); 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))); orig_data_len = ct->segment.orig_data_len[0]; reply_data_len = ct->segment.orig_data_len[1]; prev_data_len = (dir == IP_CT_DIR_ORIGINAL) ? orig_data_len : reply_data_len; seq_old = (ntohl(th_old->seq)); exp_seq_no = seq_old+prev_data_len; if (exp_seq_no == seq_no) return true; else return false; } } return false; } static struct sip_list *sip_coalesce_segments(struct nf_conn *ct, struct sk_buff **skb_ref, unsigned int dataoff, Loading @@ -193,6 +259,8 @@ static struct sip_list *sip_coalesce_segments(struct nf_conn *ct, bool fragstolen = false; int delta_truesize = 0; struct sip_list *sip_entry = NULL; int orig_data_len = 0; int reply_data_len = 0; th_new = (struct tcphdr *)(skb_network_header(*skb_ref) + ip_hdrlen(*skb_ref)); Loading @@ -219,9 +287,11 @@ static struct sip_list *sip_coalesce_segments(struct nf_conn *ct, th_old = ((struct tcphdr *)(skb_network_header (sip_entry->entry->skb) + ip_hdrlen(sip_entry->entry->skb))); orig_data_len = ct->segment.orig_data_len[0]; reply_data_len = ct->segment.orig_data_len[1]; prev_data_len = (dir == IP_CT_DIR_ORIGINAL) ? ct->segment.data_len[0] : ct->segment.data_len[1]; orig_data_len : reply_data_len; seq_old = (ntohl(th_old->seq)); exp_seq_no = seq_old+prev_data_len; Loading Loading @@ -1814,8 +1884,8 @@ static int sip_help_tcp(struct sk_buff *skb, unsigned int protoff, 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 datalen = 0, msglen = 0, origlen = 0, partialdatalen = 0; unsigned int dataoff_orig = 0, datalen_orig = 0; unsigned int splitlen, oldlen, oldlen1; struct sip_list *sip_entry = NULL; bool skip_sip_process = false; Loading @@ -1823,9 +1893,10 @@ static int sip_help_tcp(struct sk_buff *skb, unsigned int protoff, bool skip = false; bool skb_is_combined = false; enum ip_conntrack_dir dir = IP_CT_DIR_MAX; struct sk_buff *combined_skb = NULL; struct sk_buff *combined_skb = NULL, *tskb = NULL; bool content_len_exists = 1; unsigned int len_skb = 0; struct sk_buff *nskb = NULL; packet_count++; pr_debug("packet count %d\n", packet_count); Loading @@ -1852,6 +1923,8 @@ static int sip_help_tcp(struct sk_buff *skb, unsigned int protoff, dptr = skb->data + dataoff; datalen = skb->len - dataoff; datalen_orig = datalen; if (datalen < strlen("SIP/2.0 200")) return NF_ACCEPT; Loading @@ -1863,6 +1936,10 @@ static int sip_help_tcp(struct sk_buff *skb, unsigned int protoff, if (!ct) return NF_DROP; dir = CTINFO2DIR(ctinfo); sip_header_process: while (1) { if (ct_sip_get_header(ct, dptr, 0, datalen, SIP_HDR_CONTENT_LENGTH, Loading @@ -1875,7 +1952,24 @@ static int sip_help_tcp(struct sk_buff *skb, unsigned int protoff, break; } } /* If the below condition is true it indicates packet * is already present in Queue and we ve received a * packet with content length field present which needs * special handling. */ if ((dir == ct->dir_prev) && (ct->sip_original_dir || ct->sip_reply_dir) && sip_calculate_sequence_num(ct, &skb, ctinfo)) { /* Make a Copy of the skb for further * use and make sure to free it */ nskb = skb_copy(skb, GFP_ATOMIC); if (!nskb) { pr_err("Cannot create SKB copy\n"); return NF_DROP; } split_skb = true; } clen = simple_strtoul(dptr + matchoff, (char **)&end, 10); if (dptr + matchoff == end) { break; Loading @@ -1902,13 +1996,17 @@ destination: msglen = origlen; } pr_debug("mslgen %d datalen %d\n", msglen, datalen); if (!strncasecmp(dptr, "MESSAGE", strlen("MESSAGE"))) return NF_ACCEPT; dir = CTINFO2DIR(ctinfo); combined_skb = skb; if (nf_ct_enable_sip_segmentation) { /* Segmented Packet */ if (msglen > datalen) { skip = sip_save_segment_info(ct, skb, msglen, datalen, dptr, datalen, datalen_orig, dptr, ctinfo); if (!skip) return NF_QUEUE; Loading @@ -1917,16 +2015,32 @@ destination: /*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. /* Combine segments if they are * fragments of the same message. */ tskb = skb; if (split_skb) { /* Adjust the second skb length * and datalength to have only the * trailing part of the fragmented * SIP message */ sip_entry = sip_coalesce_segments(ct, &skb, partialdatalen = ct->segment.msg_length[dir] - ct->segment.data_len[dir]; skb_trim(nskb, dataoff+partialdatalen); tskb = nskb; } sip_entry = sip_coalesce_segments(ct, &tskb, 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); Loading Loading @@ -1966,10 +2080,17 @@ destination: } len_skb = combined_skb->len - splitlen; pr_debug("len to copy is %d\n", len_skb); if (!split_skb) skb_copy_from_linear_data_offset(combined_skb, splitlen, skb->data, len_skb); skb_split(combined_skb, skb, splitlen); /* Update the first SIP segment details */ combined_skb->data_len = 0; combined_skb->len = splitlen; skb_set_tail_pointer(combined_skb, splitlen); /* Headers need to be recalculated since during SIP processing * headers are calculated based on the change in length of the * combined message Loading @@ -1980,6 +2101,17 @@ destination: nf_reinject(sip_entry->entry, NF_ACCEPT); kfree(sip_entry); } if (split_skb) { if (dir == IP_CT_DIR_ORIGINAL) ct->sip_original_dir = 0; else ct->sip_reply_dir = 0; kfree_skb(nskb); split_skb = false; goto sip_header_process; } 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 Loading Loading
include/net/netfilter/nf_conntrack.h +2 −0 Original line number Diff line number Diff line Loading @@ -33,6 +33,7 @@ struct sip_length { int msg_length[SIP_LIST_ELEMENTS]; int skb_len[SIP_LIST_ELEMENTS]; int data_len[SIP_LIST_ELEMENTS]; int orig_data_len[SIP_LIST_ELEMENTS]; }; /* per conntrack: protocol private data */ Loading Loading @@ -132,6 +133,7 @@ struct nf_conn { struct list_head sip_segment_list; const char *dptr_prev; bool dir_prev; struct sip_length segment; bool sip_original_dir; bool sip_reply_dir; Loading
net/netfilter/nf_conntrack_sip.c +150 −18 Original line number Diff line number Diff line Loading @@ -60,6 +60,8 @@ static unsigned nf_ct_disable_sip_alg; static int sip_direct_media = 1; static unsigned nf_ct_enable_sip_segmentation; static int packet_count; static bool split_skb; static int proc_sip_segment(struct ctl_table *ctl, int write, void __user *buffer, size_t *lenp, loff_t *ppos); Loading Loading @@ -128,7 +130,7 @@ static void sip_update_params(enum ip_conntrack_dir dir, */ static bool sip_save_segment_info(struct nf_conn *ct, struct sk_buff *skb, unsigned int msglen, unsigned int datalen, const char *dptr, unsigned int datalen_orig, const char *dptr, enum ip_conntrack_info ctinfo) { enum ip_conntrack_dir dir = IP_CT_DIR_MAX; Loading @@ -151,7 +153,9 @@ static bool sip_save_segment_info(struct nf_conn *ct, struct sk_buff *skb, ct->segment.msg_length[0] = msglen; ct->segment.data_len[0] = datalen; ct->segment.skb_len[0] = skb->len; ct->segment.orig_data_len[0] = datalen_orig; ct->dptr_prev = dptr; ct->dir_prev = dir; ct->sip_original_dir = 1; skip = false; } Loading @@ -162,7 +166,9 @@ static bool sip_save_segment_info(struct nf_conn *ct, struct sk_buff *skb, ct->segment.msg_length[1] = msglen; ct->segment.data_len[1] = datalen; ct->segment.skb_len[1] = skb->len; ct->segment.orig_data_len[1] = datalen_orig; ct->dptr_prev = dptr; ct->dir_prev = dir; ct->sip_reply_dir = 1; skip = false; } Loading @@ -170,6 +176,66 @@ static bool sip_save_segment_info(struct nf_conn *ct, struct sk_buff *skb, return skip; } static bool sip_calculate_sequence_num(struct nf_conn *ct, struct sk_buff **skb_ref, enum ip_conntrack_info ctinfo) { 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; struct sip_list *sip_entry = NULL; int orig_data_len = 0; int reply_data_len = 0; th_new = (struct tcphdr *)(skb_network_header(*skb_ref) + ip_hdrlen(*skb_ref)); seq_no = ntohl(th_new->seq); 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))); orig_data_len = ct->segment.orig_data_len[0]; reply_data_len = ct->segment.orig_data_len[1]; prev_data_len = (dir == IP_CT_DIR_ORIGINAL) ? orig_data_len : reply_data_len; seq_old = (ntohl(th_old->seq)); exp_seq_no = seq_old+prev_data_len; if (exp_seq_no == seq_no) return true; else return false; } } return false; } static struct sip_list *sip_coalesce_segments(struct nf_conn *ct, struct sk_buff **skb_ref, unsigned int dataoff, Loading @@ -193,6 +259,8 @@ static struct sip_list *sip_coalesce_segments(struct nf_conn *ct, bool fragstolen = false; int delta_truesize = 0; struct sip_list *sip_entry = NULL; int orig_data_len = 0; int reply_data_len = 0; th_new = (struct tcphdr *)(skb_network_header(*skb_ref) + ip_hdrlen(*skb_ref)); Loading @@ -219,9 +287,11 @@ static struct sip_list *sip_coalesce_segments(struct nf_conn *ct, th_old = ((struct tcphdr *)(skb_network_header (sip_entry->entry->skb) + ip_hdrlen(sip_entry->entry->skb))); orig_data_len = ct->segment.orig_data_len[0]; reply_data_len = ct->segment.orig_data_len[1]; prev_data_len = (dir == IP_CT_DIR_ORIGINAL) ? ct->segment.data_len[0] : ct->segment.data_len[1]; orig_data_len : reply_data_len; seq_old = (ntohl(th_old->seq)); exp_seq_no = seq_old+prev_data_len; Loading Loading @@ -1814,8 +1884,8 @@ static int sip_help_tcp(struct sk_buff *skb, unsigned int protoff, 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 datalen = 0, msglen = 0, origlen = 0, partialdatalen = 0; unsigned int dataoff_orig = 0, datalen_orig = 0; unsigned int splitlen, oldlen, oldlen1; struct sip_list *sip_entry = NULL; bool skip_sip_process = false; Loading @@ -1823,9 +1893,10 @@ static int sip_help_tcp(struct sk_buff *skb, unsigned int protoff, bool skip = false; bool skb_is_combined = false; enum ip_conntrack_dir dir = IP_CT_DIR_MAX; struct sk_buff *combined_skb = NULL; struct sk_buff *combined_skb = NULL, *tskb = NULL; bool content_len_exists = 1; unsigned int len_skb = 0; struct sk_buff *nskb = NULL; packet_count++; pr_debug("packet count %d\n", packet_count); Loading @@ -1852,6 +1923,8 @@ static int sip_help_tcp(struct sk_buff *skb, unsigned int protoff, dptr = skb->data + dataoff; datalen = skb->len - dataoff; datalen_orig = datalen; if (datalen < strlen("SIP/2.0 200")) return NF_ACCEPT; Loading @@ -1863,6 +1936,10 @@ static int sip_help_tcp(struct sk_buff *skb, unsigned int protoff, if (!ct) return NF_DROP; dir = CTINFO2DIR(ctinfo); sip_header_process: while (1) { if (ct_sip_get_header(ct, dptr, 0, datalen, SIP_HDR_CONTENT_LENGTH, Loading @@ -1875,7 +1952,24 @@ static int sip_help_tcp(struct sk_buff *skb, unsigned int protoff, break; } } /* If the below condition is true it indicates packet * is already present in Queue and we ve received a * packet with content length field present which needs * special handling. */ if ((dir == ct->dir_prev) && (ct->sip_original_dir || ct->sip_reply_dir) && sip_calculate_sequence_num(ct, &skb, ctinfo)) { /* Make a Copy of the skb for further * use and make sure to free it */ nskb = skb_copy(skb, GFP_ATOMIC); if (!nskb) { pr_err("Cannot create SKB copy\n"); return NF_DROP; } split_skb = true; } clen = simple_strtoul(dptr + matchoff, (char **)&end, 10); if (dptr + matchoff == end) { break; Loading @@ -1902,13 +1996,17 @@ destination: msglen = origlen; } pr_debug("mslgen %d datalen %d\n", msglen, datalen); if (!strncasecmp(dptr, "MESSAGE", strlen("MESSAGE"))) return NF_ACCEPT; dir = CTINFO2DIR(ctinfo); combined_skb = skb; if (nf_ct_enable_sip_segmentation) { /* Segmented Packet */ if (msglen > datalen) { skip = sip_save_segment_info(ct, skb, msglen, datalen, dptr, datalen, datalen_orig, dptr, ctinfo); if (!skip) return NF_QUEUE; Loading @@ -1917,16 +2015,32 @@ destination: /*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. /* Combine segments if they are * fragments of the same message. */ tskb = skb; if (split_skb) { /* Adjust the second skb length * and datalength to have only the * trailing part of the fragmented * SIP message */ sip_entry = sip_coalesce_segments(ct, &skb, partialdatalen = ct->segment.msg_length[dir] - ct->segment.data_len[dir]; skb_trim(nskb, dataoff+partialdatalen); tskb = nskb; } sip_entry = sip_coalesce_segments(ct, &tskb, 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); Loading Loading @@ -1966,10 +2080,17 @@ destination: } len_skb = combined_skb->len - splitlen; pr_debug("len to copy is %d\n", len_skb); if (!split_skb) skb_copy_from_linear_data_offset(combined_skb, splitlen, skb->data, len_skb); skb_split(combined_skb, skb, splitlen); /* Update the first SIP segment details */ combined_skb->data_len = 0; combined_skb->len = splitlen; skb_set_tail_pointer(combined_skb, splitlen); /* Headers need to be recalculated since during SIP processing * headers are calculated based on the change in length of the * combined message Loading @@ -1980,6 +2101,17 @@ destination: nf_reinject(sip_entry->entry, NF_ACCEPT); kfree(sip_entry); } if (split_skb) { if (dir == IP_CT_DIR_ORIGINAL) ct->sip_original_dir = 0; else ct->sip_reply_dir = 0; kfree_skb(nskb); split_skb = false; goto sip_header_process; } 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 Loading