Commit 4b5fb65e authored by Rusty Russell's avatar Rusty Russell Committed by David S. Miller

[NETFILTER]: Non-linear iptables: core code.

Adjusts the IPTables core to handle non-linear packets.  Extensions
done separately in next patch.  Also, comments about when to set
"me" field in struct ipt_match, ipt_table and ipt_target are
corrected.
parent 07ca08b1
...@@ -347,13 +347,14 @@ struct ipt_match ...@@ -347,13 +347,14 @@ struct ipt_match
/* Return true or false: return FALSE and set *hotdrop = 1 to /* Return true or false: return FALSE and set *hotdrop = 1 to
force immediate packet drop. */ force immediate packet drop. */
/* Arguments changed since 2.4, as this must now handle
non-linear skbs, using skb_copy_bits and
skb_ip_make_writable. */
int (*match)(const struct sk_buff *skb, int (*match)(const struct sk_buff *skb,
const struct net_device *in, const struct net_device *in,
const struct net_device *out, const struct net_device *out,
const void *matchinfo, const void *matchinfo,
int offset, int offset,
const void *hdr,
u_int16_t datalen,
int *hotdrop); int *hotdrop);
/* Called when user tries to insert an entry of this type. */ /* Called when user tries to insert an entry of this type. */
...@@ -367,7 +368,7 @@ struct ipt_match ...@@ -367,7 +368,7 @@ struct ipt_match
/* Called when entry of this type deleted. */ /* Called when entry of this type deleted. */
void (*destroy)(void *matchinfo, unsigned int matchinfosize); void (*destroy)(void *matchinfo, unsigned int matchinfosize);
/* Set this to THIS_MODULE if you are a module, otherwise NULL */ /* Set this to THIS_MODULE. */
struct module *me; struct module *me;
}; };
...@@ -378,14 +379,6 @@ struct ipt_target ...@@ -378,14 +379,6 @@ struct ipt_target
const char name[IPT_FUNCTION_MAXNAMELEN]; const char name[IPT_FUNCTION_MAXNAMELEN];
/* Returns verdict. */
unsigned int (*target)(struct sk_buff **pskb,
unsigned int hooknum,
const struct net_device *in,
const struct net_device *out,
const void *targinfo,
void *userdata);
/* Called when user tries to insert an entry of this type: /* Called when user tries to insert an entry of this type:
hook_mask is a bitmask of hooks from which it can be hook_mask is a bitmask of hooks from which it can be
called. */ called. */
...@@ -399,7 +392,17 @@ struct ipt_target ...@@ -399,7 +392,17 @@ struct ipt_target
/* Called when entry of this type deleted. */ /* Called when entry of this type deleted. */
void (*destroy)(void *targinfo, unsigned int targinfosize); void (*destroy)(void *targinfo, unsigned int targinfosize);
/* Set this to THIS_MODULE if you are a module, otherwise NULL */ /* Returns verdict. Argument order changed since 2.4, as this
must now handle non-linear skbs, using skb_copy_bits and
skb_ip_make_writable. */
unsigned int (*target)(struct sk_buff **pskb,
const struct net_device *in,
const struct net_device *out,
unsigned int hooknum,
const void *targinfo,
void *userdata);
/* Set this to THIS_MODULE. */
struct module *me; struct module *me;
}; };
...@@ -429,7 +432,7 @@ struct ipt_table ...@@ -429,7 +432,7 @@ struct ipt_table
/* Man behind the curtain... */ /* Man behind the curtain... */
struct ipt_table_info *private; struct ipt_table_info *private;
/* Set this to THIS_MODULE if you are a module, otherwise NULL */ /* Set to THIS_MODULE. */
struct module *me; struct module *me;
}; };
......
...@@ -111,9 +111,9 @@ static struct ipt_table nat_table = { ...@@ -111,9 +111,9 @@ static struct ipt_table nat_table = {
/* Source NAT */ /* Source NAT */
static unsigned int ipt_snat_target(struct sk_buff **pskb, static unsigned int ipt_snat_target(struct sk_buff **pskb,
unsigned int hooknum,
const struct net_device *in, const struct net_device *in,
const struct net_device *out, const struct net_device *out,
unsigned int hooknum,
const void *targinfo, const void *targinfo,
void *userinfo) void *userinfo)
{ {
...@@ -132,9 +132,9 @@ static unsigned int ipt_snat_target(struct sk_buff **pskb, ...@@ -132,9 +132,9 @@ static unsigned int ipt_snat_target(struct sk_buff **pskb,
} }
static unsigned int ipt_dnat_target(struct sk_buff **pskb, static unsigned int ipt_dnat_target(struct sk_buff **pskb,
unsigned int hooknum,
const struct net_device *in, const struct net_device *in,
const struct net_device *out, const struct net_device *out,
unsigned int hooknum,
const void *targinfo, const void *targinfo,
void *userinfo) void *userinfo)
{ {
......
...@@ -214,9 +214,9 @@ ip_checkentry(const struct ipt_ip *ip) ...@@ -214,9 +214,9 @@ ip_checkentry(const struct ipt_ip *ip)
static unsigned int static unsigned int
ipt_error(struct sk_buff **pskb, ipt_error(struct sk_buff **pskb,
unsigned int hooknum,
const struct net_device *in, const struct net_device *in,
const struct net_device *out, const struct net_device *out,
unsigned int hooknum,
const void *targinfo, const void *targinfo,
void *userinfo) void *userinfo)
{ {
...@@ -232,13 +232,10 @@ int do_match(struct ipt_entry_match *m, ...@@ -232,13 +232,10 @@ int do_match(struct ipt_entry_match *m,
const struct net_device *in, const struct net_device *in,
const struct net_device *out, const struct net_device *out,
int offset, int offset,
const void *hdr,
u_int16_t datalen,
int *hotdrop) int *hotdrop)
{ {
/* Stop iteration if it doesn't match */ /* Stop iteration if it doesn't match */
if (!m->u.kernel.match->match(skb, in, out, m->data, if (!m->u.kernel.match->match(skb, in, out, m->data, offset, hotdrop))
offset, hdr, datalen, hotdrop))
return 1; return 1;
else else
return 0; return 0;
...@@ -262,7 +259,6 @@ ipt_do_table(struct sk_buff **pskb, ...@@ -262,7 +259,6 @@ ipt_do_table(struct sk_buff **pskb,
static const char nulldevname[IFNAMSIZ] = { 0 }; static const char nulldevname[IFNAMSIZ] = { 0 };
u_int16_t offset; u_int16_t offset;
struct iphdr *ip; struct iphdr *ip;
void *protohdr;
u_int16_t datalen; u_int16_t datalen;
int hotdrop = 0; int hotdrop = 0;
/* Initializing verdict to NF_DROP keeps gcc happy. */ /* Initializing verdict to NF_DROP keeps gcc happy. */
...@@ -271,13 +267,8 @@ ipt_do_table(struct sk_buff **pskb, ...@@ -271,13 +267,8 @@ ipt_do_table(struct sk_buff **pskb,
void *table_base; void *table_base;
struct ipt_entry *e, *back; struct ipt_entry *e, *back;
/* FIXME: Push down to extensions --RR */
if (skb_is_nonlinear(*pskb) && skb_linearize(*pskb, GFP_ATOMIC) != 0)
return NF_DROP;
/* Initialization */ /* Initialization */
ip = (*pskb)->nh.iph; ip = (*pskb)->nh.iph;
protohdr = (u_int32_t *)ip + ip->ihl;
datalen = (*pskb)->len - ip->ihl * 4; datalen = (*pskb)->len - ip->ihl * 4;
indev = in ? in->name : nulldevname; indev = in ? in->name : nulldevname;
outdev = out ? out->name : nulldevname; outdev = out ? out->name : nulldevname;
...@@ -320,8 +311,7 @@ ipt_do_table(struct sk_buff **pskb, ...@@ -320,8 +311,7 @@ ipt_do_table(struct sk_buff **pskb,
if (IPT_MATCH_ITERATE(e, do_match, if (IPT_MATCH_ITERATE(e, do_match,
*pskb, in, out, *pskb, in, out,
offset, protohdr, offset, &hotdrop) != 0)
datalen, &hotdrop) != 0)
goto no_match; goto no_match;
ADD_COUNTER(e->counters, ntohs(ip->tot_len), 1); ADD_COUNTER(e->counters, ntohs(ip->tot_len), 1);
...@@ -364,8 +354,8 @@ ipt_do_table(struct sk_buff **pskb, ...@@ -364,8 +354,8 @@ ipt_do_table(struct sk_buff **pskb,
= 0xeeeeeeec; = 0xeeeeeeec;
#endif #endif
verdict = t->u.kernel.target->target(pskb, verdict = t->u.kernel.target->target(pskb,
hook,
in, out, in, out,
hook,
t->data, t->data,
userdata); userdata);
...@@ -382,7 +372,6 @@ ipt_do_table(struct sk_buff **pskb, ...@@ -382,7 +372,6 @@ ipt_do_table(struct sk_buff **pskb,
#endif #endif
/* Target might have changed stuff. */ /* Target might have changed stuff. */
ip = (*pskb)->nh.iph; ip = (*pskb)->nh.iph;
protohdr = (u_int32_t *)ip + ip->ihl;
datalen = (*pskb)->len - ip->ihl * 4; datalen = (*pskb)->len - ip->ihl * 4;
if (verdict == IPT_CONTINUE) if (verdict == IPT_CONTINUE)
...@@ -1458,22 +1447,24 @@ port_match(u_int16_t min, u_int16_t max, u_int16_t port, int invert) ...@@ -1458,22 +1447,24 @@ port_match(u_int16_t min, u_int16_t max, u_int16_t port, int invert)
static int static int
tcp_find_option(u_int8_t option, tcp_find_option(u_int8_t option,
const struct tcphdr *tcp, const struct sk_buff *skb,
u_int16_t datalen, unsigned int optlen,
int invert, int invert,
int *hotdrop) int *hotdrop)
{ {
unsigned int i = sizeof(struct tcphdr); /* tcp.doff is only 4 bits, ie. max 15 * 4 bytes */
const u_int8_t *opt = (u_int8_t *)tcp; char opt[60 - sizeof(struct tcphdr)];
unsigned int i;
duprintf("tcp_match: finding option\n"); duprintf("tcp_match: finding option\n");
/* If we don't have the whole header, drop packet. */ /* If we don't have the whole header, drop packet. */
if (tcp->doff * 4 > datalen) { if (skb_copy_bits(skb, skb->nh.iph->ihl*4 + sizeof(struct tcphdr),
opt, optlen) < 0) {
*hotdrop = 1; *hotdrop = 1;
return 0; return 0;
} }
while (i < tcp->doff * 4) { for (i = 0; i < optlen; ) {
if (opt[i] == option) return !invert; if (opt[i] == option) return !invert;
if (opt[i] < 2) i++; if (opt[i] < 2) i++;
else i += opt[i+1]?:1; else i += opt[i+1]?:1;
...@@ -1488,25 +1479,29 @@ tcp_match(const struct sk_buff *skb, ...@@ -1488,25 +1479,29 @@ tcp_match(const struct sk_buff *skb,
const struct net_device *out, const struct net_device *out,
const void *matchinfo, const void *matchinfo,
int offset, int offset,
const void *hdr,
u_int16_t datalen,
int *hotdrop) int *hotdrop)
{ {
const struct tcphdr *tcp = hdr; struct tcphdr tcph;
const struct ipt_tcp *tcpinfo = matchinfo; const struct ipt_tcp *tcpinfo = matchinfo;
if (offset) {
/* To quote Alan: /* To quote Alan:
Don't allow a fragment of TCP 8 bytes in. Nobody normal Don't allow a fragment of TCP 8 bytes in. Nobody normal
causes this. Its a cracker trying to break in by doing a causes this. Its a cracker trying to break in by doing a
flag overwrite to pass the direction checks. flag overwrite to pass the direction checks.
*/ */
if (offset == 1) { if (offset == 1) {
duprintf("Dropping evil TCP offset=1 frag.\n"); duprintf("Dropping evil TCP offset=1 frag.\n");
*hotdrop = 1; *hotdrop = 1;
}
/* Must not be a fragment. */
return 0; return 0;
} else if (offset == 0 && datalen < sizeof(struct tcphdr)) { }
#define FWINVTCP(bool,invflg) ((bool) ^ !!(tcpinfo->invflags & invflg))
if (skb_copy_bits(skb, skb->nh.iph->ihl*4, &tcph, sizeof(tcph)) < 0) {
/* We've been asked to examine this packet, and we /* We've been asked to examine this packet, and we
can't. Hence, no choice but to drop. */ can't. Hence, no choice but to drop. */
duprintf("Dropping evil TCP offset=0 tinygram.\n"); duprintf("Dropping evil TCP offset=0 tinygram.\n");
...@@ -1514,27 +1509,24 @@ tcp_match(const struct sk_buff *skb, ...@@ -1514,27 +1509,24 @@ tcp_match(const struct sk_buff *skb,
return 0; return 0;
} }
/* FIXME: Try tcp doff >> packet len against various stacks --RR */ if (!port_match(tcpinfo->spts[0], tcpinfo->spts[1],
ntohs(tcph.source),
#define FWINVTCP(bool,invflg) ((bool) ^ !!(tcpinfo->invflags & invflg)) !!(tcpinfo->invflags & IPT_TCP_INV_SRCPT)))
return 0;
/* Must not be a fragment. */ if (!port_match(tcpinfo->dpts[0], tcpinfo->dpts[1],
return !offset ntohs(tcph.dest),
&& port_match(tcpinfo->spts[0], tcpinfo->spts[1], !!(tcpinfo->invflags & IPT_TCP_INV_DSTPT)))
ntohs(tcp->source), return 0;
!!(tcpinfo->invflags & IPT_TCP_INV_SRCPT)) if (!FWINVTCP((((unsigned char *)&tcph)[13] & tcpinfo->flg_mask)
&& port_match(tcpinfo->dpts[0], tcpinfo->dpts[1],
ntohs(tcp->dest),
!!(tcpinfo->invflags & IPT_TCP_INV_DSTPT))
&& FWINVTCP((((unsigned char *)tcp)[13]
& tcpinfo->flg_mask)
== tcpinfo->flg_cmp, == tcpinfo->flg_cmp,
IPT_TCP_INV_FLAGS) IPT_TCP_INV_FLAGS))
&& (!tcpinfo->option return 0;
|| tcp_find_option(tcpinfo->option, tcp, datalen, if (tcpinfo->option &&
tcpinfo->invflags !tcp_find_option(tcpinfo->option, skb, tcph.doff*4 - sizeof(tcph),
& IPT_TCP_INV_OPTION, tcpinfo->invflags & IPT_TCP_INV_OPTION,
hotdrop)); hotdrop))
return 0;
return 1;
} }
/* Called when user tries to insert an entry of this type. */ /* Called when user tries to insert an entry of this type. */
...@@ -1560,14 +1552,16 @@ udp_match(const struct sk_buff *skb, ...@@ -1560,14 +1552,16 @@ udp_match(const struct sk_buff *skb,
const struct net_device *out, const struct net_device *out,
const void *matchinfo, const void *matchinfo,
int offset, int offset,
const void *hdr,
u_int16_t datalen,
int *hotdrop) int *hotdrop)
{ {
const struct udphdr *udp = hdr; struct udphdr udph;
const struct ipt_udp *udpinfo = matchinfo; const struct ipt_udp *udpinfo = matchinfo;
if (offset == 0 && datalen < sizeof(struct udphdr)) { /* Must not be a fragment. */
if (offset)
return 0;
if (skb_copy_bits(skb, skb->nh.iph->ihl*4, &udph, sizeof(udph)) < 0) {
/* We've been asked to examine this packet, and we /* We've been asked to examine this packet, and we
can't. Hence, no choice but to drop. */ can't. Hence, no choice but to drop. */
duprintf("Dropping evil UDP tinygram.\n"); duprintf("Dropping evil UDP tinygram.\n");
...@@ -1575,13 +1569,11 @@ udp_match(const struct sk_buff *skb, ...@@ -1575,13 +1569,11 @@ udp_match(const struct sk_buff *skb,
return 0; return 0;
} }
/* Must not be a fragment. */ return port_match(udpinfo->spts[0], udpinfo->spts[1],
return !offset ntohs(udph.source),
&& port_match(udpinfo->spts[0], udpinfo->spts[1],
ntohs(udp->source),
!!(udpinfo->invflags & IPT_UDP_INV_SRCPT)) !!(udpinfo->invflags & IPT_UDP_INV_SRCPT))
&& port_match(udpinfo->dpts[0], udpinfo->dpts[1], && port_match(udpinfo->dpts[0], udpinfo->dpts[1],
ntohs(udp->dest), ntohs(udph.dest),
!!(udpinfo->invflags & IPT_UDP_INV_DSTPT)); !!(udpinfo->invflags & IPT_UDP_INV_DSTPT));
} }
...@@ -1631,14 +1623,16 @@ icmp_match(const struct sk_buff *skb, ...@@ -1631,14 +1623,16 @@ icmp_match(const struct sk_buff *skb,
const struct net_device *out, const struct net_device *out,
const void *matchinfo, const void *matchinfo,
int offset, int offset,
const void *hdr,
u_int16_t datalen,
int *hotdrop) int *hotdrop)
{ {
const struct icmphdr *icmp = hdr; struct icmphdr icmph;
const struct ipt_icmp *icmpinfo = matchinfo; const struct ipt_icmp *icmpinfo = matchinfo;
if (offset == 0 && datalen < 2) { /* Must not be a fragment. */
if (offset)
return 0;
if (skb_copy_bits(skb, skb->nh.iph->ihl*4, &icmph, sizeof(icmph)) < 0){
/* We've been asked to examine this packet, and we /* We've been asked to examine this packet, and we
can't. Hence, no choice but to drop. */ can't. Hence, no choice but to drop. */
duprintf("Dropping evil ICMP tinygram.\n"); duprintf("Dropping evil ICMP tinygram.\n");
...@@ -1646,12 +1640,10 @@ icmp_match(const struct sk_buff *skb, ...@@ -1646,12 +1640,10 @@ icmp_match(const struct sk_buff *skb,
return 0; return 0;
} }
/* Must not be a fragment. */ return icmp_type_code_match(icmpinfo->type,
return !offset
&& icmp_type_code_match(icmpinfo->type,
icmpinfo->code[0], icmpinfo->code[0],
icmpinfo->code[1], icmpinfo->code[1],
icmp->type, icmp->code, icmph.type, icmph.code,
!!(icmpinfo->invflags&IPT_ICMP_INV)); !!(icmpinfo->invflags&IPT_ICMP_INV));
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment