static int tftp_help(struct sk_buff **pskb, struct ip_conntrack *ct, enum ip_conntrack_info ctinfo) { struct tftphdr _tftph, *tfh; struct ip_conntrack_expect *exp; unsigned int ret = NF_ACCEPT; tfh = skb_header_pointer(*pskb, (*pskb)->nh.iph->ihl*4+sizeof(struct udphdr), sizeof(_tftph), &_tftph); if (tfh == NULL) return NF_ACCEPT; switch (ntohs(tfh->opcode)) { /* RRQ and WRQ works the same way */ case TFTP_OPCODE_READ: case TFTP_OPCODE_WRITE: DEBUGP(""); DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_REPLY].tuple); exp = ip_conntrack_expect_alloc(ct); if (exp == NULL) return NF_DROP; exp->tuple = ct->tuplehash[IP_CT_DIR_REPLY].tuple; exp->mask.src.ip = 0xffffffff; exp->mask.src.u.udp.port = 0; exp->mask.dst.ip = 0xffffffff; exp->mask.dst.u.udp.port = 0xffff; exp->mask.dst.protonum = 0xff; exp->expectfn = NULL; exp->flags = 0; DEBUGP("expect: "); DUMP_TUPLE(&exp->tuple); DUMP_TUPLE(&exp->mask); if (ip_nat_tftp_hook) ret = ip_nat_tftp_hook(pskb, ctinfo, exp); else if (ip_conntrack_expect_related(exp) != 0) ret = NF_DROP; ip_conntrack_expect_put(exp); break; case TFTP_OPCODE_DATA: case TFTP_OPCODE_ACK: DEBUGP("Data/ACK opcode\n"); break; case TFTP_OPCODE_ERROR: DEBUGP("Error opcode\n"); break; default: DEBUGP("Unknown opcode\n"); } return NF_ACCEPT; }
/* * Handle an incoming packet. */ static int ipsec_help(struct sk_buff *skb, struct ip_conntrack *ct, enum ip_conntrack_info ctinfo) { struct ip_conntrack_expect *exp; #if IPSEC_CONNTRACK_DEBUG IPSEC_DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); IPSEC_DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_REPLY].tuple); printk (KERN_DEBUG "Received packet Direction %s\n", ( ctinfo >= IP_CT_IS_REPLY ) ? "REPLY" : "ORIG"); #endif /* * We got to trust the netfilter to call this routine only on * receiving the first packet without an expectation. * There will be at least one more packet for this tuple * so set our expectation for it here. */ /* Allocate expectation which will be inserted */ exp = ip_conntrack_expect_alloc(ct); if (exp == NULL) { goto out; } memset(exp, 0, sizeof(exp)); exp->tuple = ct->tuplehash[IP_CT_DIR_REPLY].tuple; exp->mask.src.ip = 0xFFFFFFFF; exp->mask.dst.ip = 0xFFFFFFFF; exp->mask.dst.u.udp.port = ntohs(IPSEC_UDP_PORT); exp->mask.dst.protonum = 0xFFFF; exp->expectfn = NULL; #if IPSEC_CONNTRACK_DEBUG printk( KERN_DEBUG "%s:%s expect: 0x%x 0x%x" " %u.%u.%u.%u:%u\n <--> %u.%u.%u.%u:%u\n", __FILE__, __FUNCTION__, ntohl(isakmph->initcookie[0]), ntohl(isakmph->initcookie[1]), NIPQUAD(iph->saddr), udph->source, NIPQUAD(iph->daddr), udph->dest); #endif if( ip_nat_ipsec_hook ) ip_nat_ipsec_hook(ct , exp , ct->nat.info , ctinfo , hooknum , &skb); else ip_conntrack_expect_related(exp); out: return NF_ACCEPT; }
/* FIXME: This should be in userspace. Later. */ static int help(struct sk_buff *skb, struct ip_conntrack *ct, enum ip_conntrack_info ctinfo) { int dir = CTINFO2DIR(ctinfo); struct tcphdr tcph; struct ip_conntrack_expect exp; int i; /* ** We only do this for the new packet */ // printk("wm_help: Conntrackinfo = %u dir=%d\n", ctinfo,dir); if ( ctinfo != IP_CT_NEW) return NF_ACCEPT; if ( dir != 0) return NF_ACCEPT; //DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); //DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_REPLY].tuple); if (skb_copy_bits(skb, skb->nh.iph->ihl*4, &tcph, sizeof(tcph)) != 0) return NF_ACCEPT; LOCK_BH(&ip_wm_lock); //for(i = WMMIN; i <= WMMIN; i++) { for(i = WMMIN; i <= WMMAX; i++) { memset(&exp, 0, sizeof(exp)); exp.tuple = ct->tuplehash[IP_CT_DIR_REPLY].tuple; exp.tuple.dst.u.udp.port = htons(i); 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 = 0xffff; exp.expectfn = NULL; exp.seq = ntohl(tcph.seq); DEBUGP("wm_help: expect: "); DUMP_TUPLE(&exp.tuple); DUMP_TUPLE(&exp.mask); ip_conntrack_expect_related(&exp, ct); } UNLOCK_BH(&ip_wm_lock); 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_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 int tftp_help(const struct iphdr *iph, size_t len, struct ip_conntrack *ct, enum ip_conntrack_info ctinfo) { struct udphdr *udph = (void *)iph + iph->ihl * 4; struct tftphdr *tftph = (void *)udph + 8; struct ip_conntrack_expect exp; switch (ntohs(tftph->opcode)) { /* RRQ and WRQ works the same way */ case TFTP_OPCODE_READ: case TFTP_OPCODE_WRITE: DEBUGP(""); DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_REPLY].tuple); memset(&exp, 0, sizeof(exp)); exp.tuple = ct->tuplehash[IP_CT_DIR_REPLY].tuple; exp.mask.src.ip = 0xffffffff; exp.mask.dst.ip = 0xffffffff; exp.mask.dst.u.udp.port = 0xffff; exp.mask.dst.protonum = 0xffff; exp.expectfn = NULL; DEBUGP("expect: "); DUMP_TUPLE(&exp.tuple); DUMP_TUPLE(&exp.mask); ip_conntrack_expect_related(ct, &exp); break; case TFTP_OPCODE_DATA: case TFTP_OPCODE_ACK: DEBUGP("Data/ACK opcode\n"); break; case TFTP_OPCODE_ERROR: DEBUGP("Error opcode\n"); break; default: DEBUGP("Unknown opcode\n"); } return NF_ACCEPT; }
static int nat_t120(struct sk_buff **pskb, struct ip_conntrack *ct, enum ip_conntrack_info ctinfo, unsigned char **data, int dataoff, H245_TransportAddress * addr, u_int16_t port, struct ip_conntrack_expect *exp) { int dir = CTINFO2DIR(ctinfo); u_int16_t nated_port = port; /* Set expectations for NAT */ exp->saved_proto.tcp.port = exp->tuple.dst.u.tcp.port; exp->expectfn = ip_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 (ip_conntrack_expect_related(exp) == 0) break; } if (nated_port == 0) { /* No port available */ if (net_ratelimit()) printk("ip_nat_h323: out of TCP ports\n"); return 0; } /* Modify signal */ if (set_h245_addr(pskb, data, dataoff, addr, ct->tuplehash[!dir].tuple.dst.ip, nated_port) < 0) { ip_conntrack_unexpect_related(exp); return -1; } DEBUGP("ip_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; }
/* 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 ip_conntrack_expect *exp, const char *dptr) { struct ip_conntrack *ct = exp->master; enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); u_int32_t newip; u_int16_t port; DEBUGP("ip_nat_sdp():\n"); /* Connection will come from reply */ newip = ct->tuplehash[!dir].tuple.dst.ip; exp->tuple.dst.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_follow_master; /* 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 (ip_conntrack_expect_related(exp) == 0) break; } if (port == 0) return NF_DROP; if (!mangle_sdp(pskb, ctinfo, ct, newip, port, dptr)) { 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 tftp_help(struct sk_buff *skb, struct ip_conntrack *ct, enum ip_conntrack_info ctinfo) { struct tftphdr tftph; struct ip_conntrack_expect exp; if (skb_copy_bits(skb, skb->nh.iph->ihl * 4 + sizeof(struct udphdr), &tftph, sizeof(tftph)) != 0) return NF_ACCEPT; switch (ntohs(tftph.opcode)) { /* RRQ and WRQ works the same way */ case TFTP_OPCODE_READ: case TFTP_OPCODE_WRITE: DEBUGP(""); DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_REPLY].tuple); memset(&exp, 0, sizeof(exp)); exp.tuple = ct->tuplehash[IP_CT_DIR_REPLY].tuple; exp.mask.src.ip = 0xffffffff; exp.mask.dst.ip = 0xffffffff; exp.mask.dst.u.udp.port = 0xffff; exp.mask.dst.protonum = 0xffff; exp.expectfn = NULL; DEBUGP("expect: "); DUMP_TUPLE(&exp.tuple); DUMP_TUPLE(&exp.mask); ip_conntrack_expect_related(ct, &exp); break; default: DEBUGP("Unknown opcode\n"); } return NF_ACCEPT; }
/* FIXME: This should be in userspace. Later. */ static int help(const struct iphdr *iph, size_t len, struct ip_conntrack *ct, enum ip_conntrack_info ctinfo) { //int dir = CTINFO2DIR(ctinfo); //struct udphdr *udph = (void *)iph + iph->ihl * 4; struct ip_conntrack_expect exp; int i; u_int16_t port; u_int16_t outproto; u_int16_t outport; //DEBUGP("pt_help: Conntrackinfo = %u dir=%d\n", ctinfo,dir); DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_REPLY].tuple); /* ** We need to search the pt_record[] to see what inports need to be triggered ** based on the tuple's destination protonum and port number ** we know from the ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple the protonum and port number ** ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum ** ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u.all */ //dump_pt_record(); outproto = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum; outport = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u.all; //printk("ip_conntrack_pt: outproto=%d outport=%hu\n",outproto,ntohs(outport)); /*convert proto */ if (outproto == IPPROTO_TCP) outproto=1; else if (outproto == IPPROTO_UDP) outproto=2; else outproto=0; memset(&exp, 0, sizeof(exp)); exp.tuple = ct->tuplehash[IP_CT_DIR_REPLY].tuple; //dump_pt_record(); for(i = 0; i < entries; i++) { /* we only call expect_related for entries match our outgoing port(outport) */ if ((outproto == pt_record[i].outproto || pt_record[i].outproto== 0) && (ntohs(outport) >= pt_record[i].outport[0] && ntohs(outport) <= pt_record[i].outport[1])) { for (port = pt_record[i].inport[0]; port <= pt_record[i].inport[1];port++) { //printk("******** ip_conntrack_pt: help port=%d \n",port); if ( pt_record[i].inproto == 1 ) { //printk("******** ip_conntrack_pt: help TCP port=%d \n",port); exp.tuple.dst.protonum = IPPROTO_TCP; exp.tuple.dst.u.tcp.port = htons(port); //exp.mask.src.ip = 0xffffffff; exp.mask.src.ip = 0; exp.mask.src.u.all = 0; exp.mask.dst.ip = 0xffffffff; exp.mask.dst.u.tcp.port = 0xffff; exp.mask.dst.protonum = 0xffff; } else if ( pt_record[i].inproto == 2 ) { //printk("******** ip_conntrack_pt: help UDP port=%d \n",port); exp.tuple.dst.u.udp.port = htons(port); exp.tuple.dst.protonum = IPPROTO_UDP; //exp.mask.src.ip = 0xffffffff; exp.mask.src.ip = 0; exp.mask.src.u.all = 0; exp.mask.dst.ip = 0xffffffff; exp.mask.dst.u.udp.port = 0xffff; exp.mask.dst.protonum = 0xffff; } else { //printk("******** ip_conntrack_pt: help BOTH port=%x \n",port); exp.tuple.dst.u.all = htons(port); //exp.mask.src.ip = 0xffffffff; exp.mask.src.ip = 0; exp.mask.src.u.all = 0; exp.mask.dst.ip = 0xffffffff; exp.mask.dst.u.all = 0xffff; exp.mask.dst.protonum = 0; } exp.expectfn = NULL; //DEBUGP("expect: "); //DUMP_TUPLE(&exp.tuple); //DUMP_TUPLE(&exp.mask); ip_conntrack_expect_related(ct, &exp); } /* end of for */ } /* end of if */ } return NF_ACCEPT; }
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; }
/* expect GRE connections (PNS->PAC and PAC->PNS direction) */ static inline int exp_gre(struct ip_conntrack *master, u_int32_t seq, u_int16_t callid, u_int16_t peer_callid) { struct ip_conntrack_expect exp; struct ip_conntrack_tuple inv_tuple; memset(&exp, 0, sizeof(exp)); /* tuple in original direction, PNS->PAC */ exp.tuple.src.ip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip; exp.tuple.src.u.gre.key = htonl(ntohs(peer_callid)); exp.tuple.dst.ip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip; exp.tuple.dst.u.gre.key = htonl(ntohs(callid)); exp.tuple.dst.u.gre.protocol = __constant_htons(GRE_PROTOCOL_PPTP); exp.tuple.dst.u.gre.version = GRE_VERSION_PPTP; exp.tuple.dst.protonum = IPPROTO_GRE; exp.mask.src.ip = 0xffffffff; exp.mask.src.u.all = 0; exp.mask.dst.u.all = 0; exp.mask.dst.u.gre.key = 0xffffffff; exp.mask.dst.u.gre.version = 0xff; exp.mask.dst.u.gre.protocol = 0xffff; exp.mask.dst.ip = 0xffffffff; exp.mask.dst.protonum = 0xffff; exp.seq = seq; exp.expectfn = pptp_expectfn; exp.help.exp_pptp_info.pac_call_id = ntohs(callid); exp.help.exp_pptp_info.pns_call_id = ntohs(peer_callid); DEBUGP("calling expect_related "); DUMP_TUPLE_RAW(&exp.tuple); /* Add GRE keymap entries */ if (ip_ct_gre_keymap_add(&exp, &exp.tuple, 0) != 0) return 1; invert_tuplepr(&inv_tuple, &exp.tuple); if (ip_ct_gre_keymap_add(&exp, &inv_tuple, 1) != 0) { ip_ct_gre_keymap_destroy(&exp); return 1; } if (ip_conntrack_expect_related(master, &exp) != 0) { ip_ct_gre_keymap_destroy(&exp); DEBUGP("cannot expect_related()\n"); return 1; } /* tuple in reply direction, PAC->PNS */ exp.tuple.src.ip = master->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip; exp.tuple.src.u.gre.key = htonl(ntohs(callid)); exp.tuple.dst.ip = master->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; exp.tuple.dst.u.gre.key = htonl(ntohs(peer_callid)); DEBUGP("calling expect_related "); DUMP_TUPLE_RAW(&exp.tuple); /* Add GRE keymap entries */ ip_ct_gre_keymap_add(&exp, &exp.tuple, 0); invert_tuplepr(&inv_tuple, &exp.tuple); ip_ct_gre_keymap_add(&exp, &inv_tuple, 1); /* FIXME: cannot handle error correctly, since we need to free * the above keymap :( */ if (ip_conntrack_expect_related(master, &exp) != 0) { /* free the second pair of keypmaps */ ip_ct_gre_keymap_destroy(&exp); DEBUGP("cannot expect_related():\n"); return 1; } return 0; }
/* expect GRE connection in PNS->PAC direction */ static inline int exp_gre(struct ip_conntrack *master, u_int32_t seq, u_int16_t callid, u_int16_t peer_callid) { struct ip_conntrack_expect exp; struct ip_conntrack_tuple inv_tuple; memset(&exp, 0, sizeof(exp)); /* tuple in original direction, PNS->PAC */ exp.tuple.src.ip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip; exp.tuple.src.u.gre.key = htonl(ntohs(peer_callid)); exp.tuple.dst.ip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip; exp.tuple.dst.u.gre.key = htonl(ntohs(callid)); exp.tuple.dst.u.gre.protocol = __constant_htons(GRE_PROTOCOL_PPTP); exp.tuple.dst.u.gre.version = GRE_VERSION_PPTP; exp.tuple.dst.protonum = IPPROTO_GRE; exp.mask.src.ip = 0xffffffff; exp.mask.src.u.all = 0; exp.mask.dst.u.all = 0; exp.mask.dst.u.gre.key = 0xffffffff; exp.mask.dst.u.gre.version = 0xff; exp.mask.dst.u.gre.protocol = 0xffff; exp.mask.dst.ip = 0xffffffff; exp.mask.dst.protonum = 0xffff; exp.seq = seq; exp.expectfn = pptp_expectfn; exp.help.exp_pptp_info.pac_call_id = ntohs(callid); exp.help.exp_pptp_info.pns_call_id = ntohs(peer_callid); DEBUGP("calling expect_related "); DUMP_TUPLE_RAW(&exp.tuple); /* Add GRE keymap entries */ ip_ct_gre_keymap_add(&exp, &exp.tuple, 0); invert_tuplepr(&inv_tuple, &exp.tuple); ip_ct_gre_keymap_add(&exp, &inv_tuple, 1); ip_conntrack_expect_related(master, &exp); /* tuple in reply direction, PAC->PNS */ exp.tuple.src.ip = master->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip; exp.tuple.src.u.gre.key = htonl(ntohs(callid)); exp.tuple.dst.ip = master->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; exp.tuple.dst.u.gre.key = htonl(ntohs(peer_callid)); DEBUGP("calling expect_related "); DUMP_TUPLE_RAW(&exp.tuple); /* Add GRE keymap entries */ ip_ct_gre_keymap_add(&exp, &exp.tuple, 0); invert_tuplepr(&inv_tuple, &exp.tuple); ip_ct_gre_keymap_add(&exp, &inv_tuple, 1); ip_conntrack_expect_related(master, &exp); return 0; }
static int nat_rtp_rtcp(struct sk_buff **pskb, struct ip_conntrack *ct, enum ip_conntrack_info ctinfo, unsigned char **data, int dataoff, H245_TransportAddress * addr, u_int16_t port, u_int16_t rtp_port, struct ip_conntrack_expect *rtp_exp, struct ip_conntrack_expect *rtcp_exp) { struct ip_ct_h323_master *info = &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 = ip_nat_follow_master; rtp_exp->dir = !dir; rtcp_exp->saved_proto.udp.port = rtcp_exp->tuple.dst.u.udp.port; rtcp_exp->expectfn = ip_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 = htons(info->rtp_port[i][dir]); rtcp_exp->tuple.dst.u.udp.port = htons(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("ip_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 (ip_conntrack_expect_related(rtp_exp) == 0) { rtcp_exp->tuple.dst.u.udp.port = htons(nated_port + 1); if (ip_conntrack_expect_related(rtcp_exp) == 0) break; ip_conntrack_unexpect_related(rtp_exp); } } if (nated_port == 0) { /* No port available */ if (net_ratelimit()) printk("ip_nat_h323: out of RTP ports\n"); return 0; } /* Modify signal */ if (set_h245_addr(pskb, data, dataoff, addr, ct->tuplehash[!dir].tuple.dst.ip, (port & 1) ? nated_port + 1 : nated_port) == 0) { /* Save ports */ info->rtp_port[i][dir] = rtp_port; info->rtp_port[i][!dir] = nated_port; } else { ip_conntrack_unexpect_related(rtp_exp); ip_conntrack_unexpect_related(rtcp_exp); return -1; } /* Success */ DEBUGP("ip_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("ip_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; }
/* 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(ct); 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_put(exp); ret = NF_DROP; } goto out; } out: return ret; }
static int help(struct sk_buff **pskb, struct ip_conntrack *ct, enum ip_conntrack_info ctinfo) { struct ts_state ts; struct ip_conntrack_expect *exp; unsigned int dataoff, start, stop, off, i; char pbuf[sizeof("65535")], *tmp; u_int16_t port, len; int ret = NF_ACCEPT; /* Only look at packets from the Amanda server */ if (CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) return NF_ACCEPT; /* increase the UDP timeout of the master connection as replies from * Amanda clients to the server can be quite delayed */ ip_ct_refresh(ct, *pskb, master_timeout * HZ); /* No data? */ dataoff = (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr); if (dataoff >= (*pskb)->len) { if (net_ratelimit()) printk("amanda_help: skblen = %u\n", (*pskb)->len); return NF_ACCEPT; } memset(&ts, 0, sizeof(ts)); start = skb_find_text(*pskb, dataoff, (*pskb)->len, search[SEARCH_CONNECT].ts, &ts); if (start == UINT_MAX) goto out; start += dataoff + search[SEARCH_CONNECT].len; memset(&ts, 0, sizeof(ts)); stop = skb_find_text(*pskb, start, (*pskb)->len, search[SEARCH_NEWLINE].ts, &ts); if (stop == UINT_MAX) goto out; stop += start; for (i = SEARCH_DATA; i <= SEARCH_INDEX; i++) { memset(&ts, 0, sizeof(ts)); off = skb_find_text(*pskb, start, stop, search[i].ts, &ts); if (off == UINT_MAX) continue; off += start + search[i].len; len = min_t(unsigned int, sizeof(pbuf) - 1, stop - off); if (skb_copy_bits(*pskb, off, pbuf, len)) break; pbuf[len] = '\0'; port = simple_strtoul(pbuf, &tmp, 10); len = tmp - pbuf; if (port == 0 || len > 5) break; exp = ip_conntrack_expect_alloc(ct); if (exp == NULL) { ret = NF_DROP; goto out; } exp->expectfn = NULL; exp->flags = 0; exp->tuple.src.ip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip; exp->tuple.src.u.tcp.port = 0; exp->tuple.dst.ip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip; exp->tuple.dst.protonum = IPPROTO_TCP; exp->tuple.dst.u.tcp.port = htons(port); exp->mask.src.ip = htonl(0xFFFFFFFF); exp->mask.src.u.tcp.port = 0; exp->mask.dst.ip = htonl(0xFFFFFFFF); exp->mask.dst.protonum = 0xFF; exp->mask.dst.u.tcp.port = htons(0xFFFF); if (ip_nat_amanda_hook) ret = ip_nat_amanda_hook(pskb, ctinfo, off - dataoff, len, exp); else if (ip_conntrack_expect_related(exp) != 0) ret = NF_DROP; ip_conntrack_expect_put(exp); } out: return ret; }
static int help(struct sk_buff **pskb, struct ip_conntrack *ct, enum ip_conntrack_info ctinfo) { struct ip_conntrack_expect *exp; char *data, *data_limit, *tmp; unsigned int dataoff, i; u_int16_t port, len; int ret = NF_ACCEPT; /* Only look at packets from the Amanda server */ if (CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) return NF_ACCEPT; /* increase the UDP timeout of the master connection as replies from * Amanda clients to the server can be quite delayed */ ip_ct_refresh(ct, *pskb, master_timeout * HZ); /* No data? */ dataoff = (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr); if (dataoff >= (*pskb)->len) { if (net_ratelimit()) printk("amanda_help: skblen = %u\n", (*pskb)->len); return NF_ACCEPT; } spin_lock_bh(&amanda_buffer_lock); skb_copy_bits(*pskb, dataoff, amanda_buffer, (*pskb)->len - dataoff); data = amanda_buffer; data_limit = amanda_buffer + (*pskb)->len - dataoff; *data_limit = '\0'; /* Search for the CONNECT string */ data = strstr(data, "CONNECT "); if (!data) goto out; data += strlen("CONNECT "); /* Only search first line. */ if ((tmp = strchr(data, '\n'))) *tmp = '\0'; for (i = 0; i < ARRAY_SIZE(conns); i++) { char *match = strstr(data, conns[i]); if (!match) continue; tmp = data = match + strlen(conns[i]); port = simple_strtoul(data, &data, 10); len = data - tmp; if (port == 0 || len > 5) break; exp = ip_conntrack_expect_alloc(ct); if (exp == NULL) { ret = NF_DROP; goto out; } exp->expectfn = NULL; exp->flags = 0; exp->tuple.src.ip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip; exp->tuple.src.u.tcp.port = 0; exp->tuple.dst.ip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip; exp->tuple.dst.protonum = IPPROTO_TCP; exp->tuple.dst.u.tcp.port = htons(port); exp->mask.src.ip = 0xFFFFFFFF; exp->mask.src.u.tcp.port = 0; exp->mask.dst.ip = 0xFFFFFFFF; exp->mask.dst.protonum = 0xFF; exp->mask.dst.u.tcp.port = 0xFFFF; if (ip_nat_amanda_hook) ret = ip_nat_amanda_hook(pskb, ctinfo, tmp - amanda_buffer, len, exp); else if (ip_conntrack_expect_related(exp) != 0) ret = NF_DROP; ip_conntrack_expect_put(exp); } out: spin_unlock_bh(&amanda_buffer_lock); 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; }
/* FIXME: This should be in userspace. Later. */ static int help(const struct iphdr *iph, size_t len, struct ip_conntrack *ct, enum ip_conntrack_info ctinfo) { /* tcplen not negative guarenteed by ip_conntrack_tcp.c */ struct tcphdr *tcph = (void *) iph + iph->ihl * 4; const char *data = (const char *) tcph + tcph->doff * 4; u_int32_t tcplen = len - iph->ihl * 4; int dir = CTINFO2DIR(ctinfo); struct ip_conntrack_expect expect, *exp = &expect; struct ip_ct_rsh_expect *exp_rsh_info = &exp->help.exp_rsh_info; u_int16_t port; int maxoctet; /* note that "maxoctet" is used to maintain sanity (8 was the * original array size used in rshd/glibc) -- is there a * vulnerability in rshd.c in the looped port *= 10? */ DEBUGP("entered\n"); /* bail if packet is not from RSH client */ if (dir == IP_CT_DIR_REPLY) return NF_ACCEPT; /* Until there's been traffic both ways, don't look in packets. */ if (ctinfo != IP_CT_ESTABLISHED && ctinfo != IP_CT_ESTABLISHED + IP_CT_IS_REPLY) { DEBUGP("Conntrackinfo = %u\n", ctinfo); return NF_ACCEPT; } /* Not whole TCP header? */ if (tcplen < sizeof(struct tcphdr) || tcplen < tcph->doff * 4) { DEBUGP("tcplen = %u\n", (unsigned) tcplen); return NF_ACCEPT; } /* Checksum invalid? Ignore. */ /* FIXME: Source route IP option packets --RR */ if (tcp_v4_check(tcph, tcplen, iph->saddr, iph->daddr, csum_partial((char *) tcph, tcplen, 0))) { DEBUGP("bad csum: %p %u %u.%u.%u.%u %u.%u.%u.%u\n", tcph, tcplen, NIPQUAD(iph->saddr), NIPQUAD(iph->daddr)); return NF_ACCEPT; } /* find the rsh stderr port */ maxoctet = 4; port = 0; for ( ; *data != 0 && maxoctet != 0; data++, maxoctet--) { if (*data < 0) return(1); if (*data == 0) break; if (*data < 48 || *data > 57) { DEBUGP("these aren't the packets you're looking for ..\n"); return NF_ACCEPT; } port = port * 10 + ( *data - 48 ); } /* dont relate sessions that try to expose the client */ DEBUGP("found port %u\n", port); if (port > range) { DEBUGP("skipping, expected port size is greater than range!\n"); return NF_ACCEPT; } LOCK_BH(&ip_rsh_lock); /* new(,related) connection is; * reply + dst (uint)port + src port (0:1023) */ memset(&expect, 0, sizeof(expect)); /* save some discovered data, in case someone ever wants to write * a NAT module for this bastard .. */ exp_rsh_info->port = port; DEBUGP("wrote info port=%u\n", exp_rsh_info->port); /* 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->tuple.src.u.tcp.port = 0; exp->tuple.dst.u.tcp.port = htons(exp_rsh_info->port); exp->tuple.dst.protonum = IPPROTO_TCP; exp->mask.src.ip = 0xffffffff; exp->mask.dst.ip = 0xffffffff; exp->mask.src.u.tcp.port = htons(rangemask); exp->mask.dst.u.tcp.port = htons(0xffff); exp->mask.dst.protonum = 0xffff; exp->expectfn = NULL; ip_conntrack_expect_related(ct, &expect); DEBUGP("expect related ip %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)); DEBUGP("expect related mask %u.%u.%u.%u:%u-%u.%u.%u.%u:%u\n", NIPQUAD(exp->mask.src.ip), ntohs(exp->mask.src.u.tcp.port), NIPQUAD(exp->mask.dst.ip), ntohs(exp->mask.dst.u.tcp.port)); UNLOCK_BH(&ip_rsh_lock); return NF_ACCEPT; }
/* FIXME: This should be in userspace. Later. */ static int help(struct sk_buff **pskb, struct ip_conntrack *ct, enum ip_conntrack_info ctinfo) { struct tcphdr _tcph, *th; char *data, *rb_ptr; int ret = NF_ACCEPT; int dir = CTINFO2DIR(ctinfo); struct ip_conntrack_expect *exp; unsigned int dataoff, datalen; u_int16_t port; int maxoctet = 4; /* note that "maxoctet" is used to maintain sanity (8 was the * original array size used in rshd/glibc) -- is there a * vulnerability in rshd.c in the looped port *= 10? */ DEBUGP("entered\n"); /* bail if packet is not from RSH client */ if (dir == IP_CT_DIR_REPLY) { return NF_ACCEPT; } /* Until there's been traffic both ways, don't look in packets. */ if (ctinfo != IP_CT_ESTABLISHED && ctinfo != IP_CT_ESTABLISHED + IP_CT_IS_REPLY) { DEBUGP("Conntrackinfo = %u\n", ctinfo); return NF_ACCEPT; } /* Not a full tcp header? */ th = skb_header_pointer(*pskb, (*pskb)->nh.iph->ihl*4, sizeof(_tcph), &_tcph); if (!th) { DEBUGP("rsh: skb_header_pointer null\n"); return NF_ACCEPT; } /* No data? */ dataoff = (*pskb)->nh.iph->ihl*4 + th->doff*4; if (dataoff >= (*pskb)->len) { return NF_ACCEPT; } datalen = (*pskb)->len - dataoff; spin_lock_bh(&rsh_buffer_lock); rb_ptr = skb_header_pointer(*pskb, dataoff, datalen, rsh_buffer); BUG_ON(rb_ptr == NULL); data = rb_ptr; DEBUGP("rsh: find rsh stderr port datalen %u\n",datalen); maxoctet = 5; port = 0; for ( ; *data != 0 && maxoctet != 0; data++, maxoctet--) { if (*data < 0) { ret = 1; goto out; } if (*data == 0) { break; } if (*data < 48 || *data > 57) { DEBUGP("these aren't the packets you're looking for ..\n"); ret = NF_ACCEPT; goto out; } port = port * 10 + ( *data - 48 ); } /* dont relate sessions that try to expose the client */ if (port == 0) { DEBUGP("skipping, port is 0!\n"); ret = NF_ACCEPT;goto out; } DEBUGP("found port %u\n", port); if (port > range) { DEBUGP("skipping, expected port size is greater than range!\n"); return NF_ACCEPT; } exp = ip_conntrack_expect_alloc(ct); if (!exp) { ret = NF_DROP; goto out; } /* new(,related) connection is; * reply + dst (uint)port + src port (0:1023) */ /* 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->tuple.src.u.tcp.port = 0; exp->tuple.dst.u.tcp.port = htons(port); exp->tuple.dst.protonum = IPPROTO_TCP; exp->mask.src.ip = 0xffffffff; exp->mask.dst.ip = 0xffffffff; exp->mask.src.u.tcp.port = htons(rangemask); exp->mask.dst.u.tcp.port = htons(0xffff); exp->mask.dst.protonum = 0xff; exp->expectfn = NULL; exp->master = ct; DEBUGP("expect related ip %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)); DEBUGP("expect related mask %u.%u.%u.%u:%u-%u.%u.%u.%u:%u\n", NIPQUAD(exp->mask.src.ip), ntohs(exp->mask.src.u.tcp.port), NIPQUAD(exp->mask.dst.ip), ntohs(exp->mask.dst.u.tcp.port)); if (ip_nat_rsh_hook) ret = ip_nat_rsh_hook(pskb, ctinfo, rb_ptr - data, exp); else if (ip_conntrack_expect_related(exp) != 0) { ret = NF_DROP; } ip_conntrack_expect_put(exp); out: spin_unlock_bh(&rsh_buffer_lock); return ret; }
static unsigned int help(struct sk_buff **pskb, enum ip_conntrack_info ctinfo, unsigned int matchoff, unsigned int matchlen, struct ip_conntrack_expect *exp) { u_int16_t port; unsigned int ret; /* "4294967296 65635 " */ char buffer[18]; DEBUGP("IRC_NAT: info (seq %u + %u) in %u\n", expect->seq, exp_irc_info->len, ntohl(tcph->seq)); /* Reply comes from server. */ exp->saved_proto.tcp.port = exp->tuple.dst.u.tcp.port; exp->dir = IP_CT_DIR_REPLY; /* 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) return NF_DROP; /* strlen("\1DCC CHAT chat AAAAAAAA P\1\n")=27 * strlen("\1DCC SCHAT chat AAAAAAAA P\1\n")=28 * strlen("\1DCC SEND F AAAAAAAA P S\1\n")=26 * strlen("\1DCC MOVE F AAAAAAAA P S\1\n")=26 * strlen("\1DCC TSEND F AAAAAAAA P S\1\n")=27 * AAAAAAAAA: bound addr (1.0.0.0==16777216, min 8 digits, * 255.255.255.255==4294967296, 10 digits) * P: bound port (min 1 d, max 5d (65635)) * F: filename (min 1 d ) * S: size (min 1 d ) * 0x01, \n: terminators */ /* AAA = "us", ie. where server normally talks to. */ sprintf(buffer, "%u %u", ntohl(exp->master->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip), port); DEBUGP("ip_nat_irc: Inserting '%s' == %u.%u.%u.%u, port %u\n", buffer, NIPQUAD(exp->tuple.src.ip), port); ret = ip_nat_mangle_tcp_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; spin_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(ct); 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_put(exp); ret = NF_DROP; } goto out; } } out: spin_unlock_bh(&quake3_buffer_lock); return ret; }
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; }