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; }
static int timeout_ct_or_exp(const struct ip_conntrack_tuple *t) { struct ip_conntrack_tuple_hash *h; struct ip_conntrack_expect *exp; DEBUGP("trying to timeout ct or exp for tuple "); DUMP_TUPLE(t); h = __ip_conntrack_find(t, NULL); if (h) { struct ip_conntrack *sibling = 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; /* refresh_acct will not modify counters if skb == NULL */ ip_ct_refresh_acct(sibling, 0, NULL, 0); return 1; } else { exp = __ip_conntrack_exp_find(t); if (exp) { DEBUGP("unexpect_related of expect %p\n", exp); ip_conntrack_unexpect_related(exp); return 1; } } return 0; }
static void pptp_expectfn(struct ip_conntrack *ct, struct ip_conntrack_expect *exp) { 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 */ if (!ip_nat_pptp_hook_expectfn) { struct ip_conntrack_tuple inv_t; struct ip_conntrack_expect *exp_other; /* obviously this tuple inversion only works until you do NAT */ invert_tuplepr(&inv_t, &exp->tuple); DEBUGP("trying to unexpect other dir: "); DUMP_TUPLE(&inv_t); exp_other = __ip_conntrack_exp_find(&inv_t); if (exp_other) { /* delete other expectation. */ DEBUGP("found\n"); ip_conntrack_unexpect_related(exp_other); } else { DEBUGP("not found\n"); } } else { /* we need more than simple inversion */ ip_nat_pptp_hook_expectfn(ct, exp); } }
static int destroy_sibling_or_exp(const struct ip_conntrack_tuple *t) { struct ip_conntrack_tuple_hash *h; struct ip_conntrack_expect *exp; DEBUGP("trying to timeout ct or exp for tuple "); DUMP_TUPLE(t); h = ip_conntrack_find_get(t, NULL); if (h) { struct ip_conntrack *sibling = 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; if (del_timer(&sibling->timeout)) sibling->timeout.function((unsigned long)sibling); ip_conntrack_put(sibling); return 1; } else { exp = ip_conntrack_expect_find(t); if (exp) { DEBUGP("unexpect_related of expect %p\n", exp); ip_conntrack_unexpect_related(exp); ip_conntrack_expect_put(exp); return 1; } } return 0; }
/* 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; }
static unsigned int autofw_nat_expected(struct sk_buff **pskb, unsigned int hooknum, struct ip_conntrack *ct, struct ip_nat_info *info) { struct ip_nat_multi_range mr; u_int32_t newdstip, newsrcip, newip; u_int16_t port; struct ip_conntrack *master = master_ct(ct); IP_NF_ASSERT(info); IP_NF_ASSERT(master); IP_NF_ASSERT(!(info->initialized & (1<<HOOK2MANIP(hooknum)))); DEBUGP("autofw_nat_expected: got "); DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); LOCK_BH(&ip_autofw_lock); port = ntohs(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u.all); newdstip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip; #ifdef NEW_PORT_TRIG newsrcip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip; #else newsrcip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip; #endif if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC) newip = newsrcip; else { if (port < ntohs(ct->master->help.exp_autofw_info.dport[0]) || port > ntohs(ct->master->help.exp_autofw_info.dport[1])) { UNLOCK_BH(&ip_autofw_lock); return NF_DROP; } newip = newdstip; } mr.rangesize = 1; /* We don't want to manip the per-protocol, just the IPs... */ mr.range[0].flags = IP_NAT_RANGE_MAP_IPS; mr.range[0].min_ip = mr.range[0].max_ip = newip; /* ... unless we're doing a MANIP_DST, in which case, make sure we map to the correct port */ port -= ntohs(ct->master->help.exp_autofw_info.dport[0]); port += ntohs(ct->master->help.exp_autofw_info.to[0]); if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_DST) { mr.range[0].flags |= IP_NAT_RANGE_PROTO_SPECIFIED; mr.range[0].min = mr.range[0].max = ((union ip_conntrack_manip_proto) { htons(port) });
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 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; }
/* Called when a new connection for this protocol found. */ static int icmp_new(struct ip_conntrack *conntrack, const struct sk_buff *skb) { static const u_int8_t valid_new[] = { [ICMP_ECHO] = 1, [ICMP_TIMESTAMP] = 1, [ICMP_INFO_REQUEST] = 1, [ICMP_ADDRESS] = 1 }; if (conntrack->tuplehash[0].tuple.dst.u.icmp.type >= sizeof(valid_new) || !valid_new[conntrack->tuplehash[0].tuple.dst.u.icmp.type]) { /* Can't create a new ICMP `conn' with this. */ DEBUGP("icmp: can't create new conn with type %u\n", conntrack->tuplehash[0].tuple.dst.u.icmp.type); DUMP_TUPLE(&conntrack->tuplehash[0].tuple); return 0; } atomic_set(&conntrack->proto.icmp.count, 0); return 1; }
static void pptp_nat_expected(struct ip_conntrack *ct, struct ip_conntrack_expect *exp) { struct ip_conntrack *master = ct->master; struct ip_conntrack_expect *other_exp; struct ip_conntrack_tuple t; struct ip_ct_pptp_master *ct_pptp_info; struct ip_nat_pptp *nat_pptp_info; struct ip_nat_range range; ct_pptp_info = &master->help.ct_pptp_info; nat_pptp_info = &master->nat.help.nat_pptp_info; /* And here goes the grand finale of corrosion... */ if (exp->dir == IP_CT_DIR_ORIGINAL) { DEBUGP("we are PNS->PAC\n"); /* therefore, build tuple for PAC->PNS */ t.src.ip = master->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip; t.src.u.gre.key = htons(master->help.ct_pptp_info.pac_call_id); t.dst.ip = master->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; t.dst.u.gre.key = htons(master->help.ct_pptp_info.pns_call_id); t.dst.protonum = IPPROTO_GRE; } else { DEBUGP("we are PAC->PNS\n"); /* build tuple for PNS->PAC */ t.src.ip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip; t.src.u.gre.key = htons(master->nat.help.nat_pptp_info.pns_call_id); t.dst.ip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip; t.dst.u.gre.key = htons(master->nat.help.nat_pptp_info.pac_call_id); t.dst.protonum = IPPROTO_GRE; } DEBUGP("trying to unexpect other dir: \n"); DUMP_TUPLE(&t); other_exp = __ip_conntrack_exp_find(&t); if (other_exp) { ip_conntrack_unexpect_related(other_exp); DEBUGP("success\n"); } else { DEBUGP("not found!\n"); } /* This must be a fresh one. */ BUG_ON(ct->status & IPS_NAT_DONE_MASK); /* Change src to where master sends to */ range.flags = IP_NAT_RANGE_MAP_IPS; range.min_ip = range.max_ip = ct->master->tuplehash[!exp->dir].tuple.dst.ip; if (exp->dir == IP_CT_DIR_ORIGINAL) { range.flags |= IP_NAT_RANGE_PROTO_SPECIFIED; range.min = range.max = exp->saved_proto; } /* hook doesn't matter, but it has to do source manip */ ip_nat_setup_info(ct, &range, NF_IP_POST_ROUTING); /* For DST manip, map port here to where it's expected. */ range.flags = IP_NAT_RANGE_MAP_IPS; range.min_ip = range.max_ip = ct->master->tuplehash[!exp->dir].tuple.src.ip; //sam add +++ 2008.10.03 for pptp passthrough can not work. ct->tuplehash[!exp->dir].tuple.dst.u.gre.key = t.dst.u.gre.key; //sam add --- if (exp->dir == IP_CT_DIR_REPLY) { range.flags |= IP_NAT_RANGE_PROTO_SPECIFIED; range.min = range.max = exp->saved_proto; } /* hook doesn't matter, but it has to do destination manip */ ip_nat_setup_info(ct, &range, NF_IP_PRE_ROUTING); }
/* 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 h225_nat_expected(struct sk_buff **pskb, unsigned int hooknum, struct ip_conntrack *ct, struct ip_nat_info *info) { struct ip_nat_multi_range mr; u_int32_t newdstip, newsrcip, newip; u_int16_t port; struct ip_ct_h225_expect *exp_info; struct ip_ct_h225_master *master_info; struct ip_conntrack *master = master_ct(ct); unsigned int is_h225, ret; IP_NF_ASSERT(info); IP_NF_ASSERT(master); IP_NF_ASSERT(!(info->initialized & (1<<HOOK2MANIP(hooknum)))); DEBUGP("h225_nat_expected: We have a connection!\n"); master_info = &ct->master->expectant->help.ct_h225_info; exp_info = &ct->master->help.exp_h225_info; LOCK_BH(&ip_h323_lock); DEBUGP("master: "); DUMP_TUPLE(&master->tuplehash[IP_CT_DIR_ORIGINAL].tuple); DUMP_TUPLE(&master->tuplehash[IP_CT_DIR_REPLY].tuple); DEBUGP("conntrack: "); DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); if (exp_info->dir == IP_CT_DIR_ORIGINAL) { /* Make connection go to the client. */ newdstip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip; newsrcip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip; DEBUGP("h225_nat_expected: %u.%u.%u.%u->%u.%u.%u.%u (to client)\n", NIPQUAD(newsrcip), NIPQUAD(newdstip)); } else { /* Make the connection go to the server */ newdstip = master->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip; newsrcip = master->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip; DEBUGP("h225_nat_expected: %u.%u.%u.%u->%u.%u.%u.%u (to server)\n", NIPQUAD(newsrcip), NIPQUAD(newdstip)); } port = exp_info->port; is_h225 = master_info->is_h225 == H225_PORT; UNLOCK_BH(&ip_h323_lock); if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC) newip = newsrcip; else newip = newdstip; DEBUGP("h225_nat_expected: IP to %u.%u.%u.%u\n", NIPQUAD(newip)); mr.rangesize = 1; /* We don't want to manip the per-protocol, just the IPs... */ mr.range[0].flags = IP_NAT_RANGE_MAP_IPS; mr.range[0].min_ip = mr.range[0].max_ip = newip; /* ... unless we're doing a MANIP_DST, in which case, make sure we map to the correct port */ if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_DST) { mr.range[0].flags |= IP_NAT_RANGE_PROTO_SPECIFIED; mr.range[0].min = mr.range[0].max = ((union ip_conntrack_manip_proto) { .tcp = { port } });