static int destroy_sibling_or_exp(const struct nf_conntrack_tuple *t) { struct nf_conntrack_tuple_hash *h; struct nf_conntrack_expect *exp; struct nf_conn *sibling; pr_debug("trying to timeout ct or exp for tuple "); NF_CT_DUMP_TUPLE(t); h = nf_conntrack_find_get(t); if (h) { sibling = nf_ct_tuplehash_to_ctrack(h); pr_debug("setting timeout of conntrack %p to 0\n", sibling); sibling->proto.gre.timeout = 0; sibling->proto.gre.stream_timeout = 0; if (del_timer(&sibling->timeout)) sibling->timeout.function((unsigned long)sibling); nf_ct_put(sibling); return 1; } else { exp = nf_conntrack_expect_find_get(t); if (exp) { pr_debug("unexpect_related of expect %p\n", exp); nf_conntrack_unexpect_related(exp); nf_conntrack_expect_put(exp); return 1; } } return 0; }
static int destroy_sibling_or_exp(struct vrf* vrf, const struct nf_conntrack_tuple *t) { struct nf_conntrack_tuple_hash *h; struct nf_conntrack_expect *exp; struct nf_conn *sibling; DEBUGP("trying to timeout ct or exp for tuple "); NF_CT_DUMP_TUPLE(t); h = nf_conntrack_find_get(vrf, t, NULL, 0); if (h) { sibling = nf_ct_tuplehash_to_ctrack(h); DEBUGP("setting timeout of conntrack %p to 0\n", sibling); sibling->proto.gre.timeout = 0; sibling->proto.gre.stream_timeout = 0; nf_ct_put(sibling); return 1; } else { exp = nf_conntrack_expect_find_get(vrf,t); if (exp) { DEBUGP("unexpect_related of expect %p\n", exp); nf_conntrack_unexpect_related(exp); nf_conntrack_expect_put(exp); return 1; } } return 0; }
static unsigned int ip_nat_sip_expect(struct sk_buff *skb, unsigned int dataoff, const char **dptr, unsigned int *datalen, struct nf_conntrack_expect *exp, unsigned int matchoff, unsigned int matchlen) { enum ip_conntrack_info ctinfo; struct nf_conn *ct = nf_ct_get(skb, &ctinfo); enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); __be32 newip; u_int16_t port; char buffer[sizeof("nnn.nnn.nnn.nnn:nnnnn")]; unsigned buflen; /* Connection will come from reply */ if (ct->tuplehash[dir].tuple.src.u3.ip == ct->tuplehash[!dir].tuple.dst.u3.ip) newip = exp->tuple.dst.u3.ip; else newip = ct->tuplehash[!dir].tuple.dst.u3.ip; /* 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) port = ntohs(ct->tuplehash[!dir].tuple.dst.u.udp.port); else port = ntohs(exp->tuple.dst.u.udp.port); exp->saved_ip = exp->tuple.dst.u3.ip; exp->tuple.dst.u3.ip = newip; exp->saved_proto.udp.port = exp->tuple.dst.u.udp.port; exp->dir = !dir; exp->expectfn = ip_nat_sip_expected; for (; port != 0; port++) { exp->tuple.dst.u.udp.port = htons(port); if (nf_conntrack_expect_related(exp) == 0) break; } if (port == 0) return NF_DROP; if (exp->tuple.dst.u3.ip != exp->saved_ip || exp->tuple.dst.u.udp.port != exp->saved_proto.udp.port) { buflen = sprintf(buffer, "%u.%u.%u.%u:%u", NIPQUAD(newip), port); if (!mangle_packet(skb, dataoff, dptr, datalen, matchoff, matchlen, buffer, buflen)) goto err; } return NF_ACCEPT; err: nf_conntrack_unexpect_related(exp); return NF_DROP; }
static int nat_h245(struct sk_buff **pskb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, unsigned char **data, int dataoff, TransportAddress *taddr, __be16 port, struct nf_conntrack_expect *exp) { struct nf_ct_h323_master *info = &nfct_help(ct)->help.ct_h323_info; int dir = CTINFO2DIR(ctinfo); u_int16_t nated_port = ntohs(port); /* Set expectations for NAT */ exp->saved_proto.tcp.port = exp->tuple.dst.u.tcp.port; exp->expectfn = nf_nat_follow_master; exp->dir = !dir; /* Check existing expects */ if (info->sig_port[dir] == port) nated_port = ntohs(info->sig_port[!dir]); /* Try to get same port: if not, try to change it. */ for (; nated_port != 0; nated_port++) { int ret; exp->tuple.dst.u.tcp.port = htons(nated_port); ret = nf_conntrack_expect_related(exp); if (ret == 0) break; else if (ret != -EBUSY) { nated_port = 0; break; } } if (nated_port == 0) { /* No port available */ if (net_ratelimit()) printk("nf_nat_q931: out of TCP ports\n"); return 0; } /* Modify signal */ if (set_h225_addr(pskb, data, dataoff, taddr, &ct->tuplehash[!dir].tuple.dst.u3, htons(nated_port)) == 0) { /* Save ports */ info->sig_port[dir] = port; info->sig_port[!dir] = htons(nated_port); } else { nf_conntrack_unexpect_related(exp); return -1; } DEBUGP("nf_nat_q931: expect H.245 %u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n", NIPQUAD(exp->tuple.src.ip), ntohs(exp->tuple.src.u.tcp.port), NIPQUAD(exp->tuple.dst.ip), ntohs(exp->tuple.dst.u.tcp.port)); return 0; }
/* So, this packet has hit the connection tracking matching code. Mangle it, and change the expectation to match the new version. */ static unsigned int nf_nat_ftp(struct sk_buff **pskb, enum ip_conntrack_info ctinfo, enum nf_ct_ftp_type type, unsigned int matchoff, unsigned int matchlen, struct nf_conntrack_expect *exp, u32 *seq) { __be32 newip; u_int16_t port; int dir = CTINFO2DIR(ctinfo); struct nf_conn *ct = exp->master; DEBUGP("FTP_NAT: type %i, off %u len %u\n", type, matchoff, matchlen); /* Connection will come from wherever this packet goes, hence !dir */ newip = ct->tuplehash[!dir].tuple.dst.u3.ip; exp->saved_proto.tcp.port = exp->tuple.dst.u.tcp.port; exp->dir = !dir; /* When you see the packet, we need to NAT it the same as the * this one. */ exp->expectfn = nf_nat_follow_master; /* Try to get same port: if not, try to change it. */ for (port = ntohs(exp->saved_proto.tcp.port); port != 0; port++) { int ret; exp->tuple.dst.u.tcp.port = htons(port); ret = nf_conntrack_expect_related(exp); if (ret == 0) break; else if (ret != -EBUSY) { port = 0; break; } } if (port == 0) return NF_DROP; if (!mangle[type](pskb, newip, port, matchoff, matchlen, ct, ctinfo, seq)) { nf_conntrack_unexpect_related(exp); return NF_DROP; } return NF_ACCEPT; }
/* So, this packet has hit the connection tracking matching code. Mangle it, and change the expectation to match the new version. */ static unsigned int ip_nat_sdp(struct sk_buff **pskb, enum ip_conntrack_info ctinfo, struct nf_conntrack_expect *exp, const char *dptr) { struct nf_conn *ct = exp->master; enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); __be32 newip; u_int16_t port; /* Connection will come from reply */ if (ct->tuplehash[dir].tuple.src.u3.ip == ct->tuplehash[!dir].tuple.dst.u3.ip) newip = exp->tuple.dst.u3.ip; else newip = ct->tuplehash[!dir].tuple.dst.u3.ip; exp->saved_ip = exp->tuple.dst.u3.ip; exp->tuple.dst.u3.ip = newip; exp->saved_proto.udp.port = exp->tuple.dst.u.udp.port; exp->dir = !dir; /* When you see the packet, we need to NAT it the same as the this one. */ exp->expectfn = ip_nat_sdp_expect; /* Try to get same port: if not, try to change it. */ for (port = ntohs(exp->saved_proto.udp.port); port != 0; port++) { exp->tuple.dst.u.udp.port = htons(port); if (nf_conntrack_expect_related(exp) == 0) break; } if (port == 0) return NF_DROP; if (!mangle_sdp(pskb, ctinfo, ct, newip, port, dptr)) { nf_conntrack_unexpect_related(exp); return NF_DROP; } return NF_ACCEPT; }
static int nat_t120(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, unsigned char **data, int dataoff, H245_TransportAddress *taddr, __be16 port, struct nf_conntrack_expect *exp) { int dir = CTINFO2DIR(ctinfo); u_int16_t nated_port = ntohs(port); /* Set expectations for NAT */ exp->saved_proto.tcp.port = exp->tuple.dst.u.tcp.port; exp->expectfn = nf_nat_follow_master; exp->dir = !dir; /* Try to get same port: if not, try to change it. */ for (; nated_port != 0; nated_port++) { exp->tuple.dst.u.tcp.port = htons(nated_port); if (nf_conntrack_expect_related(exp) == 0) break; } if (nated_port == 0) { /* No port available */ if (net_ratelimit()) printk("nf_nat_h323: out of TCP ports\n"); return 0; } /* Modify signal */ if (set_h245_addr(skb, data, dataoff, taddr, &ct->tuplehash[!dir].tuple.dst.u3, htons(nated_port)) < 0) { nf_conntrack_unexpect_related(exp); return -1; } DEBUGP("nf_nat_h323: expect T.120 %u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n", NIPQUAD(exp->tuple.src.ip), ntohs(exp->tuple.src.u.tcp.port), NIPQUAD(exp->tuple.dst.ip), ntohs(exp->tuple.dst.u.tcp.port)); return 0; }
static void pptp_expectfn(struct nf_conn *ct, struct nf_conntrack_expect *exp) { struct vrf *vrf = nf_ct_vrf(ct); typeof(nf_nat_pptp_hook_expectfn) nf_nat_pptp_expectfn; DEBUGP("increasing timeouts\n"); /* increase timeout of GRE data channel conntrack entry */ ct->proto.gre.timeout = PPTP_GRE_TIMEOUT; ct->proto.gre.stream_timeout = PPTP_GRE_STREAM_TIMEOUT; /* Can you see how rusty this code is, compared with the pre-2.6.11 * one? That's what happened to my shiny newnat of 2002 ;( -HW */ rcu_read_lock(); nf_nat_pptp_expectfn = rcu_dereference(nf_nat_pptp_hook_expectfn); if (nf_nat_pptp_expectfn && ct->master->status & IPS_NAT_MASK) nf_nat_pptp_expectfn(ct, exp); else { struct nf_conntrack_tuple inv_t; struct nf_conntrack_expect *exp_other; /* obviously this tuple inversion only works until you do NAT */ nf_ct_invert_tuplepr(&inv_t, &exp->tuple); DEBUGP("trying to unexpect other dir: "); NF_CT_DUMP_TUPLE(&inv_t); exp_other = nf_conntrack_expect_find_get(vrf, &inv_t); if (exp_other) { /* delete other expectation. */ DEBUGP("found\n"); nf_conntrack_unexpect_related(exp_other); nf_conntrack_expect_put(exp_other); } else { DEBUGP("not found\n"); } } rcu_read_unlock(); }
static int nat_rtp_rtcp(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, unsigned char **data, int dataoff, H245_TransportAddress *taddr, __be16 port, __be16 rtp_port, struct nf_conntrack_expect *rtp_exp, struct nf_conntrack_expect *rtcp_exp) { struct nf_ct_h323_master *info = &nfct_help(ct)->help.ct_h323_info; int dir = CTINFO2DIR(ctinfo); int i; u_int16_t nated_port; /* Set expectations for NAT */ rtp_exp->saved_proto.udp.port = rtp_exp->tuple.dst.u.udp.port; rtp_exp->expectfn = nf_nat_follow_master; rtp_exp->dir = !dir; rtcp_exp->saved_proto.udp.port = rtcp_exp->tuple.dst.u.udp.port; rtcp_exp->expectfn = nf_nat_follow_master; rtcp_exp->dir = !dir; /* Lookup existing expects */ for (i = 0; i < H323_RTP_CHANNEL_MAX; i++) { if (info->rtp_port[i][dir] == rtp_port) { /* Expected */ /* Use allocated ports first. This will refresh * the expects */ rtp_exp->tuple.dst.u.udp.port = info->rtp_port[i][dir]; rtcp_exp->tuple.dst.u.udp.port = htons(ntohs(info->rtp_port[i][dir]) + 1); break; } else if (info->rtp_port[i][dir] == 0) { /* Not expected */ break; } } /* Run out of expectations */ if (i >= H323_RTP_CHANNEL_MAX) { if (net_ratelimit()) printk("nf_nat_h323: out of expectations\n"); return 0; } /* Try to get a pair of ports. */ for (nated_port = ntohs(rtp_exp->tuple.dst.u.udp.port); nated_port != 0; nated_port += 2) { rtp_exp->tuple.dst.u.udp.port = htons(nated_port); if (nf_conntrack_expect_related(rtp_exp) == 0) { rtcp_exp->tuple.dst.u.udp.port = htons(nated_port + 1); if (nf_conntrack_expect_related(rtcp_exp) == 0) break; nf_conntrack_unexpect_related(rtp_exp); } } if (nated_port == 0) { /* No port available */ if (net_ratelimit()) printk("nf_nat_h323: out of RTP ports\n"); return 0; } /* Modify signal */ if (set_h245_addr(skb, data, dataoff, taddr, &ct->tuplehash[!dir].tuple.dst.u3, htons((port & htons(1)) ? nated_port + 1 : nated_port)) == 0) { /* Save ports */ info->rtp_port[i][dir] = rtp_port; info->rtp_port[i][!dir] = htons(nated_port); } else { nf_conntrack_unexpect_related(rtp_exp); nf_conntrack_unexpect_related(rtcp_exp); return -1; } /* Success */ DEBUGP("nf_nat_h323: expect RTP %u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n", NIPQUAD(rtp_exp->tuple.src.ip), ntohs(rtp_exp->tuple.src.u.udp.port), NIPQUAD(rtp_exp->tuple.dst.ip), ntohs(rtp_exp->tuple.dst.u.udp.port)); DEBUGP("nf_nat_h323: expect RTCP %u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n", NIPQUAD(rtcp_exp->tuple.src.ip), ntohs(rtcp_exp->tuple.src.u.udp.port), NIPQUAD(rtcp_exp->tuple.dst.ip), ntohs(rtcp_exp->tuple.dst.u.udp.port)); return 0; }
/* expect GRE connections (PNS->PAC and PAC->PNS direction) */ static int exp_gre(struct nf_conn *ct, __be16 callid, __be16 peer_callid) { struct nf_conntrack_expect *exp_orig, *exp_reply; enum ip_conntrack_dir dir; int ret = 1; typeof(nf_nat_pptp_hook_exp_gre) nf_nat_pptp_exp_gre; exp_orig = nf_conntrack_expect_alloc(ct); if (exp_orig == NULL) goto out; exp_reply = nf_conntrack_expect_alloc(ct); if (exp_reply == NULL) goto out_put_orig; /* original direction, PNS->PAC */ dir = IP_CT_DIR_ORIGINAL; nf_conntrack_expect_init(exp_orig, NF_CT_EXPECT_CLASS_DEFAULT, ct->tuplehash[dir].tuple.src.l3num, &ct->tuplehash[dir].tuple.src.u3, &ct->tuplehash[dir].tuple.dst.u3, IPPROTO_GRE, &peer_callid, &callid); exp_orig->expectfn = pptp_expectfn; /* reply direction, PAC->PNS */ dir = IP_CT_DIR_REPLY; nf_conntrack_expect_init(exp_reply, NF_CT_EXPECT_CLASS_DEFAULT, ct->tuplehash[dir].tuple.src.l3num, &ct->tuplehash[dir].tuple.src.u3, &ct->tuplehash[dir].tuple.dst.u3, IPPROTO_GRE, &callid, &peer_callid); exp_reply->expectfn = pptp_expectfn; nf_nat_pptp_exp_gre = rcu_dereference(nf_nat_pptp_hook_exp_gre); if (nf_nat_pptp_exp_gre && ct->status & IPS_NAT_MASK) nf_nat_pptp_exp_gre(exp_orig, exp_reply); if (nf_conntrack_expect_related(exp_orig) != 0) goto out_put_both; if (nf_conntrack_expect_related(exp_reply) != 0) goto out_unexpect_orig; /* Add GRE keymap entries */ if (nf_ct_gre_keymap_add(ct, IP_CT_DIR_ORIGINAL, &exp_orig->tuple) != 0) goto out_unexpect_both; if (nf_ct_gre_keymap_add(ct, IP_CT_DIR_REPLY, &exp_reply->tuple) != 0) { nf_ct_gre_keymap_destroy(ct); goto out_unexpect_both; } ret = 0; out_put_both: nf_conntrack_expect_put(exp_reply); out_put_orig: nf_conntrack_expect_put(exp_orig); out: return ret; out_unexpect_both: nf_conntrack_unexpect_related(exp_reply); out_unexpect_orig: nf_conntrack_unexpect_related(exp_orig); goto out_put_both; }
/* * 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 *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) */ u_int32_t newip; u_int16_t loport, hiport; uint off = 0; uint diff; /* Number of bytes we removed */ struct nf_conn *ct = exp->master; struct nf_conntrack_tuple *t; char szextaddr[15+1]; 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) { INFOP("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; t = &exp->tuple; t->dst.u3.ip = newip; extaddrlen = extip ? sprintf(szextaddr, "%pI4", &extip) : sprintf(szextaddr, "%pI4", &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->dst.u.udp.port = htons(loport); if (nf_conntrack_expect_related(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? */ { t->dst.u.udp.port = htons(loport); if (nf_conntrack_expect_related(exp) == 0) { hiport = loport + 1; //~exp->mask.dst.u.udp.port; pr_debug("using ports %hu-%hu\n", loport, hiport); break; } } if (loport != 0) { rbuf1len = sprintf(rbuf1, "%hu", loport); rbufalen = sprintf(rbufa, "%hu-%hu", loport, loport+1); } break; case pb_discon: for (loport = prtspexp->loport; loport != 0; loport++) /* XXX: improper wrap? */ { t->dst.u.udp.port = htons(loport); if (nf_conntrack_expect_related(exp) == 0) { pr_debug("using port %hu (1 of 2)\n", loport); break; } } for (hiport = prtspexp->hiport; hiport != 0; hiport++) /* XXX: improper wrap? */ { t->dst.u.udp.port = htons(hiport); if (nf_conntrack_expect_related(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; /* * 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)) { diff = nextfieldoff-off; if (!nf_nat_mangle_tcp_packet(skb, ct, ctinfo, off, diff, NULL, 0)) { /* mangle failed, all we can do is bail */ nf_conntrack_unexpect_related(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 (!nf_nat_mangle_tcp_packet(skb, ct, ctinfo, origoff, origlen, rbuf, rbuflen)) { /* mangle failed, all we can do is bail */ nf_conntrack_unexpect_related(exp); return 0; } get_skb_tcpdata(skb, &ptcp, &tcplen); ptran = ptcp+tranoff; tranlen -= diff; nextparamoff -= diff; nextfieldoff -= diff; } } off = nextfieldoff; } off = nextparamoff; } return 1; }
/* So, this packet has hit the connection tracking matching code. Mangle it, and change the expectation to match the new version. */ static unsigned int ip_nat_sdp(struct sk_buff **pskb, enum ip_conntrack_info ctinfo, struct nf_conntrack_expect *exp, const char *dptr) { struct nf_conn *ct = exp->master; enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); __be32 newip; u_int16_t port; DEBUGP("ip_nat_sdp():\n"); #if 0 /* Connection will come from reply */ newip = ct->tuplehash[!dir].tuple.dst.u3.ip; //printk("newip :0x%08lx \n",newip); #else /*20091028 pork patch: according to 2.6.24, modify SDP error as registering*/ if (ct->tuplehash[dir].tuple.src.u3.ip == ct->tuplehash[!dir].tuple.dst.u3.ip){ newip = exp->tuple.dst.u3.ip; /*printk("newip :0x%08lx \n",newip);*/ } else{ newip = ct->tuplehash[!dir].tuple.dst.u3.ip; /*printk("newip :0x%08lx \n",newip);*/ } #endif exp->tuple.dst.u3.ip = newip; exp->saved_proto.udp.port = exp->tuple.dst.u.udp.port; exp->dir = !dir; /* When you see the packet, we need to NAT it the same as the this one. */ exp->expectfn = nf_nat_follow_master; /* Try to get same port: if not, try to change it. */ for (port = ntohs(exp->saved_proto.udp.port); port != 0; port++) { int ret; exp->tuple.dst.u.udp.port = htons(port); ret = nf_conntrack_expect_related(exp); if (ret == 0) break; else if (ret != -EBUSY) { port = 0; break; } } if (port == 0){ return NF_DROP; } /*printk("new port :%d \n", htons(port));*/ if (!mangle_sdp(pskb, ctinfo, ct, newip, port, dptr)) { nf_conntrack_unexpect_related(exp); return NF_DROP; } return NF_ACCEPT; }
/* So, this packet has hit the connection tracking matching code. Mangle it, and change the expectation to match the new version. */ static unsigned int ip_nat_sdp_media(struct sk_buff *skb, unsigned int dataoff, const char **dptr, unsigned int *datalen, struct nf_conntrack_expect *rtp_exp, struct nf_conntrack_expect *rtcp_exp, unsigned int mediaoff, unsigned int medialen, union nf_inet_addr *rtp_addr) { enum ip_conntrack_info ctinfo; struct nf_conn *ct = nf_ct_get(skb, &ctinfo); enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); u_int16_t port; /* Connection will come from reply */ if (ct->tuplehash[dir].tuple.src.u3.ip == ct->tuplehash[!dir].tuple.dst.u3.ip) rtp_addr->ip = rtp_exp->tuple.dst.u3.ip; else rtp_addr->ip = ct->tuplehash[!dir].tuple.dst.u3.ip; rtp_exp->saved_ip = rtp_exp->tuple.dst.u3.ip; rtp_exp->tuple.dst.u3.ip = rtp_addr->ip; rtp_exp->saved_proto.udp.port = rtp_exp->tuple.dst.u.udp.port; rtp_exp->dir = !dir; rtp_exp->expectfn = ip_nat_sip_expected; rtcp_exp->saved_ip = rtcp_exp->tuple.dst.u3.ip; rtcp_exp->tuple.dst.u3.ip = rtp_addr->ip; rtcp_exp->saved_proto.udp.port = rtcp_exp->tuple.dst.u.udp.port; rtcp_exp->dir = !dir; 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) { rtp_exp->tuple.dst.u.udp.port = htons(port); if (nf_conntrack_expect_related(rtp_exp) != 0) continue; rtcp_exp->tuple.dst.u.udp.port = htons(port + 1); if (nf_conntrack_expect_related(rtcp_exp) == 0) break; nf_conntrack_unexpect_related(rtp_exp); } if (port == 0) goto err1; /* Update media port. */ if (rtp_exp->tuple.dst.u.udp.port != rtp_exp->saved_proto.udp.port && !ip_nat_sdp_port(skb, dataoff, dptr, datalen, mediaoff, medialen, port)) goto err2; return NF_ACCEPT; err2: nf_conntrack_unexpect_related(rtp_exp); nf_conntrack_unexpect_related(rtcp_exp); err1: return NF_DROP; }