diff options
author | Patrick McHardy <kaber@trash.net> | 2008-03-25 23:26:24 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2008-03-25 23:26:24 -0400 |
commit | 0d0ab0378d67517a4f4ae3497706c13d9dd24af1 (patch) | |
tree | c2fbd60d9b524fefa1ec96aef95db7942025c427 /net | |
parent | 4ab9e64e5e3c0516577818804aaf13a630d67bc9 (diff) |
[NETFILTER]: nf_conntrack_sip: support multiple media channels
Add support for multiple media channels and use it to create
expectations for video streams when present.
Signed-off-by: Patrick McHardy <kaber@trash.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r-- | net/netfilter/nf_conntrack_sip.c | 121 |
1 files changed, 90 insertions, 31 deletions
diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c index f929add324f3..f40a525732d1 100644 --- a/net/netfilter/nf_conntrack_sip.c +++ b/net/netfilter/nf_conntrack_sip.c | |||
@@ -112,6 +112,21 @@ static int digits_len(const struct nf_conn *ct, const char *dptr, | |||
112 | return len; | 112 | return len; |
113 | } | 113 | } |
114 | 114 | ||
115 | /* get media type + port length */ | ||
116 | static int media_len(const struct nf_conn *ct, const char *dptr, | ||
117 | const char *limit, int *shift) | ||
118 | { | ||
119 | int len = string_len(ct, dptr, limit, shift); | ||
120 | |||
121 | dptr += len; | ||
122 | if (dptr >= limit || *dptr != ' ') | ||
123 | return 0; | ||
124 | len++; | ||
125 | dptr++; | ||
126 | |||
127 | return len + digits_len(ct, dptr, limit, shift); | ||
128 | } | ||
129 | |||
115 | static int parse_addr(const struct nf_conn *ct, const char *cp, | 130 | static int parse_addr(const struct nf_conn *ct, const char *cp, |
116 | const char **endp, union nf_inet_addr *addr, | 131 | const char **endp, union nf_inet_addr *addr, |
117 | const char *limit) | 132 | const char *limit) |
@@ -563,7 +578,7 @@ static const struct sip_header ct_sdp_hdrs[] = { | |||
563 | [SDP_HDR_CONNECTION_IP4] = SDP_HDR("c=", "IN IP4 ", epaddr_len), | 578 | [SDP_HDR_CONNECTION_IP4] = SDP_HDR("c=", "IN IP4 ", epaddr_len), |
564 | [SDP_HDR_OWNER_IP6] = SDP_HDR("o=", "IN IP6 ", epaddr_len), | 579 | [SDP_HDR_OWNER_IP6] = SDP_HDR("o=", "IN IP6 ", epaddr_len), |
565 | [SDP_HDR_CONNECTION_IP6] = SDP_HDR("c=", "IN IP6 ", epaddr_len), | 580 | [SDP_HDR_CONNECTION_IP6] = SDP_HDR("c=", "IN IP6 ", epaddr_len), |
566 | [SDP_HDR_MEDIA] = SDP_HDR("m=", "audio ", digits_len), | 581 | [SDP_HDR_MEDIA] = SDP_HDR("m=", NULL, media_len), |
567 | }; | 582 | }; |
568 | 583 | ||
569 | /* Linear string search within SDP header values */ | 584 | /* Linear string search within SDP header values */ |
@@ -705,6 +720,7 @@ static void flush_expectations(struct nf_conn *ct, bool media) | |||
705 | static int set_expected_rtp_rtcp(struct sk_buff *skb, | 720 | static int set_expected_rtp_rtcp(struct sk_buff *skb, |
706 | const char **dptr, unsigned int *datalen, | 721 | const char **dptr, unsigned int *datalen, |
707 | union nf_inet_addr *daddr, __be16 port, | 722 | union nf_inet_addr *daddr, __be16 port, |
723 | enum sip_expectation_classes class, | ||
708 | unsigned int mediaoff, unsigned int medialen) | 724 | unsigned int mediaoff, unsigned int medialen) |
709 | { | 725 | { |
710 | struct nf_conntrack_expect *exp, *rtp_exp, *rtcp_exp; | 726 | struct nf_conntrack_expect *exp, *rtp_exp, *rtcp_exp; |
@@ -743,7 +759,7 @@ static int set_expected_rtp_rtcp(struct sk_buff *skb, | |||
743 | exp = __nf_ct_expect_find(&tuple); | 759 | exp = __nf_ct_expect_find(&tuple); |
744 | if (exp && exp->master != ct && | 760 | if (exp && exp->master != ct && |
745 | nfct_help(exp->master)->helper == nfct_help(ct)->helper && | 761 | nfct_help(exp->master)->helper == nfct_help(ct)->helper && |
746 | exp->class == SIP_EXPECT_AUDIO) | 762 | exp->class == class) |
747 | skip_expect = 1; | 763 | skip_expect = 1; |
748 | rcu_read_unlock(); | 764 | rcu_read_unlock(); |
749 | 765 | ||
@@ -757,13 +773,13 @@ static int set_expected_rtp_rtcp(struct sk_buff *skb, | |||
757 | rtp_exp = nf_ct_expect_alloc(ct); | 773 | rtp_exp = nf_ct_expect_alloc(ct); |
758 | if (rtp_exp == NULL) | 774 | if (rtp_exp == NULL) |
759 | goto err1; | 775 | goto err1; |
760 | nf_ct_expect_init(rtp_exp, SIP_EXPECT_AUDIO, family, saddr, daddr, | 776 | nf_ct_expect_init(rtp_exp, class, family, saddr, daddr, |
761 | IPPROTO_UDP, NULL, &rtp_port); | 777 | IPPROTO_UDP, NULL, &rtp_port); |
762 | 778 | ||
763 | rtcp_exp = nf_ct_expect_alloc(ct); | 779 | rtcp_exp = nf_ct_expect_alloc(ct); |
764 | if (rtcp_exp == NULL) | 780 | if (rtcp_exp == NULL) |
765 | goto err2; | 781 | goto err2; |
766 | nf_ct_expect_init(rtcp_exp, SIP_EXPECT_AUDIO, family, saddr, daddr, | 782 | nf_ct_expect_init(rtcp_exp, class, family, saddr, daddr, |
767 | IPPROTO_UDP, NULL, &rtcp_port); | 783 | IPPROTO_UDP, NULL, &rtcp_port); |
768 | 784 | ||
769 | nf_nat_sdp_media = rcu_dereference(nf_nat_sdp_media_hook); | 785 | nf_nat_sdp_media = rcu_dereference(nf_nat_sdp_media_hook); |
@@ -785,6 +801,28 @@ err1: | |||
785 | return ret; | 801 | return ret; |
786 | } | 802 | } |
787 | 803 | ||
804 | static const struct sdp_media_type sdp_media_types[] = { | ||
805 | SDP_MEDIA_TYPE("audio ", SIP_EXPECT_AUDIO), | ||
806 | SDP_MEDIA_TYPE("video ", SIP_EXPECT_VIDEO), | ||
807 | }; | ||
808 | |||
809 | static const struct sdp_media_type *sdp_media_type(const char *dptr, | ||
810 | unsigned int matchoff, | ||
811 | unsigned int matchlen) | ||
812 | { | ||
813 | const struct sdp_media_type *t; | ||
814 | unsigned int i; | ||
815 | |||
816 | for (i = 0; i < ARRAY_SIZE(sdp_media_types); i++) { | ||
817 | t = &sdp_media_types[i]; | ||
818 | if (matchlen < t->len || | ||
819 | strncmp(dptr + matchoff, t->name, t->len)) | ||
820 | continue; | ||
821 | return t; | ||
822 | } | ||
823 | return NULL; | ||
824 | } | ||
825 | |||
788 | static int process_sdp(struct sk_buff *skb, | 826 | static int process_sdp(struct sk_buff *skb, |
789 | const char **dptr, unsigned int *datalen, | 827 | const char **dptr, unsigned int *datalen, |
790 | unsigned int cseq) | 828 | unsigned int cseq) |
@@ -796,13 +834,16 @@ static int process_sdp(struct sk_buff *skb, | |||
796 | unsigned int mediaoff, medialen; | 834 | unsigned int mediaoff, medialen; |
797 | unsigned int sdpoff; | 835 | unsigned int sdpoff; |
798 | unsigned int caddr_len, maddr_len; | 836 | unsigned int caddr_len, maddr_len; |
837 | unsigned int i; | ||
799 | union nf_inet_addr caddr, maddr, rtp_addr; | 838 | union nf_inet_addr caddr, maddr, rtp_addr; |
800 | unsigned int port; | 839 | unsigned int port; |
801 | enum sdp_header_types c_hdr; | 840 | enum sdp_header_types c_hdr; |
802 | int ret; | 841 | const struct sdp_media_type *t; |
842 | int ret = NF_ACCEPT; | ||
803 | typeof(nf_nat_sdp_addr_hook) nf_nat_sdp_addr; | 843 | typeof(nf_nat_sdp_addr_hook) nf_nat_sdp_addr; |
804 | typeof(nf_nat_sdp_session_hook) nf_nat_sdp_session; | 844 | typeof(nf_nat_sdp_session_hook) nf_nat_sdp_session; |
805 | 845 | ||
846 | nf_nat_sdp_addr = rcu_dereference(nf_nat_sdp_addr_hook); | ||
806 | c_hdr = family == AF_INET ? SDP_HDR_CONNECTION_IP4 : | 847 | c_hdr = family == AF_INET ? SDP_HDR_CONNECTION_IP4 : |
807 | SDP_HDR_CONNECTION_IP6; | 848 | SDP_HDR_CONNECTION_IP6; |
808 | 849 | ||
@@ -822,41 +863,55 @@ static int process_sdp(struct sk_buff *skb, | |||
822 | &matchoff, &matchlen, &caddr) > 0) | 863 | &matchoff, &matchlen, &caddr) > 0) |
823 | caddr_len = matchlen; | 864 | caddr_len = matchlen; |
824 | 865 | ||
825 | if (ct_sip_get_sdp_header(ct, *dptr, sdpoff, *datalen, | 866 | mediaoff = sdpoff; |
826 | SDP_HDR_MEDIA, SDP_HDR_UNSPEC, | 867 | for (i = 0; i < ARRAY_SIZE(sdp_media_types); ) { |
827 | &mediaoff, &medialen) <= 0) | 868 | if (ct_sip_get_sdp_header(ct, *dptr, mediaoff, *datalen, |
828 | return NF_ACCEPT; | 869 | SDP_HDR_MEDIA, SDP_HDR_UNSPEC, |
870 | &mediaoff, &medialen) <= 0) | ||
871 | break; | ||
829 | 872 | ||
830 | port = simple_strtoul(*dptr + mediaoff, NULL, 10); | 873 | /* Get media type and port number. A media port value of zero |
831 | if (port < 1024 || port > 65535) | 874 | * indicates an inactive stream. */ |
832 | return NF_DROP; | 875 | t = sdp_media_type(*dptr, mediaoff, medialen); |
876 | if (!t) { | ||
877 | mediaoff += medialen; | ||
878 | continue; | ||
879 | } | ||
880 | mediaoff += t->len; | ||
881 | medialen -= t->len; | ||
833 | 882 | ||
834 | /* The media description overrides the session description. */ | 883 | port = simple_strtoul(*dptr + mediaoff, NULL, 10); |
835 | maddr_len = 0; | 884 | if (port == 0) |
836 | if (ct_sip_parse_sdp_addr(ct, *dptr, mediaoff, *datalen, | 885 | continue; |
837 | c_hdr, SDP_HDR_MEDIA, | 886 | if (port < 1024 || port > 65535) |
838 | &matchoff, &matchlen, &maddr) > 0) { | 887 | return NF_DROP; |
839 | maddr_len = matchlen; | ||
840 | memcpy(&rtp_addr, &maddr, sizeof(rtp_addr)); | ||
841 | } else if (caddr_len) | ||
842 | memcpy(&rtp_addr, &caddr, sizeof(rtp_addr)); | ||
843 | else | ||
844 | return NF_DROP; | ||
845 | 888 | ||
846 | ret = set_expected_rtp_rtcp(skb, dptr, datalen, &rtp_addr, htons(port), | 889 | /* The media description overrides the session description. */ |
847 | mediaoff, medialen); | 890 | maddr_len = 0; |
848 | if (ret != NF_ACCEPT) | 891 | if (ct_sip_parse_sdp_addr(ct, *dptr, mediaoff, *datalen, |
849 | return ret; | 892 | c_hdr, SDP_HDR_MEDIA, |
893 | &matchoff, &matchlen, &maddr) > 0) { | ||
894 | maddr_len = matchlen; | ||
895 | memcpy(&rtp_addr, &maddr, sizeof(rtp_addr)); | ||
896 | } else if (caddr_len) | ||
897 | memcpy(&rtp_addr, &caddr, sizeof(rtp_addr)); | ||
898 | else | ||
899 | return NF_DROP; | ||
900 | |||
901 | ret = set_expected_rtp_rtcp(skb, dptr, datalen, | ||
902 | &rtp_addr, htons(port), t->class, | ||
903 | mediaoff, medialen); | ||
904 | if (ret != NF_ACCEPT) | ||
905 | return ret; | ||
850 | 906 | ||
851 | /* Update media connection address if present */ | 907 | /* Update media connection address if present */ |
852 | if (maddr_len) { | 908 | if (maddr_len && nf_nat_sdp_addr && ct->status & IPS_NAT_MASK) { |
853 | nf_nat_sdp_addr = rcu_dereference(nf_nat_sdp_addr_hook); | ||
854 | if (nf_nat_sdp_addr && ct->status & IPS_NAT_MASK) { | ||
855 | ret = nf_nat_sdp_addr(skb, dptr, mediaoff, datalen, | 909 | ret = nf_nat_sdp_addr(skb, dptr, mediaoff, datalen, |
856 | c_hdr, SDP_HDR_MEDIA, &rtp_addr); | 910 | c_hdr, SDP_HDR_MEDIA, &rtp_addr); |
857 | if (ret != NF_ACCEPT) | 911 | if (ret != NF_ACCEPT) |
858 | return ret; | 912 | return ret; |
859 | } | 913 | } |
914 | i++; | ||
860 | } | 915 | } |
861 | 916 | ||
862 | /* Update session connection and owner addresses */ | 917 | /* Update session connection and owner addresses */ |
@@ -1210,6 +1265,10 @@ static const struct nf_conntrack_expect_policy sip_exp_policy[SIP_EXPECT_MAX + 1 | |||
1210 | .max_expected = 2 * IP_CT_DIR_MAX, | 1265 | .max_expected = 2 * IP_CT_DIR_MAX, |
1211 | .timeout = 3 * 60, | 1266 | .timeout = 3 * 60, |
1212 | }, | 1267 | }, |
1268 | [SIP_EXPECT_VIDEO] = { | ||
1269 | .max_expected = 2 * IP_CT_DIR_MAX, | ||
1270 | .timeout = 3 * 60, | ||
1271 | }, | ||
1213 | }; | 1272 | }; |
1214 | 1273 | ||
1215 | static void nf_conntrack_sip_fini(void) | 1274 | static void nf_conntrack_sip_fini(void) |