#endif
-struct conn {
- char* match;
- int matchlen;
-};
-
-#define NUM_MSGS 3
-
-
struct ip_ct_amanda_expect
{
u_int16_t port; /* port number of this expectation */
extern struct ip_conntrack *icmp_error_track(struct sk_buff *skb,
enum ip_conntrack_info *ctinfo,
unsigned int hooknum);
-extern int get_tuple(const struct iphdr *iph, size_t len,
+extern int get_tuple(const struct iphdr *iph,
+ const struct sk_buff *skb,
+ unsigned int dataoff,
struct ip_conntrack_tuple *tuple,
- struct ip_conntrack_protocol *protocol);
+ const struct ip_conntrack_protocol *protocol);
/* Find a connection corresponding to a tuple. */
struct ip_conntrack_tuple_hash *
/* Function to call when data passes; return verdict, or -1 to
invalidate. */
- int (*help)(const struct iphdr *, size_t len,
+ int (*help)(struct sk_buff *skb,
struct ip_conntrack *ct,
enum ip_conntrack_info conntrackinfo);
};
#define IRC_PORT 6667
-struct dccproto {
- char* match;
- int matchlen;
-};
-
/* Protects irc part of conntracks */
DECLARE_LOCK_EXTERN(ip_irc_lock);
/* Protocol name */
const char *name;
- /* Try to fill in the third arg; return true if possible. */
- int (*pkt_to_tuple)(const void *datah, size_t datalen,
- struct ip_conntrack_tuple *tuple);
+ /* Try to fill in the third arg: dataoff is offset past IP
+ hdr. Return true if possible. */
+ int (*pkt_to_tuple)(const struct sk_buff *skb,
+ unsigned int dataoff,
+ struct ip_conntrack_tuple *tuple);
/* Invert the per-proto part of the tuple: ie. turn xmit into reply.
* Some packets can't be inverted: return 0 in that case.
/* Returns verdict for packet, or -1 for invalid. */
int (*packet)(struct ip_conntrack *conntrack,
- struct iphdr *iph, size_t len,
+ const struct sk_buff *skb,
enum ip_conntrack_info ctinfo);
/* Called when a new connection for this protocol found;
* returns TRUE if it's OK. If so, packet() called next. */
- int (*new)(struct ip_conntrack *conntrack, struct iphdr *iph,
- size_t len);
+ int (*new)(struct ip_conntrack *conntrack, const struct sk_buff *skb);
/* Called when a conntrack entry is destroyed */
void (*destroy)(struct ip_conntrack *conntrack);
/* Has to decide if a expectation matches one packet or not */
int (*exp_matches_pkt)(struct ip_conntrack_expect *exp,
- struct sk_buff **pskb);
+ const struct sk_buff *skb);
/* Module (if any) which this is connected to. */
struct module *me;
MODULE_PARM_DESC(master_timeout, "timeout for the master connection");
DECLARE_LOCK(ip_amanda_lock);
-struct module *ip_conntrack_amanda = THIS_MODULE;
-#define MAXMATCHLEN 6
-struct conn conns[NUM_MSGS] = {
- {"DATA ", 5},
- {"MESG ", 5},
- {"INDEX ", 6},
-};
+char *conns[] = { "DATA ", "MESG ", "INDEX " };
#if 0
#define DEBUGP printk
#define DEBUGP(format, args...)
#endif
+/* This is slow, but it's simple. --RR */
+static char amanda_buffer[65536];
-/* FIXME: This should be in userspace. Later. */
-static int help(const struct iphdr *iph, size_t len,
+static int help(struct sk_buff *skb,
struct ip_conntrack *ct, enum ip_conntrack_info ctinfo)
{
- struct udphdr *udph = (void *)iph + iph->ihl * 4;
- u_int32_t udplen = len - iph->ihl * 4;
- u_int32_t datalen = udplen - sizeof(struct udphdr);
- char *data = (char *)udph + sizeof(struct udphdr);
- char *datap = data;
- char *data_limit = (char *) data + datalen;
+ char *data, *data_limit;
int dir = CTINFO2DIR(ctinfo);
+ unsigned int dataoff, i;
struct ip_ct_amanda *info =
(struct ip_ct_amanda *)&ct->help.ct_ftp_info;
if (dir == IP_CT_DIR_ORIGINAL)
return NF_ACCEPT;
- /* Not whole UDP header? */
- if (udplen < sizeof(struct udphdr)) {
- printk("ip_conntrack_amanda_help: udplen = %u\n",
- (unsigned)udplen);
+ /* No data? */
+ dataoff = skb->nh.iph->ihl*4 + sizeof(struct udphdr);
+ if (dataoff >= skb->len) {
+ if (net_ratelimit())
+ printk("ip_conntrack_amanda_help: skblen = %u\n",
+ (unsigned)skb->len);
return NF_ACCEPT;
}
- /* Checksum invalid? Ignore. */
- if (csum_tcpudp_magic(iph->saddr, iph->daddr, udplen, IPPROTO_UDP,
- csum_partial((char *)udph, udplen, 0))) {
- DEBUGP("ip_ct_talk_help: bad csum: %p %u %u.%u.%u.%u "
- "%u.%u.%u.%u\n",
- udph, udplen, NIPQUAD(iph->saddr),
- NIPQUAD(iph->daddr));
- return NF_ACCEPT;
- }
-
+ LOCK_BH(&ip_amanda_lock);
+ skb_copy_bits(skb, dataoff, amanda_buffer, skb->len - dataoff);
+ data = amanda_buffer;
+ data_limit = amanda_buffer + skb->len - dataoff;
+ *data_limit = '\0';
+
/* Search for the CONNECT string */
- while (data < data_limit) {
- if (!memcmp(data, "CONNECT ", 8)) {
- break;
- }
- data++;
- }
- if (memcmp(data, "CONNECT ", 8))
- return NF_ACCEPT;
+ data = strstr(data, "CONNECT ");
+ if (!data)
+ goto out;
DEBUGP("ip_conntrack_amanda_help: CONNECT found in connection "
"%u.%u.%u.%u:%u %u.%u.%u.%u:%u\n",
NIPQUAD(iph->saddr), htons(udph->source),
NIPQUAD(iph->daddr), htons(udph->dest));
- data += 8;
- while (*data != 0x0a && data < data_limit) {
-
- int i;
-
- for (i = 0; i < NUM_MSGS; i++) {
- if (!memcmp(data, conns[i].match,
- conns[i].matchlen)) {
-
- char *portchr;
- struct ip_conntrack_expect expect;
- struct ip_ct_amanda_expect
- *exp_amanda_info =
- &expect.help.exp_amanda_info;
-
- memset(&expect, 0, sizeof(expect));
-
- data += conns[i].matchlen;
- /* this is not really tcp, but let's steal an
- * idea from a tcp stream helper :-)
+ data += strlen("CONNECT ");
+
+ /* Only search first line. */
+ if (strchr(data, '\n'))
+ *strchr(data, '\n') = '\0';
+
+ for (i = 0; i < ARRAY_SIZE(conns); i++) {
+ char *match = strstr(data, conns[i]);
+ if (match) {
+ char *portchr;
+ struct ip_conntrack_expect expect;
+ struct ip_ct_amanda_expect *exp_amanda_info =
+ &expect.help.exp_amanda_info;
+
+ memset(&expect, 0, sizeof(expect));
+
+ data += strlen(conns[i]);
+ /* this is not really tcp, but let's steal an
+ * idea from a tcp stream helper :-) */
+ // XXX expect.seq = data - amanda_buffer;
+ exp_amanda_info->offset = data - amanda_buffer;
+// XXX DEBUGP("expect.seq = %p - %p = %d\n", data, amanda_buffer, expect.seq);
+DEBUGP("exp_amanda_info->offset = %p - %p = %d\n", data, amanda_buffer, exp_amanda_info->offset);
+ portchr = data;
+ exp_amanda_info->port = simple_strtoul(data, &data,10);
+ exp_amanda_info->len = data - portchr;
+
+ /* eat whitespace */
+ while (*data == ' ')
+ data++;
+ DEBUGP("ip_conntrack_amanda_help: "
+ "CONNECT %s request with port "
+ "%u found\n", conns[i],
+ exp_amanda_info->port);
+
+ expect.tuple = ((struct ip_conntrack_tuple)
+ { { ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip,
+ { 0 } },
+ { ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip,
+ { htons(exp_amanda_info->port) },
+ IPPROTO_TCP }});
+ expect.mask = ((struct ip_conntrack_tuple)
+ { { 0, { 0 } },
+ { 0xFFFFFFFF, { 0xFFFF }, 0xFFFF }});
+
+ expect.expectfn = NULL;
+
+ DEBUGP ("ip_conntrack_amanda_help: "
+ "expect_related: %u.%u.%u.%u:%u - "
+ "%u.%u.%u.%u:%u\n",
+ NIPQUAD(expect.tuple.src.ip),
+ ntohs(expect.tuple.src.u.tcp.port),
+ NIPQUAD(expect.tuple.dst.ip),
+ ntohs(expect.tuple.dst.u.tcp.port));
+ if (ip_conntrack_expect_related(ct, &expect)
+ == -EEXIST) {
+ ;
+ /* this must be a packet being resent */
+ /* XXX - how do I get the
+ * ip_conntrack_expect that
+ * already exists so that I can
+ * update the .seq so that the
+ * nat module rewrites the port
+ * numbers?
+ * Perhaps I should use the
+ * exp_amanda_info instead of
+ * .seq.
*/
- // XXX expect.seq = data - datap;
- exp_amanda_info->offset = data - datap;
-// XXX DEBUGP("expect.seq = %p - %p = %d\n", data, datap, expect.seq);
-DEBUGP("exp_amanda_info->offset = %p - %p = %d\n", data, datap, exp_amanda_info->offset);
- portchr = data;
- exp_amanda_info->port =
- simple_strtoul(data, &data, 10);
- exp_amanda_info->len = data - portchr;
-
- /* eat whitespace */
- while (*data == ' ')
- data++;
- DEBUGP ("ip_conntrack_amanda_help: "
- "CONNECT %s request with port "
- "%u found\n", conns[i].match,
- exp_amanda_info->port);
-
- LOCK_BH(&ip_amanda_lock);
-
- expect.tuple = ((struct ip_conntrack_tuple)
- { { ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip,
- { 0 } },
- { ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip,
- { htons(exp_amanda_info->port) },
- IPPROTO_TCP }});
- expect.mask = ((struct ip_conntrack_tuple)
- { { 0, { 0 } },
- { 0xFFFFFFFF, { 0xFFFF }, 0xFFFF }});
-
- expect.expectfn = NULL;
-
- DEBUGP ("ip_conntrack_amanda_help: "
- "expect_related: %u.%u.%u.%u:%u - "
- "%u.%u.%u.%u:%u\n",
- NIPQUAD(expect.tuple.src.ip),
- ntohs(expect.tuple.src.u.tcp.port),
- NIPQUAD(expect.tuple.dst.ip),
- ntohs(expect.tuple.dst.u.tcp.port));
- if (ip_conntrack_expect_related(ct, &expect) ==
- -EEXIST) {
- ;
- /* this must be a packet being resent */
- /* XXX - how do I get the
- * ip_conntrack_expect that
- * already exists so that I can
- * update the .seq so that the
- * nat module rewrites the port
- * numbers?
- * Perhaps I should use the
- * exp_amanda_info instead of
- * .seq.
- */
- }
- UNLOCK_BH(&ip_amanda_lock);
- } /* if memcmp(conns) */
- } /* for .. NUM_MSGS */
- data++;
- } /* while (*data != 0x0a && data < data_limit) */
-
+ }
+ }
+ }
+ out:
+ UNLOCK_BH(&ip_amanda_lock);
return NF_ACCEPT;
}
-static struct ip_conntrack_helper amanda_helper;
+static struct ip_conntrack_helper amanda_helper = {
+ .max_expected = ARRAY_SIZE(conns),
+ .timeout = 180,
+ .flags = IP_CT_HELPER_F_REUSE_EXPECT,
+ .me = THIS_MODULE,
+ .help = help,
+ .name = "amanda",
+
+ .tuple = { .src = { .u = { __constant_htons(10080) } },
+ .dst = { .protonum = IPPROTO_UDP },
+ },
+ .mask = { .src = { .u = { 0xFFFF } },
+ .dst = { .protonum = 0xFFFF },
+ },
+};
static void fini(void)
{
{
int ret;
- memset(&amanda_helper, 0, sizeof(struct ip_conntrack_helper));
- amanda_helper.tuple.src.u.udp.port = htons(10080);
- amanda_helper.tuple.dst.protonum = IPPROTO_UDP;
- amanda_helper.mask.src.u.udp.port = 0xFFFF;
- amanda_helper.mask.dst.protonum = 0xFFFF;
- amanda_helper.max_expected = NUM_MSGS;
- amanda_helper.timeout = 180;
- amanda_helper.flags = IP_CT_HELPER_F_REUSE_EXPECT;
- amanda_helper.me = ip_conntrack_amanda;
- amanda_helper.help = help;
- amanda_helper.name = "amanda";
-
DEBUGP("ip_ct_amanda: registering helper for port 10080\n");
-
ret = ip_conntrack_helper_register(&amanda_helper);
if (ret) {
% ip_conntrack_htable_size;
}
-inline int
-get_tuple(const struct iphdr *iph, size_t len,
+int
+get_tuple(const struct iphdr *iph,
+ const struct sk_buff *skb,
+ unsigned int dataoff,
struct ip_conntrack_tuple *tuple,
- struct ip_conntrack_protocol *protocol)
+ const struct ip_conntrack_protocol *protocol)
{
- int ret;
-
/* Never happen */
if (iph->frag_off & htons(IP_OFFSET)) {
printk("ip_conntrack_core: Frag of proto %u.\n",
iph->protocol);
return 0;
}
- /* Guarantee 8 protocol bytes: if more wanted, use len param */
- else if (iph->ihl * 4 + 8 > len)
- return 0;
tuple->src.ip = iph->saddr;
tuple->dst.ip = iph->daddr;
tuple->dst.protonum = iph->protocol;
- ret = protocol->pkt_to_tuple((u_int32_t *)iph + iph->ihl,
- len - 4*iph->ihl,
- tuple);
- return ret;
+ return protocol->pkt_to_tuple(skb, dataoff, tuple);
}
static int
enum ip_conntrack_info *ctinfo,
unsigned int hooknum)
{
- const struct iphdr *iph;
- struct icmphdr *hdr;
struct ip_conntrack_tuple innertuple, origtuple;
- struct iphdr *inner;
- size_t datalen;
+ struct {
+ struct icmphdr icmp;
+ struct iphdr ip;
+ } inside;
struct ip_conntrack_protocol *innerproto;
struct ip_conntrack_tuple_hash *h;
+ int dataoff;
- IP_NF_ASSERT(iph->protocol == IPPROTO_ICMP);
IP_NF_ASSERT(skb->nfct == NULL);
- iph = skb->nh.iph;
- hdr = (struct icmphdr *)((u_int32_t *)iph + iph->ihl);
- inner = (struct iphdr *)(hdr + 1);
- datalen = skb->len - iph->ihl*4 - sizeof(*hdr);
-
- if (skb->len < iph->ihl * 4 + sizeof(*hdr) + sizeof(*iph)) {
- DEBUGP("icmp_error_track: too short\n");
+ /* Not enough header? */
+ if (skb_copy_bits(skb, skb->nh.iph->ihl*4, &inside, sizeof(inside))!=0)
return NULL;
- }
- if (hdr->type != ICMP_DEST_UNREACH
- && hdr->type != ICMP_SOURCE_QUENCH
- && hdr->type != ICMP_TIME_EXCEEDED
- && hdr->type != ICMP_PARAMETERPROB
- && hdr->type != ICMP_REDIRECT)
+ if (inside.icmp.type != ICMP_DEST_UNREACH
+ && inside.icmp.type != ICMP_SOURCE_QUENCH
+ && inside.icmp.type != ICMP_TIME_EXCEEDED
+ && inside.icmp.type != ICMP_PARAMETERPROB
+ && inside.icmp.type != ICMP_REDIRECT)
return NULL;
/* Ignore ICMP's containing fragments (shouldn't happen) */
- if (inner->frag_off & htons(IP_OFFSET)) {
+ if (inside.ip.frag_off & htons(IP_OFFSET)) {
DEBUGP("icmp_error_track: fragment of proto %u\n",
- inner->protocol);
- return NULL;
- }
-
- /* Ignore it if the checksum's bogus. */
- if (ip_compute_csum((unsigned char *)hdr, sizeof(*hdr) + datalen)) {
- DEBUGP("icmp_error_track: bad csum\n");
+ inside.ip.protocol);
return NULL;
}
- innerproto = ip_ct_find_proto(inner->protocol);
+ innerproto = ip_ct_find_proto(inside.ip.protocol);
+ dataoff = skb->nh.iph->ihl*4 + sizeof(inside.icmp) + inside.ip.ihl*4;
/* Are they talking about one of our connections? */
- if (inner->ihl * 4 + 8 > datalen
- || !get_tuple(inner, datalen, &origtuple, innerproto)) {
- DEBUGP("icmp_error: ! get_tuple p=%u (%u*4+%u dlen=%u)\n",
- inner->protocol, inner->ihl, 8,
- datalen);
+ if (!get_tuple(&inside.ip, skb, dataoff, &origtuple, innerproto)) {
+ DEBUGP("icmp_error: ! get_tuple p=%u", inside.ip.protocol);
return NULL;
}
for (i=0; i < IP_CT_NUMBER; i++)
conntrack->infos[i].master = &conntrack->ct_general;
- if (!protocol->new(conntrack, skb->nh.iph, skb->len)) {
+ if (!protocol->new(conntrack, skb)) {
kmem_cache_free(ip_conntrack_cachep, conntrack);
return NULL;
}
IP_NF_ASSERT((skb->nh.iph->frag_off & htons(IP_OFFSET)) == 0);
- if (!get_tuple(skb->nh.iph, skb->len, &tuple, proto))
+ if (!get_tuple(skb->nh.iph, skb, skb->nh.iph->ihl*4, &tuple, proto))
return NULL;
/* look for tuple match */
if ((*pskb)->nfct)
return NF_ACCEPT;
- /* FIXME: Push down to extensions --RR */
- if (skb_is_nonlinear(*pskb) && skb_linearize(*pskb, GFP_ATOMIC) != 0)
- return NF_DROP;
-
/* Gather fragments. */
if ((*pskb)->nh.iph->frag_off & htons(IP_MF|IP_OFFSET)) {
*pskb = ip_ct_gather_frags(*pskb);
IP_NF_ASSERT((*pskb)->nfct);
- ret = proto->packet(ct, (*pskb)->nh.iph, (*pskb)->len, ctinfo);
+ ret = proto->packet(ct, *pskb, ctinfo);
if (ret == -1) {
/* Invalid */
nf_conntrack_put((*pskb)->nfct);
}
if (ret != NF_DROP && ct->helper) {
- ret = ct->helper->help((*pskb)->nh.iph, (*pskb)->len,
- ct, ctinfo);
+ ret = ct->helper->help(*pskb, ct, ctinfo);
if (ret == -1) {
/* Invalid */
nf_conntrack_put((*pskb)->nfct);
#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
#include <linux/netfilter_ipv4/ip_conntrack_ftp.h>
+/* This is slow, but it's simple. --RR */
+static char ftp_buffer[65536];
+
DECLARE_LOCK(ip_ftp_lock);
struct module *ip_conntrack_ftp = THIS_MODULE;
return 1;
}
-/* FIXME: This should be in userspace. Later. */
-static int help(const struct iphdr *iph, size_t len,
+static int help(struct sk_buff *skb,
struct ip_conntrack *ct,
enum ip_conntrack_info ctinfo)
{
- /* tcplen not negative guaranteed by ip_conntrack_tcp.c */
- struct tcphdr *tcph = (void *)iph + iph->ihl * 4;
- const char *data = (const char *)tcph + tcph->doff * 4;
- unsigned int tcplen = len - iph->ihl * 4;
- unsigned int datalen = tcplen - tcph->doff * 4;
+ unsigned int dataoff, datalen;
+ struct tcphdr tcph;
u_int32_t old_seq_aft_nl;
- int old_seq_aft_nl_set;
+ int old_seq_aft_nl_set, ret;
u_int32_t array[6] = { 0 };
int dir = CTINFO2DIR(ctinfo);
unsigned int matchlen, matchoff;
return NF_ACCEPT;
}
- /* Not whole TCP header? */
- if (tcplen < sizeof(struct tcphdr) || tcplen < tcph->doff*4) {
- DEBUGP("ftp: tcplen = %u\n", (unsigned)tcplen);
+ if (skb_copy_bits(skb, skb->nh.iph->ihl*4, &tcph, sizeof(tcph)) != 0)
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("ftp_help: bad csum: %p %u %u.%u.%u.%u %u.%u.%u.%u\n",
- tcph, tcplen, NIPQUAD(iph->saddr),
- NIPQUAD(iph->daddr));
+ dataoff = skb->nh.iph->ihl*4 + tcph.doff*4;
+ /* No data? */
+ if (dataoff >= skb->len) {
+ DEBUGP("ftp: skblen = %u\n", skb->len);
return NF_ACCEPT;
}
+ datalen = skb->len - dataoff;
LOCK_BH(&ip_ftp_lock);
+ skb_copy_bits(skb, dataoff, ftp_buffer, skb->len - dataoff);
+
old_seq_aft_nl_set = ct_ftp_info->seq_aft_nl_set[dir];
old_seq_aft_nl = ct_ftp_info->seq_aft_nl[dir];
DEBUGP("conntrack_ftp: datalen %u\n", datalen);
- if ((datalen > 0) && (data[datalen-1] == '\n')) {
+ if (ftp_buffer[datalen - 1] == '\n') {
DEBUGP("conntrack_ftp: datalen %u ends in \\n\n", datalen);
if (!old_seq_aft_nl_set
- || after(ntohl(tcph->seq) + datalen, old_seq_aft_nl)) {
+ || after(ntohl(tcph.seq) + datalen, old_seq_aft_nl)) {
DEBUGP("conntrack_ftp: updating nl to %u\n",
- ntohl(tcph->seq) + datalen);
+ ntohl(tcph.seq) + datalen);
ct_ftp_info->seq_aft_nl[dir] =
- ntohl(tcph->seq) + datalen;
+ ntohl(tcph.seq) + datalen;
ct_ftp_info->seq_aft_nl_set[dir] = 1;
}
}
- UNLOCK_BH(&ip_ftp_lock);
if(!old_seq_aft_nl_set ||
- (ntohl(tcph->seq) != old_seq_aft_nl)) {
+ (ntohl(tcph.seq) != old_seq_aft_nl)) {
DEBUGP("ip_conntrack_ftp_help: wrong seq pos %s(%u)\n",
old_seq_aft_nl_set ? "":"(UNSET) ", old_seq_aft_nl);
- return NF_ACCEPT;
+ ret = NF_ACCEPT;
+ goto out;
}
/* Initialize IP array to expected address (it's not mentioned
for (i = 0; i < sizeof(search) / sizeof(search[0]); i++) {
if (search[i].dir != dir) continue;
- found = find_pattern(data, datalen,
+ found = find_pattern(ftp_buffer, skb->len - dataoff,
search[i].pattern,
search[i].plen,
search[i].skip,
if (net_ratelimit())
printk("conntrack_ftp: partial %s %u+%u\n",
search[i].pattern,
- ntohl(tcph->seq), datalen);
- return NF_DROP;
- } else if (found == 0) /* No match */
- return NF_ACCEPT;
+ ntohl(tcph.seq), datalen);
+ ret = NF_DROP;
+ goto out;
+ } else if (found == 0) { /* No match */
+ ret = NF_ACCEPT;
+ goto out;
+ }
DEBUGP("conntrack_ftp: match `%.*s' (%u bytes at %u)\n",
(int)matchlen, data + matchoff,
- matchlen, ntohl(tcph->seq) + matchoff);
+ matchlen, ntohl(tcph.seq) + matchoff);
memset(&expect, 0, sizeof(expect));
/* Update the ftp info */
- LOCK_BH(&ip_ftp_lock);
if (htonl((array[0] << 24) | (array[1] << 16) | (array[2] << 8) | array[3])
== ct->tuplehash[dir].tuple.src.ip) {
- exp->seq = ntohl(tcph->seq) + matchoff;
+ exp->seq = ntohl(tcph.seq) + matchoff;
exp_ftp_info->len = matchlen;
exp_ftp_info->ftptype = search[i].ftptype;
exp_ftp_info->port = array[4] << 8 | array[5];
<lincoln@cesar.org.br> for reporting this potential
problem (DMZ machines opening holes to internal
networks, or the packet filter itself). */
- if (!loose) goto out;
+ if (!loose) {
+ ret = NF_ACCEPT;
+ goto out;
+ }
}
exp->tuple = ((struct ip_conntrack_tuple)
/* Ignore failure; should only happen with NAT */
ip_conntrack_expect_related(ct, &expect);
+ ret = NF_ACCEPT;
out:
UNLOCK_BH(&ip_ftp_lock);
-
- return NF_ACCEPT;
+ return ret;
}
static struct ip_conntrack_helper ftp[MAX_PORTS];
static int ports_c = 0;
static int max_dcc_channels = 8;
static unsigned int dcc_timeout = 300;
+/* This is slow, but it's simple. --RR */
+static char irc_buffer[65536];
MODULE_AUTHOR("Harald Welte <laforge@gnumonks.org>");
MODULE_DESCRIPTION("IRC (DCC) connection tracking module");
MODULE_PARM_DESC(dcc_timeout, "timeout on for unestablished DCC channels");
#endif
-#define NUM_DCCPROTO 5
-struct dccproto dccprotos[NUM_DCCPROTO] = {
- {"SEND ", 5},
- {"CHAT ", 5},
- {"MOVE ", 5},
- {"TSEND ", 6},
- {"SCHAT ", 6}
-};
+static char *dccprotos[] = { "SEND ", "CHAT ", "MOVE ", "TSEND ", "SCHAT " };
#define MAXMATCHLEN 6
DECLARE_LOCK(ip_irc_lock);
return 0;
}
-
-/* FIXME: This should be in userspace. Later. */
-static int help(const struct iphdr *iph, size_t len,
+static int help(struct sk_buff *skb,
struct ip_conntrack *ct, enum ip_conntrack_info ctinfo)
{
- /* tcplen not negative guaranteed by ip_conntrack_tcp.c */
- struct tcphdr *tcph = (void *) iph + iph->ihl * 4;
- const char *data = (const char *) tcph + tcph->doff * 4;
- const char *_data = data;
- char *data_limit;
- u_int32_t tcplen = len - iph->ihl * 4;
- u_int32_t datalen = tcplen - tcph->doff * 4;
+ unsigned int dataoff;
+ struct tcphdr tcph;
+ char *data, *data_limit;
int dir = CTINFO2DIR(ctinfo);
struct ip_conntrack_expect expect, *exp = &expect;
struct ip_ct_irc_expect *exp_irc_info = &exp->help.exp_irc_info;
return NF_ACCEPT;
}
- /* Not whole TCP header? */
- if (tcplen < sizeof(struct tcphdr) || tcplen < tcph->doff * 4) {
- DEBUGP("tcplen = %u\n", (unsigned) tcplen);
+ /* Not a full tcp header? */
+ if (skb_copy_bits(skb, skb->nh.iph->ihl*4, &tcph, sizeof(tcph)) != 0)
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));
+ /* No data? */
+ dataoff = skb->nh.iph->ihl*4 + tcph.doff*4;
+ if (dataoff >= skb->len)
return NF_ACCEPT;
- }
- data_limit = (char *) data + datalen;
+ 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;
while (data < (data_limit - (22 + MAXMATCHLEN))) {
if (memcmp(data, "\1DCC ", 5)) {
data++;
data += 5;
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));
+ NIPQUAD(iph->saddr), ntohs(tcph.source),
+ NIPQUAD(iph->daddr), ntohs(tcph.dest));
- for (i = 0; i < NUM_DCCPROTO; i++) {
- if (memcmp(data, dccprotos[i].match,
- dccprotos[i].matchlen)) {
+ 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].match);
- data += dccprotos[i].matchlen;
- if (parse_dcc((char *) data, data_limit, &dcc_ip,
+ DEBUGP("DCC %s detected\n", dccprotos[i]);
+ data += strlen(dccprotos[i]);
+ 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");
memset(&expect, 0, sizeof(expect));
- LOCK_BH(&ip_irc_lock);
-
/* 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 - _data);
+ 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",
ntohs(exp->tuple.dst.u.tcp.port));
ip_conntrack_expect_related(ct, &expect);
- UNLOCK_BH(&ip_irc_lock);
- return NF_ACCEPT;
+ goto out;
} /* for .. NUM_DCCPROTO */
} /* while data < ... */
+ out:
+ UNLOCK_BH(&ip_irc_lock);
return NF_ACCEPT;
}
#define GENERIC_TIMEOUT (600*HZ)
-static int generic_pkt_to_tuple(const void *datah, size_t datalen,
+static int generic_pkt_to_tuple(const struct sk_buff *skb,
+ unsigned int dataoff,
struct ip_conntrack_tuple *tuple)
{
tuple->src.u.all = 0;
}
/* Returns verdict for packet, or -1 for invalid. */
-static int established(struct ip_conntrack *conntrack,
- struct iphdr *iph, size_t len,
- enum ip_conntrack_info conntrackinfo)
+static int packet(struct ip_conntrack *conntrack,
+ const struct sk_buff *skb,
+ enum ip_conntrack_info conntrackinfo)
{
ip_ct_refresh(conntrack, GENERIC_TIMEOUT);
return NF_ACCEPT;
}
/* Called when a new connection for this protocol found. */
-static int
-new(struct ip_conntrack *conntrack, struct iphdr *iph, size_t len)
+static int new(struct ip_conntrack *conntrack, const struct sk_buff *skb)
{
return 1;
}
struct ip_conntrack_protocol ip_conntrack_generic_protocol
= { { NULL, NULL }, 0, "unknown",
generic_pkt_to_tuple, generic_invert_tuple, generic_print_tuple,
- generic_print_conntrack, established, new, NULL, NULL, NULL };
+ generic_print_conntrack, packet, new, NULL, NULL, NULL };
#define DEBUGP(format, args...)
#endif
-static int icmp_pkt_to_tuple(const void *datah, size_t datalen,
+static int icmp_pkt_to_tuple(const struct sk_buff *skb,
+ unsigned int dataoff,
struct ip_conntrack_tuple *tuple)
{
- const struct icmphdr *hdr = datah;
+ struct icmphdr hdr;
- tuple->dst.u.icmp.type = hdr->type;
- tuple->src.u.icmp.id = hdr->un.echo.id;
- tuple->dst.u.icmp.code = hdr->code;
+ if (skb_copy_bits(skb, dataoff, &hdr, sizeof(hdr)) != 0)
+ return 0;
+
+ tuple->dst.u.icmp.type = hdr.type;
+ tuple->src.u.icmp.id = hdr.un.echo.id;
+ tuple->dst.u.icmp.code = hdr.code;
return 1;
}
/* Returns verdict for packet, or -1 for invalid. */
static int icmp_packet(struct ip_conntrack *ct,
- struct iphdr *iph, size_t len,
+ const struct sk_buff *skb,
enum ip_conntrack_info ctinfo)
{
/* Try to delete connection immediately after all replies:
/* Called when a new connection for this protocol found. */
static int icmp_new(struct ip_conntrack *conntrack,
- struct iphdr *iph, size_t len)
+ const struct sk_buff *skb)
{
static u_int8_t valid_new[]
= { [ICMP_ECHO] = 1,
}
};
-static int tcp_pkt_to_tuple(const void *datah, size_t datalen,
- struct ip_conntrack_tuple *tuple)
+static int tcp_pkt_to_tuple(const struct sk_buff *skb,
+ unsigned int dataoff,
+ struct ip_conntrack_tuple *tuple)
{
- const struct tcphdr *hdr = datah;
+ struct tcphdr hdr;
- tuple->src.u.tcp.port = hdr->source;
- tuple->dst.u.tcp.port = hdr->dest;
+ /* Actually only need first 8 bytes. */
+ if (skb_copy_bits(skb, dataoff, &hdr, 8) != 0)
+ return 0;
+
+ tuple->src.u.tcp.port = hdr.source;
+ tuple->dst.u.tcp.port = hdr.dest;
return 1;
}
/* Returns verdict for packet, or -1 for invalid. */
static int tcp_packet(struct ip_conntrack *conntrack,
- struct iphdr *iph, size_t len,
+ const struct sk_buff *skb,
enum ip_conntrack_info ctinfo)
{
enum tcp_conntrack newconntrack, oldtcpstate;
- struct tcphdr *tcph = (struct tcphdr *)((u_int32_t *)iph + iph->ihl);
+ struct tcphdr tcph;
- /* We're guaranteed to have the base header, but maybe not the
- options. */
- if (len < (iph->ihl + tcph->doff) * 4) {
- DEBUGP("ip_conntrack_tcp: Truncated packet.\n");
+ if (skb_copy_bits(skb, skb->nh.iph->ihl * 4, &tcph, sizeof(tcph)) != 0)
return -1;
- }
WRITE_LOCK(&tcp_lock);
oldtcpstate = conntrack->proto.tcp.state;
newconntrack
= tcp_conntracks
[CTINFO2DIR(ctinfo)]
- [get_conntrack_index(tcph)][oldtcpstate];
+ [get_conntrack_index(&tcph)][oldtcpstate];
/* Invalid */
if (newconntrack == TCP_CONNTRACK_MAX) {
DEBUGP("ip_conntrack_tcp: Invalid dir=%i index=%u conntrack=%u\n",
- CTINFO2DIR(ctinfo), get_conntrack_index(tcph),
+ CTINFO2DIR(ctinfo), get_conntrack_index(&tcph),
conntrack->proto.tcp.state);
WRITE_UNLOCK(&tcp_lock);
return -1;
/* Poor man's window tracking: record SYN/ACK for handshake check */
if (oldtcpstate == TCP_CONNTRACK_SYN_SENT
&& CTINFO2DIR(ctinfo) == IP_CT_DIR_REPLY
- && tcph->syn && tcph->ack)
+ && tcph.syn && tcph.ack)
conntrack->proto.tcp.handshake_ack
- = htonl(ntohl(tcph->seq) + 1);
+ = htonl(ntohl(tcph.seq) + 1);
/* If only reply is a RST, we can consider ourselves not to
have an established connection: this is a fairly common
problem case, so we can delete the conntrack
immediately. --RR */
- if (!test_bit(IPS_SEEN_REPLY_BIT, &conntrack->status) && tcph->rst) {
+ if (!test_bit(IPS_SEEN_REPLY_BIT, &conntrack->status) && tcph.rst) {
WRITE_UNLOCK(&tcp_lock);
if (del_timer(&conntrack->timeout))
conntrack->timeout.function((unsigned long)conntrack);
/* Set ASSURED if we see see valid ack in ESTABLISHED after SYN_RECV */
if (oldtcpstate == TCP_CONNTRACK_SYN_RECV
&& CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL
- && tcph->ack && !tcph->syn
- && tcph->ack_seq == conntrack->proto.tcp.handshake_ack)
+ && tcph.ack && !tcph.syn
+ && tcph.ack_seq == conntrack->proto.tcp.handshake_ack)
set_bit(IPS_ASSURED_BIT, &conntrack->status);
WRITE_UNLOCK(&tcp_lock);
}
/* Called when a new connection for this protocol found. */
-static int tcp_new(struct ip_conntrack *conntrack,
- struct iphdr *iph, size_t len)
+static int tcp_new(struct ip_conntrack *conntrack, const struct sk_buff *skb)
{
enum tcp_conntrack newconntrack;
- struct tcphdr *tcph = (struct tcphdr *)((u_int32_t *)iph + iph->ihl);
+ struct tcphdr tcph;
+
+ if (skb_copy_bits(skb, skb->nh.iph->ihl * 4, &tcph, sizeof(tcph)) != 0)
+ return -1;
/* Don't need lock here: this conntrack not in circulation yet */
newconntrack
- = tcp_conntracks[0][get_conntrack_index(tcph)]
+ = tcp_conntracks[0][get_conntrack_index(&tcph)]
[TCP_CONNTRACK_NONE];
/* Invalid: delete conntrack */
}
static int tcp_exp_matches_pkt(struct ip_conntrack_expect *exp,
- struct sk_buff **pskb)
+ const struct sk_buff *skb)
{
- struct iphdr *iph = (*pskb)->nh.iph;
- struct tcphdr *tcph = (struct tcphdr *)((u_int32_t *)iph + iph->ihl);
+ const struct iphdr *iph = skb->nh.iph;
+ struct tcphdr tcph;
unsigned int datalen;
- datalen = (*pskb)->len - iph->ihl*4 - tcph->doff*4;
+ if (skb_copy_bits(skb, skb->nh.iph->ihl * 4, &tcph, sizeof(tcph)) != 0)
+ return 0;
+ datalen = skb->len - iph->ihl*4 - tcph.doff*4;
- return between(exp->seq, ntohl(tcph->seq), ntohl(tcph->seq) + datalen);
+ return between(exp->seq, ntohl(tcph.seq), ntohl(tcph.seq) + datalen);
}
struct ip_conntrack_protocol ip_conntrack_protocol_tcp
#define UDP_TIMEOUT (30*HZ)
#define UDP_STREAM_TIMEOUT (180*HZ)
-static int udp_pkt_to_tuple(const void *datah, size_t datalen,
- struct ip_conntrack_tuple *tuple)
+static int udp_pkt_to_tuple(const struct sk_buff *skb,
+ unsigned int dataoff,
+ struct ip_conntrack_tuple *tuple)
{
- const struct udphdr *hdr = datah;
+ struct udphdr hdr;
- tuple->src.u.udp.port = hdr->source;
- tuple->dst.u.udp.port = hdr->dest;
+ /* Actually only need first 8 bytes. */
+ if (skb_copy_bits(skb, dataoff, &hdr, 8) != 0)
+ return 0;
+
+ tuple->src.u.udp.port = hdr.source;
+ tuple->dst.u.udp.port = hdr.dest;
return 1;
}
/* Returns verdict for packet, and may modify conntracktype */
static int udp_packet(struct ip_conntrack *conntrack,
- struct iphdr *iph, size_t len,
+ const struct sk_buff *skb,
enum ip_conntrack_info conntrackinfo)
{
/* If we've seen traffic both ways, this is some kind of UDP
}
/* Called when a new connection for this protocol found. */
-static int udp_new(struct ip_conntrack *conntrack,
- struct iphdr *iph, size_t len)
+static int udp_new(struct ip_conntrack *conntrack, const struct sk_buff *skb)
{
return 1;
}
{
struct rtable *rt = (struct rtable *)(*pskb)->dst;
- /* FIXME: Push down to extensions --RR */
- if (skb_is_nonlinear(*pskb) && skb_linearize(*pskb, GFP_ATOMIC) != 0)
- return NF_DROP;
-
/* We've seen it coming out the other side: confirm */
if (ip_confirm(hooknum, pskb, in, out, okfn) != NF_ACCEPT)
return NF_DROP;
const struct net_device *out,
int (*okfn)(struct sk_buff *))
{
- /* FIXME: Push down to extensions --RR */
- if (skb_is_nonlinear(*pskb) && skb_linearize(*pskb, GFP_ATOMIC) != 0)
- return NF_DROP;
-
/* root is playing with raw sockets. */
if ((*pskb)->len < sizeof(struct iphdr)
|| (*pskb)->nh.iph->ihl * 4 < sizeof(struct iphdr)) {
#define DEBUGP(format, args...)
#endif
-static int tftp_help(const struct iphdr *iph, size_t len,
- struct ip_conntrack *ct,
- enum ip_conntrack_info ctinfo)
+static int tftp_help(struct sk_buff *skb,
+ struct ip_conntrack *ct,
+ enum ip_conntrack_info ctinfo)
{
- struct udphdr *udph = (void *)iph + iph->ihl * 4;
- struct tftphdr *tftph = (void *)udph + 8;
+ struct tftphdr tftph;
struct ip_conntrack_expect exp;
-
- switch (ntohs(tftph->opcode)) {
+
+ if (skb_copy_bits(skb, skb->nh.iph->ihl * 4 + sizeof(struct udphdr),
+ &tftph, sizeof(tftph)) != 0)
+ return -1;
+
+ switch (ntohs(tftph.opcode)) {
/* RRQ and WRQ works the same way */
case TFTP_OPCODE_READ:
case TFTP_OPCODE_WRITE:
server here (== DNAT). Do SNAT icmp manips
in POST_ROUTING handling. */
if (CTINFO2DIR(ctinfo) == IP_CT_DIR_REPLY) {
+ /* FIXME: Remove once NAT handled non-linear.
+ */
+ if (skb_is_nonlinear(*pskb)
+ && skb_linearize(*pskb, GFP_ATOMIC) != 0)
+ return NF_DROP;
+
icmp_reply_translation(*pskb, ct,
NF_IP_PRE_ROUTING,
CTINFO2DIR(ctinfo));
case IPPROTO_UDP:
IP_NF_ASSERT(((*pskb)->nh.iph->frag_off & htons(IP_OFFSET)) == 0);
- if (!get_tuple(iph, (*pskb)->len, &tuple, protocol)) {
+ if (!get_tuple(iph, *pskb, iph->ihl*4, &tuple, protocol)) {
if (net_ratelimit())
printk("ip_fw_compat_masq: Can't get tuple\n");
return NF_ACCEPT;
}
static inline int exp_for_packet(struct ip_conntrack_expect *exp,
- struct sk_buff **pskb)
+ struct sk_buff *skb)
{
struct ip_conntrack_protocol *proto;
int ret = 1;
MUST_BE_READ_LOCKED(&ip_conntrack_lock);
- proto = __ip_ct_find_proto((*pskb)->nh.iph->protocol);
+ proto = __ip_ct_find_proto(skb->nh.iph->protocol);
if (proto->exp_matches_pkt)
- ret = proto->exp_matches_pkt(exp, pskb);
+ ret = proto->exp_matches_pkt(exp, skb);
return ret;
}
if (exp->sibling)
continue;
- if (exp_for_packet(exp, pskb)) {
+ if (exp_for_packet(exp, *pskb)) {
/* FIXME: May be true multiple times in the case of UDP!! */
DEBUGP("calling nat helper (exp=%p) for packet\n",
exp);
static int __init init(void)
{
- int i, ret;
+ int i, ret = 0;
char *tmpname;
if (!ports[0])