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; }
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 help(struct sk_buff **pskb, struct ip_conntrack *ct, enum ip_conntrack_info ctinfo) { unsigned int dataoff; struct tcphdr _tcph, *th; char *data, *data_limit, *ib_ptr; int dir = CTINFO2DIR(ctinfo); struct ip_conntrack_expect *exp; u32 seq; u_int32_t dcc_ip; u_int16_t dcc_port; int i, ret = NF_ACCEPT; char *addr_beg_p, *addr_end_p; DEBUGP("entered\n"); /* If packet is coming from IRC server */ 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 == NULL) return NF_ACCEPT; /* No data? */ dataoff = (*pskb)->nh.iph->ihl*4 + th->doff*4; if (dataoff >= (*pskb)->len) return NF_ACCEPT; spin_lock_bh(&irc_buffer_lock); ib_ptr = skb_header_pointer(*pskb, dataoff, (*pskb)->len - dataoff, irc_buffer); BUG_ON(ib_ptr == NULL); data = ib_ptr; data_limit = ib_ptr + (*pskb)->len - dataoff; /* strlen("\1DCC SENT t AAAAAAAA P\1\n")=24 * 5+MINMATCHLEN+strlen("t AAAAAAAA P\1\n")=14 */ while (data < (data_limit - (19 + MINMATCHLEN))) { if (memcmp(data, "\1DCC ", 5)) { data++; continue; } data += 5; /* we have at least (19+MINMATCHLEN)-5 bytes valid data left */ DEBUGP("DCC found in master %u.%u.%u.%u:%u %u.%u.%u.%u:%u...\n", NIPQUAD(iph->saddr), ntohs(th->source), NIPQUAD(iph->daddr), ntohs(th->dest)); for (i = 0; i < ARRAY_SIZE(dccprotos); i++) { if (memcmp(data, dccprotos[i], strlen(dccprotos[i]))) { /* no match */ continue; } DEBUGP("DCC %s detected\n", dccprotos[i]); data += strlen(dccprotos[i]); /* we have at least * (19+MINMATCHLEN)-5-dccprotos[i].matchlen bytes valid * data left (== 14/13 bytes) */ if (parse_dcc((char *)data, data_limit, &dcc_ip, &dcc_port, &addr_beg_p, &addr_end_p)) { /* unable to parse */ DEBUGP("unable to parse dcc command\n"); continue; } DEBUGP("DCC bound ip/port: %u.%u.%u.%u:%u\n", HIPQUAD(dcc_ip), dcc_port); /* dcc_ip can be the internal OR external (NAT'ed) IP * Tiago Sousa <*****@*****.**> */ if (ct->tuplehash[dir].tuple.src.ip != htonl(dcc_ip) && ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip != htonl(dcc_ip)) { if (net_ratelimit()) printk(KERN_WARNING "Forged DCC command from " "%u.%u.%u.%u: %u.%u.%u.%u:%u\n", NIPQUAD(ct->tuplehash[dir].tuple.src.ip), HIPQUAD(dcc_ip), dcc_port); continue; } exp = ip_conntrack_expect_alloc(ct); if (exp == NULL) { ret = NF_DROP; goto out; } /* save position of address in dcc string, * necessary for NAT */ DEBUGP("tcph->seq = %u\n", th->seq); seq = ntohl(th->seq) + (addr_beg_p - ib_ptr); /* We refer to the reverse direction ("!dir") * tuples here, because we're expecting * something in the other * direction. * Doesn't matter unless NAT is happening. */ exp->tuple = ((struct ip_conntrack_tuple) { { 0, { 0 } }, { ct->tuplehash[!dir].tuple.dst.ip, { .tcp = { htons(dcc_port) } }, IPPROTO_TCP }});
static int help(struct sk_buff *skb, struct ip_conntrack *ct, enum ip_conntrack_info ctinfo) { unsigned int dataoff; struct tcphdr tcph; char *data, *data_limit; int dir = CTINFO2DIR(ctinfo); struct ip_conntrack_expect *exp; struct ip_ct_irc_expect *exp_irc_info = NULL; u_int32_t dcc_ip; u_int16_t dcc_port; int i; char *addr_beg_p, *addr_end_p; DEBUGP("entered\n"); /* If packet is coming from IRC server */ 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? */ if (skb_copy_bits(skb, skb->nh.iph->ihl*4, &tcph, sizeof(tcph)) != 0) return NF_ACCEPT; /* No data? */ dataoff = skb->nh.iph->ihl*4 + tcph.doff*4; if (dataoff >= skb->len) return NF_ACCEPT; LOCK_BH(&ip_irc_lock); skb_copy_bits(skb, dataoff, irc_buffer, skb->len - dataoff); data = irc_buffer; data_limit = irc_buffer + skb->len - dataoff; /* strlen("\1DCC SENT t AAAAAAAA P\1\n")=24 * 5+MINMATCHLEN+strlen("t AAAAAAAA P\1\n")=14 */ while (data < (data_limit - (19 + MINMATCHLEN))) { if (memcmp(data, "\1DCC ", 5)) { data++; continue; } data += 5; /* we have at least (19+MINMATCHLEN)-5 bytes valid data left */ DEBUGP("DCC found in master %u.%u.%u.%u:%u %u.%u.%u.%u:%u...\n", NIPQUAD(iph->saddr), ntohs(tcph.source), NIPQUAD(iph->daddr), ntohs(tcph.dest)); for (i = 0; i < ARRAY_SIZE(dccprotos); i++) { if (memcmp(data, dccprotos[i], strlen(dccprotos[i]))) { /* no match */ continue; } DEBUGP("DCC %s detected\n", dccprotos[i]); data += strlen(dccprotos[i]); /* we have at least * (19+MINMATCHLEN)-5-dccprotos[i].matchlen bytes valid * data left (== 14/13 bytes) */ if (parse_dcc((char *)data, data_limit, &dcc_ip, &dcc_port, &addr_beg_p, &addr_end_p)) { /* unable to parse */ DEBUGP("unable to parse dcc command\n"); continue; } DEBUGP("DCC bound ip/port: %u.%u.%u.%u:%u\n", HIPQUAD(dcc_ip), dcc_port); /* dcc_ip can be the internal OR external (NAT'ed) IP * Tiago Sousa <*****@*****.**> */ if (ct->tuplehash[dir].tuple.src.ip != htonl(dcc_ip) && ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip != htonl(dcc_ip)) { if (net_ratelimit()) printk(KERN_WARNING "Forged DCC command from " "%u.%u.%u.%u: %u.%u.%u.%u:%u\n", NIPQUAD(ct->tuplehash[dir].tuple.src.ip), HIPQUAD(dcc_ip), dcc_port); continue; } exp = ip_conntrack_expect_alloc(); if (exp == NULL) goto out; exp_irc_info = &exp->help.exp_irc_info; /* save position of address in dcc string, * necessary for NAT */ DEBUGP("tcph->seq = %u\n", tcph.seq); exp->seq = ntohl(tcph.seq) + (addr_beg_p - irc_buffer); exp_irc_info->len = (addr_end_p - addr_beg_p); exp_irc_info->port = dcc_port; DEBUGP("wrote info seq=%u (ofs=%u), len=%d\n", exp->seq, (addr_end_p - _data), exp_irc_info->len); exp->tuple = ((struct ip_conntrack_tuple) { { 0, { 0 } }, { ct->tuplehash[dir].tuple.src.ip, { .tcp = { htons(dcc_port) } }, IPPROTO_TCP }});
/* 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; }
/* 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 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 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; }
/* FIXME: This should be in userspace. Later. */ static int help(struct sk_buff **pskb, struct ip_conntrack *ct, enum ip_conntrack_info ctinfo) { int ret = NF_DROP; struct tcphdr _tcph, *th; char *data, *mb_ptr; unsigned int datalen, dataoff; //struct tcphdr *tcph = (void *)iph + iph->ihl * 4; //unsigned int tcplen = len - iph->ihl * 4; //unsigned int datalen = tcplen - tcph->doff * 4; int dir = CTINFO2DIR(ctinfo); struct ip_conntrack_expect *exp; struct ip_ct_mms_expect _emmi, *exp_mms_info = &_emmi; u_int32_t mms_ip; u_int16_t mms_proto; char mms_proto_string[8]; u_int16_t mms_port; char *mms_string_b, *mms_string_e, *mms_padding_e; /* 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("ip_conntrack_mms: Conntrackinfo = %u\n", ctinfo); return NF_ACCEPT; } /* Not whole TCP header? */ th = skb_header_pointer(*pskb, (*pskb)->nh.iph->ihl*4, sizeof(_tcph), &_tcph); if (!th) return NF_ACCEPT; /* No data ? */ dataoff = (*pskb)->nh.iph->ihl*4 + th->doff*4; datalen = (*pskb)->len - dataoff; if (dataoff >= (*pskb)->len) return NF_ACCEPT; LOCK_BH(&mms_buffer_lock); mb_ptr = skb_header_pointer(*pskb, dataoff, (*pskb)->len - dataoff, mms_buffer); BUG_ON(mb_ptr == NULL); data = mb_ptr; #if 0 /* 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("mms_help: bad csum: %p %u %u.%u.%u.%u %u.%u.%u.%u\n", tcph, tcplen, NIPQUAD(iph->saddr), NIPQUAD(iph->daddr)); return NF_ACCEPT; } #endif /* Only look at packets with 0x00030002/196610 on bytes 36->39 of TCP * payload */ /* FIXME: There is an issue with only looking at this packet: before * this packet, the client has already sent a packet to the server with * the server's hostname according to the client (think of it as the * "Host: " header in HTTP/1.1). The server will break the connection * if this doesn't correspond to its own host header. The client can * also connect to an IP address; if it's the server's IP address, it * will not break the connection. When doing DNAT on a connection where * the client uses a server's IP address, the nat module should detect * this and change this string accordingly to the DNATed address. This * should probably be done by checking for an IP address, then storing * it as a member of struct ip_ct_mms_expect and checking for it in * ip_nat_mms... */ if ((MMS_SRV_MSG_OFFSET < datalen) && ((*(u32 *)(data+MMS_SRV_MSG_OFFSET)) == MMS_SRV_MSG_ID)) { DEBUGP("ip_conntrack_mms: offset 37: %u %u %u %u, datalen:%u\n", (u8)*(data+36), (u8)*(data+37), (u8)*(data+38), (u8)*(data+39), datalen); if (parse_mms(data, datalen, &mms_ip, &mms_proto, &mms_port, &mms_string_b, &mms_string_e, &mms_padding_e)) if (net_ratelimit()) /* FIXME: more verbose debugging ? */ printk(KERN_WARNING "ip_conntrack_mms: Unable to parse " "data payload\n"); sprintf(mms_proto_string, "(%u)", mms_proto); DEBUGP("ip_conntrack_mms: adding %s expectation " "%u.%u.%u.%u -> %u.%u.%u.%u:%u\n", mms_proto == IPPROTO_TCP ? "TCP" : mms_proto == IPPROTO_UDP ? "UDP":mms_proto_string, NIPQUAD(ct->tuplehash[!dir].tuple.src.ip), NIPQUAD(mms_ip), mms_port); /* it's possible that the client will just ask the server to * tunnel the stream over the same TCP session (from port * 1755): there's shouldn't be a need to add an expectation in * that case, but it makes NAT packet mangling so much easier * */ DEBUGP("ip_conntrack_mms: tcph->seq = %u\n", tcph->seq); exp = ip_conntrack_expect_alloc(); if (!exp) { ret = NF_DROP; goto out; } exp_mms_info->offset = (mms_string_b - data); exp_mms_info->len = (mms_string_e - mms_string_b); exp_mms_info->padding = (mms_padding_e - mms_string_e); exp_mms_info->port = mms_port; DEBUGP("ip_conntrack_mms: wrote info seq=%u (ofs=%u), " "len=%d, padding=%u\n", exp->seq, (mms_string_e - data), exp_mms_info->len, exp_mms_info->padding); exp->tuple = ((struct ip_conntrack_tuple) { { ct->tuplehash[!dir].tuple.src.ip, { 0 } }, { mms_ip,
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; }