Commit 7266507d89991fa1e989283e4e032c6d9357fe26
Committed by
Pablo Neira Ayuso
1 parent
247fa82be1
Exists in
master
and in
20 other branches
netfilter: nf_ct_sip: support Cisco 7941/7945 IP phones
Most SIP devices use a source port of 5060/udp on SIP requests, so the response automatically comes back to port 5060: phone_ip:5060 -> proxy_ip:5060 REGISTER proxy_ip:5060 -> phone_ip:5060 100 Trying The newer Cisco IP phones, however, use a randomly chosen high source port for the SIP request but expect the response on port 5060: phone_ip:49173 -> proxy_ip:5060 REGISTER proxy_ip:5060 -> phone_ip:5060 100 Trying Standard Linux NAT, with or without nf_nat_sip, will send the reply back to port 49173, not 5060: phone_ip:49173 -> proxy_ip:5060 REGISTER proxy_ip:5060 -> phone_ip:49173 100 Trying But the phone is not listening on 49173, so it will never see the reply. This patch modifies nf_*_sip to work around this quirk by extracting the SIP response port from the Via: header, iff the source IP in the packet header matches the source IP in the SIP request. Signed-off-by: Kevin Cernekee <cernekee@gmail.com> Acked-by: Eric Dumazet <eric.dumazet@gmail.com> Cc: Patrick McHardy <kaber@trash.net> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Showing 3 changed files with 44 additions and 3 deletions Side-by-side Diff
include/linux/netfilter/nf_conntrack_sip.h
... | ... | @@ -4,12 +4,15 @@ |
4 | 4 | |
5 | 5 | #include <net/netfilter/nf_conntrack_expect.h> |
6 | 6 | |
7 | +#include <linux/types.h> | |
8 | + | |
7 | 9 | #define SIP_PORT 5060 |
8 | 10 | #define SIP_TIMEOUT 3600 |
9 | 11 | |
10 | 12 | struct nf_ct_sip_master { |
11 | 13 | unsigned int register_cseq; |
12 | 14 | unsigned int invite_cseq; |
15 | + __be16 forced_dport; | |
13 | 16 | }; |
14 | 17 | |
15 | 18 | enum sip_expectation_classes { |
net/netfilter/nf_conntrack_sip.c
... | ... | @@ -1440,8 +1440,25 @@ |
1440 | 1440 | { |
1441 | 1441 | enum ip_conntrack_info ctinfo; |
1442 | 1442 | struct nf_conn *ct = nf_ct_get(skb, &ctinfo); |
1443 | + struct nf_ct_sip_master *ct_sip_info = nfct_help_data(ct); | |
1444 | + enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); | |
1443 | 1445 | unsigned int matchoff, matchlen; |
1444 | 1446 | unsigned int cseq, i; |
1447 | + union nf_inet_addr addr; | |
1448 | + __be16 port; | |
1449 | + | |
1450 | + /* Many Cisco IP phones use a high source port for SIP requests, but | |
1451 | + * listen for the response on port 5060. If we are the local | |
1452 | + * router for one of these phones, save the port number from the | |
1453 | + * Via: header so that nf_nat_sip can redirect the responses to | |
1454 | + * the correct port. | |
1455 | + */ | |
1456 | + if (ct_sip_parse_header_uri(ct, *dptr, NULL, *datalen, | |
1457 | + SIP_HDR_VIA_UDP, NULL, &matchoff, | |
1458 | + &matchlen, &addr, &port) > 0 && | |
1459 | + port != ct->tuplehash[dir].tuple.src.u.udp.port && | |
1460 | + nf_inet_addr_cmp(&addr, &ct->tuplehash[dir].tuple.src.u3)) | |
1461 | + ct_sip_info->forced_dport = port; | |
1445 | 1462 | |
1446 | 1463 | for (i = 0; i < ARRAY_SIZE(sip_handlers); i++) { |
1447 | 1464 | const struct sip_handler *handler; |
net/netfilter/nf_nat_sip.c
... | ... | @@ -95,6 +95,7 @@ |
95 | 95 | enum ip_conntrack_info ctinfo; |
96 | 96 | struct nf_conn *ct = nf_ct_get(skb, &ctinfo); |
97 | 97 | enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); |
98 | + struct nf_ct_sip_master *ct_sip_info = nfct_help_data(ct); | |
98 | 99 | char buffer[INET6_ADDRSTRLEN + sizeof("[]:nnnnn")]; |
99 | 100 | unsigned int buflen; |
100 | 101 | union nf_inet_addr newaddr; |
... | ... | @@ -107,7 +108,8 @@ |
107 | 108 | } else if (nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.dst.u3, addr) && |
108 | 109 | ct->tuplehash[dir].tuple.dst.u.udp.port == port) { |
109 | 110 | newaddr = ct->tuplehash[!dir].tuple.src.u3; |
110 | - newport = ct->tuplehash[!dir].tuple.src.u.udp.port; | |
111 | + newport = ct_sip_info->forced_dport ? : | |
112 | + ct->tuplehash[!dir].tuple.src.u.udp.port; | |
111 | 113 | } else |
112 | 114 | return 1; |
113 | 115 | |
... | ... | @@ -144,6 +146,7 @@ |
144 | 146 | enum ip_conntrack_info ctinfo; |
145 | 147 | struct nf_conn *ct = nf_ct_get(skb, &ctinfo); |
146 | 148 | enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); |
149 | + struct nf_ct_sip_master *ct_sip_info = nfct_help_data(ct); | |
147 | 150 | unsigned int coff, matchoff, matchlen; |
148 | 151 | enum sip_header_types hdr; |
149 | 152 | union nf_inet_addr addr; |
... | ... | @@ -258,6 +261,21 @@ |
258 | 261 | !map_sip_addr(skb, protoff, dataoff, dptr, datalen, SIP_HDR_TO)) |
259 | 262 | return NF_DROP; |
260 | 263 | |
264 | + /* Mangle destination port for Cisco phones, then fix up checksums */ | |
265 | + if (dir == IP_CT_DIR_REPLY && ct_sip_info->forced_dport) { | |
266 | + struct udphdr *uh; | |
267 | + | |
268 | + if (!skb_make_writable(skb, skb->len)) | |
269 | + return NF_DROP; | |
270 | + | |
271 | + uh = (void *)skb->data + protoff; | |
272 | + uh->dest = ct_sip_info->forced_dport; | |
273 | + | |
274 | + if (!nf_nat_mangle_udp_packet(skb, ct, ctinfo, protoff, | |
275 | + 0, 0, NULL, 0)) | |
276 | + return NF_DROP; | |
277 | + } | |
278 | + | |
261 | 279 | return NF_ACCEPT; |
262 | 280 | } |
263 | 281 | |
264 | 282 | |
... | ... | @@ -311,8 +329,10 @@ |
311 | 329 | enum ip_conntrack_info ctinfo; |
312 | 330 | struct nf_conn *ct = nf_ct_get(skb, &ctinfo); |
313 | 331 | enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); |
332 | + struct nf_ct_sip_master *ct_sip_info = nfct_help_data(ct); | |
314 | 333 | union nf_inet_addr newaddr; |
315 | 334 | u_int16_t port; |
335 | + __be16 srcport; | |
316 | 336 | char buffer[INET6_ADDRSTRLEN + sizeof("[]:nnnnn")]; |
317 | 337 | unsigned int buflen; |
318 | 338 | |
... | ... | @@ -326,8 +346,9 @@ |
326 | 346 | /* If the signalling port matches the connection's source port in the |
327 | 347 | * original direction, try to use the destination port in the opposite |
328 | 348 | * direction. */ |
329 | - if (exp->tuple.dst.u.udp.port == | |
330 | - ct->tuplehash[dir].tuple.src.u.udp.port) | |
349 | + srcport = ct_sip_info->forced_dport ? : | |
350 | + ct->tuplehash[dir].tuple.src.u.udp.port; | |
351 | + if (exp->tuple.dst.u.udp.port == srcport) | |
331 | 352 | port = ntohs(ct->tuplehash[!dir].tuple.dst.u.udp.port); |
332 | 353 | else |
333 | 354 | port = ntohs(exp->tuple.dst.u.udp.port); |