diff options
| author | David S. Miller <davem@nuts.ninka.net> | 2003-05-21 10:54:44 -0700 |
|---|---|---|
| committer | David S. Miller <davem@nuts.ninka.net> | 2003-05-21 10:54:44 -0700 |
| commit | 73baeace66f0d4b38de07595ac2cfd308bd22452 (patch) | |
| tree | fc754c08811c2b460f40902a6f4bd5db3ceaf9eb | |
| parent | 6c17d503531ee9e528d2b581de38e8f6e18c33a4 (diff) | |
| parent | f989c276d46a747dad537f1c3fae0db7799c1f16 (diff) | |
Merge bk://kernel.bkbits.net/acme/net-2.5
into nuts.ninka.net:/home/davem/src/BK/net-2.5
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; } |
