diff options
author | Patrick McHardy <kaber@trash.net> | 2006-11-28 20:35:30 -0500 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2006-12-03 00:31:26 -0500 |
commit | 1b683b551209ca46ae59b29572018001db5af078 (patch) | |
tree | 8d88690faf3d819e42719165cae62e9953555140 /net | |
parent | 77a78dec48386ce958196bf69f192ee76537c07d (diff) |
[NETFILTER]: sip conntrack: better NAT handling
The NAT handling of the SIP helper has a few problems:
- Request headers are only mangled in the reply direction, From/To headers
not at all, which can lead to authentication failures with DNAT in case
the authentication domain is the IP address
- Contact headers in responses are only mangled for REGISTER responses
- Headers may be mangled even though they contain addresses not
participating in the connection, like alternative addresses
- Packets are droppen when domain names are used where the helper expects
IP addresses
This patch takes a different approach, instead of fixed rules what field
to mangle to what content, it adds symetric mapping of From/To/Via/Contact
headers, which allows to deal properly with echoed addresses in responses
and foreign addresses not belonging to the connection.
Signed-off-by: Patrick McHardy <kaber@trash.net>
Diffstat (limited to 'net')
-rw-r--r-- | net/ipv4/netfilter/ip_conntrack_sip.c | 29 | ||||
-rw-r--r-- | net/ipv4/netfilter/ip_nat_sip.c | 171 |
2 files changed, 131 insertions, 69 deletions
diff --git a/net/ipv4/netfilter/ip_conntrack_sip.c b/net/ipv4/netfilter/ip_conntrack_sip.c index 0a6a13c45b07..3a26d63eed88 100644 --- a/net/ipv4/netfilter/ip_conntrack_sip.c +++ b/net/ipv4/netfilter/ip_conntrack_sip.c | |||
@@ -69,13 +69,38 @@ struct sip_header_nfo { | |||
69 | }; | 69 | }; |
70 | 70 | ||
71 | static struct sip_header_nfo ct_sip_hdrs[] = { | 71 | static struct sip_header_nfo ct_sip_hdrs[] = { |
72 | [POS_REQ_HEADER] = { /* SIP Requests headers */ | 72 | [POS_REG_REQ_URI] = { /* SIP REGISTER request URI */ |
73 | .lname = "sip:", | ||
74 | .lnlen = sizeof("sip:") - 1, | ||
75 | .ln_str = ":", | ||
76 | .ln_strlen = sizeof(":") - 1, | ||
77 | .match_len = epaddr_len | ||
78 | }, | ||
79 | [POS_REQ_URI] = { /* SIP request URI */ | ||
73 | .lname = "sip:", | 80 | .lname = "sip:", |
74 | .lnlen = sizeof("sip:") - 1, | 81 | .lnlen = sizeof("sip:") - 1, |
75 | .ln_str = "@", | 82 | .ln_str = "@", |
76 | .ln_strlen = sizeof("@") - 1, | 83 | .ln_strlen = sizeof("@") - 1, |
77 | .match_len = epaddr_len | 84 | .match_len = epaddr_len |
78 | }, | 85 | }, |
86 | [POS_FROM] = { /* SIP From header */ | ||
87 | .lname = "From:", | ||
88 | .lnlen = sizeof("From:") - 1, | ||
89 | .sname = "\r\nf:", | ||
90 | .snlen = sizeof("\r\nf:") - 1, | ||
91 | .ln_str = "sip:", | ||
92 | .ln_strlen = sizeof("sip:") - 1, | ||
93 | .match_len = skp_epaddr_len, | ||
94 | }, | ||
95 | [POS_TO] = { /* SIP To header */ | ||
96 | .lname = "To:", | ||
97 | .lnlen = sizeof("To:") - 1, | ||
98 | .sname = "\r\nt:", | ||
99 | .snlen = sizeof("\r\nt:") - 1, | ||
100 | .ln_str = "sip:", | ||
101 | .ln_strlen = sizeof("sip:") - 1, | ||
102 | .match_len = skp_epaddr_len, | ||
103 | }, | ||
79 | [POS_VIA] = { /* SIP Via header */ | 104 | [POS_VIA] = { /* SIP Via header */ |
80 | .lname = "Via:", | 105 | .lname = "Via:", |
81 | .lnlen = sizeof("Via:") - 1, | 106 | .lnlen = sizeof("Via:") - 1, |
@@ -284,7 +309,7 @@ int ct_sip_get_info(const char *dptr, size_t dlen, | |||
284 | 309 | ||
285 | while (dptr <= limit) { | 310 | while (dptr <= limit) { |
286 | if ((strncmp(dptr, hnfo->lname, hnfo->lnlen) != 0) && | 311 | if ((strncmp(dptr, hnfo->lname, hnfo->lnlen) != 0) && |
287 | (hinfo->sname == NULL || | 312 | (hnfo->sname == NULL || |
288 | strncmp(dptr, hnfo->sname, hnfo->snlen) != 0)) { | 313 | strncmp(dptr, hnfo->sname, hnfo->snlen) != 0)) { |
289 | dptr++; | 314 | dptr++; |
290 | continue; | 315 | continue; |
diff --git a/net/ipv4/netfilter/ip_nat_sip.c b/net/ipv4/netfilter/ip_nat_sip.c index e16604c4339d..6223abc924ff 100644 --- a/net/ipv4/netfilter/ip_nat_sip.c +++ b/net/ipv4/netfilter/ip_nat_sip.c | |||
@@ -29,25 +29,70 @@ MODULE_DESCRIPTION("SIP NAT helper"); | |||
29 | #define DEBUGP(format, args...) | 29 | #define DEBUGP(format, args...) |
30 | #endif | 30 | #endif |
31 | 31 | ||
32 | static unsigned int mangle_sip_packet(struct sk_buff **pskb, | 32 | struct addr_map { |
33 | enum ip_conntrack_info ctinfo, | 33 | struct { |
34 | struct ip_conntrack *ct, | 34 | char src[sizeof("nnn.nnn.nnn.nnn:nnnnn")]; |
35 | const char **dptr, size_t dlen, | 35 | char dst[sizeof("nnn.nnn.nnn.nnn:nnnnn")]; |
36 | char *buffer, int bufflen, | 36 | unsigned int srclen, srciplen; |
37 | enum sip_header_pos pos) | 37 | unsigned int dstlen, dstiplen; |
38 | } addr[IP_CT_DIR_MAX]; | ||
39 | }; | ||
40 | |||
41 | static void addr_map_init(struct ip_conntrack *ct, struct addr_map *map) | ||
38 | { | 42 | { |
39 | unsigned int matchlen, matchoff; | 43 | struct ip_conntrack_tuple *t; |
44 | enum ip_conntrack_dir dir; | ||
45 | unsigned int n; | ||
46 | |||
47 | for (dir = 0; dir < IP_CT_DIR_MAX; dir++) { | ||
48 | t = &ct->tuplehash[dir].tuple; | ||
49 | |||
50 | n = sprintf(map->addr[dir].src, "%u.%u.%u.%u", | ||
51 | NIPQUAD(t->src.ip)); | ||
52 | map->addr[dir].srciplen = n; | ||
53 | n += sprintf(map->addr[dir].src + n, ":%u", | ||
54 | ntohs(t->src.u.udp.port)); | ||
55 | map->addr[dir].srclen = n; | ||
56 | |||
57 | n = sprintf(map->addr[dir].dst, "%u.%u.%u.%u", | ||
58 | NIPQUAD(t->dst.ip)); | ||
59 | map->addr[dir].dstiplen = n; | ||
60 | n += sprintf(map->addr[dir].dst + n, ":%u", | ||
61 | ntohs(t->dst.u.udp.port)); | ||
62 | map->addr[dir].dstlen = n; | ||
63 | } | ||
64 | } | ||
65 | |||
66 | static int map_sip_addr(struct sk_buff **pskb, enum ip_conntrack_info ctinfo, | ||
67 | struct ip_conntrack *ct, const char **dptr, size_t dlen, | ||
68 | enum sip_header_pos pos, struct addr_map *map) | ||
69 | { | ||
70 | enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); | ||
71 | unsigned int matchlen, matchoff, addrlen; | ||
72 | char *addr; | ||
40 | 73 | ||
41 | if (ct_sip_get_info(*dptr, dlen, &matchoff, &matchlen, pos) <= 0) | 74 | if (ct_sip_get_info(*dptr, dlen, &matchoff, &matchlen, pos) <= 0) |
42 | return 0; | 75 | return 1; |
76 | |||
77 | if ((matchlen == map->addr[dir].srciplen || | ||
78 | matchlen == map->addr[dir].srclen) && | ||
79 | memcmp(*dptr + matchoff, map->addr[dir].src, matchlen) == 0) { | ||
80 | addr = map->addr[!dir].dst; | ||
81 | addrlen = map->addr[!dir].dstlen; | ||
82 | } else if ((matchlen == map->addr[dir].dstiplen || | ||
83 | matchlen == map->addr[dir].dstlen) && | ||
84 | memcmp(*dptr + matchoff, map->addr[dir].dst, matchlen) == 0) { | ||
85 | addr = map->addr[!dir].src; | ||
86 | addrlen = map->addr[!dir].srclen; | ||
87 | } else | ||
88 | return 1; | ||
43 | 89 | ||
44 | if (!ip_nat_mangle_udp_packet(pskb, ct, ctinfo, | 90 | if (!ip_nat_mangle_udp_packet(pskb, ct, ctinfo, |
45 | matchoff, matchlen, buffer, bufflen)) | 91 | matchoff, matchlen, addr, addrlen)) |
46 | return 0; | 92 | return 0; |
47 | |||
48 | /* We need to reload this. Thanks Patrick. */ | ||
49 | *dptr = (*pskb)->data + (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr); | 93 | *dptr = (*pskb)->data + (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr); |
50 | return 1; | 94 | return 1; |
95 | |||
51 | } | 96 | } |
52 | 97 | ||
53 | static unsigned int ip_nat_sip(struct sk_buff **pskb, | 98 | static unsigned int ip_nat_sip(struct sk_buff **pskb, |
@@ -55,69 +100,61 @@ static unsigned int ip_nat_sip(struct sk_buff **pskb, | |||
55 | struct ip_conntrack *ct, | 100 | struct ip_conntrack *ct, |
56 | const char **dptr) | 101 | const char **dptr) |
57 | { | 102 | { |
58 | enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); | 103 | enum sip_header_pos pos; |
59 | char buffer[sizeof("nnn.nnn.nnn.nnn:nnnnn")]; | 104 | struct addr_map map; |
60 | unsigned int bufflen, dataoff; | 105 | int dataoff, datalen; |
61 | __be32 ip; | ||
62 | __be16 port; | ||
63 | 106 | ||
64 | dataoff = (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr); | 107 | dataoff = (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr); |
108 | datalen = (*pskb)->len - dataoff; | ||
109 | if (datalen < sizeof("SIP/2.0") - 1) | ||
110 | return NF_DROP; | ||
111 | |||
112 | addr_map_init(ct, &map); | ||
113 | |||
114 | /* Basic rules: requests and responses. */ | ||
115 | if (strncmp(*dptr, "SIP/2.0", sizeof("SIP/2.0") - 1) != 0) { | ||
116 | /* 10.2: Constructing the REGISTER Request: | ||
117 | * | ||
118 | * The "userinfo" and "@" components of the SIP URI MUST NOT | ||
119 | * be present. | ||
120 | */ | ||
121 | if (datalen >= sizeof("REGISTER") - 1 && | ||
122 | strncmp(*dptr, "REGISTER", sizeof("REGISTER") - 1) == 0) | ||
123 | pos = POS_REG_REQ_URI; | ||
124 | else | ||
125 | pos = POS_REQ_URI; | ||
126 | |||
127 | if (!map_sip_addr(pskb, ctinfo, ct, dptr, datalen, pos, &map)) | ||
128 | return NF_DROP; | ||
129 | } | ||
65 | 130 | ||
66 | ip = ct->tuplehash[!dir].tuple.dst.ip; | 131 | if (!map_sip_addr(pskb, ctinfo, ct, dptr, datalen, POS_FROM, &map) || |
67 | port = ct->tuplehash[!dir].tuple.dst.u.udp.port; | 132 | !map_sip_addr(pskb, ctinfo, ct, dptr, datalen, POS_TO, &map) || |
68 | bufflen = sprintf(buffer, "%u.%u.%u.%u:%u", NIPQUAD(ip), ntohs(port)); | 133 | !map_sip_addr(pskb, ctinfo, ct, dptr, datalen, POS_VIA, &map) || |
134 | !map_sip_addr(pskb, ctinfo, ct, dptr, datalen, POS_CONTACT, &map)) | ||
135 | return NF_DROP; | ||
136 | return NF_ACCEPT; | ||
137 | } | ||
138 | |||
139 | static unsigned int mangle_sip_packet(struct sk_buff **pskb, | ||
140 | enum ip_conntrack_info ctinfo, | ||
141 | struct ip_conntrack *ct, | ||
142 | const char **dptr, size_t dlen, | ||
143 | char *buffer, int bufflen, | ||
144 | enum sip_header_pos pos) | ||
145 | { | ||
146 | unsigned int matchlen, matchoff; | ||
69 | 147 | ||
70 | /* short packet ? */ | 148 | if (ct_sip_get_info(*dptr, dlen, &matchoff, &matchlen, pos) <= 0) |
71 | if (((*pskb)->len - dataoff) < (sizeof("SIP/2.0") - 1)) | ||
72 | return 0; | 149 | return 0; |
73 | 150 | ||
74 | /* Basic rules: requests and responses. */ | 151 | if (!ip_nat_mangle_udp_packet(pskb, ct, ctinfo, |
75 | if (memcmp(*dptr, "SIP/2.0", sizeof("SIP/2.0") - 1) == 0) { | 152 | matchoff, matchlen, buffer, bufflen)) |
76 | const char *aux; | 153 | return 0; |
77 | |||
78 | if ((ctinfo) < IP_CT_IS_REPLY) { | ||
79 | mangle_sip_packet(pskb, ctinfo, ct, dptr, | ||
80 | (*pskb)->len - dataoff, | ||
81 | buffer, bufflen, POS_CONTACT); | ||
82 | return 1; | ||
83 | } | ||
84 | 154 | ||
85 | if (!mangle_sip_packet(pskb, ctinfo, ct, dptr, | 155 | /* We need to reload this. Thanks Patrick. */ |
86 | (*pskb)->len - dataoff, | 156 | *dptr = (*pskb)->data + (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr); |
87 | buffer, bufflen, POS_VIA)) | 157 | return 1; |
88 | return 0; | ||
89 | |||
90 | aux = ct_sip_search("CSeq:", *dptr, sizeof("CSeq:") - 1, | ||
91 | (*pskb)->len - dataoff, 0); | ||
92 | if (!aux) | ||
93 | return 0; | ||
94 | |||
95 | if (!ct_sip_search("REGISTER", aux, sizeof("REGISTER"), | ||
96 | ct_sip_lnlen(aux, | ||
97 | *dptr + (*pskb)->len - dataoff), | ||
98 | 1)) | ||
99 | return 1; | ||
100 | |||
101 | return mangle_sip_packet(pskb, ctinfo, ct, dptr, | ||
102 | (*pskb)->len - dataoff, | ||
103 | buffer, bufflen, POS_CONTACT); | ||
104 | } | ||
105 | if ((ctinfo) < IP_CT_IS_REPLY) { | ||
106 | if (!mangle_sip_packet(pskb, ctinfo, ct, dptr, | ||
107 | (*pskb)->len - dataoff, | ||
108 | buffer, bufflen, POS_VIA)) | ||
109 | return 0; | ||
110 | |||
111 | /* Mangle Contact if exists only. - watch udp_nat_mangle()! */ | ||
112 | mangle_sip_packet(pskb, ctinfo, ct, dptr, (*pskb)->len - dataoff, | ||
113 | buffer, bufflen, POS_CONTACT); | ||
114 | return 1; | ||
115 | } | ||
116 | /* This mangle requests headers. */ | ||
117 | return mangle_sip_packet(pskb, ctinfo, ct, dptr, | ||
118 | ct_sip_lnlen(*dptr, | ||
119 | *dptr + (*pskb)->len - dataoff), | ||
120 | buffer, bufflen, POS_REQ_HEADER); | ||
121 | } | 158 | } |
122 | 159 | ||
123 | static int mangle_content_len(struct sk_buff **pskb, | 160 | static int mangle_content_len(struct sk_buff **pskb, |