Commit 73baeace authored by David S. Miller's avatar David S. Miller

Merge bk://kernel.bkbits.net/acme/net-2.5

into nuts.ninka.net:/home/davem/src/BK/net-2.5
parents 6c17d503 f989c276
......@@ -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;
};
......
......@@ -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);
......
......@@ -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.
*/
......
......@@ -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;
......
......@@ -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.
......
......@@ -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)
{
......
......@@ -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;
if (offset) {
/* 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 == 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)
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)
&& (!tcpinfo->option
|| tcp_find_option(tcpinfo->option, tcp, datalen,
tcpinfo->invflags
& IPT_TCP_INV_OPTION,
hotdrop));
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),
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,12 +1640,10 @@ icmp_match(const struct sk_buff *skb,
return 0;
}
/* Must not be a fragment. */
return !offset
&& icmp_type_code_match(icmpinfo->type,
return icmp_type_code_match(icmpinfo->type,
icmpinfo->code[0],
icmpinfo->code[1],
icmp->type, icmp->code,
icmph.type, icmph.code,
!!(icmpinfo->invflags&IPT_ICMP_INV));
}
......
......@@ -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)
if (!skb_ip_make_writable(pskb, sizeof(struct iphdr)))
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,
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),
iph->check^0xFFFF));
(*pskb)->nh.iph->check
^ 0xFFFF));
(*pskb)->nfcache |= NFC_ALTERED;
}
return IPT_CONTINUE;
......
......@@ -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;
}
if (!skb_ip_make_writable(pskb, sizeof(struct iphdr)))
return 0;
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,
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),
iph->check^0xFFFF));
(*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;
}
diffs[0] = *tcpflags;
/* Not enought header? */
if (skb_copy_bits(*pskb, (*pskb)->nh.iph->ihl*4, &tcph, sizeof(tcph))
< 0)
return 0;
if (einfo->operation & IPT_ECN_OP_SET_ECE
&& tcph->ece != einfo->proto.tcp.ece) {
tcph->ece = einfo->proto.tcp.ece;
}
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_CWR
&& tcph->cwr != einfo->proto.tcp.cwr) {
tcph->cwr = einfo->proto.tcp.cwr;
}
if (einfo->operation & IPT_ECN_OP_SET_CWR)
tcph.cwr = einfo->proto.tcp.cwr;
diffs[1] = ((u_int16_t *)&tcph)[6];
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;
}
......
......@@ -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);
......
......@@ -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)
{
......
......@@ -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)
{
......
......@@ -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;
if (!skb_ip_make_writable(pskb, sizeof(struct iphdr)))
return 0;
skb->nfcache |= NFC_ALTERED;
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:
......
......@@ -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)
{
......
......@@ -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,
(*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;
/* 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)
/* Ensure we have at least 8 bytes of proto header. */
if (skb_in->len < skb_in->nh.iph->ihl*4 + 8)
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,
skb_copy_bits(skb_in, 0, data,
length - sizeof(struct iphdr) - sizeof(struct icmphdr));
icmph->checksum = ip_compute_csum((unsigned char *)icmph,
length - sizeof(struct iphdr));
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;
......
......@@ -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)
if (!skb_ip_make_writable(pskb, (*pskb)->len))
return NF_DROP;
kfree_skb(*pskb);
*pskb = nskb;
}
iph = (*pskb)->nh.iph;
tcplen = (*pskb)->len - iph->ihl*4;
......
......@@ -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)
if (!skb_ip_make_writable(pskb, sizeof(struct iphdr)))
return NF_DROP;
kfree_skb(*pskb);
*pskb = nskb;
iph = (*pskb)->nh.iph;
}
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,
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),
iph->check^0xFFFF));
(*pskb)->nh.iph->check
^0xFFFF));
(*pskb)->nfcache |= NFC_ALTERED;
}
return IPT_CONTINUE;
......
......@@ -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) {
......
......@@ -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,10 +52,8 @@ 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),
return spi_match(ahinfo->spis[0], ahinfo->spis[1],
ntohl(ah.spi),
!!(ahinfo->invflags & IPT_AH_INV_SPI));
}
......
......@@ -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;
......
......@@ -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;
......
......@@ -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;
}
......
......@@ -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,10 +52,8 @@ 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),
return spi_match(espinfo->spis[0], espinfo->spis[1],
ntohl(esp.spi),
!!(espinfo->invflags & IPT_ESP_INV_SPI));
}
......
......@@ -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;
......
......@@ -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;
......
......@@ -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;
......
......@@ -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;
......
......@@ -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;
......
......@@ -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,
return ports_match(multiinfo->ports,
multiinfo->flags, multiinfo->count,
ntohs(udp->source), ntohs(udp->dest));
ntohs(ports[0]), ntohs(ports[1]));
}
/* Called when user tries to insert an entry of this type. */
......
......@@ -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;
}
......
......@@ -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;
......
......@@ -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;
......
......@@ -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;
......
......@@ -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);
}
......
......@@ -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
......
......@@ -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",
......
......@@ -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);
return 0;
}
for (i = sizeof(struct tcphdr); i < tcph->doff * 4; ) {
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 = 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. */
......
......@@ -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;
}
/*
......
......@@ -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;
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment