diff options
-rw-r--r-- | include/linux/netfilter/nf_conntrack_sip.h | 6 | ||||
-rw-r--r-- | net/ipv4/netfilter/nf_nat_sip.c | 3 | ||||
-rw-r--r-- | net/netfilter/nf_conntrack_sip.c | 59 |
3 files changed, 58 insertions, 10 deletions
diff --git a/include/linux/netfilter/nf_conntrack_sip.h b/include/linux/netfilter/nf_conntrack_sip.h index 71fa3eb5f485..5da04e586a3f 100644 --- a/include/linux/netfilter/nf_conntrack_sip.h +++ b/include/linux/netfilter/nf_conntrack_sip.h | |||
@@ -114,6 +114,12 @@ extern unsigned int (*nf_nat_sdp_addr_hook)(struct sk_buff *skb, | |||
114 | enum sdp_header_types type, | 114 | enum sdp_header_types type, |
115 | enum sdp_header_types term, | 115 | enum sdp_header_types term, |
116 | const union nf_inet_addr *addr); | 116 | const union nf_inet_addr *addr); |
117 | extern unsigned int (*nf_nat_sdp_port_hook)(struct sk_buff *skb, | ||
118 | const char **dptr, | ||
119 | unsigned int *datalen, | ||
120 | unsigned int matchoff, | ||
121 | unsigned int matchlen, | ||
122 | u_int16_t port); | ||
117 | extern unsigned int (*nf_nat_sdp_session_hook)(struct sk_buff *skb, | 123 | extern unsigned int (*nf_nat_sdp_session_hook)(struct sk_buff *skb, |
118 | const char **dptr, | 124 | const char **dptr, |
119 | unsigned int dataoff, | 125 | unsigned int dataoff, |
diff --git a/net/ipv4/netfilter/nf_nat_sip.c b/net/ipv4/netfilter/nf_nat_sip.c index 4429069d9b42..bcddccddf768 100644 --- a/net/ipv4/netfilter/nf_nat_sip.c +++ b/net/ipv4/netfilter/nf_nat_sip.c | |||
@@ -461,6 +461,7 @@ static void __exit nf_nat_sip_fini(void) | |||
461 | rcu_assign_pointer(nf_nat_sip_hook, NULL); | 461 | rcu_assign_pointer(nf_nat_sip_hook, NULL); |
462 | rcu_assign_pointer(nf_nat_sip_expect_hook, NULL); | 462 | rcu_assign_pointer(nf_nat_sip_expect_hook, NULL); |
463 | rcu_assign_pointer(nf_nat_sdp_addr_hook, NULL); | 463 | rcu_assign_pointer(nf_nat_sdp_addr_hook, NULL); |
464 | rcu_assign_pointer(nf_nat_sdp_port_hook, NULL); | ||
464 | rcu_assign_pointer(nf_nat_sdp_session_hook, NULL); | 465 | rcu_assign_pointer(nf_nat_sdp_session_hook, NULL); |
465 | rcu_assign_pointer(nf_nat_sdp_media_hook, NULL); | 466 | rcu_assign_pointer(nf_nat_sdp_media_hook, NULL); |
466 | synchronize_rcu(); | 467 | synchronize_rcu(); |
@@ -471,11 +472,13 @@ static int __init nf_nat_sip_init(void) | |||
471 | BUG_ON(nf_nat_sip_hook != NULL); | 472 | BUG_ON(nf_nat_sip_hook != NULL); |
472 | BUG_ON(nf_nat_sip_expect_hook != NULL); | 473 | BUG_ON(nf_nat_sip_expect_hook != NULL); |
473 | BUG_ON(nf_nat_sdp_addr_hook != NULL); | 474 | BUG_ON(nf_nat_sdp_addr_hook != NULL); |
475 | BUG_ON(nf_nat_sdp_port_hook != NULL); | ||
474 | BUG_ON(nf_nat_sdp_session_hook != NULL); | 476 | BUG_ON(nf_nat_sdp_session_hook != NULL); |
475 | BUG_ON(nf_nat_sdp_media_hook != NULL); | 477 | BUG_ON(nf_nat_sdp_media_hook != NULL); |
476 | rcu_assign_pointer(nf_nat_sip_hook, ip_nat_sip); | 478 | rcu_assign_pointer(nf_nat_sip_hook, ip_nat_sip); |
477 | rcu_assign_pointer(nf_nat_sip_expect_hook, ip_nat_sip_expect); | 479 | rcu_assign_pointer(nf_nat_sip_expect_hook, ip_nat_sip_expect); |
478 | rcu_assign_pointer(nf_nat_sdp_addr_hook, ip_nat_sdp_addr); | 480 | rcu_assign_pointer(nf_nat_sdp_addr_hook, ip_nat_sdp_addr); |
481 | rcu_assign_pointer(nf_nat_sdp_port_hook, ip_nat_sdp_port); | ||
479 | rcu_assign_pointer(nf_nat_sdp_session_hook, ip_nat_sdp_session); | 482 | rcu_assign_pointer(nf_nat_sdp_session_hook, ip_nat_sdp_session); |
480 | rcu_assign_pointer(nf_nat_sdp_media_hook, ip_nat_sdp_media); | 483 | rcu_assign_pointer(nf_nat_sdp_media_hook, ip_nat_sdp_media); |
481 | return 0; | 484 | return 0; |
diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c index f40a525732d1..57de22c770a3 100644 --- a/net/netfilter/nf_conntrack_sip.c +++ b/net/netfilter/nf_conntrack_sip.c | |||
@@ -70,6 +70,14 @@ unsigned int (*nf_nat_sdp_addr_hook)(struct sk_buff *skb, | |||
70 | __read_mostly; | 70 | __read_mostly; |
71 | EXPORT_SYMBOL_GPL(nf_nat_sdp_addr_hook); | 71 | EXPORT_SYMBOL_GPL(nf_nat_sdp_addr_hook); |
72 | 72 | ||
73 | unsigned int (*nf_nat_sdp_port_hook)(struct sk_buff *skb, | ||
74 | const char **dptr, | ||
75 | unsigned int *datalen, | ||
76 | unsigned int matchoff, | ||
77 | unsigned int matchlen, | ||
78 | u_int16_t port) __read_mostly; | ||
79 | EXPORT_SYMBOL_GPL(nf_nat_sdp_port_hook); | ||
80 | |||
73 | unsigned int (*nf_nat_sdp_session_hook)(struct sk_buff *skb, | 81 | unsigned int (*nf_nat_sdp_session_hook)(struct sk_buff *skb, |
74 | const char **dptr, | 82 | const char **dptr, |
75 | unsigned int dataoff, | 83 | unsigned int dataoff, |
@@ -730,9 +738,10 @@ static int set_expected_rtp_rtcp(struct sk_buff *skb, | |||
730 | union nf_inet_addr *saddr; | 738 | union nf_inet_addr *saddr; |
731 | struct nf_conntrack_tuple tuple; | 739 | struct nf_conntrack_tuple tuple; |
732 | int family = ct->tuplehash[!dir].tuple.src.l3num; | 740 | int family = ct->tuplehash[!dir].tuple.src.l3num; |
733 | int skip_expect = 0, ret = NF_DROP; | 741 | int direct_rtp = 0, skip_expect = 0, ret = NF_DROP; |
734 | u_int16_t base_port; | 742 | u_int16_t base_port; |
735 | __be16 rtp_port, rtcp_port; | 743 | __be16 rtp_port, rtcp_port; |
744 | typeof(nf_nat_sdp_port_hook) nf_nat_sdp_port; | ||
736 | typeof(nf_nat_sdp_media_hook) nf_nat_sdp_media; | 745 | typeof(nf_nat_sdp_media_hook) nf_nat_sdp_media; |
737 | 746 | ||
738 | saddr = NULL; | 747 | saddr = NULL; |
@@ -746,6 +755,14 @@ static int set_expected_rtp_rtcp(struct sk_buff *skb, | |||
746 | * to register it since we can see the same media description multiple | 755 | * to register it since we can see the same media description multiple |
747 | * times on different connections in case multiple endpoints receive | 756 | * times on different connections in case multiple endpoints receive |
748 | * the same call. | 757 | * the same call. |
758 | * | ||
759 | * RTP optimization: if we find a matching media channel expectation | ||
760 | * and both the expectation and this connection are SNATed, we assume | ||
761 | * both sides can reach each other directly and use the final | ||
762 | * destination address from the expectation. We still need to keep | ||
763 | * the NATed expectations for media that might arrive from the | ||
764 | * outside, and additionally need to expect the direct RTP stream | ||
765 | * in case it passes through us even without NAT. | ||
749 | */ | 766 | */ |
750 | memset(&tuple, 0, sizeof(tuple)); | 767 | memset(&tuple, 0, sizeof(tuple)); |
751 | if (saddr) | 768 | if (saddr) |
@@ -756,20 +773,42 @@ static int set_expected_rtp_rtcp(struct sk_buff *skb, | |||
756 | tuple.dst.u.udp.port = port; | 773 | tuple.dst.u.udp.port = port; |
757 | 774 | ||
758 | rcu_read_lock(); | 775 | rcu_read_lock(); |
759 | exp = __nf_ct_expect_find(&tuple); | 776 | do { |
760 | if (exp && exp->master != ct && | 777 | exp = __nf_ct_expect_find(&tuple); |
761 | nfct_help(exp->master)->helper == nfct_help(ct)->helper && | ||
762 | exp->class == class) | ||
763 | skip_expect = 1; | ||
764 | rcu_read_unlock(); | ||
765 | 778 | ||
766 | if (skip_expect) | 779 | if (!exp || exp->master == ct || |
767 | return NF_ACCEPT; | 780 | nfct_help(exp->master)->helper != nfct_help(ct)->helper || |
781 | exp->class != class) | ||
782 | break; | ||
783 | |||
784 | if (exp->tuple.src.l3num == AF_INET && !direct_rtp && | ||
785 | (exp->saved_ip != exp->tuple.dst.u3.ip || | ||
786 | exp->saved_proto.udp.port != exp->tuple.dst.u.udp.port) && | ||
787 | ct->status & IPS_NAT_MASK) { | ||
788 | daddr->ip = exp->saved_ip; | ||
789 | tuple.dst.u3.ip = exp->saved_ip; | ||
790 | tuple.dst.u.udp.port = exp->saved_proto.udp.port; | ||
791 | direct_rtp = 1; | ||
792 | } else | ||
793 | skip_expect = 1; | ||
794 | } while (!skip_expect); | ||
795 | rcu_read_unlock(); | ||
768 | 796 | ||
769 | base_port = ntohs(tuple.dst.u.udp.port) & ~1; | 797 | base_port = ntohs(tuple.dst.u.udp.port) & ~1; |
770 | rtp_port = htons(base_port); | 798 | rtp_port = htons(base_port); |
771 | rtcp_port = htons(base_port + 1); | 799 | rtcp_port = htons(base_port + 1); |
772 | 800 | ||
801 | if (direct_rtp) { | ||
802 | nf_nat_sdp_port = rcu_dereference(nf_nat_sdp_port_hook); | ||
803 | if (nf_nat_sdp_port && | ||
804 | !nf_nat_sdp_port(skb, dptr, datalen, | ||
805 | mediaoff, medialen, ntohs(rtp_port))) | ||
806 | goto err1; | ||
807 | } | ||
808 | |||
809 | if (skip_expect) | ||
810 | return NF_ACCEPT; | ||
811 | |||
773 | rtp_exp = nf_ct_expect_alloc(ct); | 812 | rtp_exp = nf_ct_expect_alloc(ct); |
774 | if (rtp_exp == NULL) | 813 | if (rtp_exp == NULL) |
775 | goto err1; | 814 | goto err1; |
@@ -783,7 +822,7 @@ static int set_expected_rtp_rtcp(struct sk_buff *skb, | |||
783 | IPPROTO_UDP, NULL, &rtcp_port); | 822 | IPPROTO_UDP, NULL, &rtcp_port); |
784 | 823 | ||
785 | nf_nat_sdp_media = rcu_dereference(nf_nat_sdp_media_hook); | 824 | nf_nat_sdp_media = rcu_dereference(nf_nat_sdp_media_hook); |
786 | if (nf_nat_sdp_media && ct->status & IPS_NAT_MASK) | 825 | if (nf_nat_sdp_media && ct->status & IPS_NAT_MASK && !direct_rtp) |
787 | ret = nf_nat_sdp_media(skb, dptr, datalen, rtp_exp, rtcp_exp, | 826 | ret = nf_nat_sdp_media(skb, dptr, datalen, rtp_exp, rtcp_exp, |
788 | mediaoff, medialen, daddr); | 827 | mediaoff, medialen, daddr); |
789 | else { | 828 | else { |