/* 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_ftp(struct sk_buff **pskb, enum ip_conntrack_info ctinfo, enum ip_ct_ftp_type type, unsigned int matchoff, unsigned int matchlen, struct ip_conntrack_expect *exp, u32 *seq) { u_int32_t newip; u_int16_t port; int dir = CTINFO2DIR(ctinfo); struct ip_conntrack *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.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 = ip_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++) { exp->tuple.dst.u.tcp.port = htons(port); if (ip_conntrack_expect_related(exp) == 0) break; } if (port == 0) { ip_conntrack_expect_free(exp); return NF_DROP; } if (!mangle[type](pskb, newip, port, matchoff, matchlen, ct, ctinfo, seq)) { ip_conntrack_unexpect_related(exp); return NF_DROP; } return NF_ACCEPT; }
static unsigned int help(struct sk_buff **pskb, enum ip_conntrack_info ctinfo, unsigned int matchoff, unsigned int matchlen, struct ip_conntrack_expect *exp) { char buffer[sizeof("65535")]; u_int16_t port; unsigned int ret; /* Connection comes from client. */ exp->saved_proto.tcp.port = exp->tuple.dst.u.tcp.port; exp->dir = IP_CT_DIR_ORIGINAL; /* When you see the packet, we need to NAT it the same as the * this one (ie. same IP: it will be TCP and master is UDP). */ exp->expectfn = ip_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++) { exp->tuple.dst.u.tcp.port = htons(port); if (ip_conntrack_expect_related(exp) == 0) break; } if (port == 0) { ip_conntrack_expect_free(exp); return NF_DROP; } sprintf(buffer, "%u", port); ret = ip_nat_mangle_udp_packet(pskb, exp->master, ctinfo, matchoff, matchlen, buffer, strlen(buffer)); if (ret != NF_ACCEPT) ip_conntrack_unexpect_related(exp); return ret; }
static int quake3_help(struct sk_buff **pskb, struct ip_conntrack *ct, enum ip_conntrack_info ctinfo) { struct udphdr _udph, *uh; struct ip_conntrack_expect *exp; void *data, *qb_ptr; int dir = CTINFO2DIR(ctinfo); int i, dataoff; int ret = NF_ACCEPT; /* Until there's been traffic both ways, don't look in packets. note: * it's UDP ! */ if (ctinfo != IP_CT_ESTABLISHED && ctinfo != IP_CT_IS_REPLY) { DEBUGP("ip_conntrack_quake3: not ok ! Conntrackinfo = %u\n", ctinfo); return NF_ACCEPT; } else { DEBUGP("ip_conntrack_quake3: it's ok ! Conntrackinfo = %u\n", ctinfo); } /* Valid UDP header? */ uh = skb_header_pointer(*pskb, (*pskb)->nh.iph->ihl*4, sizeof(_udph), &_udph); if (!uh) return NF_ACCEPT; /* Any data? */ dataoff = (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr); if (dataoff >= (*pskb)->len) return NF_ACCEPT; LOCK_BH(&quake3_buffer_lock); qb_ptr = skb_header_pointer(*pskb, dataoff, (*pskb)->len - dataoff, quake3_buffer); BUG_ON(qb_ptr == NULL); data = qb_ptr; if (strnicmp(data + 4, quake3s_conntrack.pattern, quake3s_conntrack.plen) == 0) { for(i=23; /* 4 bytes filler, 18 bytes "getserversResponse", 1 byte "\" */ i+6 < ntohs(uh->len); i+=7) { u_int32_t *ip = data+i; u_int16_t *port = data+i+4; #if 0 DEBUGP("ip_conntrack_quake3: adding server at offset " "%u/%u %u.%u.%u.%u:%u\n", i, ntohs(uh->len), NIPQUAD(*ip), ntohs(*port)); #endif exp = ip_conntrack_expect_alloc(); if (!exp) { ret = NF_DROP; goto out; } memset(exp, 0, sizeof(*exp)); exp->tuple.src.ip = ct->tuplehash[!dir].tuple.src.ip; exp->tuple.dst.ip = *ip; exp->tuple.dst.u.udp.port = *port; exp->tuple.dst.protonum = IPPROTO_UDP; exp->mask.src.ip = 0xffffffff; exp->mask.dst.ip = 0xffffffff; exp->mask.dst.u.udp.port = 0xffff; exp->mask.dst.protonum = 0xff; if (ip_nat_quake3_hook) ret = ip_nat_quake3_hook(exp); else if (ip_conntrack_expect_related(exp) != 0) { ip_conntrack_expect_free(exp); ret = NF_DROP; } goto out; } } out: return ret; }
static int pptp_exp_gre(struct ip_conntrack_expect *expect_orig, struct ip_conntrack_expect *expect_reply) { struct ip_ct_pptp_master *ct_pptp_info = &expect_orig->master->help.ct_pptp_info; struct ip_nat_pptp *nat_pptp_info = &expect_orig->master->nat.help.nat_pptp_info; struct ip_conntrack *ct = expect_orig->master; struct ip_conntrack_tuple inv_t; struct ip_conntrack_tuple *orig_t, *reply_t; /* save original PAC call ID in nat_info */ nat_pptp_info->pac_call_id = ct_pptp_info->pac_call_id; /* alter expectation */ orig_t = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple; reply_t = &ct->tuplehash[IP_CT_DIR_REPLY].tuple; /* alter expectation for PNS->PAC direction */ invert_tuplepr(&inv_t, &expect_orig->tuple); expect_orig->saved_proto.gre.key = htons(ct_pptp_info->pns_call_id); expect_orig->tuple.src.u.gre.key = htons(nat_pptp_info->pns_call_id); expect_orig->tuple.dst.u.gre.key = htons(ct_pptp_info->pac_call_id); inv_t.src.ip = reply_t->src.ip; inv_t.dst.ip = reply_t->dst.ip; inv_t.src.u.gre.key = htons(nat_pptp_info->pac_call_id); inv_t.dst.u.gre.key = htons(ct_pptp_info->pns_call_id); if (!ip_conntrack_expect_related(expect_orig)) { DEBUGP("successfully registered expect\n"); } else { DEBUGP("can't expect_related(expect_orig)\n"); ip_conntrack_expect_free(expect_orig); return 1; } /* alter expectation for PAC->PNS direction */ invert_tuplepr(&inv_t, &expect_reply->tuple); expect_reply->saved_proto.gre.key = htons(nat_pptp_info->pns_call_id); expect_reply->tuple.src.u.gre.key = htons(nat_pptp_info->pac_call_id); expect_reply->tuple.dst.u.gre.key = htons(ct_pptp_info->pns_call_id); inv_t.src.ip = orig_t->src.ip; inv_t.dst.ip = orig_t->dst.ip; inv_t.src.u.gre.key = htons(nat_pptp_info->pns_call_id); inv_t.dst.u.gre.key = htons(ct_pptp_info->pac_call_id); if (!ip_conntrack_expect_related(expect_reply)) { DEBUGP("successfully registered expect\n"); } else { DEBUGP("can't expect_related(expect_reply)\n"); ip_conntrack_unexpect_related(expect_orig); ip_conntrack_expect_free(expect_reply); return 1; } if (ip_ct_gre_keymap_add(ct, &expect_reply->tuple, 0) < 0) { DEBUGP("can't register original keymap\n"); ip_conntrack_unexpect_related(expect_orig); ip_conntrack_unexpect_related(expect_reply); return 1; } if (ip_ct_gre_keymap_add(ct, &inv_t, 1) < 0) { DEBUGP("can't register reply keymap\n"); ip_conntrack_unexpect_related(expect_orig); ip_conntrack_unexpect_related(expect_reply); ip_ct_gre_keymap_destroy(ct); return 1; } return 0; }
/* outbound packet: client->server */ static inline int help_out(struct sk_buff **pskb, unsigned char *rb_ptr, unsigned int datalen, struct ip_conntrack* ct, enum ip_conntrack_info ctinfo) { struct ip_ct_rtsp_expect expinfo; int dir = CTINFO2DIR(ctinfo); /* = IP_CT_DIR_ORIGINAL */ //struct tcphdr* tcph = (void*)iph + iph->ihl * 4; //uint tcplen = pktlen - iph->ihl * 4; char* pdata = rb_ptr; //uint datalen = tcplen - tcph->doff * 4; uint dataoff = 0; int ret = NF_ACCEPT; struct ip_conntrack_expect *exp; memset(&expinfo, 0, sizeof(expinfo)); while (dataoff < datalen) { uint cmdoff = dataoff; uint hdrsoff = 0; uint hdrslen = 0; uint cseqoff = 0; uint cseqlen = 0; uint lineoff = 0; uint linelen = 0; uint off; if (!rtsp_parse_message(pdata, datalen, &dataoff, &hdrsoff, &hdrslen, &cseqoff, &cseqlen)) { break; /* not a valid message */ } if (strncmp(pdata+cmdoff, "SETUP ", 6) != 0) { continue; /* not a SETUP message */ } DEBUGP("found a setup message\n"); off = 0; while (nf_mime_nextline(pdata+hdrsoff, hdrslen, &off, &lineoff, &linelen)) { if (linelen == 0) { break; } if (off > hdrsoff+hdrslen) { INFOP("!! overrun !!"); break; } if (nf_strncasecmp(pdata+hdrsoff+lineoff, "Transport:", 10) == 0) { rtsp_parse_transport(pdata+hdrsoff+lineoff, linelen, &expinfo); } } if (expinfo.loport == 0) { DEBUGP("no udp transports found\n"); continue; /* no udp transports found */ } DEBUGP("udp transport found, ports=(%d,%hu,%hu)\n", (int)expinfo.pbtype, expinfo.loport, expinfo.hiport); exp = ip_conntrack_expect_alloc(); if (!exp) { ret = NF_DROP; goto out; } //exp->seq = ntohl(tcph->seq) + hdrsoff; /* mark all the headers */ exp->master = ct; //exp.help.exp_rtsp_info.len = hdrslen; exp->tuple.src.ip = ct->tuplehash[!dir].tuple.src.ip; exp->mask.src.ip = 0xffffffff; exp->tuple.dst.ip = ct->tuplehash[dir].tuple.src.ip; exp->mask.dst.ip = 0xffffffff; exp->tuple.dst.u.udp.port = expinfo.loport; exp->mask.dst.u.udp.port = (expinfo.pbtype == pb_range) ? 0xfffe : 0xffff; exp->tuple.dst.protonum = IPPROTO_UDP; exp->mask.dst.protonum = 0xff; DEBUGP("expect_related %u.%u.%u.%u:%u-%u.%u.%u.%u:%u\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)); if (ip_nat_rtsp_hook) /* pass the request off to the nat helper */ ret = ip_nat_rtsp_hook(pskb, ctinfo, &expinfo, exp); else if (ip_conntrack_expect_related(exp) != 0) { INFOP("ip_conntrack_expect_related failed\n"); ip_conntrack_expect_free(exp); ret = NF_DROP; } goto out; } out: return ret; }
static unsigned int mms_data_fixup(struct sk_buff **pskb, enum ip_conntrack_info ctinfo, const struct ip_ct_mms_expect *ct_mms_info, struct ip_conntrack_expect *expect) { u_int32_t newip; struct ip_conntrack *ct = expect->master; struct iphdr *iph = (*pskb)->nh.iph; struct tcphdr *tcph = (void *) iph + iph->ihl * 4; char *data = (char *)tcph + tcph->doff * 4; int i, j, k, port; u_int16_t mms_proto; u_int32_t *mms_chunkLenLV = (u_int32_t *)(data + MMS_SRV_CHUNKLENLV_OFFSET); u_int32_t *mms_chunkLenLM = (u_int32_t *)(data + MMS_SRV_CHUNKLENLM_OFFSET); u_int32_t *mms_messageLength = (u_int32_t *)(data + MMS_SRV_MESSAGELENGTH_OFFSET); int zero_padding; char buffer[28]; /* "\\255.255.255.255\UDP\65635" * 2 (for unicode) */ char unicode_buffer[75]; /* 27*2 (unicode) + 20 + 1 */ char proto_string[6]; /* what was the protocol again ? */ mms_proto = expect->tuple.dst.protonum; sprintf(proto_string, "%u", mms_proto); DEBUGP("ip_nat_mms: mms_data_fixup: info (seq %u + %u) " "in %u, proto %s\n", expect->seq, ct_mms_info->len, ntohl(tcph->seq), mms_proto == IPPROTO_UDP ? "UDP" : mms_proto == IPPROTO_TCP ? "TCP":proto_string); newip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; expect->saved_proto.tcp.port = expect->tuple.dst.u.tcp.port; expect->expectfn = ip_nat_follow_master; /* Alter conntrack's expectations. */ for (port = ct_mms_info->port; port != 0; port++) { expect->tuple.dst.u.tcp.port = htons(port); if (ip_conntrack_expect_related(expect) == 0) { DEBUGP("ip_nat_mms: mms_data_fixup: using port %d\n", port); break; } } if (port == 0) { ip_conntrack_expect_free(expect); return NF_DROP; } sprintf(buffer, "\\\\%u.%u.%u.%u\\%s\\%u", NIPQUAD(newip), expect->tuple.dst.protonum == IPPROTO_UDP ? "UDP" : expect->tuple.dst.protonum == IPPROTO_TCP ? "TCP":proto_string, port); DEBUGP("ip_nat_mms: new unicode string=%s\n", buffer); memset(unicode_buffer, 0, sizeof(char)*75); for (i=0; i<strlen(buffer); ++i) *(unicode_buffer+i*2)=*(buffer+i); DEBUGP("ip_nat_mms: mms_data_fixup: padding: %u len: %u\n", ct_mms_info->padding, ct_mms_info->len); DEBUGP("ip_nat_mms: mms_data_fixup: offset: %u\n", MMS_SRV_UNICODE_STRING_OFFSET+ct_mms_info->len); DUMP_BYTES(data+MMS_SRV_UNICODE_STRING_OFFSET, 60); /* add end of packet to it */ for (j=0; j<ct_mms_info->padding; ++j) { DEBUGP("ip_nat_mms: mms_data_fixup: i=%u j=%u byte=%u\n", i, j, (u8)*(data+MMS_SRV_UNICODE_STRING_OFFSET+ct_mms_info->len+j)); *(unicode_buffer+i*2+j) = *(data+MMS_SRV_UNICODE_STRING_OFFSET+ct_mms_info->len+j); } /* pad with zeroes at the end ? see explanation of weird math below */ zero_padding = (8-(strlen(buffer)*2 + ct_mms_info->padding + 4)%8)%8; for (k=0; k<zero_padding; ++k) *(unicode_buffer+i*2+j+k)= (char)0; DEBUGP("ip_nat_mms: mms_data_fixup: zero_padding = %u\n", zero_padding); DEBUGP("ip_nat_mms: original=> chunkLenLV=%u chunkLenLM=%u " "messageLength=%u\n", *mms_chunkLenLV, *mms_chunkLenLM, *mms_messageLength); /* explanation, before I forget what I did: strlen(buffer)*2 + ct_mms_info->padding + 4 must be divisable by 8; divide by 8 and add 3 to compute the mms_chunkLenLM field, but note that things may have to be padded with zeroes to align by 8 bytes, hence we add 7 and divide by 8 to get the correct length */ *mms_chunkLenLM = (u_int32_t) (3+(strlen(buffer)*2+ct_mms_info->padding+11)/8); *mms_chunkLenLV = *mms_chunkLenLM+2; *mms_messageLength = *mms_chunkLenLV*8; DEBUGP("ip_nat_mms: modified=> chunkLenLV=%u chunkLenLM=%u" " messageLength=%u\n", *mms_chunkLenLV, *mms_chunkLenLM, *mms_messageLength); ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, ct_mms_info->offset, ct_mms_info->len + ct_mms_info->padding, unicode_buffer, strlen(buffer)*2 + ct_mms_info->padding + zero_padding); DUMP_BYTES(unicode_buffer, 60); return NF_ACCEPT; }
static int check_rpc_packet(const u_int32_t *data, int dir, struct ip_conntrack *ct, struct list_head request_p_list) { int ret = NF_ACCEPT; u_int32_t xid; struct request_p *req_p; struct ip_conntrack_expect *exp; /* Translstion's buffer for XDR */ u_int16_t port_buf; /* Get XID */ xid = *data; /* This does sanity checking on RPC payloads, * and permits only the RPC "get port" (3) * in authorised procedures in client * communications with the portmapper. */ /* perform direction dependant RPC work */ if (dir == IP_CT_DIR_ORIGINAL) { data += 5; /* Get RPC requestor */ if (IXDR_GET_INT32(data) != 3) { DEBUGP("RPC packet contains an invalid (non \"get\") requestor. [skip]\n"); return NF_ACCEPT; } DEBUGP("RPC packet contains a \"get\" requestor. [cont]\n"); data++; /* Jump Credentials and Verfifier */ data = data + IXDR_GET_INT32(data) + 2; data = data + IXDR_GET_INT32(data) + 2; /* Get RPC procedure */ DEBUGP("RPC packet contains procedure request [%u]. [cont]\n", (unsigned int)IXDR_GET_INT32(data)); /* Get RPC protocol and store against client parameters */ data = data + 2; alloc_request_p(xid, IXDR_GET_INT32(data), ct->tuplehash[dir].tuple.src.ip, ct->tuplehash[dir].tuple.src.u.all); DEBUGP("allocated RPC req_p for xid=%u proto=%u %u.%u.%u.%u:%u\n", xid, IXDR_GET_INT32(data), NIPQUAD(ct->tuplehash[dir].tuple.src.ip), ntohs(ct->tuplehash[dir].tuple.src.u.all)); DEBUGP("allocated RPC request for protocol %u. [done]\n", (unsigned int)IXDR_GET_INT32(data)); } else { /* Check for returning packet's stored counterpart */ req_p = LIST_FIND(&request_p_list_udp, request_p_cmp, struct request_p *, xid, ct->tuplehash[!dir].tuple.src.ip, ct->tuplehash[!dir].tuple.src.u.all); /* Drop unexpected packets */ if (!req_p) { DEBUGP("packet is not expected. [skip]\n"); return NF_ACCEPT; } /* Verifies if packet is really an RPC reply packet */ data++; if (IXDR_GET_INT32(data) != 1) { DEBUGP("packet is not a valid RPC reply. [skip]\n"); return NF_ACCEPT; } /* Is status accept? */ data++; if (IXDR_GET_INT32(data)) { DEBUGP("packet is not an RPC accept. [skip]\n"); return NF_ACCEPT; } /* Get Verifier length. Jump verifier */ data++; data = data + IXDR_GET_INT32(data) + 2; /* Is accpet status "success"? */ if (IXDR_GET_INT32(data)) { DEBUGP("packet is not an RPC accept status of success. [skip]\n"); return NF_ACCEPT; } /* Get server port number */ data++; port_buf = (u_int16_t) IXDR_GET_INT32(data); /* If a packet has made it this far then it deserves an * expectation ... if port == 0, then this service is * not going to be registered. */ if (port_buf) { DEBUGP("port found: %u\n", port_buf); exp = ip_conntrack_expect_alloc(); if (!exp) { ret = NF_DROP; goto out; } /* Watch out, Radioactive-Man! */ exp->tuple.src.ip = ct->tuplehash[!dir].tuple.src.ip; exp->tuple.dst.ip = ct->tuplehash[!dir].tuple.dst.ip; exp->mask.src.ip = 0xffffffff; exp->mask.dst.ip = 0xffffffff; switch (req_p->proto) { case IPPROTO_UDP: exp->tuple.src.u.udp.port = 0; exp->tuple.dst.u.udp.port = htons(port_buf); exp->tuple.dst.protonum = IPPROTO_UDP; exp->mask.src.u.udp.port = 0; exp->mask.dst.u.udp.port = htons(0xffff); exp->mask.dst.protonum = 0xff; break; case IPPROTO_TCP: exp->tuple.src.u.tcp.port = 0; exp->tuple.dst.u.tcp.port = htons(port_buf); exp->tuple.dst.protonum = IPPROTO_TCP; exp->mask.src.u.tcp.port = 0; exp->mask.dst.u.tcp.port = htons(0xffff); exp->mask.dst.protonum = 0xff; break; } exp->expectfn = NULL; exp->master = ct; DEBUGP("expect related ip %u.%u.%u.%u:0-%u.%u.%u.%u:%u proto=%u\n", NIPQUAD(exp->tuple.src.ip), NIPQUAD(exp->tuple.dst.ip), port_buf, req_p->proto); DEBUGP("expect related mask %u.%u.%u.%u:0-%u.%u.%u.%u:65535 proto=%u\n", NIPQUAD(exp->mask.src.ip), NIPQUAD(exp->mask.dst.ip), exp->mask.dst.protonum); if (ip_conntrack_expect_related(exp) != 0) { ip_conntrack_expect_free(exp); ret = NF_DROP; } } out: req_cl(req_p); DEBUGP("packet evaluated. [expect]\n"); } return ret; }