--- zzzz-none-000/linux-2.6.39.4/net/ipv4/netfilter/nf_nat_sip.c 2011-08-03 19:43:28.000000000 +0000 +++ puma6-arm-6490-729/linux-2.6.39.4/net/ipv4/netfilter/nf_nat_sip.c 2021-11-10 13:23:11.000000000 +0000 @@ -73,19 +73,20 @@ enum ip_conntrack_info ctinfo; struct nf_conn *ct = nf_ct_get(skb, &ctinfo); enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); + struct nf_conn_help *help = nfct_help(ct); char buffer[sizeof("nnn.nnn.nnn.nnn:nnnnn")]; unsigned int buflen; __be32 newaddr; __be16 newport; - if (ct->tuplehash[dir].tuple.src.u3.ip == addr->ip && - ct->tuplehash[dir].tuple.src.u.udp.port == port) { + if (ct->tuplehash[dir].tuple.src.u3.ip == addr->ip) { newaddr = ct->tuplehash[!dir].tuple.dst.u3.ip; newport = ct->tuplehash[!dir].tuple.dst.u.udp.port; } else if (ct->tuplehash[dir].tuple.dst.u3.ip == addr->ip && ct->tuplehash[dir].tuple.dst.u.udp.port == port) { newaddr = ct->tuplehash[!dir].tuple.src.u3.ip; - newport = ct->tuplehash[!dir].tuple.src.u.udp.port; + newport = help->help.ct_sip_info.forced_dport ? : + ct->tuplehash[!dir].tuple.src.u.udp.port; } else return 1; @@ -121,6 +122,7 @@ enum ip_conntrack_info ctinfo; struct nf_conn *ct = nf_ct_get(skb, &ctinfo); enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); + struct nf_conn_help *help = nfct_help(ct); unsigned int coff, matchoff, matchlen; enum sip_header_types hdr; union nf_inet_addr addr; @@ -154,8 +156,7 @@ /* We're only interested in headers related to this * connection */ if (request) { - if (addr.ip != ct->tuplehash[dir].tuple.src.u3.ip || - port != ct->tuplehash[dir].tuple.src.u.udp.port) + if (addr.ip != ct->tuplehash[dir].tuple.src.u3.ip) goto next; } else { if (addr.ip != ct->tuplehash[dir].tuple.dst.u3.ip || @@ -229,6 +230,45 @@ !map_sip_addr(skb, dataoff, dptr, datalen, SIP_HDR_TO)) return NF_DROP; + /* Mangle destination port for Cisco phones, then fix up checksums */ + if (dir == IP_CT_DIR_REPLY && help->help.ct_sip_info.forced_dport) { + struct udphdr *uh; + + if (!skb_make_writable(skb, skb->len)) + return NF_DROP; + + uh = (struct udphdr *)(skb->data + ip_hdrlen(skb)); + uh->dest = help->help.ct_sip_info.forced_dport; + + if (!nf_nat_mangle_udp_packet(skb, ct, ctinfo, 0, 0, NULL, 0)) + return NF_DROP; + } + + return NF_ACCEPT; +} + +static unsigned int ip_not_nat_via(struct sk_buff *skb, unsigned int dataoff, + const char **dptr, unsigned int *datalen) +{ + /* if we are here then proto is udp and its reply + this function is called in case if forced_dport is detected. + in case of NAT the same case handled by ip_nat_sip func above. + */ + enum ip_conntrack_info ctinfo; + struct nf_conn *ct = nf_ct_get(skb, &ctinfo); + struct nf_conn_help *help = nfct_help(ct); + struct udphdr *uh; + + /* next code duplicates code above. it may be moved to inline func */ + if (!skb_make_writable(skb, skb->len)) + return NF_DROP; + + uh = (struct udphdr *)(skb->data + ip_hdrlen(skb)); + uh->dest = help->help.ct_sip_info.forced_dport; + + if (!nf_nat_mangle_udp_packet(skb, ct, ctinfo, 0, 0, NULL, 0)) + return NF_DROP; + return NF_ACCEPT; } @@ -280,8 +320,10 @@ enum ip_conntrack_info ctinfo; struct nf_conn *ct = nf_ct_get(skb, &ctinfo); enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); + struct nf_conn_help *help = nfct_help(ct); __be32 newip; u_int16_t port; + __be16 srcport; char buffer[sizeof("nnn.nnn.nnn.nnn:nnnnn")]; unsigned buflen; @@ -294,8 +336,9 @@ /* If the signalling port matches the connection's source port in the * original direction, try to use the destination port in the opposite * direction. */ - if (exp->tuple.dst.u.udp.port == - ct->tuplehash[dir].tuple.src.u.udp.port) + srcport = help->help.ct_sip_info.forced_dport ? : + ct->tuplehash[dir].tuple.src.u.udp.port; + if (exp->tuple.dst.u.udp.port == srcport) port = ntohs(ct->tuplehash[!dir].tuple.dst.u.udp.port); else port = ntohs(exp->tuple.dst.u.udp.port); @@ -464,6 +507,8 @@ struct nf_conn *ct = nf_ct_get(skb, &ctinfo); enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); u_int16_t port; + u_int16_t orig_port; + bool rtp_port_found = false; /* Connection will come from reply */ if (ct->tuplehash[dir].tuple.src.u3.ip == @@ -485,30 +530,37 @@ rtcp_exp->expectfn = ip_nat_sip_expected; /* Try to get same pair of ports: if not, try to change them. */ - for (port = ntohs(rtp_exp->tuple.dst.u.udp.port); - port != 0; port += 2) { + orig_port = ntohs(rtp_exp->tuple.dst.u.udp.port); + if (orig_port < 1024) + goto err1; + + port = orig_port; + do { int ret; rtp_exp->tuple.dst.u.udp.port = htons(port); ret = nf_ct_expect_related(rtp_exp); - if (ret == -EBUSY) - continue; - else if (ret < 0) { - port = 0; + if (ret != -EBUSY) { + if (ret < 0) { break; } rtcp_exp->tuple.dst.u.udp.port = htons(port + 1); ret = nf_ct_expect_related(rtcp_exp); - if (ret == 0) + if (ret == 0) { + rtp_port_found = true; break; - else if (ret != -EBUSY) { + } else if (ret != -EBUSY) { nf_ct_unexpect_related(rtp_exp); - port = 0; break; } } + port += 2; + if (port == 0) { + port = 1024; + } + } while (port != orig_port); - if (port == 0) + if (rtp_port_found == false) goto err1; /* Update media port. */ @@ -529,6 +581,7 @@ static void __exit nf_nat_sip_fini(void) { rcu_assign_pointer(nf_nat_sip_hook, NULL); + rcu_assign_pointer(nf_not_nat_sip_via_hook, NULL); rcu_assign_pointer(nf_nat_sip_seq_adjust_hook, NULL); rcu_assign_pointer(nf_nat_sip_expect_hook, NULL); rcu_assign_pointer(nf_nat_sdp_addr_hook, NULL); @@ -541,6 +594,7 @@ static int __init nf_nat_sip_init(void) { BUG_ON(nf_nat_sip_hook != NULL); + BUG_ON(nf_not_nat_sip_via_hook != NULL); BUG_ON(nf_nat_sip_seq_adjust_hook != NULL); BUG_ON(nf_nat_sip_expect_hook != NULL); BUG_ON(nf_nat_sdp_addr_hook != NULL); @@ -548,6 +602,7 @@ BUG_ON(nf_nat_sdp_session_hook != NULL); BUG_ON(nf_nat_sdp_media_hook != NULL); rcu_assign_pointer(nf_nat_sip_hook, ip_nat_sip); + rcu_assign_pointer(nf_not_nat_sip_via_hook, ip_not_nat_via); rcu_assign_pointer(nf_nat_sip_seq_adjust_hook, ip_nat_sip_seq_adjust); rcu_assign_pointer(nf_nat_sip_expect_hook, ip_nat_sip_expect); rcu_assign_pointer(nf_nat_sdp_addr_hook, ip_nat_sdp_addr);