Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Kevin Cernekee and committed by
Pablo Neira Ayuso
7266507d 247fa82b

+44 -3
+3
include/linux/netfilter/nf_conntrack_sip.h
··· 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 {
+17
net/netfilter/nf_conntrack_sip.c
··· 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;
+24 -3
net/netfilter/nf_nat_sip.c
··· 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; ··· 108 107 } else if (nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.dst.u3, addr) && 109 108 ct->tuplehash[dir].tuple.dst.u.udp.port == port) { 110 109 newaddr = ct->tuplehash[!dir].tuple.src.u3; 111 - newport = ct->tuplehash[!dir].tuple.src.u.udp.port; 110 + newport = ct_sip_info->forced_dport ? : 111 + ct->tuplehash[!dir].tuple.src.u.udp.port; 112 112 } else 113 113 return 1; 114 114 ··· 146 144 enum ip_conntrack_info ctinfo; 147 145 struct nf_conn *ct = nf_ct_get(skb, &ctinfo); 148 146 enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); 147 + struct nf_ct_sip_master *ct_sip_info = nfct_help_data(ct); 149 148 unsigned int coff, matchoff, matchlen; 150 149 enum sip_header_types hdr; 151 150 union nf_inet_addr addr; ··· 261 258 !map_sip_addr(skb, protoff, dataoff, dptr, datalen, SIP_HDR_TO)) 262 259 return NF_DROP; 263 260 261 + /* Mangle destination port for Cisco phones, then fix up checksums */ 262 + if (dir == IP_CT_DIR_REPLY && ct_sip_info->forced_dport) { 263 + struct udphdr *uh; 264 + 265 + if (!skb_make_writable(skb, skb->len)) 266 + return NF_DROP; 267 + 268 + uh = (void *)skb->data + protoff; 269 + uh->dest = ct_sip_info->forced_dport; 270 + 271 + if (!nf_nat_mangle_udp_packet(skb, ct, ctinfo, protoff, 272 + 0, 0, NULL, 0)) 273 + return NF_DROP; 274 + } 275 + 264 276 return NF_ACCEPT; 265 277 } 266 278 ··· 329 311 enum ip_conntrack_info ctinfo; 330 312 struct nf_conn *ct = nf_ct_get(skb, &ctinfo); 331 313 enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); 314 + struct nf_ct_sip_master *ct_sip_info = nfct_help_data(ct); 332 315 union nf_inet_addr newaddr; 333 316 u_int16_t port; 317 + __be16 srcport; 334 318 char buffer[INET6_ADDRSTRLEN + sizeof("[]:nnnnn")]; 335 319 unsigned int buflen; 336 320 ··· 346 326 /* If the signalling port matches the connection's source port in the 347 327 * original direction, try to use the destination port in the opposite 348 328 * direction. */ 349 - if (exp->tuple.dst.u.udp.port == 350 - ct->tuplehash[dir].tuple.src.u.udp.port) 329 + srcport = ct_sip_info->forced_dport ? : 330 + ct->tuplehash[dir].tuple.src.u.udp.port; 331 + if (exp->tuple.dst.u.udp.port == srcport) 351 332 port = ntohs(ct->tuplehash[!dir].tuple.dst.u.udp.port); 352 333 else 353 334 port = ntohs(exp->tuple.dst.u.udp.port);