static int map_addr(struct sk_buff *skb, unsigned int protoff, unsigned int dataoff, const char **dptr, unsigned int *datalen, unsigned int matchoff, unsigned int matchlen, union nf_inet_addr *addr, __be16 port) { enum ip_conntrack_info ctinfo; struct nf_conn *ct = nf_ct_get(skb, &ctinfo); enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); char buffer[INET6_ADDRSTRLEN + sizeof("[]:nnnnn")]; unsigned int buflen; union nf_inet_addr newaddr; __be16 newport; if (nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.src.u3, addr) && ct->tuplehash[dir].tuple.src.u.udp.port == port) { newaddr = ct->tuplehash[!dir].tuple.dst.u3; newport = ct->tuplehash[!dir].tuple.dst.u.udp.port; } else if (nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.dst.u3, addr) && ct->tuplehash[dir].tuple.dst.u.udp.port == port) { newaddr = ct->tuplehash[!dir].tuple.src.u3; newport = ct->tuplehash[!dir].tuple.src.u.udp.port; } else return 1; if (nf_inet_addr_cmp(&newaddr, addr) && newport == port) return 1; buflen = sip_sprintf_addr_port(ct, buffer, &newaddr, ntohs(newport)); return mangle_packet(skb, protoff, dataoff, dptr, datalen, matchoff, matchlen, buffer, buflen); }
static int map_addr(struct sk_buff *skb, unsigned int dataoff, const char **dptr, unsigned int *datalen, unsigned int matchoff, unsigned int matchlen, union nf_inet_addr *addr, __be16 port) { enum ip_conntrack_info ctinfo; struct nf_conn *ct = nf_ct_get(skb, &ctinfo); enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); 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) { 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; } else return 1; if (newaddr == addr->ip && newport == port) return 1; buflen = sprintf(buffer, "%pI4:%u", &newaddr, ntohs(newport)); return mangle_packet(skb, dataoff, dptr, datalen, matchoff, matchlen, buffer, buflen); }
static int mangle_content_len(struct sk_buff *skb, unsigned int dataoff, const char **dptr, unsigned int *datalen) { enum ip_conntrack_info ctinfo; struct nf_conn *ct = nf_ct_get(skb, &ctinfo); unsigned int matchoff, matchlen; char buffer[sizeof("65536")]; int buflen, c_len; /* Get actual SDP length */ if (ct_sip_get_sdp_header(ct, *dptr, 0, *datalen, SDP_HDR_VERSION, SDP_HDR_UNSPEC, &matchoff, &matchlen) <= 0) return 0; c_len = *datalen - matchoff + strlen("v="); /* Now, update SDP length */ if (ct_sip_get_header(ct, *dptr, 0, *datalen, SIP_HDR_CONTENT_LENGTH, &matchoff, &matchlen) <= 0) return 0; buflen = sprintf(buffer, "%u", c_len); return mangle_packet(skb, dataoff, dptr, datalen, matchoff, matchlen, buffer, buflen); }
static int talk_help_msg(struct ip_conntrack *ct, struct sk_buff **pskb, u_char type, struct talk_addr *addr, struct talk_addr *ctl_addr) { u_int32_t newip; u_int16_t port; unsigned int verdict = NF_ACCEPT; DEBUGP("ip_nat_talk_help_msg: addr: %u.%u.%u.%u:%u, ctl_addr: %u.%u.%u.%u:%u, type %d\n", NIPQUAD(addr->ta_addr), ntohs(addr->ta_port), NIPQUAD(ctl_addr->ta_addr), ntohs(ctl_addr->ta_port), type); /* Change address inside packet to match way we're mapping this connection. */ newip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; port = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.udp.port; DEBUGP("ip_nat_talk_help_msg: inserting: %u.%u.%u.%u:%u\n", NIPQUAD(newip), ntohs(port)); if (!mangle_packet(pskb, ct, newip, port, addr, ctl_addr)) verdict = NF_DROP; return verdict; }
static int talk_help_response(struct ip_conntrack *ct, struct ip_conntrack_expect *exp, struct sk_buff **pskb, u_char type, u_char answer, struct talk_addr *addr) { u_int32_t newip; u_int16_t port; struct ip_conntrack_tuple t; struct ip_ct_talk_expect *ct_talk_info; DEBUGP("ip_nat_talk_help_response: addr: %u.%u.%u.%u:%u, type %d answer %d\n", NIPQUAD(addr->ta_addr), ntohs(addr->ta_port), type, answer); LOCK_BH(&ip_talk_lock); ct_talk_info = &exp->help.exp_talk_info; if (!(answer == SUCCESS && (type == LOOK_UP || type == ANNOUNCE) && exp != NULL)) { UNLOCK_BH(&ip_talk_lock); return NF_ACCEPT; } DEBUGP("ip_nat_talk_help_response: talkinfo port %u (%s)\n", ntohs(ct_talk_info->port), type == LOOK_UP ? "LOOK_UP" : "ANNOUNCE"); /* Change address inside packet to match way we're mapping this connection. */ newip = ct->tuplehash[type == LOOK_UP ? IP_CT_DIR_ORIGINAL : IP_CT_DIR_REPLY].tuple.dst.ip; /* We can read expect here without conntrack lock, since it's only set in ip_conntrack_talk , with ip_talk_lock held writable */ t = exp->tuple; t.dst.ip = newip; /* Try to get same port: if not, try to change it. */ for (port = ntohs(ct_talk_info->port); port != 0; port++) { if (type == LOOK_UP) t.dst.u.tcp.port = htons(port); else t.dst.u.udp.port = htons(port); if (ip_conntrack_change_expect(exp, &t) == 0) { DEBUGP("ip_nat_talk_help_response: using %u.%u.%u.%u:%u\n", NIPQUAD(newip), port); break; } } UNLOCK_BH(&ip_talk_lock); if (port == 0 || !mangle_packet(pskb, ct, newip, htons(port), addr, NULL)) return NF_DROP; return NF_ACCEPT; }
static unsigned int ip_nat_sip_expect(struct sk_buff *skb, 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_ct_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, dptr, datalen, matchoff, matchlen, buffer, buflen)) goto err; } return NF_ACCEPT; err: nf_ct_unexpect_related(exp); return NF_DROP; }
static unsigned int ip_nat_sdp_port(struct sk_buff *skb, unsigned int dataoff, const char **dptr, unsigned int *datalen, unsigned int matchoff, unsigned int matchlen, u_int16_t port) { char buffer[sizeof("nnnnn")]; unsigned int buflen; buflen = sprintf(buffer, "%u", port); if (!mangle_packet(skb, dataoff, dptr, datalen, matchoff, matchlen, buffer, buflen)) return 0; return mangle_content_len(skb, dataoff, dptr, datalen); }
static unsigned mangle_sdp_packet(struct sk_buff *skb, const char **dptr, unsigned int dataoff, unsigned int *datalen, enum sdp_header_types type, enum sdp_header_types term, char *buffer, int buflen) { enum ip_conntrack_info ctinfo; struct nf_conn *ct = nf_ct_get(skb, &ctinfo); unsigned int matchlen, matchoff; if (ct_sip_get_sdp_header(ct, *dptr, dataoff, *datalen, type, term, &matchoff, &matchlen) <= 0) return 0; return mangle_packet(skb, dptr, datalen, matchoff, matchlen, buffer, buflen); }
static int sc_data_fixup(const struct ip_ct_sc_expect *exp_sc_info, struct ip_conntrack *ct, unsigned int datalen, struct sk_buff **pskb, enum ip_conntrack_info ctinfo) { u_int32_t newip; struct iphdr *iph = (*pskb)->nh.iph; struct tcphdr *tcph = (void *)iph + iph->ihl*4; u_int16_t port, new_port; struct ip_conntrack_tuple tuple; struct sc_ip_port_data *list_t=sc_list; int ret=0; /* Don't care about source port */ const struct ip_conntrack_tuple mask = { { 0xFFFFFFFF, { 0xFFFFFFFF } }, { 0x0, { 0xFFFF }, 0xFFFF } }; memset(&tuple, 0, sizeof(tuple)); MUST_BE_LOCKED(&ip_sc_lock); #if 0 DEBUGP("SC_NAT: seq %u + %u in %u + %u\n", exp_sc_info->seq, exp_sc_info->len, ntohl(tcph->seq), datalen); #endif /* Change address inside packet to match way we're mapping this connection. */ newip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; DEBUGP("sc_data_fixup: %u.%u.%u.%u->%u.%u.%u.%u\n", NIPQUAD(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip), NIPQUAD(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip)); /* Expect something from server->client */ tuple.src.ip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip; tuple.dst.protonum = IPPROTO_UDP; jiq_task.routine = open_sc_socket; memset(task_data, 0, 64); sprintf(task_data,"IP:%u.%u.%u.%uPORT:%dEND",NIPQUAD(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip), port); jiq_task.data = (void *)task_data; schedule_task(&jiq_task); if (!mangle_packet(pskb, newip, exp_sc_info->seq - ntohl(tcph->seq), exp_sc_info->len, ct, ctinfo)) return 0; return 1; }
static unsigned int ip_nat_sip(struct sk_buff *skb, unsigned int dataoff, const char **dptr, unsigned int *datalen) { enum ip_conntrack_info ctinfo; struct nf_conn *ct = nf_ct_get(skb, &ctinfo); enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); unsigned int coff, matchoff, matchlen; enum sip_header_types hdr; union nf_inet_addr addr; __be16 port; int request, in_header; /* Basic rules: requests and responses. */ if (strnicmp(*dptr, "SIP/2.0", strlen("SIP/2.0")) != 0) { if (ct_sip_parse_request(ct, *dptr, *datalen, &matchoff, &matchlen, &addr, &port) > 0 && !map_addr(skb, dataoff, dptr, datalen, matchoff, matchlen, &addr, port)) return NF_DROP; request = 1; } else request = 0; if (nf_ct_protonum(ct) == IPPROTO_TCP) hdr = SIP_HDR_VIA_TCP; else hdr = SIP_HDR_VIA_UDP; /* Translate topmost Via header and parameters */ if (ct_sip_parse_header_uri(ct, *dptr, NULL, *datalen, hdr, NULL, &matchoff, &matchlen, &addr, &port) > 0) { unsigned int matchend, poff, plen, buflen, n; char buffer[sizeof("nnn.nnn.nnn.nnn:nnnnn")]; /* 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) goto next; } else { if (addr.ip != ct->tuplehash[dir].tuple.dst.u3.ip || port != ct->tuplehash[dir].tuple.dst.u.udp.port) goto next; } if (!map_addr(skb, dataoff, dptr, datalen, matchoff, matchlen, &addr, port)) return NF_DROP; matchend = matchoff + matchlen; /* The maddr= parameter (RFC 2361) specifies where to send * the reply. */ if (ct_sip_parse_address_param(ct, *dptr, matchend, *datalen, "maddr=", &poff, &plen, &addr) > 0 && addr.ip == ct->tuplehash[dir].tuple.src.u3.ip && addr.ip != ct->tuplehash[!dir].tuple.dst.u3.ip) { buflen = sprintf(buffer, "%pI4", &ct->tuplehash[!dir].tuple.dst.u3.ip); if (!mangle_packet(skb, dataoff, dptr, datalen, poff, plen, buffer, buflen)) return NF_DROP; } /* The received= parameter (RFC 2361) contains the address * from which the server received the request. */ if (ct_sip_parse_address_param(ct, *dptr, matchend, *datalen, "received=", &poff, &plen, &addr) > 0 && addr.ip == ct->tuplehash[dir].tuple.dst.u3.ip && addr.ip != ct->tuplehash[!dir].tuple.src.u3.ip) { buflen = sprintf(buffer, "%pI4", &ct->tuplehash[!dir].tuple.src.u3.ip); if (!mangle_packet(skb, dataoff, dptr, datalen, poff, plen, buffer, buflen)) return NF_DROP; } /* The rport= parameter (RFC 3581) contains the port number * from which the server received the request. */ if (ct_sip_parse_numerical_param(ct, *dptr, matchend, *datalen, "rport=", &poff, &plen, &n) > 0 && htons(n) == ct->tuplehash[dir].tuple.dst.u.udp.port && htons(n) != ct->tuplehash[!dir].tuple.src.u.udp.port) { __be16 p = ct->tuplehash[!dir].tuple.src.u.udp.port; buflen = sprintf(buffer, "%u", ntohs(p)); if (!mangle_packet(skb, dataoff, dptr, datalen, poff, plen, buffer, buflen)) return NF_DROP; } } next: /* Translate Contact headers */ coff = 0; in_header = 0; while (ct_sip_parse_header_uri(ct, *dptr, &coff, *datalen, SIP_HDR_CONTACT, &in_header, &matchoff, &matchlen, &addr, &port) > 0) { if (!map_addr(skb, dataoff, dptr, datalen, matchoff, matchlen, &addr, port)) return NF_DROP; } if (!map_sip_addr(skb, dataoff, dptr, datalen, SIP_HDR_FROM) || !map_sip_addr(skb, dataoff, dptr, datalen, SIP_HDR_TO)) return NF_DROP; return NF_ACCEPT; }
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; 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 (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++) { int ret; exp->tuple.dst.u.udp.port = htons(port); ret = nf_ct_expect_related(exp); if (ret == 0) break; else if (ret != -EBUSY) { port = 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, "%pI4:%u", &newip, port); if (!mangle_packet(skb, dataoff, dptr, datalen, matchoff, matchlen, buffer, buflen)) goto err; } return NF_ACCEPT; err: nf_ct_unexpect_related(exp); return NF_DROP; }
static unsigned int ip_nat_sip(struct sk_buff *skb, unsigned int dataoff, const char **dptr, unsigned int *datalen) { enum ip_conntrack_info ctinfo; struct nf_conn *ct = nf_ct_get(skb, &ctinfo); enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); unsigned int coff, matchoff, matchlen; enum sip_header_types hdr; union nf_inet_addr addr; __be16 port; int request, in_header; if (strnicmp(*dptr, "SIP/2.0", strlen("SIP/2.0")) != 0) { if (ct_sip_parse_request(ct, *dptr, *datalen, &matchoff, &matchlen, &addr, &port) > 0 && !map_addr(skb, dataoff, dptr, datalen, matchoff, matchlen, &addr, port)) return NF_DROP; request = 1; } else request = 0; if (nf_ct_protonum(ct) == IPPROTO_TCP) hdr = SIP_HDR_VIA_TCP; else hdr = SIP_HDR_VIA_UDP; if (ct_sip_parse_header_uri(ct, *dptr, NULL, *datalen, hdr, NULL, &matchoff, &matchlen, &addr, &port) > 0) { unsigned int olen, matchend, poff, plen, buflen, n; char buffer[sizeof("nnn.nnn.nnn.nnn:nnnnn")]; if (request) { if (addr.ip != ct->tuplehash[dir].tuple.src.u3.ip || port != ct->tuplehash[dir].tuple.src.u.udp.port) goto next; } else { if (addr.ip != ct->tuplehash[dir].tuple.dst.u3.ip || port != ct->tuplehash[dir].tuple.dst.u.udp.port) goto next; } olen = *datalen; if (!map_addr(skb, dataoff, dptr, datalen, matchoff, matchlen, &addr, port)) return NF_DROP; matchend = matchoff + matchlen + *datalen - olen; if (ct_sip_parse_address_param(ct, *dptr, matchend, *datalen, "maddr=", &poff, &plen, &addr) > 0 && addr.ip == ct->tuplehash[dir].tuple.src.u3.ip && addr.ip != ct->tuplehash[!dir].tuple.dst.u3.ip) { buflen = sprintf(buffer, "%pI4", &ct->tuplehash[!dir].tuple.dst.u3.ip); if (!mangle_packet(skb, dataoff, dptr, datalen, poff, plen, buffer, buflen)) return NF_DROP; } if (ct_sip_parse_address_param(ct, *dptr, matchend, *datalen, "received=", &poff, &plen, &addr) > 0 && addr.ip == ct->tuplehash[dir].tuple.dst.u3.ip && addr.ip != ct->tuplehash[!dir].tuple.src.u3.ip) { buflen = sprintf(buffer, "%pI4", &ct->tuplehash[!dir].tuple.src.u3.ip); if (!mangle_packet(skb, dataoff, dptr, datalen, poff, plen, buffer, buflen)) return NF_DROP; } if (ct_sip_parse_numerical_param(ct, *dptr, matchend, *datalen, "rport=", &poff, &plen, &n) > 0 && htons(n) == ct->tuplehash[dir].tuple.dst.u.udp.port && htons(n) != ct->tuplehash[!dir].tuple.src.u.udp.port) { __be16 p = ct->tuplehash[!dir].tuple.src.u.udp.port; buflen = sprintf(buffer, "%u", ntohs(p)); if (!mangle_packet(skb, dataoff, dptr, datalen, poff, plen, buffer, buflen)) return NF_DROP; } } next: coff = 0; in_header = 0; while (ct_sip_parse_header_uri(ct, *dptr, &coff, *datalen, SIP_HDR_CONTACT, &in_header, &matchoff, &matchlen, &addr, &port) > 0) { if (!map_addr(skb, dataoff, dptr, datalen, matchoff, matchlen, &addr, port)) return NF_DROP; } if (!map_sip_addr(skb, dataoff, dptr, datalen, SIP_HDR_FROM) || !map_sip_addr(skb, dataoff, dptr, datalen, SIP_HDR_TO)) return NF_DROP; return NF_ACCEPT; }
static unsigned int nf_nat_sip_expect(struct sk_buff *skb, unsigned int protoff, 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); union nf_inet_addr newaddr; u_int16_t port; char buffer[INET6_ADDRSTRLEN + sizeof("[]:nnnnn")]; unsigned int buflen; /* Connection will come from reply */ if (nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.src.u3, &ct->tuplehash[!dir].tuple.dst.u3)) newaddr = exp->tuple.dst.u3; else newaddr = ct->tuplehash[!dir].tuple.dst.u3; /* 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_addr = exp->tuple.dst.u3; exp->tuple.dst.u3 = newaddr; exp->saved_proto.udp.port = exp->tuple.dst.u.udp.port; exp->dir = !dir; exp->expectfn = nf_nat_sip_expected; for (; port != 0; port++) { int ret; exp->tuple.dst.u.udp.port = htons(port); ret = nf_ct_expect_related(exp); if (ret == 0) break; else if (ret != -EBUSY) { port = 0; break; } } if (port == 0) return NF_DROP; if (!nf_inet_addr_cmp(&exp->tuple.dst.u3, &exp->saved_addr) || exp->tuple.dst.u.udp.port != exp->saved_proto.udp.port) { buflen = sip_sprintf_addr_port(ct, buffer, &newaddr, port); if (!mangle_packet(skb, protoff, dataoff, dptr, datalen, matchoff, matchlen, buffer, buflen)) goto err; } return NF_ACCEPT; err: nf_ct_unexpect_related(exp); return NF_DROP; }