/*
linux-2.6.22.5/net/netfilter/nf_conntrack_proto_tcp.c
2007-12-22 10:38
¥6
howto crash netfilter with TCP SYN packet, possible?
*/
static int tcp_error(struct sk_buff *skb,
unsigned int dataoff,
enum ip_conntrack_info *ctinfo,
int pf,
unsigned int hooknum)
{
struct tcphdr _tcph, *th;
unsigned int tcplen = skb->len - dataoff;
u_int8_t tcpflags;
/* Smaller that minimal TCP header? */
th = skb_header_pointer(skb, dataoff, sizeof(_tcph), &_tcph);
if (th == NULL) {
if (LOG_INVALID(IPPROTO_TCP))
nf_log_packet(pf, 0, skb, NULL, NULL, NULL,
"nf_ct_tcp: short packet ");
return -NF_ACCEPT;
}
/* Not whole TCP header or malformed packet */
if (th->doff*4 < sizeof(struct tcphdr)
|| tcplen < th->doff*4) {
[color=Red] /*
* 其一 隐患;/
* TCP头长大于60字节是合法的 8-(:
*/
[/color] if (LOG_INVALID(IPPROTO_TCP))
nf_log_packet(pf, 0, skb, NULL, NULL, NULL,
"nf_ct_tcp: truncated/malformed packet ");
return -NF_ACCEPT;
}
/* Checksum invalid? Ignore.
* We skip checking packets on the outgoing path
* because the checksum is assumed to be correct.
*/
/* FIXME: Source route IP option packets --RR */
if (nf_conntrack_checksum &&
((pf == PF_INET && hooknum == NF_IP_PRE_ROUTING) ||
(pf == PF_INET6 && hooknum == NF_IP6_PRE_ROUTING)) &&
nf_checksum(skb, hooknum, dataoff, IPPROTO_TCP, pf)) {
if (LOG_INVALID(IPPROTO_TCP))
nf_log_packet(pf, 0, skb, NULL, NULL, NULL,
"nf_ct_tcp: bad TCP checksum ");
return -NF_ACCEPT;
}
/* Check TCP flags. */
tcpflags = (((u_int8_t *)th)[13] & ~(TH_ECE|TH_CWR|TH_PUSH));
if (!tcp_valid_flags[tcpflags]) {
if (LOG_INVALID(IPPROTO_TCP))
nf_log_packet(pf, 0, skb, NULL, NULL, NULL,
"nf_ct_tcp: invalid TCP flag combination ");
return -NF_ACCEPT;
}
return NF_ACCEPT;
}
static void tcp_options(const struct sk_buff *skb,
unsigned int dataoff,
struct tcphdr *tcph,
struct ip_ct_tcp_state *state)
{
unsigned char buff[(15 * 4) - sizeof(struct tcphdr)];
unsigned char *ptr;
int length = (tcph->doff*4) - sizeof(struct tcphdr);
/*
* length可以大于40;/
* but sizeof(buff) = 40
*/
if (!length)
return;
ptr = skb_header_pointer(skb, dataoff + sizeof(struct tcphdr),
length, buff);
[color=Red] /*
* 其二 buff可能overflow
*/
[/color] BUG_ON(ptr == NULL);
state->td_scale = state->flags = 0;
while (length > 0) {
int opcode = *ptr++;
int opsize;
switch (opcode) {
case TCPOPT_EOL:
return;
case TCPOPT_NOP: /* Ref: RFC 793 section 3.1 */
length--;
continue;
default:
opsize = *ptr++;
if (opsize < 2) /* "silly options" */
return;
if (opsize > length)
break; /* don't parse partial options */
if (opcode == TCPOPT_SACK_PERM
&& opsize == TCPOLEN_SACK_PERM) {
state->flags |= IP_CT_TCP_FLAG_SACK_PERM;
}
else if (opcode == TCPOPT_WINDOW
&& opsize == TCPOLEN_WINDOW) {
state->td_scale = *(u_int8_t *)ptr;
if (state->td_scale > 14) {
/* See RFC1323 */
state->td_scale = 14;
}
state->flags |= IP_CT_TCP_FLAG_WINDOW_SCALE;
}
ptr += opsize - 2;
length -= opsize;
}
}
}
static inline void * skb_header_pointer (const struct sk_buff *skb,
int offset, int len, void *buffer)
{ /* in <linux/skbuff.h> */
int hlen = skb_headlen(skb);
if (hlen - offset >= len)
return skb->data + offset;
[color=Red] /*
* 其三 如果是大SYN包,使得skb_copy_bits被调用了,
* if (len > 40 bytes)
* buffer overflow;
*
* 但是,netfilter看到的包都是整合过的,
* 这样的机会几乎没有,要看ipfrag reasm的结果
*/
[/color] if (skb_copy_bits(skb, offset, buffer, len) < 0)
return NULL;
return buffer;
}
int skb_copy_bits(const struct sk_buff *skb, int offset, void *to, int len)
{
int i, copy;
int start = skb_headlen(skb);
if (offset > (int)skb->len - len)
goto fault;
/* Copy header. */
if ((copy = start - offset) > 0) {
if (copy > len)
copy = len;
/*
这里没有check len,
而是默认len <= sizeof(to)
*/
skb_copy_from_linear_data_offset(skb, offset, to, copy);
if ((len -= copy) == 0)
return 0;
offset += copy;
to += copy;
}
...
}
void * memcpy(void *dest, const void *src, size_t count)
{
/*
C版的memcpy in lib/string.c
memcpy没有检测overflow的义务;/
*/
char *tmp = dest;
const char *s = src;
while (count--)
*tmp++ = *s++;
return dest;
}
static struct sk_buff * ip_frag_reasm (struct ipq *qp, struct net_device *dev)
{ /* net/ipv4/ip_fragment.c */
struct iphdr *iph;
struct sk_buff *fp, *head = qp->fragments;
int len;
int ihlen;
ipq_kill(qp);
BUG_TRAP(head != NULL);
BUG_TRAP(FRAG_CB(head)->offset == 0);
/* Allocate a new buffer for the datagram. */
ihlen = ip_hdrlen(head);
len = ihlen + qp->len;
if (len > 65535)
goto out_oversize;
/* Head of list must not be cloned. */
if (skb_cloned(head) && pskb_expand_head(head, 0, 0, GFP_ATOMIC))
goto out_nomem;
/* If the first fragment is fragmented itself, we split
* it to two chunks: the first with data and paged part
* and the second, holding only fragments. */
if (skb_shinfo(head)->frag_list) {
struct sk_buff *clone;
int i, plen = 0;
if ((clone = alloc_skb(0, GFP_ATOMIC)) == NULL)
goto out_nomem;
clone->next = head->next;
head->next = clone;
skb_shinfo(clone)->frag_list = skb_shinfo(head)->frag_list;
skb_shinfo(head)->frag_list = NULL;
for (i=0; i<skb_shinfo(head)->nr_frags; i++)
plen += skb_shinfo(head)->frags[i].size;
clone->len = clone->data_len = head->data_len - plen;
head->data_len -= clone->len;
head->len -= clone->len;
clone->csum = 0;
clone->ip_summed = head->ip_summed;
atomic_add(clone->truesize, &ip_frag_mem);
}
skb_shinfo(head)->frag_list = head->next;
[color=Red] /*
* 其四 linux skb精巧的设计,可以方便地处理大包 &)
*
* 在这里没有实施linearize,性能可佳。
*/
[/color] skb_push(head, head->data - skb_network_header(head));
atomic_sub(head->truesize, &ip_frag_mem);
for (fp = head->next; fp; fp = fp->next) {
head->data_len += fp->len;
[color=Red] /*
* 由于没有linearize操作,
* 虽然总包长要求len < 65535 byte,
* 首包长确可以是
* 82byte = 20byte<IP> + 60byte<TCP> + 2byte<trash>
*
* 所以 tcp->doff*4 = 88byte,
* 将迫使skb_header_pointer调用skb_copy_bits,
* 结果导致40byte的buff overflow
*/
[/color] head->len += fp->len;
if (head->ip_summed != fp->ip_summed)
head->ip_summed = CHECKSUM_NONE;
else if (head->ip_summed == CHECKSUM_COMPLETE)
head->csum = csum_add(head->csum, fp->csum);
head->truesize += fp->truesize;
atomic_sub(fp->truesize, &ip_frag_mem);
}
head->next = NULL;
head->dev = dev;
head->tstamp = qp->stamp;
iph = ip_hdr(head);
iph->frag_off = 0;
iph->tot_len = htons(len);
IP_INC_STATS_BH(IPSTATS_MIB_REASMOKS);
qp->fragments = NULL;
return head;
out_nomem:
LIMIT_NETDEBUG(KERN_ERR "IP: queue_glue: no memory for gluing "
"queue %p\n", qp);
goto out_fail;
out_oversize:
if (net_ratelimit())
printk(KERN_INFO
"Oversized IP packet from %d.%d.%d.%d.\n",
NIPQUAD(qp->saddr));
out_fail:
IP_INC_STATS_BH(IPSTATS_MIB_REASMFAILS);
return NULL;
}