/* * Find lo/hi client ports (if any) in transport header * In: * ptcp, tcplen = packet * tranoff, tranlen = buffer to search * * Out: * pport_lo, pport_hi = lo/hi ports (host endian) * * Returns nonzero if any client ports found * * Note: it is valid (and expected) for the client to request multiple * transports, so we need to parse the entire line. */ static int rtsp_parse_transport(char* ptran, uint tranlen, struct ip_ct_rtsp_expect* prtspexp) { int rc = 0; uint off = 0; if (tranlen < 10 || !iseol(ptran[tranlen-1]) || nf_strncasecmp(ptran, "Transport:", 10) != 0) { DEBUGP("sanity check failed\n"); return 0; } DEBUGP("tran='%.*s'\n", (int)tranlen, ptran); off += 10; SKIP_WSPACE(ptran, tranlen, off); /* Transport: tran;field;field=val,tran;field;field=val,... */ while (off < tranlen) { const char* pparamend; uint nextparamoff; pparamend = memchr(ptran+off, ',', tranlen-off); pparamend = (pparamend == NULL) ? ptran+tranlen : pparamend+1; nextparamoff = pparamend-ptran; while (off < nextparamoff) { const char* pfieldend; uint nextfieldoff; pfieldend = memchr(ptran+off, ';', nextparamoff-off); nextfieldoff = (pfieldend == NULL) ? nextparamoff : pfieldend-ptran+1; if (strncmp(ptran+off, "client_port=", 12) == 0) { u_int16_t port; uint numlen; off += 12; numlen = nf_strtou16(ptran+off, &port); off += numlen; if (prtspexp->loport != 0 && prtspexp->loport != port) DEBUGP("multiple ports found, port %hu ignored\n", port); else { DEBUGP("lo port found : %hu\n", port); prtspexp->loport = prtspexp->hiport = port; if (ptran[off] == '-') { off++; numlen = nf_strtou16(ptran+off, &port); off += numlen; prtspexp->pbtype = pb_range; prtspexp->hiport = port; // If we have a range, assume rtp: // loport must be even, hiport must be loport+1 if ((prtspexp->loport & 0x0001) != 0 || prtspexp->hiport != prtspexp->loport+1) { DEBUGP("incorrect range: %hu-%hu, correcting\n", prtspexp->loport, prtspexp->hiport); prtspexp->loport &= 0xfffe; prtspexp->hiport = prtspexp->loport+1; } } else if (ptran[off] == '/') { off++; numlen = nf_strtou16(ptran+off, &port); off += numlen; prtspexp->pbtype = pb_discon; prtspexp->hiport = port; } rc = 1; } } /* * Note we don't look for the destination parameter here. * If we are using NAT, the NAT module will handle it. If not, * and the client is sending packets elsewhere, the expectation * will quietly time out. */ off = nextfieldoff; } off = nextparamoff; } return rc; }
/* * Mangle the "Transport:" header: * - Replace all occurences of "client_port=<spec>" * - Handle destination parameter * * In: * ct, ctinfo = conntrack context * skb = packet * tranoff = Transport header offset from TCP data * tranlen = Transport header length (incl. CRLF) * rport_lo = replacement low port (host endian) * rport_hi = replacement high port (host endian) * * Returns packet size difference. * * Assumes that a complete transport header is present, ending with CR or LF */ static int rtsp_mangle_tran(enum ip_conntrack_info ctinfo, struct nf_conntrack_expect* rtp_exp, struct nf_conntrack_expect* rtcp_exp, struct ip_ct_rtsp_expect* prtspexp, struct sk_buff* skb, uint tranoff, uint tranlen) { char* ptcp; char* ptcp_seq; uint tcplen; char* ptran; char rbuf1[16]; /* Replacement buffer (one port) */ uint rbuf1len; /* Replacement len (one port) */ char rbufa[16]; /* Replacement buffer (all ports) */ uint rbufalen; /* Replacement len (all ports) */ u_int32_t newip; u_int16_t loport, hiport; uint off = 0; uint diff; /* Number of bytes we removed */ int diff_len = 0; #define MAX_RTSP_MANGLE_BUFFER 1400 char mangle_buffer[MAX_RTSP_MANGLE_BUFFER]; uint len_old = 0, len_new = 0; struct nf_conn *ct = rtp_exp->master; struct nf_conntrack_tuple *t_rtp, *t_rtcp; char szextaddr[15+1]; uint extaddrlen; int is_stun; #if 1 /* patch ...*/ ptcp = mangle_buffer; memset(ptcp, 0, MAX_RTSP_MANGLE_BUFFER); get_skb_tcpdata(skb, &ptcp_seq, &tcplen); memcpy(mangle_buffer, ptcp_seq, tcplen); #else get_skb_tcpdata(skb, &ptcp, &tcplen); #endif ptran = ptcp+tranoff; if (tranoff+tranlen > tcplen || tcplen-tranoff < tranlen || tranlen < 10 || !iseol(ptran[tranlen-1]) || nf_strncasecmp(ptran, "Transport:", 10) != 0) { pr_info("sanity check failed\n"); return 0; } off += 10; SKIP_WSPACE(ptcp+tranoff, tranlen, off); newip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3.ip; #ifdef CONFIG_NF_NAT_NEEDED rtp_exp->saved_proto.udp.port = rtp_exp->tuple.dst.u.udp.port; rtcp_exp->saved_proto.udp.port = rtcp_exp->tuple.dst.u.udp.port; #endif t_rtp = &rtp_exp->tuple; t_rtcp = &rtcp_exp->tuple; t_rtp->dst.u3.ip = newip; t_rtcp->dst.u3.ip = newip; //extaddrlen = extip ? sprintf(szextaddr, "%pI4", &extip) // : sprintf(szextaddr, "%pI4", &newip); extaddrlen = extip ? sprintf(szextaddr, "%u.%u.%u.%u", NIPQUAD(extip)) : sprintf(szextaddr, "%u.%u.%u.%u", NIPQUAD(newip)); pr_debug("stunaddr=%s (%s)\n", szextaddr, (extip?"forced":"auto")); rbuf1len = rbufalen = 0; switch (prtspexp->pbtype) { case pb_single: for (loport = prtspexp->loport; loport != 0; loport++) /* XXX: improper wrap? */ { t_rtp->dst.u.udp.port = htons(loport); if (nf_ct_expect_related(rtp_exp) == 0) { printk("using port %hu\n", loport); break; } } if (loport != 0) { rbuf1len = sprintf(rbuf1, "%hu", loport); rbufalen = sprintf(rbufa, "%hu", loport); } break; case pb_range: for (loport = prtspexp->loport; loport != 0; loport += 2) /* XXX: improper wrap? */ { t_rtp->dst.u.udp.port = htons(loport); if (nf_ct_expect_related(rtp_exp) == 0) { t_rtcp->dst.u.udp.port = htons(loport+1); if (nf_ct_expect_related(rtcp_exp) == 0) { hiport = loport+1; pr_debug("using ports %d-%d\n", loport, loport+1); } else { hiport = 0; } break; } } if (loport != 0 && hiport != 0) { rbuf1len = sprintf(rbuf1, "%hu", loport); rbufalen = sprintf(rbufa, "%hu-%hu", loport, hiport); } break; case pb_discon: for (loport = prtspexp->loport; loport != 0; loport++) /* XXX: improper wrap? */ { t_rtp->dst.u.udp.port = htons(loport); if (nf_ct_expect_related(rtp_exp) == 0) { pr_debug("using port %hu (1 of 2)\n", loport); break; } } for (hiport = prtspexp->hiport; hiport != 0; hiport++) /* XXX: improper wrap? */ { t_rtcp->dst.u.udp.port = htons(hiport); if (nf_ct_expect_related(rtcp_exp) == 0) { pr_debug("using port %hu (2 of 2)\n", hiport); break; } } if (loport != 0 && hiport != 0) { rbuf1len = sprintf(rbuf1, "%hu", loport); if (hiport == loport+1) { rbufalen = sprintf(rbufa, "%hu-%hu", loport, hiport); } else { rbufalen = sprintf(rbufa, "%hu/%hu", loport, hiport); } } break; } if (rbuf1len == 0) { return 0; /* cannot get replacement port(s) */ } /* Transport: tran;field;field=val,tran;field;field=val,... */ while (off < tranlen) { uint saveoff; const char* pparamend; uint nextparamoff; pparamend = memchr(ptran+off, ',', tranlen-off); pparamend = (pparamend == NULL) ? ptran+tranlen : pparamend+1; //nextparamoff = pparamend-ptcp; nextparamoff = pparamend-ptran; /* * We pass over each param twice. On the first pass, we look for a * destination= field. It is handled by the security policy. If it * is present, allowed, and equal to our external address, we assume * that STUN is being used and we leave the client_port= field alone. */ is_stun = 0; saveoff = off; while (off < nextparamoff) { const char* pfieldend; uint nextfieldoff; uint replen; pfieldend = memchr(ptran+off, ';', nextparamoff-off); nextfieldoff = (pfieldend == NULL) ? nextparamoff : pfieldend-ptran+1; if (dstact != DSTACT_NONE && strncmp(ptran+off, "destination=", 12) == 0) { if (strncmp(ptran+off+12, szextaddr, extaddrlen) == 0) { is_stun = 1; } if (dstact == DSTACT_STRIP || (dstact == DSTACT_AUTO && !is_stun)) { off += 12; replen = (pfieldend == NULL) ? nextfieldoff-off : nextfieldoff-off-1; diff = replen - extaddrlen; #if 1 if( replace_contents(ptran+off, replen, szextaddr, extaddrlen, tcplen-off-(ptran-ptcp)) == 0) { // mangle failed, all we can do is bail nf_ct_unexpect_related(rtp_exp); nf_ct_unexpect_related(rtcp_exp); return 0; } tcplen = tcplen + extaddrlen - replen; diff_len = diff_len + extaddrlen - replen; #else if (!nf_nat_mangle_tcp_packet(skb, ct, ctinfo, off, diff, NULL, 0)) { // mangle failed, all we can do is bail nf_ct_unexpect_related(rtp_exp); nf_ct_unexpect_related(rtcp_exp); return 0; } #endif #if 0 get_skb_tcpdata(skb, &ptcp, &tcplen); #endif ptran = ptcp+tranoff; tranlen -= diff; nextparamoff -= diff; nextfieldoff -= diff; } } off = nextfieldoff; } if (is_stun) { continue; } off = saveoff; while (off < nextparamoff) { const char* pfieldend; uint nextfieldoff; pfieldend = memchr(ptran+off, ';', nextparamoff-off); nextfieldoff = (pfieldend == NULL) ? nextparamoff : pfieldend-ptran+1; if (strncmp(ptran+off, "client_port=", 12) == 0) { u_int16_t port; uint numlen; uint origoff; uint origlen; char* rbuf = rbuf1; uint rbuflen = rbuf1len; uint off1; off += 12; off1 = off; origoff = (ptran-ptcp)+off; origlen = 0; numlen = nf_strtou16(ptran+off, &port); off += numlen; origlen += numlen; if (port != prtspexp->loport) { pr_debug("multiple ports found, port %hu ignored\n", port); } else { if (ptran[off] == '-' || ptran[off] == '/') { off++; origlen++; numlen = nf_strtou16(ptran+off, &port); off += numlen; origlen += numlen; rbuf = rbufa; rbuflen = rbufalen; } /* * note we cannot just memcpy() if the sizes are the same. * the mangle function does skb resizing, checks for a * cloned skb, and updates the checksums. * * parameter 4 below is offset from start of tcp data. */ diff = origlen-rbuflen; #if 1 if( replace_contents(ptran+off1, origlen, rbuf, rbuflen, tcplen-off1-(ptran-ptcp)) == 0) { /* mangle failed, all we can do is bail */ nf_ct_unexpect_related(rtp_exp); nf_ct_unexpect_related(rtcp_exp); return 0; } tcplen = tcplen + rbuflen - origlen; diff_len = diff_len + rbuflen - origlen; #else if (!nf_nat_mangle_tcp_packet(skb, ct, ctinfo, origoff, origlen, rbuf, rbuflen)) { /* mangle failed, all we can do is bail */ nf_ct_unexpect_related(rtp_exp); nf_ct_unexpect_related(rtcp_exp); return 0; } #endif #if 0 get_skb_tcpdata(skb, &ptcp, &tcplen); #endif ptran = ptcp+tranoff; tranlen -= diff; nextparamoff -= diff; nextfieldoff -= diff; } } off = nextfieldoff; } off = nextparamoff; } if (!nf_nat_mangle_tcp_packet(skb, ct, ctinfo, 0, tcplen-diff_len, mangle_buffer, tcplen)) { /* mangle failed, all we can do is bail */ //DEBUGP(" mangle failed client_port\n"); nf_ct_unexpect_related(rtp_exp); nf_ct_unexpect_related(rtcp_exp); return 0; } return 1; }
/* * Mangle the "Transport:" header: * - Replace all occurences of "client_port=<spec>" * - Handle destination parameter * * In: * ct, ctinfo = conntrack context * skb = packet * tranoff = Transport header offset from TCP data * tranlen = Transport header length (incl. CRLF) * rport_lo = replacement low port (host endian) * rport_hi = replacement high port (host endian) * * Returns packet size difference. * * Assumes that a complete transport header is present, ending with CR or LF */ static int rtsp_mangle_tran(enum ip_conntrack_info ctinfo, #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0) unsigned int protoff, #endif struct nf_conntrack_expect* rtp_exp, struct nf_conntrack_expect* rtcp_exp, struct ip_ct_rtsp_expect* prtspexp, struct sk_buff* skb, uint tranoff, uint tranlen) { char* ptcp; uint tcplen; char* ptran; char rbuf1[16]; /* Replacement buffer (one port) */ uint rbuf1len; /* Replacement len (one port) */ char rbufa[16]; /* Replacement buffer (all ports) */ uint rbufalen; /* Replacement len (all ports) */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0) union nf_inet_addr newip; #else u_int32_t newip; #endif u_int16_t loport, hiport; uint off = 0; uint diff; /* Number of bytes we removed */ struct nf_conn *ct = rtp_exp->master; /* struct nf_conn *ct = nf_ct_get(skb, &ctinfo); */ struct nf_conntrack_tuple *rtp_t; #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0) char szextaddr[INET6_ADDRSTRLEN]; #else char szextaddr[INET_ADDRSTRLEN]; #endif uint extaddrlen; int is_stun; get_skb_tcpdata(skb, &ptcp, &tcplen); ptran = ptcp+tranoff; if (tranoff+tranlen > tcplen || tcplen-tranoff < tranlen || tranlen < 10 || !iseol(ptran[tranlen-1]) || nf_strncasecmp(ptran, "Transport:", 10) != 0) { pr_info("sanity check failed\n"); return 0; } off += 10; SKIP_WSPACE(ptcp+tranoff, tranlen, off); #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0) newip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3; rtp_t = &rtp_exp->tuple; rtp_t->dst.u3 = newip; if (rtcp_exp) { rtcp_exp->tuple.dst.u3 = newip; } extaddrlen = rtsp_sprintf_addr(ct, szextaddr, &newip, true); // FIXME handle extip pr_debug("stunaddr=%s (auto)\n", szextaddr); #else newip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3.ip; rtp_t = &rtp_exp->tuple; rtp_t->dst.u3.ip = newip; if (rtcp_exp) { rtcp_exp->tuple.dst.u3.ip = newip; } extaddrlen = extip ? sprintf(szextaddr, "%pI4", &extip) : sprintf(szextaddr, "%pI4", &newip); pr_debug("stunaddr=%s (%s)\n", szextaddr, (extip?"forced":"auto")); #endif hiport = 0; rbuf1len = rbufalen = 0; switch (prtspexp->pbtype) { case pb_single: for (loport = prtspexp->loport; loport != 0; loport++) { /* XXX: improper wrap? */ rtp_t->dst.u.udp.port = htons(loport); if (nf_ct_expect_related(rtp_exp) == 0) { pr_debug("using port %hu\n", loport); break; } } if (loport != 0) { rbuf1len = sprintf(rbuf1, "%hu", loport); rbufalen = sprintf(rbufa, "%hu", loport); } break; case pb_range: for (loport = prtspexp->loport; loport != 0; loport += 2) { /* XXX: improper wrap? */ rtp_t->dst.u.udp.port = htons(loport); if (nf_ct_expect_related(rtp_exp) != 0) { continue; } hiport = loport + 1; rtcp_exp->tuple.dst.u.udp.port = htons(hiport); if (nf_ct_expect_related(rtcp_exp) != 0) { nf_ct_unexpect_related(rtp_exp); continue; } /* FIXME: invalid print in case of ipv6 */ pr_debug("nat expect_related %pI4:%u-%u-%pI4:%u-%u\n", &rtp_exp->tuple.src.u3.ip, ntohs(rtp_exp->tuple.src.u.udp.port), ntohs(rtcp_exp->tuple.src.u.udp.port), &rtp_exp->tuple.dst.u3.ip, ntohs(rtp_exp->tuple.dst.u.udp.port), ntohs(rtcp_exp->tuple.dst.u.udp.port)); break; } if (loport != 0) { rbuf1len = sprintf(rbuf1, "%hu", loport); rbufalen = sprintf(rbufa, "%hu-%hu", loport, hiport); } break; case pb_discon: for (loport = prtspexp->loport; loport != 0; loport++) { /* XXX: improper wrap? */ rtp_t->dst.u.udp.port = htons(loport); if (nf_ct_expect_related(rtp_exp) == 0) { pr_debug("using port %hu (1 of 2)\n", loport); break; } } for (hiport = prtspexp->hiport; hiport != 0; hiport++) { /* XXX: improper wrap? */ rtp_t->dst.u.udp.port = htons(hiport); if (nf_ct_expect_related(rtp_exp) == 0) { pr_debug("using port %hu (2 of 2)\n", hiport); break; } } if (loport != 0 && hiport != 0) { rbuf1len = sprintf(rbuf1, "%hu", loport); rbufalen = sprintf(rbufa, hiport == loport+1 ? "%hu-%hu":"%hu/%hu", loport, hiport); } break; } if (rbuf1len == 0) return 0; /* cannot get replacement port(s) */ /* Transport: tran;field;field=val,tran;field;field=val,... `off` is set to the start of Transport value from start of line */ while (off < tranlen) { uint saveoff; const char* pparamend; uint nextparamoff; pparamend = memchr(ptran+off, ',', tranlen-off); pparamend = (pparamend == NULL) ? ptran+tranlen : pparamend+1; nextparamoff = pparamend-ptran; /* * We pass over each param twice. On the first pass, we look for a * destination= field. It is handled by the security policy. If it * is present, allowed, and equal to our external address, we assume * that STUN is being used and we leave the client_port= field alone. */ is_stun = 0; saveoff = off; while (off < nextparamoff) { const char* pfieldend; uint nextfieldoff; pfieldend = memchr(ptran+off, ';', nextparamoff-off); nextfieldoff = (pfieldend == NULL) ? nextparamoff : pfieldend-ptran+1; if (dstact != DSTACT_NONE && strncmp(ptran+off, "destination=", 12) == 0) { if (strncmp(ptran+off+12, szextaddr, extaddrlen) == 0) is_stun = 1; if (dstact == DSTACT_STRIP || (dstact == DSTACT_AUTO && !is_stun)) { uint dstoff = (ptran-ptcp)+off; uint dstlen = nextfieldoff-off; char* pdstrep = NULL; uint dstreplen = 0; diff = dstlen; if (dstact == DSTACT_AUTO && !is_stun) { pr_debug("RTSP: replace dst addr\n"); dstoff += 12; dstlen -= 13; pdstrep = szextaddr; dstreplen = extaddrlen; diff = nextfieldoff-off-13-extaddrlen; } #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0) if (!nf_nat_mangle_tcp_packet(skb, ct, ctinfo, protoff, dstoff, dstlen, pdstrep, dstreplen)) { #else if (!nf_nat_mangle_tcp_packet(skb, ct, ctinfo, dstoff, dstlen, pdstrep, dstreplen)) { #endif /* mangle failed, all we can do is bail */ nf_ct_unexpect_related(rtp_exp); if (rtcp_exp) nf_ct_unexpect_related(rtcp_exp); return 0; } get_skb_tcpdata(skb, &ptcp, &tcplen); ptran = ptcp+tranoff; tranlen -= diff; nextparamoff -= diff; nextfieldoff -= diff; } } off = nextfieldoff; } if (is_stun) continue; off = saveoff; while (off < nextparamoff) { const char* pfieldend; uint nextfieldoff; pfieldend = memchr(ptran+off, ';', nextparamoff-off); nextfieldoff = (pfieldend == NULL) ? nextparamoff : pfieldend-ptran+1; if (strncmp(ptran+off, "client_port=", 12) == 0) { u_int16_t port; uint numlen; uint origoff; uint origlen; char* rbuf = rbuf1; uint rbuflen = rbuf1len; off += 12; origoff = (ptran-ptcp)+off; origlen = 0; numlen = nf_strtou16(ptran+off, &port); off += numlen; origlen += numlen; if (port != prtspexp->loport) { pr_debug("multiple ports found, port %hu ignored\n", port); } else { if (ptran[off] == '-' || ptran[off] == '/') { off++; origlen++; numlen = nf_strtou16(ptran+off, &port); off += numlen; origlen += numlen; rbuf = rbufa; rbuflen = rbufalen; } /* * note we cannot just memcpy() if the sizes are the same. * the mangle function does skb resizing, checks for a * cloned skb, and updates the checksums. * * parameter 4 below is offset from start of tcp data. */ diff = origlen-rbuflen; #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0) if (!nf_nat_mangle_tcp_packet(skb, ct, ctinfo, protoff, origoff, origlen, rbuf, rbuflen)) { #else if (!nf_nat_mangle_tcp_packet(skb, ct, ctinfo, origoff, origlen, rbuf, rbuflen)) { #endif /* mangle failed, all we can do is bail */ nf_ct_unexpect_related(rtp_exp); if (rtcp_exp) nf_ct_unexpect_related(rtcp_exp); return 0; } get_skb_tcpdata(skb, &ptcp, &tcplen); ptran = ptcp+tranoff; tranlen -= diff; nextparamoff -= diff; nextfieldoff -= diff; } } off = nextfieldoff; } off = nextparamoff; } return 1; } static uint help_out(struct sk_buff *skb, enum ip_conntrack_info ctinfo, #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0) unsigned int protoff, #endif unsigned int matchoff, unsigned int matchlen, struct ip_ct_rtsp_expect* prtspexp, struct nf_conntrack_expect* rtp_exp, struct nf_conntrack_expect* rtcp_exp) { char* ptcp; uint tcplen; uint hdrsoff; uint hdrslen; uint lineoff; uint linelen; uint off; int dir = CTINFO2DIR(ctinfo); #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0) union nf_inet_addr saddr = rtp_exp->master->tuplehash[dir].tuple.src.u3; #else __be32 saddr = rtp_exp->master->tuplehash[dir].tuple.src.u3.ip; #endif //struct iphdr* iph = (struct iphdr*)(*pskb)->nh.iph; //struct tcphdr* tcph = (struct tcphdr*)((void*)iph + iph->ihl*4); get_skb_tcpdata(skb, &ptcp, &tcplen); hdrsoff = matchoff;//exp->seq - ntohl(tcph->seq); hdrslen = matchlen; off = hdrsoff; pr_debug("NAT rtsp help_out\n"); while (nf_mime_nextline(ptcp, hdrsoff+hdrslen, &off, &lineoff, &linelen)) { if (linelen == 0) break; if (off > hdrsoff+hdrslen) { pr_info("!! overrun !!"); break; } pr_debug("hdr: len=%u, %.*s", linelen, (int)linelen, ptcp+lineoff); if (nf_strncasecmp(ptcp+lineoff, "Transport:", 10) == 0) { uint oldtcplen = tcplen; pr_debug("hdr: Transport\n"); #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0) if (!rtsp_mangle_tran(ctinfo, protoff, rtp_exp, rtcp_exp, prtspexp, skb, lineoff, linelen)) { #else if (!rtsp_mangle_tran(ctinfo, rtp_exp, rtcp_exp, prtspexp, skb, lineoff, linelen)) { #endif pr_debug("hdr: Transport mangle failed"); break; } rtp_exp->expectfn = nf_nat_rtsp_expected; #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0) rtp_exp->saved_addr = saddr; #else rtp_exp->saved_ip = saddr; #endif rtp_exp->saved_proto.udp.port = htons(prtspexp->loport); rtp_exp->dir = !dir; if (rtcp_exp) { rtcp_exp->expectfn = nf_nat_rtsp_expected; #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0) rtcp_exp->saved_addr = saddr; #else rtcp_exp->saved_ip = saddr; #endif rtcp_exp->saved_proto.udp.port = htons(prtspexp->hiport); rtcp_exp->dir = !dir; } get_skb_tcpdata(skb, &ptcp, &tcplen); hdrslen -= (oldtcplen-tcplen); off -= (oldtcplen-tcplen); lineoff -= (oldtcplen-tcplen); linelen -= (oldtcplen-tcplen); pr_debug("rep: len=%u, %.*s", linelen, (int)linelen, ptcp+lineoff); } } return NF_ACCEPT; } static unsigned int nf_nat_rtsp(struct sk_buff *skb, enum ip_conntrack_info ctinfo, #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0) unsigned int protoff, #endif unsigned int matchoff, unsigned int matchlen, struct ip_ct_rtsp_expect* prtspexp, struct nf_conntrack_expect* rtp_exp, struct nf_conntrack_expect* rtcp_exp) { int dir = CTINFO2DIR(ctinfo); int rc = NF_ACCEPT; switch (dir) { case IP_CT_DIR_ORIGINAL: #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0) rc = help_out(skb, ctinfo, protoff, matchoff, matchlen, prtspexp, rtp_exp, rtcp_exp); #else rc = help_out(skb, ctinfo, matchoff, matchlen, prtspexp, rtp_exp, rtcp_exp); #endif break; case IP_CT_DIR_REPLY: pr_debug("unmangle ! %u\n", ctinfo); /* XXX: unmangle */ rc = NF_ACCEPT; break; } //UNLOCK_BH(&ip_rtsp_lock); return rc; } static void nf_nat_rtsp_expected(struct nf_conn* ct, struct nf_conntrack_expect *exp) { #if LINUX_VERSION_CODE < KERNEL_VERSION(3,3,0) || LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0) struct nf_nat_range range; #else struct nf_nat_ipv4_range range; #endif /* This must be a fresh one. */ BUG_ON(ct->status & IPS_NAT_DONE_MASK); /* For DST manip, map port here to where it's expected. */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0) range.min_proto = range.max_proto = exp->saved_proto; range.min_addr = range.max_addr = exp->saved_addr; #else range.min = range.max = exp->saved_proto; range.min_ip = range.max_ip = exp->saved_ip; #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,3,0) range.flags = (NF_NAT_RANGE_MAP_IPS | NF_NAT_RANGE_PROTO_SPECIFIED); nf_nat_setup_info(ct, &range, NF_NAT_MANIP_DST); #else range.flags = (IP_NAT_RANGE_MAP_IPS | IP_NAT_RANGE_PROTO_SPECIFIED); nf_nat_setup_info(ct, &range, IP_NAT_MANIP_DST); #endif /* Change src to where master sends to, but only if the connection * actually came from the same source. */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0) if (nf_inet_addr_cmp(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3, &ct->master->tuplehash[exp->dir].tuple.src.u3)) { range.min_addr = range.max_addr = ct->master->tuplehash[!exp->dir].tuple.dst.u3; #else if (ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip == ct->master->tuplehash[exp->dir].tuple.src.u3.ip) { range.min_ip = range.max_ip = ct->master->tuplehash[!exp->dir].tuple.dst.u3.ip; #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,3,0) range.flags = NF_NAT_RANGE_MAP_IPS; nf_nat_setup_info(ct, &range, NF_NAT_MANIP_SRC); #else range.flags = IP_NAT_RANGE_MAP_IPS; nf_nat_setup_info(ct, &range, IP_NAT_MANIP_SRC); #endif } } static void __exit fini(void) { rcu_assign_pointer(nf_nat_rtsp_hook, NULL); synchronize_net(); } static int __init init(void) { printk("nf_nat_rtsp v" IP_NF_RTSP_VERSION " loading\n"); BUG_ON(nf_nat_rtsp_hook); rcu_assign_pointer(nf_nat_rtsp_hook, nf_nat_rtsp); if (stunaddr != NULL) extip = in_aton(stunaddr); if (destaction != NULL) { if (strcmp(destaction, "auto") == 0) dstact = DSTACT_AUTO; if (strcmp(destaction, "strip") == 0) dstact = DSTACT_STRIP; if (strcmp(destaction, "none") == 0) dstact = DSTACT_NONE; } return 0; }