summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid S. Miller <davem@nuts.ninka.net>2003-05-21 10:54:44 -0700
committerDavid S. Miller <davem@nuts.ninka.net>2003-05-21 10:54:44 -0700
commit73baeace66f0d4b38de07595ac2cfd308bd22452 (patch)
treefc754c08811c2b460f40902a6f4bd5db3ceaf9eb
parent6c17d503531ee9e528d2b581de38e8f6e18c33a4 (diff)
parentf989c276d46a747dad537f1c3fae0db7799c1f16 (diff)
Merge bk://kernel.bkbits.net/acme/net-2.5
into nuts.ninka.net:/home/davem/src/BK/net-2.5
-rw-r--r--include/linux/netfilter_ipv4/ip_tables.h29
-rw-r--r--include/net/transp_v6.h1
-rw-r--r--net/core/dev.c7
-rw-r--r--net/core/dst.c1
-rw-r--r--net/core/dv.c3
-rw-r--r--net/ipv4/netfilter/ip_nat_rule.c4
-rw-r--r--net/ipv4/netfilter/ip_tables.c144
-rw-r--r--net/ipv4/netfilter/ipt_DSCP.c34
-rw-r--r--net/ipv4/netfilter/ipt_ECN.c110
-rw-r--r--net/ipv4/netfilter/ipt_LOG.c180
-rw-r--r--net/ipv4/netfilter/ipt_MARK.c2
-rw-r--r--net/ipv4/netfilter/ipt_MASQUERADE.c2
-rw-r--r--net/ipv4/netfilter/ipt_MIRROR.c35
-rw-r--r--net/ipv4/netfilter/ipt_REDIRECT.c2
-rw-r--r--net/ipv4/netfilter/ipt_REJECT.c267
-rw-r--r--net/ipv4/netfilter/ipt_TCPMSS.c13
-rw-r--r--net/ipv4/netfilter/ipt_TOS.c33
-rw-r--r--net/ipv4/netfilter/ipt_ULOG.c7
-rw-r--r--net/ipv4/netfilter/ipt_ah.c18
-rw-r--r--net/ipv4/netfilter/ipt_conntrack.c2
-rw-r--r--net/ipv4/netfilter/ipt_dscp.c3
-rw-r--r--net/ipv4/netfilter/ipt_ecn.c34
-rw-r--r--net/ipv4/netfilter/ipt_esp.c18
-rw-r--r--net/ipv4/netfilter/ipt_helper.c2
-rw-r--r--net/ipv4/netfilter/ipt_length.c2
-rw-r--r--net/ipv4/netfilter/ipt_limit.c2
-rw-r--r--net/ipv4/netfilter/ipt_mac.c2
-rw-r--r--net/ipv4/netfilter/ipt_mark.c2
-rw-r--r--net/ipv4/netfilter/ipt_multiport.c21
-rw-r--r--net/ipv4/netfilter/ipt_owner.c7
-rw-r--r--net/ipv4/netfilter/ipt_physdev.c2
-rw-r--r--net/ipv4/netfilter/ipt_pkttype.c2
-rw-r--r--net/ipv4/netfilter/ipt_state.c2
-rw-r--r--net/ipv4/netfilter/ipt_tcpmss.c45
-rw-r--r--net/ipv4/netfilter/ipt_tos.c5
-rw-r--r--net/ipv4/netfilter/ipt_ttl.c12
-rw-r--r--net/ipv4/netfilter/ipt_unclean.c334
-rw-r--r--net/ipv6/anycast.c41
-rw-r--r--net/ipv6/ndisc.c8
39 files changed, 700 insertions, 738 deletions
diff --git a/include/linux/netfilter_ipv4/ip_tables.h b/include/linux/netfilter_ipv4/ip_tables.h
index d2a7f4b40ccd..fb557e487797 100644
--- a/include/linux/netfilter_ipv4/ip_tables.h
+++ b/include/linux/netfilter_ipv4/ip_tables.h
@@ -347,13 +347,14 @@ struct ipt_match
/* Return true or false: return FALSE and set *hotdrop = 1 to
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,
const struct net_device *in,
const struct net_device *out,
const void *matchinfo,
int offset,
- const void *hdr,
- u_int16_t datalen,
int *hotdrop);
/* Called when user tries to insert an entry of this type. */
@@ -367,7 +368,7 @@ struct ipt_match
/* Called when entry of this type deleted. */
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;
};
@@ -378,14 +379,6 @@ struct ipt_target
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:
hook_mask is a bitmask of hooks from which it can be
called. */
@@ -399,7 +392,17 @@ struct ipt_target
/* Called when entry of this type deleted. */
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;
};
@@ -429,7 +432,7 @@ struct ipt_table
/* Man behind the curtain... */
struct ipt_table_info *private;
- /* Set this to THIS_MODULE if you are a module, otherwise NULL */
+ /* Set to THIS_MODULE. */
struct module *me;
};
diff --git a/include/net/transp_v6.h b/include/net/transp_v6.h
index e27d15ea2f18..8b075ab7a26c 100644
--- a/include/net/transp_v6.h
+++ b/include/net/transp_v6.h
@@ -16,7 +16,6 @@ extern struct proto tcpv6_prot;
struct flowi;
/* extention headers */
-extern void ipv6_hopopts_init(void);
extern void ipv6_rthdr_init(void);
extern void ipv6_frag_init(void);
extern void ipv6_nodata_init(void);
diff --git a/net/core/dev.c b/net/core/dev.c
index d6d458ec7e4a..4a6c784220ef 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -2863,9 +2863,6 @@ int unregister_netdevice(struct net_device *dev)
extern void net_device_init(void);
extern void ip_auto_config(void);
-#ifdef CONFIG_NET_DIVERT
-extern void dv_init(void);
-#endif /* CONFIG_NET_DIVERT */
/*
@@ -2889,10 +2886,6 @@ static int __init net_dev_init(void)
for (i = 0; i < 16; i++)
INIT_LIST_HEAD(&ptype_base[i]);
-#ifdef CONFIG_NET_DIVERT
- dv_init();
-#endif /* CONFIG_NET_DIVERT */
-
/*
* Initialise the packet receive queues.
*/
diff --git a/net/core/dst.c b/net/core/dst.c
index 293a6e4987a1..02d61283873a 100644
--- a/net/core/dst.c
+++ b/net/core/dst.c
@@ -123,6 +123,7 @@ void * dst_alloc(struct dst_ops * ops)
if (!dst)
return NULL;
memset(dst, 0, ops->entry_size);
+ atomic_set(&dst->__refcnt, 0);
dst->ops = ops;
dst->lastuse = jiffies;
dst->path = dst;
diff --git a/net/core/dv.c b/net/core/dv.c
index d2b2920312d1..d8ce4366f667 100644
--- a/net/core/dv.c
+++ b/net/core/dv.c
@@ -40,11 +40,12 @@
const char sysctl_divert_version[32]="0.46"; /* Current version */
-int __init dv_init(void)
+static int __init dv_init(void)
{
printk(KERN_INFO "NET4: Frame Diverter %s\n", sysctl_divert_version);
return 0;
}
+module_init(dv_init);
/*
* Allocate a divert_blk for a device. This must be an ethernet nic.
diff --git a/net/ipv4/netfilter/ip_nat_rule.c b/net/ipv4/netfilter/ip_nat_rule.c
index e994863bedc5..d6d14ad65d46 100644
--- a/net/ipv4/netfilter/ip_nat_rule.c
+++ b/net/ipv4/netfilter/ip_nat_rule.c
@@ -111,9 +111,9 @@ static struct ipt_table nat_table = {
/* Source NAT */
static unsigned int ipt_snat_target(struct sk_buff **pskb,
- unsigned int hooknum,
const struct net_device *in,
const struct net_device *out,
+ unsigned int hooknum,
const void *targinfo,
void *userinfo)
{
@@ -132,9 +132,9 @@ static unsigned int ipt_snat_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 *out,
+ unsigned int hooknum,
const void *targinfo,
void *userinfo)
{
diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c
index d89effc16d0b..9a218ac068e2 100644
--- a/net/ipv4/netfilter/ip_tables.c
+++ b/net/ipv4/netfilter/ip_tables.c
@@ -214,9 +214,9 @@ ip_checkentry(const struct ipt_ip *ip)
static unsigned int
ipt_error(struct sk_buff **pskb,
- unsigned int hooknum,
const struct net_device *in,
const struct net_device *out,
+ unsigned int hooknum,
const void *targinfo,
void *userinfo)
{
@@ -232,13 +232,10 @@ int do_match(struct ipt_entry_match *m,
const struct net_device *in,
const struct net_device *out,
int offset,
- const void *hdr,
- u_int16_t datalen,
int *hotdrop)
{
/* Stop iteration if it doesn't match */
- if (!m->u.kernel.match->match(skb, in, out, m->data,
- offset, hdr, datalen, hotdrop))
+ if (!m->u.kernel.match->match(skb, in, out, m->data, offset, hotdrop))
return 1;
else
return 0;
@@ -262,7 +259,6 @@ ipt_do_table(struct sk_buff **pskb,
static const char nulldevname[IFNAMSIZ] = { 0 };
u_int16_t offset;
struct iphdr *ip;
- void *protohdr;
u_int16_t datalen;
int hotdrop = 0;
/* Initializing verdict to NF_DROP keeps gcc happy. */
@@ -271,13 +267,8 @@ ipt_do_table(struct sk_buff **pskb,
void *table_base;
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 */
ip = (*pskb)->nh.iph;
- protohdr = (u_int32_t *)ip + ip->ihl;
datalen = (*pskb)->len - ip->ihl * 4;
indev = in ? in->name : nulldevname;
outdev = out ? out->name : nulldevname;
@@ -320,8 +311,7 @@ ipt_do_table(struct sk_buff **pskb,
if (IPT_MATCH_ITERATE(e, do_match,
*pskb, in, out,
- offset, protohdr,
- datalen, &hotdrop) != 0)
+ offset, &hotdrop) != 0)
goto no_match;
ADD_COUNTER(e->counters, ntohs(ip->tot_len), 1);
@@ -364,8 +354,8 @@ ipt_do_table(struct sk_buff **pskb,
= 0xeeeeeeec;
#endif
verdict = t->u.kernel.target->target(pskb,
- hook,
in, out,
+ hook,
t->data,
userdata);
@@ -382,7 +372,6 @@ ipt_do_table(struct sk_buff **pskb,
#endif
/* Target might have changed stuff. */
ip = (*pskb)->nh.iph;
- protohdr = (u_int32_t *)ip + ip->ihl;
datalen = (*pskb)->len - ip->ihl * 4;
if (verdict == IPT_CONTINUE)
@@ -1458,22 +1447,24 @@ port_match(u_int16_t min, u_int16_t max, u_int16_t port, int invert)
static int
tcp_find_option(u_int8_t option,
- const struct tcphdr *tcp,
- u_int16_t datalen,
+ const struct sk_buff *skb,
+ unsigned int optlen,
int invert,
int *hotdrop)
{
- unsigned int i = sizeof(struct tcphdr);
- const u_int8_t *opt = (u_int8_t *)tcp;
+ /* tcp.doff is only 4 bits, ie. max 15 * 4 bytes */
+ char opt[60 - sizeof(struct tcphdr)];
+ unsigned int i;
duprintf("tcp_match: finding option\n");
/* 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;
return 0;
}
- while (i < tcp->doff * 4) {
+ for (i = 0; i < optlen; ) {
if (opt[i] == option) return !invert;
if (opt[i] < 2) i++;
else i += opt[i+1]?:1;
@@ -1488,25 +1479,29 @@ tcp_match(const struct sk_buff *skb,
const struct net_device *out,
const void *matchinfo,
int offset,
- const void *hdr,
- u_int16_t datalen,
int *hotdrop)
{
- const struct tcphdr *tcp = hdr;
+ struct tcphdr tcph;
const struct ipt_tcp *tcpinfo = matchinfo;
- /* To quote Alan:
-
- Don't allow a fragment of TCP 8 bytes in. Nobody normal
- causes this. Its a cracker trying to break in by doing a
- flag overwrite to pass the direction checks.
- */
+ if (offset) {
+ /* To quote Alan:
- if (offset == 1) {
- duprintf("Dropping evil TCP offset=1 frag.\n");
- *hotdrop = 1;
+ Don't allow a fragment of TCP 8 bytes in. Nobody normal
+ causes this. Its a cracker trying to break in by doing a
+ flag overwrite to pass the direction checks.
+ */
+ if (offset == 1) {
+ duprintf("Dropping evil TCP offset=1 frag.\n");
+ *hotdrop = 1;
+ }
+ /* Must not be a fragment. */
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
can't. Hence, no choice but to drop. */
duprintf("Dropping evil TCP offset=0 tinygram.\n");
@@ -1514,27 +1509,24 @@ tcp_match(const struct sk_buff *skb,
return 0;
}
- /* FIXME: Try tcp doff >> packet len against various stacks --RR */
-
-#define FWINVTCP(bool,invflg) ((bool) ^ !!(tcpinfo->invflags & invflg))
-
- /* Must not be a fragment. */
- return !offset
- && port_match(tcpinfo->spts[0], tcpinfo->spts[1],
- ntohs(tcp->source),
- !!(tcpinfo->invflags & IPT_TCP_INV_SRCPT))
- && 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,
- IPT_TCP_INV_FLAGS)
- && (!tcpinfo->option
- || tcp_find_option(tcpinfo->option, tcp, datalen,
- tcpinfo->invflags
- & IPT_TCP_INV_OPTION,
- hotdrop));
+ if (!port_match(tcpinfo->spts[0], tcpinfo->spts[1],
+ ntohs(tcph.source),
+ !!(tcpinfo->invflags & IPT_TCP_INV_SRCPT)))
+ return 0;
+ if (!port_match(tcpinfo->dpts[0], tcpinfo->dpts[1],
+ ntohs(tcph.dest),
+ !!(tcpinfo->invflags & IPT_TCP_INV_DSTPT)))
+ return 0;
+ if (!FWINVTCP((((unsigned char *)&tcph)[13] & tcpinfo->flg_mask)
+ == tcpinfo->flg_cmp,
+ IPT_TCP_INV_FLAGS))
+ return 0;
+ if (tcpinfo->option &&
+ !tcp_find_option(tcpinfo->option, skb, tcph.doff*4 - sizeof(tcph),
+ tcpinfo->invflags & IPT_TCP_INV_OPTION,
+ hotdrop))
+ return 0;
+ return 1;
}
/* Called when user tries to insert an entry of this type. */
@@ -1560,14 +1552,16 @@ udp_match(const struct sk_buff *skb,
const struct net_device *out,
const void *matchinfo,
int offset,
- const void *hdr,
- u_int16_t datalen,
int *hotdrop)
{
- const struct udphdr *udp = hdr;
+ struct udphdr udph;
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
can't. Hence, no choice but to drop. */
duprintf("Dropping evil UDP tinygram.\n");
@@ -1575,13 +1569,11 @@ udp_match(const struct sk_buff *skb,
return 0;
}
- /* Must not be a fragment. */
- return !offset
- && port_match(udpinfo->spts[0], udpinfo->spts[1],
- ntohs(udp->source),
- !!(udpinfo->invflags & IPT_UDP_INV_SRCPT))
+ return port_match(udpinfo->spts[0], udpinfo->spts[1],
+ ntohs(udph.source),
+ !!(udpinfo->invflags & IPT_UDP_INV_SRCPT))
&& port_match(udpinfo->dpts[0], udpinfo->dpts[1],
- ntohs(udp->dest),
+ ntohs(udph.dest),
!!(udpinfo->invflags & IPT_UDP_INV_DSTPT));
}
@@ -1631,14 +1623,16 @@ icmp_match(const struct sk_buff *skb,
const struct net_device *out,
const void *matchinfo,
int offset,
- const void *hdr,
- u_int16_t datalen,
int *hotdrop)
{
- const struct icmphdr *icmp = hdr;
+ struct icmphdr icmph;
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
can't. Hence, no choice but to drop. */
duprintf("Dropping evil ICMP tinygram.\n");
@@ -1646,13 +1640,11 @@ icmp_match(const struct sk_buff *skb,
return 0;
}
- /* Must not be a fragment. */
- return !offset
- && icmp_type_code_match(icmpinfo->type,
- icmpinfo->code[0],
- icmpinfo->code[1],
- icmp->type, icmp->code,
- !!(icmpinfo->invflags&IPT_ICMP_INV));
+ return icmp_type_code_match(icmpinfo->type,
+ icmpinfo->code[0],
+ icmpinfo->code[1],
+ icmph.type, icmph.code,
+ !!(icmpinfo->invflags&IPT_ICMP_INV));
}
/* Called when user tries to insert an entry of this type. */
diff --git a/net/ipv4/netfilter/ipt_DSCP.c b/net/ipv4/netfilter/ipt_DSCP.c
index 0b7827279481..ddd16bb681eb 100644
--- a/net/ipv4/netfilter/ipt_DSCP.c
+++ b/net/ipv4/netfilter/ipt_DSCP.c
@@ -23,37 +23,31 @@ MODULE_LICENSE("GPL");
static unsigned int
target(struct sk_buff **pskb,
- unsigned int hooknum,
const struct net_device *in,
const struct net_device *out,
+ unsigned int hooknum,
const void *targinfo,
void *userinfo)
{
- struct iphdr *iph = (*pskb)->nh.iph;
const struct ipt_DSCP_info *dinfo = targinfo;
u_int8_t sh_dscp = ((dinfo->dscp << IPT_DSCP_SHIFT) & IPT_DSCP_MASK);
- if ((iph->tos & IPT_DSCP_MASK) != sh_dscp) {
+ if (((*pskb)->nh.iph->tos & IPT_DSCP_MASK) != sh_dscp) {
u_int16_t diffs[2];
- /* raw socket (tcpdump) may have clone of incoming
- * skb: don't disturb it --RR */
- if (skb_cloned(*pskb) && !(*pskb)->sk) {
- struct sk_buff *nskb = skb_copy(*pskb, GFP_ATOMIC);
- if (!nskb)
- return NF_DROP;
- kfree_skb(*pskb);
- *pskb = nskb;
- iph = (*pskb)->nh.iph;
- }
-
- diffs[0] = htons(iph->tos) ^ 0xFFFF;
- iph->tos = (iph->tos & ~IPT_DSCP_MASK) | sh_dscp;
- diffs[1] = htons(iph->tos);
- iph->check = csum_fold(csum_partial((char *)diffs,
- sizeof(diffs),
- iph->check^0xFFFF));
+ if (!skb_ip_make_writable(pskb, sizeof(struct iphdr)))
+ return NF_DROP;
+
+ diffs[0] = htons((*pskb)->nh.iph->tos) ^ 0xFFFF;
+ (*pskb)->nh.iph->tos = ((*pskb)->nh.iph->tos & ~IPT_DSCP_MASK)
+ | sh_dscp;
+ diffs[1] = htons((*pskb)->nh.iph->tos);
+ (*pskb)->nh.iph->check
+ = csum_fold(csum_partial((char *)diffs,
+ sizeof(diffs),
+ (*pskb)->nh.iph->check
+ ^ 0xFFFF));
(*pskb)->nfcache |= NFC_ALTERED;
}
return IPT_CONTINUE;
diff --git a/net/ipv4/netfilter/ipt_ECN.c b/net/ipv4/netfilter/ipt_ECN.c
index 02fa43bc08b8..6930e6240fdf 100644
--- a/net/ipv4/netfilter/ipt_ECN.c
+++ b/net/ipv4/netfilter/ipt_ECN.c
@@ -19,105 +19,85 @@
MODULE_LICENSE("GPL");
/* set ECT codepoint from IP header.
- * return 0 in case there was no ECT codepoint
- * return 1 in case ECT codepoint has been overwritten
- * return < 0 in case there was error */
+ * return 0 if there was an error. */
static inline int
-set_ect_ip(struct sk_buff **pskb, struct iphdr *iph,
- const struct ipt_ECN_info *einfo)
+set_ect_ip(struct sk_buff **pskb, const struct ipt_ECN_info *einfo)
{
- if ((iph->tos & IPT_ECN_IP_MASK)
+ if (((*pskb)->nh.iph->tos & IPT_ECN_IP_MASK)
!= (einfo->ip_ect & IPT_ECN_IP_MASK)) {
u_int16_t diffs[2];
- /* raw socket (tcpdump) may have clone of incoming
- * skb: don't disturb it --RR */
- if (skb_cloned(*pskb) && !(*pskb)->sk) {
- struct sk_buff *nskb = skb_copy(*pskb, GFP_ATOMIC);
- if (!nskb)
- return NF_DROP;
- kfree_skb(*pskb);
- *pskb = nskb;
- iph = (*pskb)->nh.iph;
- }
-
- diffs[0] = htons(iph->tos) ^ 0xFFFF;
- iph->tos = iph->tos & ~IPT_ECN_IP_MASK;
- iph->tos = iph->tos | (einfo->ip_ect & IPT_ECN_IP_MASK);
- diffs[1] = htons(iph->tos);
- iph->check = csum_fold(csum_partial((char *)diffs,
- sizeof(diffs),
- iph->check^0xFFFF));
+ if (!skb_ip_make_writable(pskb, sizeof(struct iphdr)))
+ return 0;
+
+ diffs[0] = htons((*pskb)->nh.iph->tos) ^ 0xFFFF;
+ (*pskb)->nh.iph->tos &= ~IPT_ECN_IP_MASK;
+ (*pskb)->nh.iph->tos |= (einfo->ip_ect & IPT_ECN_IP_MASK);
+ diffs[1] = htons((*pskb)->nh.iph->tos);
+ (*pskb)->nh.iph->check
+ = csum_fold(csum_partial((char *)diffs,
+ sizeof(diffs),
+ (*pskb)->nh.iph->check
+ ^0xFFFF));
(*pskb)->nfcache |= NFC_ALTERED;
-
- return 1;
}
- return 0;
+ return 1;
}
+/* Return 0 if there was an error. */
static inline int
-set_ect_tcp(struct sk_buff **pskb, struct iphdr *iph,
- const struct ipt_ECN_info *einfo)
+set_ect_tcp(struct sk_buff **pskb, const struct ipt_ECN_info *einfo)
{
-
- struct tcphdr *tcph = (void *) iph + iph->ihl * 4;
- u_int16_t *tcpflags = (u_int16_t *)tcph + 6;
+ struct tcphdr tcph;
u_int16_t diffs[2];
- /* raw socket (tcpdump) may have clone of incoming
- * skb: don't disturb it --RR */
- if (skb_cloned(*pskb) && !(*pskb)->sk) {
- struct sk_buff *nskb = skb_copy(*pskb, GFP_ATOMIC);
- if (!nskb)
- return NF_DROP;
- kfree_skb(*pskb);
- *pskb = nskb;
- iph = (*pskb)->nh.iph;
- }
+ /* Not enought header? */
+ if (skb_copy_bits(*pskb, (*pskb)->nh.iph->ihl*4, &tcph, sizeof(tcph))
+ < 0)
+ return 0;
- diffs[0] = *tcpflags;
+ diffs[0] = ((u_int16_t *)&tcph)[6];
+ if (einfo->operation & IPT_ECN_OP_SET_ECE)
+ tcph.ece = einfo->proto.tcp.ece;
- if (einfo->operation & IPT_ECN_OP_SET_ECE
- && tcph->ece != einfo->proto.tcp.ece) {
- tcph->ece = einfo->proto.tcp.ece;
- }
+ if (einfo->operation & IPT_ECN_OP_SET_CWR)
+ tcph.cwr = einfo->proto.tcp.cwr;
+ diffs[1] = ((u_int16_t *)&tcph)[6];
- if (einfo->operation & IPT_ECN_OP_SET_CWR
- && tcph->cwr != einfo->proto.tcp.cwr) {
- tcph->cwr = einfo->proto.tcp.cwr;
- }
-
- if (diffs[0] != *tcpflags) {
+ /* Only mangle if it's changed. */
+ if (diffs[0] != diffs[1]) {
diffs[0] = diffs[0] ^ 0xFFFF;
- diffs[1] = *tcpflags;
- tcph->check = csum_fold(csum_partial((char *)diffs,
+ if (!skb_ip_make_writable(pskb,
+ (*pskb)->nh.iph->ihl*4+sizeof(tcph)))
+ return 0;
+ tcph.check = csum_fold(csum_partial((char *)diffs,
sizeof(diffs),
- tcph->check^0xFFFF));
+ tcph.check^0xFFFF));
+ memcpy((*pskb)->data + (*pskb)->nh.iph->ihl*4,
+ &tcph, sizeof(tcph));
(*pskb)->nfcache |= NFC_ALTERED;
-
- return 1;
}
-
- return 0;
+ return 1;
}
static unsigned int
target(struct sk_buff **pskb,
- unsigned int hooknum,
const struct net_device *in,
const struct net_device *out,
+ unsigned int hooknum,
const void *targinfo,
void *userinfo)
{
- struct iphdr *iph = (*pskb)->nh.iph;
const struct ipt_ECN_info *einfo = targinfo;
if (einfo->operation & IPT_ECN_OP_SET_IP)
- set_ect_ip(pskb, iph, einfo);
+ if (!set_ect_ip(pskb, einfo))
+ return NF_DROP;
if (einfo->operation & (IPT_ECN_OP_SET_ECE | IPT_ECN_OP_SET_CWR)
- && iph->protocol == IPPROTO_TCP)
- set_ect_tcp(pskb, iph, einfo);
+ && (*pskb)->nh.iph->protocol == IPPROTO_TCP)
+ if (!set_ect_tcp(pskb, einfo))
+ return NF_DROP;
return IPT_CONTINUE;
}
diff --git a/net/ipv4/netfilter/ipt_LOG.c b/net/ipv4/netfilter/ipt_LOG.c
index 2b0dca70ddc7..56c5238a6129 100644
--- a/net/ipv4/netfilter/ipt_LOG.c
+++ b/net/ipv4/netfilter/ipt_LOG.c
@@ -29,127 +29,151 @@ static spinlock_t log_lock = SPIN_LOCK_UNLOCKED;
/* One level of recursion won't kill us */
static void dump_packet(const struct ipt_log_info *info,
- struct iphdr *iph, unsigned int len, int recurse)
+ const struct sk_buff *skb,
+ unsigned int iphoff)
{
- void *protoh = (u_int32_t *)iph + iph->ihl;
- unsigned int datalen = len - iph->ihl * 4;
+ struct iphdr iph;
+
+ if (skb_copy_bits(skb, iphoff, &iph, sizeof(iph)) < 0) {
+ printk("TRUNCATED");
+ return;
+ }
/* Important fields:
* TOS, len, DF/MF, fragment offset, TTL, src, dst, options. */
/* Max length: 40 "SRC=255.255.255.255 DST=255.255.255.255 " */
printk("SRC=%u.%u.%u.%u DST=%u.%u.%u.%u ",
- NIPQUAD(iph->saddr), NIPQUAD(iph->daddr));
+ NIPQUAD(iph.saddr), NIPQUAD(iph.daddr));
/* Max length: 46 "LEN=65535 TOS=0xFF PREC=0xFF TTL=255 ID=65535 " */
printk("LEN=%u TOS=0x%02X PREC=0x%02X TTL=%u ID=%u ",
- ntohs(iph->tot_len), iph->tos & IPTOS_TOS_MASK,
- iph->tos & IPTOS_PREC_MASK, iph->ttl, ntohs(iph->id));
+ ntohs(iph.tot_len), iph.tos & IPTOS_TOS_MASK,
+ iph.tos & IPTOS_PREC_MASK, iph.ttl, ntohs(iph.id));
/* Max length: 6 "CE DF MF " */
- if (ntohs(iph->frag_off) & IP_CE)
+ if (ntohs(iph.frag_off) & IP_CE)
printk("CE ");
- if (ntohs(iph->frag_off) & IP_DF)
+ if (ntohs(iph.frag_off) & IP_DF)
printk("DF ");
- if (ntohs(iph->frag_off) & IP_MF)
+ if (ntohs(iph.frag_off) & IP_MF)
printk("MF ");
/* Max length: 11 "FRAG:65535 " */
- if (ntohs(iph->frag_off) & IP_OFFSET)
- printk("FRAG:%u ", ntohs(iph->frag_off) & IP_OFFSET);
+ if (ntohs(iph.frag_off) & IP_OFFSET)
+ printk("FRAG:%u ", ntohs(iph.frag_off) & IP_OFFSET);
if ((info->logflags & IPT_LOG_IPOPT)
- && iph->ihl * 4 != sizeof(struct iphdr)) {
- unsigned int i;
+ && iph.ihl * 4 != sizeof(struct iphdr)) {
+ unsigned char opt[4 * 15 - sizeof(struct iphdr)];
+ unsigned int i, optsize;
+
+ optsize = iph.ihl * 4 - sizeof(struct iphdr);
+ if (skb_copy_bits(skb, iphoff+sizeof(iph), opt, optsize) < 0) {
+ printk("TRUNCATED");
+ return;
+ }
/* Max length: 127 "OPT (" 15*4*2chars ") " */
printk("OPT (");
- for (i = sizeof(struct iphdr); i < iph->ihl * 4; i++)
- printk("%02X", ((u_int8_t *)iph)[i]);
+ for (i = 0; i < optsize; i++)
+ printk("%02X", opt[i]);
printk(") ");
}
- switch (iph->protocol) {
+ switch (iph.protocol) {
case IPPROTO_TCP: {
- struct tcphdr *tcph = protoh;
+ struct tcphdr tcph;
/* Max length: 10 "PROTO=TCP " */
printk("PROTO=TCP ");
- if (ntohs(iph->frag_off) & IP_OFFSET)
+ if (ntohs(iph.frag_off) & IP_OFFSET)
break;
/* Max length: 25 "INCOMPLETE [65535 bytes] " */
- if (datalen < sizeof (*tcph)) {
- printk("INCOMPLETE [%u bytes] ", datalen);
+ if (skb_copy_bits(skb, iphoff+iph.ihl*4, &tcph, sizeof(tcph))
+ < 0) {
+ printk("INCOMPLETE [%u bytes] ",
+ skb->len - iphoff - iph.ihl*4);
break;
}
/* Max length: 20 "SPT=65535 DPT=65535 " */
printk("SPT=%u DPT=%u ",
- ntohs(tcph->source), ntohs(tcph->dest));
+ ntohs(tcph.source), ntohs(tcph.dest));
/* Max length: 30 "SEQ=4294967295 ACK=4294967295 " */
if (info->logflags & IPT_LOG_TCPSEQ)
printk("SEQ=%u ACK=%u ",
- ntohl(tcph->seq), ntohl(tcph->ack_seq));
+ ntohl(tcph.seq), ntohl(tcph.ack_seq));
/* Max length: 13 "WINDOW=65535 " */
- printk("WINDOW=%u ", ntohs(tcph->window));
+ printk("WINDOW=%u ", ntohs(tcph.window));
/* Max length: 9 "RES=0x3F " */
- printk("RES=0x%02x ", (u_int8_t)(ntohl(tcp_flag_word(tcph) & TCP_RESERVED_BITS) >> 22));
+ printk("RES=0x%02x ", (u8)(ntohl(tcp_flag_word(&tcph) & TCP_RESERVED_BITS) >> 22));
/* Max length: 32 "CWR ECE URG ACK PSH RST SYN FIN " */
- if (tcph->cwr)
+ if (tcph.cwr)
printk("CWR ");
- if (tcph->ece)
+ if (tcph.ece)
printk("ECE ");
- if (tcph->urg)
+ if (tcph.urg)
printk("URG ");
- if (tcph->ack)
+ if (tcph.ack)
printk("ACK ");
- if (tcph->psh)
+ if (tcph.psh)
printk("PSH ");
- if (tcph->rst)
+ if (tcph.rst)
printk("RST ");
- if (tcph->syn)
+ if (tcph.syn)
printk("SYN ");
- if (tcph->fin)
+ if (tcph.fin)
printk("FIN ");
/* Max length: 11 "URGP=65535 " */
- printk("URGP=%u ", ntohs(tcph->urg_ptr));
+ printk("URGP=%u ", ntohs(tcph.urg_ptr));
if ((info->logflags & IPT_LOG_TCPOPT)
- && tcph->doff * 4 != sizeof(struct tcphdr)) {
- unsigned int i;
+ && tcph.doff * 4 != sizeof(struct tcphdr)) {
+ unsigned char opt[4 * 15 - sizeof(struct tcphdr)];
+ unsigned int i, optsize;
+
+ optsize = tcph.doff * 4 - sizeof(struct tcphdr);
+ if (skb_copy_bits(skb, iphoff+iph.ihl*4 + sizeof(tcph),
+ opt, optsize) < 0) {
+ printk("TRUNCATED");
+ return;
+ }
/* Max length: 127 "OPT (" 15*4*2chars ") " */
printk("OPT (");
- for (i =sizeof(struct tcphdr); i < tcph->doff * 4; i++)
- printk("%02X", ((u_int8_t *)tcph)[i]);
+ for (i = 0; i < optsize; i++)
+ printk("%02X", opt[i]);
printk(") ");
}
break;
}
case IPPROTO_UDP: {
- struct udphdr *udph = protoh;
+ struct udphdr udph;
/* Max length: 10 "PROTO=UDP " */
printk("PROTO=UDP ");
- if (ntohs(iph->frag_off) & IP_OFFSET)
+ if (ntohs(iph.frag_off) & IP_OFFSET)
break;
/* Max length: 25 "INCOMPLETE [65535 bytes] " */
- if (datalen < sizeof (*udph)) {
- printk("INCOMPLETE [%u bytes] ", datalen);
+ if (skb_copy_bits(skb, iphoff+iph.ihl*4, &udph, sizeof(udph))
+ < 0) {
+ printk("INCOMPLETE [%u bytes] ",
+ skb->len - iphoff - iph.ihl*4);
break;
}
/* Max length: 20 "SPT=65535 DPT=65535 " */
printk("SPT=%u DPT=%u LEN=%u ",
- ntohs(udph->source), ntohs(udph->dest),
- ntohs(udph->len));
+ ntohs(udph.source), ntohs(udph.dest),
+ ntohs(udph.len));
break;
}
case IPPROTO_ICMP: {
- struct icmphdr *icmph = protoh;
+ struct icmphdr icmph;
static size_t required_len[NR_ICMP_TYPES+1]
= { [ICMP_ECHOREPLY] = 4,
[ICMP_DEST_UNREACH]
@@ -171,89 +195,93 @@ static void dump_packet(const struct ipt_log_info *info,
/* Max length: 11 "PROTO=ICMP " */
printk("PROTO=ICMP ");
- if (ntohs(iph->frag_off) & IP_OFFSET)
+ if (ntohs(iph.frag_off) & IP_OFFSET)
break;
/* Max length: 25 "INCOMPLETE [65535 bytes] " */
- if (datalen < 4) {
- printk("INCOMPLETE [%u bytes] ", datalen);
+ if (skb_copy_bits(skb, iphoff+iph.ihl*4, &icmph, sizeof(icmph))
+ < 0) {
+ printk("INCOMPLETE [%u bytes] ",
+ skb->len - iphoff - iph.ihl*4);
break;
}
/* Max length: 18 "TYPE=255 CODE=255 " */
- printk("TYPE=%u CODE=%u ", icmph->type, icmph->code);
+ printk("TYPE=%u CODE=%u ", icmph.type, icmph.code);
/* Max length: 25 "INCOMPLETE [65535 bytes] " */
- if (icmph->type <= NR_ICMP_TYPES
- && required_len[icmph->type]
- && datalen < required_len[icmph->type]) {
- printk("INCOMPLETE [%u bytes] ", datalen);
+ if (icmph.type <= NR_ICMP_TYPES
+ && required_len[icmph.type]
+ && skb->len-iphoff-iph.ihl*4 < required_len[icmph.type]) {
+ printk("INCOMPLETE [%u bytes] ",
+ skb->len - iphoff - iph.ihl*4);
break;
}
- switch (icmph->type) {
+ switch (icmph.type) {
case ICMP_ECHOREPLY:
case ICMP_ECHO:
/* Max length: 19 "ID=65535 SEQ=65535 " */
printk("ID=%u SEQ=%u ",
- ntohs(icmph->un.echo.id),
- ntohs(icmph->un.echo.sequence));
+ ntohs(icmph.un.echo.id),
+ ntohs(icmph.un.echo.sequence));
break;
case ICMP_PARAMETERPROB:
/* Max length: 14 "PARAMETER=255 " */
printk("PARAMETER=%u ",
- ntohl(icmph->un.gateway) >> 24);
+ ntohl(icmph.un.gateway) >> 24);
break;
case ICMP_REDIRECT:
/* Max length: 24 "GATEWAY=255.255.255.255 " */
- printk("GATEWAY=%u.%u.%u.%u ", NIPQUAD(icmph->un.gateway));
+ printk("GATEWAY=%u.%u.%u.%u ",
+ NIPQUAD(icmph.un.gateway));
/* Fall through */
case ICMP_DEST_UNREACH:
case ICMP_SOURCE_QUENCH:
case ICMP_TIME_EXCEEDED:
/* Max length: 3+maxlen */
- if (recurse) {
+ if (!iphoff) { /* Only recurse once. */
printk("[");
- dump_packet(info,
- (struct iphdr *)(icmph + 1),
- datalen-sizeof(struct icmphdr),
- 0);
+ dump_packet(info, skb,
+ iphoff + iph.ihl*4+sizeof(icmph));
printk("] ");
}
/* Max length: 10 "MTU=65535 " */
- if (icmph->type == ICMP_DEST_UNREACH
- && icmph->code == ICMP_FRAG_NEEDED)
- printk("MTU=%u ", ntohs(icmph->un.frag.mtu));
+ if (icmph.type == ICMP_DEST_UNREACH
+ && icmph.code == ICMP_FRAG_NEEDED)
+ printk("MTU=%u ", ntohs(icmph.un.frag.mtu));
}
break;
}
/* Max Length */
case IPPROTO_AH:
case IPPROTO_ESP: {
- struct esphdr *esph = protoh;
- int esp= (iph->protocol==IPPROTO_ESP);
+ struct esphdr esph;
+ int esp = (iph.protocol==IPPROTO_ESP);
/* Max length: 10 "PROTO=ESP " */
printk("PROTO=%s ",esp? "ESP" : "AH");
- if (ntohs(iph->frag_off) & IP_OFFSET)
+ if (ntohs(iph.frag_off) & IP_OFFSET)
break;
/* Max length: 25 "INCOMPLETE [65535 bytes] " */
- if (datalen < sizeof (*esph)) {
- printk("INCOMPLETE [%u bytes] ", datalen);
+ if (skb_copy_bits(skb, iphoff+iph.ihl*4, &esph, sizeof(esph))
+ < 0) {
+ printk("INCOMPLETE [%u bytes] ",
+ skb->len - iphoff - iph.ihl*4);
break;
}
/* Length: 15 "SPI=0xF1234567 " */
- printk("SPI=0x%x ", ntohl(esph->spi) );
+ printk("SPI=0x%x ", ntohl(esph.spi));
break;
}
/* Max length: 10 "PROTO 255 " */
default:
- printk("PROTO=%u ", iph->protocol);
+ printk("PROTO=%u ", iph.protocol);
}
/* Proto Max log string length */
@@ -272,13 +300,12 @@ static void dump_packet(const struct ipt_log_info *info,
static unsigned int
ipt_log_target(struct sk_buff **pskb,
- unsigned int hooknum,
const struct net_device *in,
const struct net_device *out,
+ unsigned int hooknum,
const void *targinfo,
void *userinfo)
{
- struct iphdr *iph = (*pskb)->nh.iph;
const struct ipt_log_info *loginfo = targinfo;
char level_string[4] = "< >";
@@ -304,7 +331,8 @@ ipt_log_target(struct sk_buff **pskb,
if (in && !out) {
/* MAC logging for input chain only. */
printk("MAC=");
- if ((*pskb)->dev && (*pskb)->dev->hard_header_len && (*pskb)->mac.raw != (void*)iph) {
+ if ((*pskb)->dev && (*pskb)->dev->hard_header_len
+ && (*pskb)->mac.raw != (void*)(*pskb)->nh.iph) {
int i;
unsigned char *p = (*pskb)->mac.raw;
for (i = 0; i < (*pskb)->dev->hard_header_len; i++,p++)
@@ -315,7 +343,7 @@ ipt_log_target(struct sk_buff **pskb,
printk(" ");
}
- dump_packet(loginfo, iph, (*pskb)->len, 1);
+ dump_packet(loginfo, *pskb, 0);
printk("\n");
spin_unlock_bh(&log_lock);
diff --git a/net/ipv4/netfilter/ipt_MARK.c b/net/ipv4/netfilter/ipt_MARK.c
index 88ee79eee79d..e84b3616c396 100644
--- a/net/ipv4/netfilter/ipt_MARK.c
+++ b/net/ipv4/netfilter/ipt_MARK.c
@@ -9,9 +9,9 @@
static unsigned int
target(struct sk_buff **pskb,
- unsigned int hooknum,
const struct net_device *in,
const struct net_device *out,
+ unsigned int hooknum,
const void *targinfo,
void *userinfo)
{
diff --git a/net/ipv4/netfilter/ipt_MASQUERADE.c b/net/ipv4/netfilter/ipt_MASQUERADE.c
index 17123f5be547..6f191a380b2b 100644
--- a/net/ipv4/netfilter/ipt_MASQUERADE.c
+++ b/net/ipv4/netfilter/ipt_MASQUERADE.c
@@ -57,9 +57,9 @@ masquerade_check(const char *tablename,
static unsigned int
masquerade_target(struct sk_buff **pskb,
- unsigned int hooknum,
const struct net_device *in,
const struct net_device *out,
+ unsigned int hooknum,
const void *targinfo,
void *userinfo)
{
diff --git a/net/ipv4/netfilter/ipt_MIRROR.c b/net/ipv4/netfilter/ipt_MIRROR.c
index 7d4e28a407f1..fcd424bd73b8 100644
--- a/net/ipv4/netfilter/ipt_MIRROR.c
+++ b/net/ipv4/netfilter/ipt_MIRROR.c
@@ -65,18 +65,22 @@ static int route_mirror(struct sk_buff *skb)
return 0;
}
-static void
-ip_rewrite(struct sk_buff *skb)
+static int ip_rewrite(struct sk_buff **pskb)
{
- struct iphdr *iph = skb->nh.iph;
- u32 odaddr = iph->saddr;
- u32 osaddr = iph->daddr;
+ u32 odaddr, osaddr;
- skb->nfcache |= NFC_ALTERED;
+ if (!skb_ip_make_writable(pskb, sizeof(struct iphdr)))
+ return 0;
+
+ odaddr = (*pskb)->nh.iph->saddr;
+ osaddr = (*pskb)->nh.iph->daddr;
+
+ (*pskb)->nfcache |= NFC_ALTERED;
/* Rewrite IP header */
- iph->daddr = odaddr;
- iph->saddr = osaddr;
+ (*pskb)->nh.iph->daddr = odaddr;
+ (*pskb)->nh.iph->saddr = osaddr;
+ return 1;
}
/* Stolen from ip_finish_output2 */
@@ -100,29 +104,28 @@ static void ip_direct_send(struct sk_buff *skb)
}
static unsigned int ipt_mirror_target(struct sk_buff **pskb,
- unsigned int hooknum,
const struct net_device *in,
const struct net_device *out,
+ unsigned int hooknum,
const void *targinfo,
void *userinfo)
{
- if (((*pskb)->dst != NULL) &&
- route_mirror(*pskb)) {
-
- ip_rewrite(*pskb);
+ if (((*pskb)->dst != NULL) && route_mirror(*pskb)) {
+ if (!ip_rewrite(pskb))
+ return NF_DROP;
/* If we are not at FORWARD hook (INPUT/PREROUTING),
* the TTL isn't decreased by the IP stack */
if (hooknum != NF_IP_FORWARD) {
- struct iphdr *iph = (*pskb)->nh.iph;
- if (iph->ttl <= 1) {
+ if ((*pskb)->nh.iph->ttl <= 1) {
/* this will traverse normal stack, and
* thus call conntrack on the icmp packet */
icmp_send(*pskb, ICMP_TIME_EXCEEDED,
ICMP_EXC_TTL, 0);
return NF_DROP;
}
- ip_decrease_ttl(iph);
+ /* Made writable by ip_rewrite */
+ ip_decrease_ttl((*pskb)->nh.iph);
}
/* Don't let conntrack code see this packet:
diff --git a/net/ipv4/netfilter/ipt_REDIRECT.c b/net/ipv4/netfilter/ipt_REDIRECT.c
index 133abf5395bf..6556510af361 100644
--- a/net/ipv4/netfilter/ipt_REDIRECT.c
+++ b/net/ipv4/netfilter/ipt_REDIRECT.c
@@ -53,9 +53,9 @@ redirect_check(const char *tablename,
static unsigned int
redirect_target(struct sk_buff **pskb,
- unsigned int hooknum,
const struct net_device *in,
const struct net_device *out,
+ unsigned int hooknum,
const void *targinfo,
void *userinfo)
{
diff --git a/net/ipv4/netfilter/ipt_REJECT.c b/net/ipv4/netfilter/ipt_REJECT.c
index f844457fbdff..2582a825f499 100644
--- a/net/ipv4/netfilter/ipt_REJECT.c
+++ b/net/ipv4/netfilter/ipt_REJECT.c
@@ -29,152 +29,140 @@ static void connection_attach(struct sk_buff *new_skb, struct nf_ct_info *nfct)
void (*attach)(struct sk_buff *, struct nf_ct_info *);
/* Avoid module unload race with ip_ct_attach being NULLed out */
- if (nfct && (attach = ip_ct_attach) != NULL)
+ if (nfct && (attach = ip_ct_attach) != NULL) {
+ mb(); /* Just to be sure: must be read before executing this */
attach(new_skb, nfct);
+ }
}
/* Send RST reply */
-static void send_reset(struct sk_buff *oldskb, int local)
+static unsigned int send_reset(struct sk_buff **pskb, int local)
{
- struct sk_buff *nskb;
- struct tcphdr *otcph, *tcph;
+ struct tcphdr tcph;
struct rtable *rt;
- unsigned int otcplen;
u_int16_t tmp_port;
u_int32_t tmp_addr;
- int needs_ack;
- int hh_len;
+ int needs_ack, hh_len, datalen;
+ struct nf_ct_info *oldnfct;
- /* IP header checks: fragment, too short. */
- if (oldskb->nh.iph->frag_off & htons(IP_OFFSET)
- || oldskb->len < (oldskb->nh.iph->ihl<<2) + sizeof(struct tcphdr))
- return;
+ /* No RSTs for fragments. */
+ if ((*pskb)->nh.iph->frag_off & htons(IP_OFFSET))
+ return NF_DROP;
- otcph = (struct tcphdr *)((u_int32_t*)oldskb->nh.iph + oldskb->nh.iph->ihl);
- otcplen = oldskb->len - oldskb->nh.iph->ihl*4;
+ if (skb_copy_bits(*pskb, (*pskb)->nh.iph->ihl*4,
+ &tcph, sizeof(tcph)) < 0)
+ return NF_DROP;
/* No RST for RST. */
- if (otcph->rst)
- return;
-
- /* Check checksum. */
- if (tcp_v4_check(otcph, otcplen, oldskb->nh.iph->saddr,
- oldskb->nh.iph->daddr,
- csum_partial((char *)otcph, otcplen, 0)) != 0)
- return;
-
+ if (tcph.rst)
+ return NF_DROP;
+ /* FIXME: Check checksum. */
{
struct flowi fl = { .nl_u = { .ip4_u =
- { .daddr = oldskb->nh.iph->saddr,
+ { .daddr = (*pskb)->nh.iph->saddr,
.saddr = (local ?
- oldskb->nh.iph->daddr :
+ (*pskb)->nh.iph->daddr :
0),
- .tos = RT_TOS(oldskb->nh.iph->tos) } } };
+ .tos = RT_TOS((*pskb)->nh.iph->tos) } } };
/* Routing: if not headed for us, route won't like source */
if (ip_route_output_key(&rt, &fl))
- return;
-
+ return NF_DROP;
+
hh_len = (rt->u.dst.dev->hard_header_len + 15)&~15;
}
-
- /* Copy skb (even if skb is about to be dropped, we can't just
- clone it because there may be other things, such as tcpdump,
- interested in it). We also need to expand headroom in case
- hh_len of incoming interface < hh_len of outgoing interface */
- nskb = skb_copy_expand(oldskb, hh_len, skb_tailroom(oldskb),
- GFP_ATOMIC);
- if (!nskb) {
- dst_release(&rt->u.dst);
- return;
+ /* We're going to flip the header around, drop options and data. */
+ if (!skb_ip_make_writable(pskb, (*pskb)->nh.iph->ihl*4+sizeof(tcph))) {
+ ip_rt_put(rt);
+ return NF_DROP;
}
- dst_release(nskb->dst);
- nskb->dst = &rt->u.dst;
+ (*pskb)->h.th = (void *)(*pskb)->nh.iph + sizeof(tcph);
+ datalen = (*pskb)->len - (*pskb)->nh.iph->ihl*4 - tcph.doff*4;
+
+ /* Change over route. */
+ dst_release((*pskb)->dst);
+ (*pskb)->dst = &rt->u.dst;
/* This packet will not be the same as the other: clear nf fields */
- nf_conntrack_put(nskb->nfct);
- nskb->nfct = NULL;
- nskb->nfcache = 0;
+ (*pskb)->nfcache = 0;
#ifdef CONFIG_NETFILTER_DEBUG
- nskb->nf_debug = 0;
+ (*pskb)->nf_debug = 0;
#endif
- nskb->nfmark = 0;
-
- tcph = (struct tcphdr *)((u_int32_t*)nskb->nh.iph + nskb->nh.iph->ihl);
+ (*pskb)->nfmark = 0;
/* Swap source and dest */
- tmp_addr = nskb->nh.iph->saddr;
- nskb->nh.iph->saddr = nskb->nh.iph->daddr;
- nskb->nh.iph->daddr = tmp_addr;
- tmp_port = tcph->source;
- tcph->source = tcph->dest;
- tcph->dest = tmp_port;
+ tmp_addr = (*pskb)->nh.iph->saddr;
+ (*pskb)->nh.iph->saddr = (*pskb)->nh.iph->daddr;
+ (*pskb)->nh.iph->daddr = tmp_addr;
+ tmp_port = (*pskb)->h.th->source;
+ (*pskb)->h.th->source = (*pskb)->h.th->dest;
+ (*pskb)->h.th->dest = tmp_port;
/* Truncate to length (no data) */
- tcph->doff = sizeof(struct tcphdr)/4;
- skb_trim(nskb, nskb->nh.iph->ihl*4 + sizeof(struct tcphdr));
- nskb->nh.iph->tot_len = htons(nskb->len);
+ (*pskb)->h.th->doff = sizeof(struct tcphdr)/4;
+ skb_trim(*pskb, (*pskb)->nh.iph->ihl*4 + sizeof(struct tcphdr));
+ (*pskb)->nh.iph->tot_len = htons((*pskb)->len);
- if (tcph->ack) {
+ if ((*pskb)->h.th->ack) {
needs_ack = 0;
- tcph->seq = otcph->ack_seq;
- tcph->ack_seq = 0;
+ (*pskb)->h.th->seq = tcph.ack_seq;
+ (*pskb)->h.th->ack_seq = 0;
} else {
needs_ack = 1;
- tcph->ack_seq = htonl(ntohl(otcph->seq) + otcph->syn + otcph->fin
- + otcplen - (otcph->doff<<2));
- tcph->seq = 0;
+ (*pskb)->h.th->ack_seq = htonl(ntohl(tcph.seq)
+ + tcph.syn + tcph.fin
+ + datalen);
+ (*pskb)->h.th->seq = 0;
}
/* Reset flags */
- ((u_int8_t *)tcph)[13] = 0;
- tcph->rst = 1;
- tcph->ack = needs_ack;
+ memset((*pskb)->h.raw + 13, 0, 1);
+ (*pskb)->h.th->rst = 1;
+ (*pskb)->h.th->ack = needs_ack;
- tcph->window = 0;
- tcph->urg_ptr = 0;
+ (*pskb)->h.th->window = 0;
+ (*pskb)->h.th->urg_ptr = 0;
/* Adjust TCP checksum */
- tcph->check = 0;
- tcph->check = tcp_v4_check(tcph, sizeof(struct tcphdr),
- nskb->nh.iph->saddr,
- nskb->nh.iph->daddr,
- csum_partial((char *)tcph,
- sizeof(struct tcphdr), 0));
+ (*pskb)->h.th->check = 0;
+ (*pskb)->h.th->check
+ = tcp_v4_check((*pskb)->h.th,
+ sizeof(struct tcphdr),
+ (*pskb)->nh.iph->saddr,
+ (*pskb)->nh.iph->daddr,
+ csum_partial((*pskb)->h.raw,
+ sizeof(struct tcphdr), 0));
/* Adjust IP TTL, DF */
- nskb->nh.iph->ttl = MAXTTL;
+ (*pskb)->nh.iph->ttl = MAXTTL;
/* Set DF, id = 0 */
- nskb->nh.iph->frag_off = htons(IP_DF);
- nskb->nh.iph->id = 0;
+ (*pskb)->nh.iph->frag_off = htons(IP_DF);
+ (*pskb)->nh.iph->id = 0;
/* Adjust IP checksum */
- nskb->nh.iph->check = 0;
- nskb->nh.iph->check = ip_fast_csum((unsigned char *)nskb->nh.iph,
- nskb->nh.iph->ihl);
+ (*pskb)->nh.iph->check = 0;
+ (*pskb)->nh.iph->check = ip_fast_csum((*pskb)->nh.raw,
+ (*pskb)->nh.iph->ihl);
/* "Never happens" */
- if (nskb->len > dst_pmtu(nskb->dst))
- goto free_nskb;
+ if ((*pskb)->len > dst_pmtu((*pskb)->dst))
+ return NF_DROP;
- connection_attach(nskb, oldskb->nfct);
+ /* Related to old connection. */
+ oldnfct = (*pskb)->nfct;
+ connection_attach(*pskb, oldnfct);
+ nf_conntrack_put(oldnfct);
- NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, nskb, NULL, nskb->dst->dev,
+ NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, *pskb, NULL, (*pskb)->dst->dev,
ip_finish_output);
- return;
-
- free_nskb:
- kfree_skb(nskb);
+ return NF_STOLEN;
}
-static void send_unreach(struct sk_buff *skb_in, int code)
+static void send_unreach(const struct sk_buff *skb_in, int code)
{
- struct iphdr *iph;
- struct udphdr *udph;
- struct icmphdr *icmph;
struct sk_buff *nskb;
u32 saddr;
u8 tos;
@@ -189,8 +177,6 @@ static void send_unreach(struct sk_buff *skb_in, int code)
if (!xrlim_allow(&rt->u.dst, 1*HZ))
return;
- iph = skb_in->nh.iph;
-
/* No replies to physical multicast/broadcast */
if (skb_in->pkt_type!=PACKET_HOST)
return;
@@ -200,46 +186,41 @@ static void send_unreach(struct sk_buff *skb_in, int code)
return;
/* Only reply to fragment 0. */
- if (iph->frag_off&htons(IP_OFFSET))
+ if (skb_in->nh.iph->frag_off&htons(IP_OFFSET))
+ return;
+
+ /* Ensure we have at least 8 bytes of proto header. */
+ if (skb_in->len < skb_in->nh.iph->ihl*4 + 8)
return;
- /* if UDP checksum is set, verify it's correct */
- if (iph->protocol == IPPROTO_UDP
- && skb_in->tail-(u8*)iph >= sizeof(struct udphdr)) {
- int datalen = skb_in->len - (iph->ihl<<2);
- udph = (struct udphdr *)((char *)iph + (iph->ihl<<2));
- if (udph->check
- && csum_tcpudp_magic(iph->saddr, iph->daddr,
- datalen, IPPROTO_UDP,
- csum_partial((char *)udph, datalen,
- 0)) != 0)
- return;
- }
-
/* If we send an ICMP error to an ICMP error a mess would result.. */
- if (iph->protocol == IPPROTO_ICMP
- && skb_in->tail-(u8*)iph >= sizeof(struct icmphdr)) {
- icmph = (struct icmphdr *)((char *)iph + (iph->ihl<<2));
+ if (skb_in->nh.iph->protocol == IPPROTO_ICMP) {
+ struct icmphdr icmph;
+
+ if (skb_copy_bits(skb_in, skb_in->nh.iph->ihl*4,
+ &icmph, sizeof(icmph)) < 0)
+ return;
+
/* Between echo-reply (0) and timestamp (13),
everything except echo-request (8) is an error.
Also, anything greater than NR_ICMP_TYPES is
unknown, and hence should be treated as an error... */
- if ((icmph->type < ICMP_TIMESTAMP
- && icmph->type != ICMP_ECHOREPLY
- && icmph->type != ICMP_ECHO)
- || icmph->type > NR_ICMP_TYPES)
+ if ((icmph.type < ICMP_TIMESTAMP
+ && icmph.type != ICMP_ECHOREPLY
+ && icmph.type != ICMP_ECHO)
+ || icmph.type > NR_ICMP_TYPES)
return;
}
- saddr = iph->daddr;
+ saddr = skb_in->nh.iph->daddr;
if (!(rt->rt_flags & RTCF_LOCAL))
saddr = 0;
- tos = (iph->tos & IPTOS_TOS_MASK) | IPTOS_PREC_INTERNETCONTROL;
-
+ tos = (skb_in->nh.iph->tos & IPTOS_TOS_MASK)
+ | IPTOS_PREC_INTERNETCONTROL;
{
struct flowi fl = { .nl_u = { .ip4_u =
- { .daddr = iph->saddr,
+ { .daddr = skb_in->nh.iph->saddr,
.saddr = saddr,
.tos = RT_TOS(tos) } } };
if (ip_route_output_key(&rt, &fl))
@@ -266,40 +247,38 @@ static void send_unreach(struct sk_buff *skb_in, int code)
skb_reserve(nskb, hh_len);
/* Set up IP header */
- iph = nskb->nh.iph
- = (struct iphdr *)skb_put(nskb, sizeof(struct iphdr));
- iph->version=4;
- iph->ihl=5;
- iph->tos=tos;
- iph->tot_len = htons(length);
+ nskb->nh.iph = (struct iphdr *)skb_put(nskb, sizeof(struct iphdr));
+ nskb->nh.iph->version=4;
+ nskb->nh.iph->ihl=5;
+ nskb->nh.iph->tos=tos;
+ nskb->nh.iph->tot_len = htons(length);
/* PMTU discovery never applies to ICMP packets. */
- iph->frag_off = 0;
+ nskb->nh.iph->frag_off = 0;
- iph->ttl = MAXTTL;
- ip_select_ident(iph, &rt->u.dst, NULL);
- iph->protocol=IPPROTO_ICMP;
- iph->saddr=rt->rt_src;
- iph->daddr=rt->rt_dst;
- iph->check=0;
- iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
+ nskb->nh.iph->ttl = MAXTTL;
+ ip_select_ident(nskb->nh.iph, &rt->u.dst, NULL);
+ nskb->nh.iph->protocol=IPPROTO_ICMP;
+ nskb->nh.iph->saddr=rt->rt_src;
+ nskb->nh.iph->daddr=rt->rt_dst;
+ nskb->nh.iph->check=0;
+ nskb->nh.iph->check = ip_fast_csum(nskb->nh.raw,
+ nskb->nh.iph->ihl);
/* Set up ICMP header. */
- icmph = nskb->h.icmph
- = (struct icmphdr *)skb_put(nskb, sizeof(struct icmphdr));
- icmph->type = ICMP_DEST_UNREACH;
- icmph->code = code;
- icmph->un.gateway = 0;
- icmph->checksum = 0;
+ nskb->h.icmph = (struct icmphdr *)skb_put(nskb,sizeof(struct icmphdr));
+ nskb->h.icmph->type = ICMP_DEST_UNREACH;
+ nskb->h.icmph->code = code;
+ nskb->h.icmph->un.gateway = 0;
+ nskb->h.icmph->checksum = 0;
/* Copy as much of original packet as will fit */
data = skb_put(nskb,
length - sizeof(struct iphdr) - sizeof(struct icmphdr));
- /* FIXME: won't work with nonlinear skbs --RR */
- memcpy(data, skb_in->nh.iph,
- length - sizeof(struct iphdr) - sizeof(struct icmphdr));
- icmph->checksum = ip_compute_csum((unsigned char *)icmph,
- length - sizeof(struct iphdr));
+ skb_copy_bits(skb_in, 0, data,
+ length - sizeof(struct iphdr) - sizeof(struct icmphdr));
+ nskb->h.icmph->checksum = ip_compute_csum(nskb->h.raw,
+ length-sizeof(struct iphdr));
connection_attach(nskb, skb_in->nfct);
@@ -308,9 +287,9 @@ static void send_unreach(struct sk_buff *skb_in, int code)
}
static unsigned int reject(struct sk_buff **pskb,
- unsigned int hooknum,
const struct net_device *in,
const struct net_device *out,
+ unsigned int hooknum,
const void *targinfo,
void *userinfo)
{
@@ -344,7 +323,7 @@ static unsigned int reject(struct sk_buff **pskb,
send_unreach(*pskb, ICMP_HOST_ANO);
break;
case IPT_TCP_RESET:
- send_reset(*pskb, hooknum == NF_IP_LOCAL_IN);
+ return send_reset(pskb, hooknum == NF_IP_LOCAL_IN);
case IPT_ICMP_ECHOREPLY:
/* Doesn't happen. */
break;
diff --git a/net/ipv4/netfilter/ipt_TCPMSS.c b/net/ipv4/netfilter/ipt_TCPMSS.c
index 637cf61d27e2..04d86da8b95e 100644
--- a/net/ipv4/netfilter/ipt_TCPMSS.c
+++ b/net/ipv4/netfilter/ipt_TCPMSS.c
@@ -36,9 +36,9 @@ optlen(const u_int8_t *opt, unsigned int offset)
static unsigned int
ipt_tcpmss_target(struct sk_buff **pskb,
- unsigned int hooknum,
const struct net_device *in,
const struct net_device *out,
+ unsigned int hooknum,
const void *targinfo,
void *userinfo)
{
@@ -49,15 +49,8 @@ ipt_tcpmss_target(struct sk_buff **pskb,
unsigned int i;
u_int8_t *opt;
- /* raw socket (tcpdump) may have clone of incoming skb: don't
- disturb it --RR */
- if (skb_cloned(*pskb) && !(*pskb)->sk) {
- struct sk_buff *nskb = skb_copy(*pskb, GFP_ATOMIC);
- if (!nskb)
- return NF_DROP;
- kfree_skb(*pskb);
- *pskb = nskb;
- }
+ if (!skb_ip_make_writable(pskb, (*pskb)->len))
+ return NF_DROP;
iph = (*pskb)->nh.iph;
tcplen = (*pskb)->len - iph->ihl*4;
diff --git a/net/ipv4/netfilter/ipt_TOS.c b/net/ipv4/netfilter/ipt_TOS.c
index 05d9a727c122..ed780929606c 100644
--- a/net/ipv4/netfilter/ipt_TOS.c
+++ b/net/ipv4/netfilter/ipt_TOS.c
@@ -9,35 +9,30 @@
static unsigned int
target(struct sk_buff **pskb,
- unsigned int hooknum,
const struct net_device *in,
const struct net_device *out,
+ unsigned int hooknum,
const void *targinfo,
void *userinfo)
{
- struct iphdr *iph = (*pskb)->nh.iph;
const struct ipt_tos_target_info *tosinfo = targinfo;
- if ((iph->tos & IPTOS_TOS_MASK) != tosinfo->tos) {
+ if (((*pskb)->nh.iph->tos & IPTOS_TOS_MASK) != tosinfo->tos) {
u_int16_t diffs[2];
- /* raw socket (tcpdump) may have clone of incoming
- skb: don't disturb it --RR */
- if (skb_cloned(*pskb) && !(*pskb)->sk) {
- struct sk_buff *nskb = skb_copy(*pskb, GFP_ATOMIC);
- if (!nskb)
- return NF_DROP;
- kfree_skb(*pskb);
- *pskb = nskb;
- iph = (*pskb)->nh.iph;
- }
+ if (!skb_ip_make_writable(pskb, sizeof(struct iphdr)))
+ return NF_DROP;
- diffs[0] = htons(iph->tos) ^ 0xFFFF;
- iph->tos = (iph->tos & IPTOS_PREC_MASK) | tosinfo->tos;
- diffs[1] = htons(iph->tos);
- iph->check = csum_fold(csum_partial((char *)diffs,
- sizeof(diffs),
- iph->check^0xFFFF));
+ diffs[0] = htons((*pskb)->nh.iph->tos) ^ 0xFFFF;
+ (*pskb)->nh.iph->tos
+ = ((*pskb)->nh.iph->tos & IPTOS_PREC_MASK)
+ | tosinfo->tos;
+ diffs[1] = htons((*pskb)->nh.iph->tos);
+ (*pskb)->nh.iph->check
+ = csum_fold(csum_partial((char *)diffs,
+ sizeof(diffs),
+ (*pskb)->nh.iph->check
+ ^0xFFFF));
(*pskb)->nfcache |= NFC_ALTERED;
}
return IPT_CONTINUE;
diff --git a/net/ipv4/netfilter/ipt_ULOG.c b/net/ipv4/netfilter/ipt_ULOG.c
index f96309864ec7..51023be976ab 100644
--- a/net/ipv4/netfilter/ipt_ULOG.c
+++ b/net/ipv4/netfilter/ipt_ULOG.c
@@ -155,9 +155,9 @@ struct sk_buff *ulog_alloc_skb(unsigned int size)
}
static unsigned int ipt_ulog_target(struct sk_buff **pskb,
- unsigned int hooknum,
const struct net_device *in,
const struct net_device *out,
+ unsigned int hooknum,
const void *targinfo, void *userinfo)
{
ulog_buff_t *ub;
@@ -238,8 +238,9 @@ static unsigned int ipt_ulog_target(struct sk_buff **pskb,
else
pm->outdev_name[0] = '\0';
- if (copy_len)
- memcpy(pm->payload, (*pskb)->data, copy_len);
+ /* copy_len <= (*pskb)->len, so can't fail. */
+ if (skb_copy_bits(*pskb, 0, pm->payload, copy_len) < 0)
+ BUG();
/* check if we are building multi-part messages */
if (ub->qlen > 1) {
diff --git a/net/ipv4/netfilter/ipt_ah.c b/net/ipv4/netfilter/ipt_ah.c
index 61bdc7a39a37..af69c2b3420c 100644
--- a/net/ipv4/netfilter/ipt_ah.c
+++ b/net/ipv4/netfilter/ipt_ah.c
@@ -35,14 +35,16 @@ match(const struct sk_buff *skb,
const struct net_device *out,
const void *matchinfo,
int offset,
- const void *hdr,
- u_int16_t datalen,
int *hotdrop)
{
- const struct ahhdr *ah = hdr;
+ struct ahhdr ah;
const struct ipt_ah *ahinfo = matchinfo;
- if (offset == 0 && datalen < sizeof(struct ahhdr)) {
+ /* Must not be a fragment. */
+ if (offset)
+ return 0;
+
+ if (skb_copy_bits(skb, skb->nh.iph->ihl*4, &ah, sizeof(ah)) < 0) {
/* We've been asked to examine this packet, and we
can't. Hence, no choice but to drop. */
duprintf("Dropping evil AH tinygram.\n");
@@ -50,11 +52,9 @@ match(const struct sk_buff *skb,
return 0;
}
- /* Must not be a fragment. */
- return !offset
- && spi_match(ahinfo->spis[0], ahinfo->spis[1],
- ntohl(ah->spi),
- !!(ahinfo->invflags & IPT_AH_INV_SPI));
+ return spi_match(ahinfo->spis[0], ahinfo->spis[1],
+ ntohl(ah.spi),
+ !!(ahinfo->invflags & IPT_AH_INV_SPI));
}
/* Called when user tries to insert an entry of this type. */
diff --git a/net/ipv4/netfilter/ipt_conntrack.c b/net/ipv4/netfilter/ipt_conntrack.c
index af9d3912a20c..1183e61fa871 100644
--- a/net/ipv4/netfilter/ipt_conntrack.c
+++ b/net/ipv4/netfilter/ipt_conntrack.c
@@ -14,8 +14,6 @@ match(const struct sk_buff *skb,
const struct net_device *out,
const void *matchinfo,
int offset,
- const void *hdr,
- u_int16_t datalen,
int *hotdrop)
{
const struct ipt_conntrack_info *sinfo = matchinfo;
diff --git a/net/ipv4/netfilter/ipt_dscp.c b/net/ipv4/netfilter/ipt_dscp.c
index 7d840322b99b..cc48b65ab191 100644
--- a/net/ipv4/netfilter/ipt_dscp.c
+++ b/net/ipv4/netfilter/ipt_dscp.c
@@ -19,8 +19,7 @@ MODULE_LICENSE("GPL");
static int match(const struct sk_buff *skb, const struct net_device *in,
const struct net_device *out, const void *matchinfo,
- int offset, const void *hdr, u_int16_t datalen,
- int *hotdrop)
+ int offset, int *hotdrop)
{
const struct ipt_dscp_info *info = matchinfo;
const struct iphdr *iph = skb->nh.iph;
diff --git a/net/ipv4/netfilter/ipt_ecn.c b/net/ipv4/netfilter/ipt_ecn.c
index 58d12ff190f4..34947c2c03b7 100644
--- a/net/ipv4/netfilter/ipt_ecn.c
+++ b/net/ipv4/netfilter/ipt_ecn.c
@@ -19,34 +19,40 @@ MODULE_DESCRIPTION("IP tables ECN matching module");
MODULE_LICENSE("GPL");
static inline int match_ip(const struct sk_buff *skb,
- const struct iphdr *iph,
const struct ipt_ecn_info *einfo)
{
- return ((iph->tos&IPT_ECN_IP_MASK) == einfo->ip_ect);
+ return ((skb->nh.iph->tos&IPT_ECN_IP_MASK) == einfo->ip_ect);
}
static inline int match_tcp(const struct sk_buff *skb,
- const struct iphdr *iph,
- const struct ipt_ecn_info *einfo)
+ const struct ipt_ecn_info *einfo,
+ int *hotdrop)
{
- struct tcphdr *tcph = (void *)iph + iph->ihl*4;
+ struct tcphdr tcph;
+
+ /* In practice, TCP match does this, so can't fail. But let's
+ be good citizens. */
+ if (skb_copy_bits(skb, skb->nh.iph->ihl*4, &tcph, sizeof(tcph)) < 0) {
+ *hotdrop = 0;
+ return 0;
+ }
if (einfo->operation & IPT_ECN_OP_MATCH_ECE) {
if (einfo->invert & IPT_ECN_OP_MATCH_ECE) {
- if (tcph->ece == 1)
+ if (tcph.ece == 1)
return 0;
} else {
- if (tcph->ece == 0)
+ if (tcph.ece == 0)
return 0;
}
}
if (einfo->operation & IPT_ECN_OP_MATCH_CWR) {
if (einfo->invert & IPT_ECN_OP_MATCH_CWR) {
- if (tcph->cwr == 1)
+ if (tcph.cwr == 1)
return 0;
} else {
- if (tcph->cwr == 0)
+ if (tcph.cwr == 0)
return 0;
}
}
@@ -56,20 +62,18 @@ static inline int match_tcp(const struct sk_buff *skb,
static int match(const struct sk_buff *skb, const struct net_device *in,
const struct net_device *out, const void *matchinfo,
- int offset, const void *hdr, u_int16_t datalen,
- int *hotdrop)
+ int offset, int *hotdrop)
{
const struct ipt_ecn_info *info = matchinfo;
- const struct iphdr *iph = skb->nh.iph;
if (info->operation & IPT_ECN_OP_MATCH_IP)
- if (!match_ip(skb, iph, info))
+ if (!match_ip(skb, info))
return 0;
if (info->operation & (IPT_ECN_OP_MATCH_ECE|IPT_ECN_OP_MATCH_CWR)) {
- if (iph->protocol != IPPROTO_TCP)
+ if (skb->nh.iph->protocol != IPPROTO_TCP)
return 0;
- if (!match_tcp(skb, iph, info))
+ if (!match_tcp(skb, info, hotdrop))
return 0;
}
diff --git a/net/ipv4/netfilter/ipt_esp.c b/net/ipv4/netfilter/ipt_esp.c
index 46ca560f358b..1a52aae22e01 100644
--- a/net/ipv4/netfilter/ipt_esp.c
+++ b/net/ipv4/netfilter/ipt_esp.c
@@ -35,14 +35,16 @@ match(const struct sk_buff *skb,
const struct net_device *out,
const void *matchinfo,
int offset,
- const void *hdr,
- u_int16_t datalen,
int *hotdrop)
{
- const struct esphdr *esp = hdr;
+ struct esphdr esp;
const struct ipt_esp *espinfo = matchinfo;
- if (offset == 0 && datalen < sizeof(struct esphdr)) {
+ /* Must not be a fragment. */
+ if (offset)
+ return 0;
+
+ if (skb_copy_bits(skb, skb->nh.iph->ihl*4, &esp, sizeof(esp)) < 0) {
/* We've been asked to examine this packet, and we
can't. Hence, no choice but to drop. */
duprintf("Dropping evil ESP tinygram.\n");
@@ -50,11 +52,9 @@ match(const struct sk_buff *skb,
return 0;
}
- /* Must not be a fragment. */
- return !offset
- && spi_match(espinfo->spis[0], espinfo->spis[1],
- ntohl(esp->spi),
- !!(espinfo->invflags & IPT_ESP_INV_SPI));
+ return spi_match(espinfo->spis[0], espinfo->spis[1],
+ ntohl(esp.spi),
+ !!(espinfo->invflags & IPT_ESP_INV_SPI));
}
/* Called when user tries to insert an entry of this type. */
diff --git a/net/ipv4/netfilter/ipt_helper.c b/net/ipv4/netfilter/ipt_helper.c
index 7f0997fbef5e..1f7efb91c5e8 100644
--- a/net/ipv4/netfilter/ipt_helper.c
+++ b/net/ipv4/netfilter/ipt_helper.c
@@ -28,8 +28,6 @@ match(const struct sk_buff *skb,
const struct net_device *out,
const void *matchinfo,
int offset,
- const void *hdr,
- u_int16_t datalen,
int *hotdrop)
{
const struct ipt_helper_info *info = matchinfo;
diff --git a/net/ipv4/netfilter/ipt_length.c b/net/ipv4/netfilter/ipt_length.c
index 91cf0a76a89c..7f3d05827fca 100644
--- a/net/ipv4/netfilter/ipt_length.c
+++ b/net/ipv4/netfilter/ipt_length.c
@@ -15,8 +15,6 @@ match(const struct sk_buff *skb,
const struct net_device *out,
const void *matchinfo,
int offset,
- const void *hdr,
- u_int16_t datalen,
int *hotdrop)
{
const struct ipt_length_info *info = matchinfo;
diff --git a/net/ipv4/netfilter/ipt_limit.c b/net/ipv4/netfilter/ipt_limit.c
index 515acef2279e..d2fa9c57b0f2 100644
--- a/net/ipv4/netfilter/ipt_limit.c
+++ b/net/ipv4/netfilter/ipt_limit.c
@@ -47,8 +47,6 @@ ipt_limit_match(const struct sk_buff *skb,
const struct net_device *out,
const void *matchinfo,
int offset,
- const void *hdr,
- u_int16_t datalen,
int *hotdrop)
{
struct ipt_rateinfo *r = ((struct ipt_rateinfo *)matchinfo)->master;
diff --git a/net/ipv4/netfilter/ipt_mac.c b/net/ipv4/netfilter/ipt_mac.c
index 7a5ed1c5993e..fc6a47974030 100644
--- a/net/ipv4/netfilter/ipt_mac.c
+++ b/net/ipv4/netfilter/ipt_mac.c
@@ -12,8 +12,6 @@ match(const struct sk_buff *skb,
const struct net_device *out,
const void *matchinfo,
int offset,
- const void *hdr,
- u_int16_t datalen,
int *hotdrop)
{
const struct ipt_mac_info *info = matchinfo;
diff --git a/net/ipv4/netfilter/ipt_mark.c b/net/ipv4/netfilter/ipt_mark.c
index 14154f02aa80..2673b477fa2b 100644
--- a/net/ipv4/netfilter/ipt_mark.c
+++ b/net/ipv4/netfilter/ipt_mark.c
@@ -11,8 +11,6 @@ match(const struct sk_buff *skb,
const struct net_device *out,
const void *matchinfo,
int offset,
- const void *hdr,
- u_int16_t datalen,
int *hotdrop)
{
const struct ipt_mark_info *info = matchinfo;
diff --git a/net/ipv4/netfilter/ipt_multiport.c b/net/ipv4/netfilter/ipt_multiport.c
index 6b7bc044f58d..ffe25aa899a3 100644
--- a/net/ipv4/netfilter/ipt_multiport.c
+++ b/net/ipv4/netfilter/ipt_multiport.c
@@ -39,15 +39,18 @@ match(const struct sk_buff *skb,
const struct net_device *out,
const void *matchinfo,
int offset,
- const void *hdr,
- u_int16_t datalen,
int *hotdrop)
{
- const struct udphdr *udp = hdr;
+ u16 ports[2];
const struct ipt_multiport *multiinfo = matchinfo;
- /* Must be big enough to read ports. */
- if (offset == 0 && datalen < sizeof(struct udphdr)) {
+ /* Must not be a fragment. */
+ if (offset)
+ return 0;
+
+ /* Must be big enough to read ports (both UDP and TCP have
+ them at the start). */
+ if (skb_copy_bits(skb, skb->nh.iph->ihl*4, ports, sizeof(ports)) < 0) {
/* We've been asked to examine this packet, and we
can't. Hence, no choice but to drop. */
duprintf("ipt_multiport:"
@@ -56,11 +59,9 @@ match(const struct sk_buff *skb,
return 0;
}
- /* Must not be a fragment. */
- return !offset
- && ports_match(multiinfo->ports,
- multiinfo->flags, multiinfo->count,
- ntohs(udp->source), ntohs(udp->dest));
+ return ports_match(multiinfo->ports,
+ multiinfo->flags, multiinfo->count,
+ ntohs(ports[0]), ntohs(ports[1]));
}
/* Called when user tries to insert an entry of this type. */
diff --git a/net/ipv4/netfilter/ipt_owner.c b/net/ipv4/netfilter/ipt_owner.c
index d1d439096820..5cd5efa04d04 100644
--- a/net/ipv4/netfilter/ipt_owner.c
+++ b/net/ipv4/netfilter/ipt_owner.c
@@ -115,8 +115,6 @@ match(const struct sk_buff *skb,
const struct net_device *out,
const void *matchinfo,
int offset,
- const void *hdr,
- u_int16_t datalen,
int *hotdrop)
{
const struct ipt_owner_info *info = matchinfo;
@@ -170,8 +168,11 @@ checkentry(const char *tablename,
return 0;
}
- if (matchsize != IPT_ALIGN(sizeof(struct ipt_owner_info)))
+ if (matchsize != IPT_ALIGN(sizeof(struct ipt_owner_info))) {
+ printk("Matchsize %u != %Zu\n", matchsize,
+ IPT_ALIGN(sizeof(struct ipt_owner_info)));
return 0;
+ }
return 1;
}
diff --git a/net/ipv4/netfilter/ipt_physdev.c b/net/ipv4/netfilter/ipt_physdev.c
index 642d32d3288b..bc439d4f95e0 100644
--- a/net/ipv4/netfilter/ipt_physdev.c
+++ b/net/ipv4/netfilter/ipt_physdev.c
@@ -14,8 +14,6 @@ match(const struct sk_buff *skb,
const struct net_device *out,
const void *matchinfo,
int offset,
- const void *hdr,
- u_int16_t datalen,
int *hotdrop)
{
int i;
diff --git a/net/ipv4/netfilter/ipt_pkttype.c b/net/ipv4/netfilter/ipt_pkttype.c
index b59cd2fddb2a..047700095dca 100644
--- a/net/ipv4/netfilter/ipt_pkttype.c
+++ b/net/ipv4/netfilter/ipt_pkttype.c
@@ -13,8 +13,6 @@ static int match(const struct sk_buff *skb,
const struct net_device *out,
const void *matchinfo,
int offset,
- const void *hdr,
- u_int16_t datalen,
int *hotdrop)
{
const struct ipt_pkttype_info *info = matchinfo;
diff --git a/net/ipv4/netfilter/ipt_state.c b/net/ipv4/netfilter/ipt_state.c
index 026f1039dc9e..0f3eaeddfa92 100644
--- a/net/ipv4/netfilter/ipt_state.c
+++ b/net/ipv4/netfilter/ipt_state.c
@@ -13,8 +13,6 @@ match(const struct sk_buff *skb,
const struct net_device *out,
const void *matchinfo,
int offset,
- const void *hdr,
- u_int16_t datalen,
int *hotdrop)
{
const struct ipt_state_info *sinfo = matchinfo;
diff --git a/net/ipv4/netfilter/ipt_tcpmss.c b/net/ipv4/netfilter/ipt_tcpmss.c
index 0f3e38b9951e..7ddfe78d5a3d 100644
--- a/net/ipv4/netfilter/ipt_tcpmss.c
+++ b/net/ipv4/netfilter/ipt_tcpmss.c
@@ -11,24 +11,32 @@
/* Returns 1 if the mss option is set and matched by the range, 0 otherwise */
static inline int
mssoption_match(u_int16_t min, u_int16_t max,
- const struct tcphdr *tcp,
- u_int16_t datalen,
+ const struct sk_buff *skb,
int invert,
int *hotdrop)
{
- unsigned int i;
- const u_int8_t *opt = (u_int8_t *)tcp;
+ struct tcphdr tcph;
+ /* tcp.doff is only 4 bits, ie. max 15 * 4 bytes */
+ u8 opt[15 * 4 - sizeof(tcph)];
+ unsigned int i, optlen;
/* If we don't have the whole header, drop packet. */
- if (tcp->doff * 4 > datalen) {
- *hotdrop = 1;
- return 0;
- }
-
- for (i = sizeof(struct tcphdr); i < tcp->doff * 4; ) {
- if ((opt[i] == TCPOPT_MSS)
- && ((tcp->doff * 4 - i) >= TCPOLEN_MSS)
- && (opt[i+1] == TCPOLEN_MSS)) {
+ if (skb_copy_bits(skb, skb->nh.iph->ihl*4, &tcph, sizeof(tcph)) < 0)
+ goto dropit;
+
+ /* Malformed. */
+ if (tcph.doff*4 < sizeof(tcph))
+ goto dropit;
+
+ optlen = tcph.doff*4 - sizeof(tcph);
+ /* Truncated options. */
+ if (skb_copy_bits(skb, skb->nh.iph->ihl*4+sizeof(tcph), opt, optlen)<0)
+ goto dropit;
+
+ for (i = 0; i < optlen; ) {
+ if (opt[i] == TCPOPT_MSS
+ && (optlen - i) >= TCPOLEN_MSS
+ && opt[i+1] == TCPOLEN_MSS) {
u_int16_t mssval;
mssval = (opt[i+2] << 8) | opt[i+3];
@@ -38,8 +46,11 @@ mssoption_match(u_int16_t min, u_int16_t max,
if (opt[i] < 2) i++;
else i += opt[i+1]?:1;
}
-
return invert;
+
+ dropit:
+ *hotdrop = 1;
+ return 0;
}
static int
@@ -48,15 +59,11 @@ match(const struct sk_buff *skb,
const struct net_device *out,
const void *matchinfo,
int offset,
- const void *hdr,
- u_int16_t datalen,
int *hotdrop)
{
const struct ipt_tcpmss_match_info *info = matchinfo;
- const struct tcphdr *tcph = (void *)skb->nh.iph + skb->nh.iph->ihl*4;
- return mssoption_match(info->mss_min, info->mss_max, tcph,
- skb->len - skb->nh.iph->ihl*4,
+ return mssoption_match(info->mss_min, info->mss_max, skb,
info->invert, hotdrop);
}
diff --git a/net/ipv4/netfilter/ipt_tos.c b/net/ipv4/netfilter/ipt_tos.c
index 11c94302c774..2c58fa826660 100644
--- a/net/ipv4/netfilter/ipt_tos.c
+++ b/net/ipv4/netfilter/ipt_tos.c
@@ -11,14 +11,11 @@ match(const struct sk_buff *skb,
const struct net_device *out,
const void *matchinfo,
int offset,
- const void *hdr,
- u_int16_t datalen,
int *hotdrop)
{
const struct ipt_tos_info *info = matchinfo;
- const struct iphdr *iph = skb->nh.iph;
- return (iph->tos == info->tos) ^ info->invert;
+ return (skb->nh.iph->tos == info->tos) ^ info->invert;
}
static int
diff --git a/net/ipv4/netfilter/ipt_ttl.c b/net/ipv4/netfilter/ipt_ttl.c
index b9657e1d9200..15534e77dd4c 100644
--- a/net/ipv4/netfilter/ipt_ttl.c
+++ b/net/ipv4/netfilter/ipt_ttl.c
@@ -19,24 +19,22 @@ MODULE_LICENSE("GPL");
static int match(const struct sk_buff *skb, const struct net_device *in,
const struct net_device *out, const void *matchinfo,
- int offset, const void *hdr, u_int16_t datalen,
- int *hotdrop)
+ int offset, int *hotdrop)
{
const struct ipt_ttl_info *info = matchinfo;
- const struct iphdr *iph = skb->nh.iph;
switch (info->mode) {
case IPT_TTL_EQ:
- return (iph->ttl == info->ttl);
+ return (skb->nh.iph->ttl == info->ttl);
break;
case IPT_TTL_NE:
- return (!(iph->ttl == info->ttl));
+ return (!(skb->nh.iph->ttl == info->ttl));
break;
case IPT_TTL_LT:
- return (iph->ttl < info->ttl);
+ return (skb->nh.iph->ttl < info->ttl);
break;
case IPT_TTL_GT:
- return (iph->ttl > info->ttl);
+ return (skb->nh.iph->ttl > info->ttl);
break;
default:
printk(KERN_WARNING "ipt_ttl: unknown mode %d\n",
diff --git a/net/ipv4/netfilter/ipt_unclean.c b/net/ipv4/netfilter/ipt_unclean.c
index 6c96d2729d94..16c3c9879f5e 100644
--- a/net/ipv4/netfilter/ipt_unclean.c
+++ b/net/ipv4/netfilter/ipt_unclean.c
@@ -31,16 +31,17 @@ struct icmp_info
};
static int
-check_ip(struct iphdr *iph, size_t length, int embedded);
+check_ip(const struct sk_buff *skb, unsigned int offset);
/* ICMP-specific checks. */
static int
-check_icmp(const struct icmphdr *icmph,
- u_int16_t datalen,
+check_icmp(const struct sk_buff *skb,
unsigned int offset,
+ unsigned int fragoff,
int more_frags,
int embedded)
{
+ struct icmphdr icmph;
static struct icmp_info info[]
= { [ICMP_ECHOREPLY]
= { 8, 65536, ICMP_NOT_ERROR, 0, 0 },
@@ -76,92 +77,95 @@ check_icmp(const struct icmphdr *icmph,
= { 12, 12, ICMP_NOT_ERROR, 0, 0 } };
/* Can't do anything if it's a fragment. */
- if (offset)
+ if (fragoff)
return 1;
- /* Must cover type and code. */
- if (datalen < 2) {
- limpk("ICMP len=%u too short\n", datalen);
+ /* CHECK: Must have whole header.. */
+ if (skb_copy_bits(skb, offset, &icmph, sizeof(icmph)) < 0) {
+ limpk("ICMP len=%u too short\n", skb->len - offset);
return 0;
}
- /* If not embedded. */
+ /* If not embedded in an ICMP error already. */
if (!embedded) {
- /* Bad checksum? Don't print, just ignore. */
- if (!more_frags
- && ip_compute_csum((unsigned char *) icmph, datalen) != 0)
- return 0;
-
/* CHECK: Truncated ICMP (even if first fragment). */
- if (icmph->type < sizeof(info)/sizeof(struct icmp_info)
- && info[icmph->type].min_len != 0
- && datalen < info[icmph->type].min_len) {
+ if (icmph.type < sizeof(info)/sizeof(struct icmp_info)
+ && info[icmph.type].min_len != 0
+ && skb->len - offset < info[icmph.type].min_len) {
limpk("ICMP type %u len %u too short\n",
- icmph->type, datalen);
+ icmph.type, skb->len - offset);
return 0;
}
/* CHECK: Check within known error ICMPs. */
- if (icmph->type < sizeof(info)/sizeof(struct icmp_info)
- && info[icmph->type].err == ICMP_IS_ERROR) {
+ if (icmph.type < sizeof(info)/sizeof(struct icmp_info)
+ && info[icmph.type].err == ICMP_IS_ERROR) {
+ /* Max IP header size = 60 */
+ char inner[60 + 8];
+ struct iphdr *inner_ip = (struct iphdr *)inner;
+
/* CHECK: Embedded packet must be at least
length of iph + 8 bytes. */
- struct iphdr *inner = (void *)icmph + 8;
-
- /* datalen > 8 since all ICMP_IS_ERROR types
- have min length > 8 */
- if (datalen - 8 < sizeof(struct iphdr)) {
+ if (skb_copy_bits(skb, offset + sizeof(icmph),
+ inner, sizeof(struct iphdr)+8) < 0) {
limpk("ICMP error internal way too short\n");
return 0;
}
- if (datalen - 8 < inner->ihl*4 + 8) {
+
+ /* iphhdr may actually be longer: still need 8
+ actual protocol bytes. */
+ if (offset + sizeof(icmph) + inner_ip->ihl*4 + 8
+ > skb->len) {
limpk("ICMP error internal too short\n");
return 0;
}
- if (!check_ip(inner, datalen - 8, 1))
+ if (!check_ip(skb, offset + sizeof(icmph)))
return 0;
}
} else {
/* CHECK: Can't embed ICMP unless known non-error. */
- if (icmph->type >= sizeof(info)/sizeof(struct icmp_info)
- || info[icmph->type].err != ICMP_NOT_ERROR) {
+ if (icmph.type >= sizeof(info)/sizeof(struct icmp_info)
+ || info[icmph.type].err != ICMP_NOT_ERROR) {
limpk("ICMP type %u not embeddable\n",
- icmph->type);
+ icmph.type);
return 0;
}
}
/* CHECK: Invalid ICMP codes. */
- if (icmph->type < sizeof(info)/sizeof(struct icmp_info)
- && (icmph->code < info[icmph->type].min_code
- || icmph->code > info[icmph->type].max_code)) {
+ if (icmph.type < sizeof(info)/sizeof(struct icmp_info)
+ && (icmph.code < info[icmph.type].min_code
+ || icmph.code > info[icmph.type].max_code)) {
limpk("ICMP type=%u code=%u\n",
- icmph->type, icmph->code);
+ icmph.type, icmph.code);
return 0;
}
/* CHECK: Above maximum length. */
- if (icmph->type < sizeof(info)/sizeof(struct icmp_info)
- && info[icmph->type].max_len != 0
- && datalen > info[icmph->type].max_len) {
+ if (icmph.type < sizeof(info)/sizeof(struct icmp_info)
+ && info[icmph.type].max_len != 0
+ && skb->len - offset > info[icmph.type].max_len) {
limpk("ICMP type=%u too long: %u bytes\n",
- icmph->type, datalen);
+ icmph.type, skb->len - offset);
return 0;
}
- switch (icmph->type) {
+ switch (icmph.type) {
case ICMP_PARAMETERPROB: {
/* CHECK: Problem param must be within error packet's
* IP header. */
- struct iphdr *iph = (void *)icmph + 8;
- u_int32_t arg = ntohl(icmph->un.gateway);
+ u_int32_t arg = ntohl(icmph.un.gateway);
- if (icmph->code == 0) {
+ if (icmph.code == 0) {
+ /* We've already made sure it's long enough. */
+ struct iphdr iph;
+ skb_copy_bits(skb, offset + sizeof(icmph), &iph,
+ sizeof(iph));
/* Code 0 means that upper 8 bits is pointer
to problem. */
- if ((arg >> 24) >= iph->ihl*4) {
+ if ((arg >> 24) >= iph.ihl*4) {
limpk("ICMP PARAMETERPROB ptr = %u\n",
- ntohl(icmph->un.gateway) >> 24);
+ ntohl(icmph.un.gateway) >> 24);
return 0;
}
arg &= 0x00FFFFFF;
@@ -179,9 +183,9 @@ check_icmp(const struct icmphdr *icmph,
case ICMP_TIME_EXCEEDED:
case ICMP_SOURCE_QUENCH:
/* CHECK: Unused must be zero. */
- if (icmph->un.gateway != 0) {
+ if (icmph.un.gateway != 0) {
limpk("ICMP type=%u unused = %u\n",
- icmph->type, ntohl(icmph->un.gateway));
+ icmph.type, ntohl(icmph.un.gateway));
return 0;
}
break;
@@ -192,32 +196,26 @@ check_icmp(const struct icmphdr *icmph,
/* UDP-specific checks. */
static int
-check_udp(const struct iphdr *iph,
- const struct udphdr *udph,
- u_int16_t datalen,
+check_udp(const struct sk_buff *skb,
unsigned int offset,
+ unsigned int fragoff,
int more_frags,
int embedded)
{
+ struct udphdr udph;
+
/* Can't do anything if it's a fragment. */
- if (offset)
+ if (fragoff)
return 1;
/* CHECK: Must cover UDP header. */
- if (datalen < sizeof(struct udphdr)) {
- limpk("UDP len=%u too short\n", datalen);
+ if (skb_copy_bits(skb, offset, &udph, sizeof(udph)) < 0) {
+ limpk("UDP len=%u too short\n", skb->len - offset);
return 0;
}
- /* Bad checksum? Don't print, just say it's unclean. */
- /* FIXME: SRC ROUTE packets won't match checksum --RR */
- if (!more_frags && !embedded && udph->check
- && csum_tcpudp_magic(iph->saddr, iph->daddr, datalen, IPPROTO_UDP,
- csum_partial((char *)udph, datalen, 0)) != 0)
- return 0;
-
/* CHECK: Destination port can't be zero. */
- if (!udph->dest) {
+ if (!udph.dest) {
limpk("UDP zero destination port\n");
return 0;
}
@@ -225,24 +223,24 @@ check_udp(const struct iphdr *iph,
if (!more_frags) {
if (!embedded) {
/* CHECK: UDP length must match. */
- if (ntohs(udph->len) != datalen) {
+ if (ntohs(udph.len) != skb->len - offset) {
limpk("UDP len too short %u vs %u\n",
- ntohs(udph->len), datalen);
+ ntohs(udph.len), skb->len - offset);
return 0;
}
} else {
/* CHECK: UDP length be >= this truncated pkt. */
- if (ntohs(udph->len) < datalen) {
+ if (ntohs(udph.len) < skb->len - offset) {
limpk("UDP len too long %u vs %u\n",
- ntohs(udph->len), datalen);
+ ntohs(udph.len), skb->len - offset);
return 0;
}
}
} else {
/* CHECK: UDP length must be > this frag's length. */
- if (ntohs(udph->len) <= datalen) {
+ if (ntohs(udph.len) <= skb->len - offset) {
limpk("UDP fragment len too short %u vs %u\n",
- ntohs(udph->len), datalen);
+ ntohs(udph.len), skb->len - offset);
return 0;
}
}
@@ -250,104 +248,104 @@ check_udp(const struct iphdr *iph,
return 1;
}
-#define TH_FIN 0x01
-#define TH_SYN 0x02
-#define TH_RST 0x04
-#define TH_PUSH 0x08
-#define TH_ACK 0x10
-#define TH_URG 0x20
-#define TH_ECE 0x40
-#define TH_CWR 0x80
-
/* TCP-specific checks. */
static int
-check_tcp(const struct iphdr *iph,
- const struct tcphdr *tcph,
- u_int16_t datalen,
+check_tcp(const struct sk_buff *skb,
unsigned int offset,
+ unsigned int fragoff,
int more_frags,
int embedded)
{
- u_int8_t *opt = (u_int8_t *)tcph;
- u_int8_t *endhdr = (u_int8_t *)tcph + tcph->doff * 4;
- u_int8_t tcpflags;
+ struct tcphdr tcph;
+ unsigned char opt[15 * 4 - sizeof(struct tcphdr)];
+ u32 tcpflags;
int end_of_options = 0;
- size_t i;
+ unsigned int i, optlen;
/* CHECK: Can't have offset=1: used to override TCP syn-checks. */
/* In fact, this is caught below (offset < 516). */
/* Can't do anything if it's a fragment. */
- if (offset)
+ if (fragoff)
return 1;
/* CHECK: Smaller than minimal TCP hdr. */
- if (datalen < sizeof(struct tcphdr)) {
+ if (skb_copy_bits(skb, offset, &tcph, sizeof(tcph)) < 0) {
+ u16 ports[2];
+
if (!embedded) {
- limpk("Packet length %u < TCP header.\n", datalen);
+ limpk("Packet length %u < TCP header.\n",
+ skb->len - offset);
return 0;
}
+
/* Must have ports available (datalen >= 8), from
check_icmp which set embedded = 1 */
/* CHECK: TCP ports inside ICMP error */
- if (!tcph->source || !tcph->dest) {
+ skb_copy_bits(skb, offset, ports, sizeof(ports));
+ if (!ports[0] || !ports[1]) {
limpk("Zero TCP ports %u/%u.\n",
- htons(tcph->source), htons(tcph->dest));
+ htons(ports[0]), htons(ports[1]));
return 0;
}
return 1;
}
- /* CHECK: Smaller than actual TCP hdr. */
- if (datalen < tcph->doff * 4) {
+ /* CHECK: TCP header claims tiny size. */
+ if (tcph.doff * 4 < sizeof(tcph)) {
+ limpk("TCP header claims tiny size %u\n", tcph.doff * 4);
+ return 0;
+ }
+
+ /* CHECK: Packet smaller than actual TCP hdr. */
+ optlen = tcph.doff*4 - sizeof(tcph);
+ if (skb_copy_bits(skb, offset + sizeof(tcph), opt, optlen) < 0) {
if (!embedded) {
limpk("Packet length %u < actual TCP header.\n",
- datalen);
+ skb->len - offset);
return 0;
} else
return 1;
}
- /* Bad checksum? Don't print, just say it's unclean. */
- /* FIXME: SRC ROUTE packets won't match checksum --RR */
- if (!more_frags && !embedded
- && csum_tcpudp_magic(iph->saddr, iph->daddr, datalen, IPPROTO_TCP,
- csum_partial((char *)tcph, datalen, 0)) != 0)
- return 0;
-
/* CHECK: TCP ports non-zero */
- if (!tcph->source || !tcph->dest) {
+ if (!tcph.source || !tcph.dest) {
limpk("Zero TCP ports %u/%u.\n",
- htons(tcph->source), htons(tcph->dest));
+ htons(tcph.source), htons(tcph.dest));
return 0;
}
+ tcpflags = tcp_flag_word(&tcph);
+
/* CHECK: TCP reserved bits zero. */
- if(tcp_flag_word(tcph) & TCP_RESERVED_BITS) {
+ if (tcpflags & TCP_RESERVED_BITS) {
limpk("TCP reserved bits not zero\n");
return 0;
}
+ tcpflags &= ~(TCP_DATA_OFFSET | TCP_FLAG_CWR | TCP_FLAG_ECE
+ | __constant_htonl(0x0000FFFF));
+
/* CHECK: TCP flags. */
- tcpflags = (((u_int8_t *)tcph)[13] & ~(TH_ECE|TH_CWR));
- if (tcpflags != TH_SYN
- && tcpflags != (TH_SYN|TH_ACK)
- && tcpflags != TH_RST
- && tcpflags != (TH_RST|TH_ACK)
- && tcpflags != (TH_RST|TH_ACK|TH_PUSH)
- && tcpflags != (TH_FIN|TH_ACK)
- && tcpflags != TH_ACK
- && tcpflags != (TH_ACK|TH_PUSH)
- && tcpflags != (TH_ACK|TH_URG)
- && tcpflags != (TH_ACK|TH_URG|TH_PUSH)
- && tcpflags != (TH_FIN|TH_ACK|TH_PUSH)
- && tcpflags != (TH_FIN|TH_ACK|TH_URG)
- && tcpflags != (TH_FIN|TH_ACK|TH_URG|TH_PUSH)) {
- limpk("TCP flags bad: %u\n", tcpflags);
+ if (tcpflags != TCP_FLAG_SYN
+ && tcpflags != (TCP_FLAG_SYN|TCP_FLAG_ACK)
+ && tcpflags != TCP_FLAG_RST
+ && tcpflags != (TCP_FLAG_RST|TCP_FLAG_ACK)
+ && tcpflags != (TCP_FLAG_RST|TCP_FLAG_ACK|TCP_FLAG_PSH)
+ && tcpflags != (TCP_FLAG_FIN|TCP_FLAG_ACK)
+ && tcpflags != TCP_FLAG_ACK
+ && tcpflags != (TCP_FLAG_ACK|TCP_FLAG_PSH)
+ && tcpflags != (TCP_FLAG_ACK|TCP_FLAG_URG)
+ && tcpflags != (TCP_FLAG_ACK|TCP_FLAG_URG|TCP_FLAG_PSH)
+ && tcpflags != (TCP_FLAG_FIN|TCP_FLAG_ACK|TCP_FLAG_PSH)
+ && tcpflags != (TCP_FLAG_FIN|TCP_FLAG_ACK|TCP_FLAG_URG)
+ && tcpflags != (TCP_FLAG_FIN|TCP_FLAG_ACK|TCP_FLAG_URG
+ |TCP_FLAG_PSH)) {
+ limpk("TCP flags bad: 0x%04X\n", ntohl(tcpflags) >> 16);
return 0;
}
- for (i = sizeof(struct tcphdr); i < tcph->doff * 4; ) {
+ for (i = 0; i < optlen; ) {
switch (opt[i]) {
case 0:
end_of_options = 1;
@@ -364,7 +362,7 @@ check_tcp(const struct iphdr *iph,
return 0;
}
/* CHECK: options at tail. */
- else if (i+1 >= tcph->doff * 4) {
+ else if (i+1 >= optlen) {
limpk("TCP option %u at tail\n",
opt[i]);
return 0;
@@ -376,8 +374,8 @@ check_tcp(const struct iphdr *iph,
return 0;
}
/* CHECK: oversize options. */
- else if (&opt[i] + opt[i+1] > endhdr) {
- limpk("TCP option %u at %Zu too long\n",
+ else if (i + opt[i+1] > optlen) {
+ limpk("TCP option %u at %u too long\n",
(unsigned int) opt[i], i);
return 0;
}
@@ -392,34 +390,44 @@ check_tcp(const struct iphdr *iph,
/* Returns 1 if ok */
/* Standard IP checks. */
static int
-check_ip(struct iphdr *iph, size_t length, int embedded)
+check_ip(const struct sk_buff *skb, unsigned int offset)
{
- u_int8_t *opt = (u_int8_t *)iph;
- u_int8_t *endhdr = (u_int8_t *)iph + iph->ihl * 4;
int end_of_options = 0;
- void *protoh;
- size_t datalen;
+ unsigned int datalen, optlen;
unsigned int i;
- unsigned int offset;
+ unsigned int fragoff;
+ struct iphdr iph;
+ unsigned char opt[15 * 4 - sizeof(struct iphdr)];
+ int embedded = offset;
/* Should only happen for local outgoing raw-socket packets. */
/* CHECK: length >= ip header. */
- if (length < sizeof(struct iphdr) || length < iph->ihl * 4) {
- limpk("Packet length %Zu < IP header.\n", length);
+ if (skb_copy_bits(skb, offset, &iph, sizeof(iph)) < 0) {
+ limpk("Packet length %u < IP header.\n", skb->len - offset);
+ return 0;
+ }
+ if (iph.ihl * 4 < sizeof(iph)) {
+ limpk("IP len %u < minimum IP header.\n", iph.ihl*4);
+ return 0;
+ }
+
+ optlen = iph.ihl * 4 - sizeof(iph);
+ if (skb_copy_bits(skb, offset+sizeof(struct iphdr), opt, optlen)<0) {
+ limpk("Packet length %u < IP header %u.\n",
+ skb->len - offset, iph.ihl * 4);
return 0;
}
- offset = ntohs(iph->frag_off) & IP_OFFSET;
- protoh = (void *)iph + iph->ihl * 4;
- datalen = length - iph->ihl * 4;
+ fragoff = (ntohs(iph.frag_off) & IP_OFFSET);
+ datalen = skb->len - (offset + sizeof(struct iphdr) + optlen);
/* CHECK: Embedded fragment. */
- if (embedded && offset) {
+ if (offset && fragoff) {
limpk("Embedded fragment.\n");
return 0;
}
- for (i = sizeof(struct iphdr); i < iph->ihl * 4; ) {
+ for (i = 0; i < optlen; ) {
switch (opt[i]) {
case 0:
end_of_options = 1;
@@ -436,7 +444,7 @@ check_ip(struct iphdr *iph, size_t length, int embedded)
return 0;
}
/* CHECK: options at tail. */
- else if (i+1 >= iph->ihl * 4) {
+ else if (i+1 >= optlen) {
limpk("IP option %u at tail\n",
opt[i]);
return 0;
@@ -448,7 +456,7 @@ check_ip(struct iphdr *iph, size_t length, int embedded)
return 0;
}
/* CHECK: oversize options. */
- else if (&opt[i] + opt[i+1] > endhdr) {
+ else if (i + opt[i+1] > optlen) {
limpk("IP option %u at %u too long\n",
opt[i], i);
return 0;
@@ -461,30 +469,30 @@ check_ip(struct iphdr *iph, size_t length, int embedded)
/* Fragment checks. */
/* CHECK: More fragments, but doesn't fill 8-byte boundary. */
- if ((ntohs(iph->frag_off) & IP_MF)
- && (ntohs(iph->tot_len) % 8) != 0) {
- limpk("Truncated fragment %u long.\n", ntohs(iph->tot_len));
+ if ((ntohs(iph.frag_off) & IP_MF)
+ && (ntohs(iph.tot_len) % 8) != 0) {
+ limpk("Truncated fragment %u long.\n", ntohs(iph.tot_len));
return 0;
}
/* CHECK: Oversize fragment a-la Ping of Death. */
- if (offset * 8 + datalen > 65535) {
- limpk("Oversize fragment to %u.\n", offset * 8);
+ if (fragoff * 8 + datalen > 65535) {
+ limpk("Oversize fragment to %u.\n", fragoff * 8);
return 0;
}
- /* CHECK: DF set and offset or MF set. */
- if ((ntohs(iph->frag_off) & IP_DF)
- && (offset || (ntohs(iph->frag_off) & IP_MF))) {
+ /* CHECK: DF set and fragoff or MF set. */
+ if ((ntohs(iph.frag_off) & IP_DF)
+ && (fragoff || (ntohs(iph.frag_off) & IP_MF))) {
limpk("DF set and offset=%u, MF=%u.\n",
- offset, ntohs(iph->frag_off) & IP_MF);
+ fragoff, ntohs(iph.frag_off) & IP_MF);
return 0;
}
/* CHECK: Zero-sized fragments. */
- if ((offset || (ntohs(iph->frag_off) & IP_MF))
+ if ((fragoff || (ntohs(iph.frag_off) & IP_MF))
&& datalen == 0) {
- limpk("Zero size fragment offset=%u\n", offset);
+ limpk("Zero size fragment offset=%u\n", fragoff);
return 0;
}
@@ -500,52 +508,54 @@ check_ip(struct iphdr *iph, size_t length, int embedded)
here. */
#define MIN_LIKELY_MTU 128
/* CHECK: Min size of first frag = 128. */
- if ((ntohs(iph->frag_off) & IP_MF)
- && offset == 0
- && ntohs(iph->tot_len) < MIN_LIKELY_MTU) {
- limpk("First fragment size %u < %u\n", ntohs(iph->tot_len),
+ if ((ntohs(iph.frag_off) & IP_MF)
+ && fragoff == 0
+ && ntohs(iph.tot_len) < MIN_LIKELY_MTU) {
+ limpk("First fragment size %u < %u\n", ntohs(iph.tot_len),
MIN_LIKELY_MTU);
return 0;
}
/* CHECK: Min offset of frag = 128 - IP hdr len. */
- if (offset && offset * 8 < MIN_LIKELY_MTU - iph->ihl * 4) {
- limpk("Fragment starts at %u < %u\n", offset * 8,
- MIN_LIKELY_MTU - iph->ihl * 4);
+ if (fragoff && fragoff * 8 < MIN_LIKELY_MTU - iph.ihl * 4) {
+ limpk("Fragment starts at %u < %u\n", fragoff * 8,
+ MIN_LIKELY_MTU - iph.ihl * 4);
return 0;
}
/* CHECK: Protocol specification non-zero. */
- if (iph->protocol == 0) {
+ if (iph.protocol == 0) {
limpk("Zero protocol\n");
return 0;
}
+ /* FIXME: This is already checked for in "Oversize fragment"
+ above --RR */
/* CHECK: Do not use what is unused.
* First bit of fragmentation flags should be unused.
* May be used by OS fingerprinting tools.
* 04 Jun 2002, Maciej Soltysiak, solt@dns.toxicfilms.tv
*/
- if (ntohs(iph->frag_off)>>15) {
+ if (ntohs(iph.frag_off)>>15) {
limpk("IP unused bit set\n");
return 0;
}
/* Per-protocol checks. */
- switch (iph->protocol) {
+ switch (iph.protocol) {
case IPPROTO_ICMP:
- return check_icmp(protoh, datalen, offset,
- (ntohs(iph->frag_off) & IP_MF),
+ return check_icmp(skb, offset + iph.ihl*4, fragoff,
+ (ntohs(iph.frag_off) & IP_MF),
embedded);
case IPPROTO_UDP:
- return check_udp(iph, protoh, datalen, offset,
- (ntohs(iph->frag_off) & IP_MF),
+ return check_udp(skb, offset + iph.ihl*4, fragoff,
+ (ntohs(iph.frag_off) & IP_MF),
embedded);
case IPPROTO_TCP:
- return check_tcp(iph, protoh, datalen, offset,
- (ntohs(iph->frag_off) & IP_MF),
+ return check_tcp(skb, offset + iph.ihl*4, fragoff,
+ (ntohs(iph.frag_off) & IP_MF),
embedded);
default:
/* Ignorance is bliss. */
@@ -559,11 +569,9 @@ match(const struct sk_buff *skb,
const struct net_device *out,
const void *matchinfo,
int offset,
- const void *hdr,
- u_int16_t datalen,
int *hotdrop)
{
- return !check_ip(skb->nh.iph, skb->len, 0);
+ return !check_ip(skb, 0);
}
/* Called when user tries to insert an entry of this type. */
diff --git a/net/ipv6/anycast.c b/net/ipv6/anycast.c
index 1fd038191f34..5dcfcf8a121f 100644
--- a/net/ipv6/anycast.c
+++ b/net/ipv6/anycast.c
@@ -127,8 +127,8 @@ int ipv6_sock_ac_join(struct sock *sk, int ifindex, struct in6_addr *addr)
dev_hold(dev);
dst_release(&rt->u.dst);
} else if (ishost) {
- sock_kfree_s(sk, pac, sizeof(*pac));
- return -EADDRNOTAVAIL;
+ err = -EADDRNOTAVAIL;
+ goto out_free_pac;
} else {
/* router, no matching interface: just pick one */
@@ -138,18 +138,17 @@ int ipv6_sock_ac_join(struct sock *sk, int ifindex, struct in6_addr *addr)
dev = dev_get_by_index(ifindex);
if (dev == NULL) {
- sock_kfree_s(sk, pac, sizeof(*pac));
- return -ENODEV;
+ err = -ENODEV;
+ goto out_free_pac;
}
idev = in6_dev_get(dev);
if (!idev) {
- sock_kfree_s(sk, pac, sizeof(*pac));
- dev_put(dev);
if (ifindex)
- return -ENODEV;
+ err = -ENODEV;
else
- return -EADDRNOTAVAIL;
+ err = -EADDRNOTAVAIL;
+ goto out_dev_put;
}
/* reset ishost, now that we have a specific device */
ishost = !idev->cnf.forwarding;
@@ -170,21 +169,17 @@ int ipv6_sock_ac_join(struct sock *sk, int ifindex, struct in6_addr *addr)
err = -EADDRNOTAVAIL;
else if (!capable(CAP_NET_ADMIN))
err = -EPERM;
- if (err) {
- sock_kfree_s(sk, pac, sizeof(*pac));
- dev_put(dev);
- return err;
- }
+ if (err)
+ goto out_dev_put;
} else if (!(ipv6_addr_type(addr) & IPV6_ADDR_ANYCAST) &&
- !capable(CAP_NET_ADMIN))
- return -EPERM;
+ !capable(CAP_NET_ADMIN)) {
+ err = -EPERM;
+ goto out_dev_put;
+ }
err = ipv6_dev_ac_inc(dev, addr);
- if (err) {
- sock_kfree_s(sk, pac, sizeof(*pac));
- dev_put(dev);
- return err;
- }
+ if (err)
+ goto out_dev_put;
write_lock_bh(&ipv6_sk_ac_lock);
pac->acl_next = np->ipv6_ac_list;
@@ -194,6 +189,12 @@ int ipv6_sock_ac_join(struct sock *sk, int ifindex, struct in6_addr *addr)
dev_put(dev);
return 0;
+
+out_dev_put:
+ dev_put(dev);
+out_free_pac:
+ sock_kfree_s(sk, pac, sizeof(*pac));
+ return err;
}
/*
diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
index 6e7a4ce74e4d..48ecdcc35dae 100644
--- a/net/ipv6/ndisc.c
+++ b/net/ipv6/ndisc.c
@@ -441,8 +441,10 @@ static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
src_addr = solicited_addr;
in6_ifa_put(ifp);
} else {
- if (ipv6_dev_get_saddr(dev, daddr, &tmpaddr, 0))
+ if (ipv6_dev_get_saddr(dev, daddr, &tmpaddr, 0)) {
+ dst_free(dst);
return;
+ }
src_addr = &tmpaddr;
}
@@ -450,11 +452,10 @@ static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
ndisc_rt_init(rt, dev, neigh);
dst = (struct dst_entry*)rt;
- dst_clone(dst);
err = xfrm_lookup(&dst, &fl, NULL, 0);
if (err < 0) {
- dst_release(dst);
+ dst_free(dst);
return;
}
@@ -470,6 +471,7 @@ static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
if (skb == NULL) {
ND_PRINTK1("send_na: alloc skb failed\n");
+ dst_free(dst);
return;
}