📄 ipfrag.c
字号:
/* * Copyright (c) Gianni Tedesco 2002. * Released under the terms of the GNU GPL v2 */#include "tcpip.h"/* * IP Defragmentation for firestorm * ================================ * * This code is a reworking of ip_fragment.c from Linux 2.4.18, * it should be relatively straight forward to understand. Its * pretty well tested. * * Should CORRECTLY cope with: * Overlapping fragments * Oversized fragments * Out of order fragments * Timed out packets * * TODO: * Use more efficient search to insert fragments * Detect ICMP_TIME_EXCEEDED/ICMP_EXC_FRAGTIME (?) * Coalesce fragments in incomplete packets (??) */#if 1#define dmesg(x...)#define __INLINE__ static inline#else#define dmesg mesg#define __INLINE__ static#endifunsigned int use_ipfrag=0;/* Statistics */unsigned int ipfrag_err_reasm=0;unsigned int ipfrag_err_mem=0;unsigned int ipfrag_err_timeout=0;unsigned int ipfrag_reassembled=0;/* Generator */struct generator ipfrag_gen=init_generator("ipfrag", &ip_tb);/* Don't decode fragments inside fragments */serial_t last_frag;unsigned int first_frag=0;/* Memory usage, high and low water marks */unsigned int ipfrag_mem=0;unsigned int ipfrag_mem_hi=1024*1024;unsigned int ipfrag_mem_lo=768*1024;/* Timeout (in seconds) */unsigned int ipfrag_timeout=60;/* Don't decode fragments with too low ttl */unsigned int ipfrag_minttl=0;struct arg ipfrag_args[]={ {"minttl", ARGTYPE_PUINT, NULL, {vp_uint:&ipfrag_minttl}}, {"timeout", ARGTYPE_PUINT, NULL, {vp_uint:&ipfrag_timeout}}, {"mem_hi", ARGTYPE_PBYTES, NULL, {vp_bytes:&ipfrag_mem_hi}}, {"mem_lo", ARGTYPE_PBYTES, NULL, {vp_bytes:&ipfrag_mem_lo}}, {NULL,ARGTYPE_NOP,NULL}};/* Reassembly */struct packet ipfr;char *reasm_buf=NULL;unsigned int reasm_len=0;struct ipq *ipq_latest=NULL;struct ipq *ipq_oldest=NULL;#define IPHASH 128 /* Must be a power of two */struct ipq *ipq_hash[IPHASH]; /* IP fragment hash table *//* Hash function for ipq_hash lookup */__INLINE__ unsigned int ipq_hashfn(u_int16_t id, u_int32_t saddr, u_int32_t daddr, u_int8_t proto){ unsigned int h=saddr^daddr; h ^= (h>>16)^id; h ^= (h>>8)^proto; return h & (IPHASH-1);}/* * Report ip fragmentation violations. */static void ipfrag_teardrop(struct packet *p){ static struct alert ta=init_alert("Teardrop", 1, 0, 5); alert(&ipfrag_gen, p, &ta);}static void ipfrag_oversize(struct packet *p){ static struct alert ta=init_alert("Oversized fragments", 2, 0, 5); alert(&ipfrag_gen, p, &ta);}static void ipfrag_attack(struct packet *p){ static struct alert ta=init_alert("Fragmentation attack", 3, 0, 5); alert(&ipfrag_gen, p, &ta);}static void ipfrag_boink(struct packet *p){ static struct alert ta=init_alert("Boink", 4, 0, 5); alert(&ipfrag_gen, p, &ta);}static void ipfrag_truncated(struct packet *p){ static struct alert ta=init_alert("Truncated fragment", 5, 0, 5); alert(&ipfrag_gen, p, &ta);}static void ipfrag_fraginfrag(struct packet *p){ static struct alert ta=init_alert("Fragment in fragment", 6, 0, 5); alert(&ipfrag_gen, p, &ta);}static void ipfrag_oom(struct packet *p){ static struct alert ta=init_alert("Too many fragments", 7, 0, 5); alert(&ipfrag_gen, p, &ta);}static void ipfrag_timedout(struct packet *p){ static struct alert ta=init_alert("Fragment arrived after timeout", 8, 0, 5); alert(&ipfrag_gen, p, &ta);}void ipq_kill(struct ipq *qp){ struct ipfrag *foo, *bar; /* Unlink from the list */ if ( qp->next ) qp->next->pprev=qp->pprev; *qp->pprev=qp->next; /* Free the fragments and descriptors */ for(foo=qp->fragments; foo;) { bar=foo; foo=foo->next; if ( bar->free ) { free(bar->fdata); ipfrag_mem-=bar->flen; } free(bar); ipfrag_mem-=sizeof(struct ipfrag); } /* Remove from LRU queue */ if ( qp->next_time) qp->next_time->prev_time=qp->prev_time; if ( qp->prev_time) qp->prev_time->next_time=qp->next_time; if ( qp == ipq_oldest ) ipq_oldest=qp->prev_time; if ( qp == ipq_latest ) ipq_latest=qp->next_time; /* Free the ipq itself */ free(qp); ipfrag_mem-=sizeof(struct ipq);}/* Create a checksum for the ip header. I don't know * why we bother really... */static void ipfrag_csum(struct pkt_iphdr *iph){ u_int16_t *tmp=(u_int16_t *)iph; u_int32_t sum=0; int i; for(i=0; i<iph->ihl<<1; i++) { /* ignore field 5, its the checksum ;) */ if ( i!=5 ) sum+=tmp[i]; if(sum & 0x80000000) { sum = (sum & 0xffff) + (sum >> 16); } } while(sum >> 16) sum = (sum & 0xffff) + (sum >> 16); iph->csum=(~sum) & 0xffff;}/* Reassemble a complete set of fragments, decode the * new packet, and send it back through the preprocessor * list, we won't touch it next time round */__INLINE__ void ipfrag_reassemble(struct ipq *qp){ struct ipfrag *f; struct pkt_iphdr *iph; unsigned int olen=qp->len; unsigned int len=0; char *buf; /* Kill oversize packets */ if ( olen > 0xffff ) { ipfrag_err_reasm++; return; } if ( !qp->fragments ) { ipfrag_err_reasm++; return; } iph=qp->fragments->fdata; olen+=iph->ihl<<2; /* Allocate the frankenpacket buffer */ if ( reasm_buf ) { char *tmp; /* Expand the buffer if it's too small */ if ( olen > reasm_len ) { if ( !(tmp=realloc(reasm_buf, olen)) ) { olen=reasm_len; }else{ reasm_buf=tmp; reasm_len=qp->len; } } }else{ if ( !(reasm_buf=malloc(olen)) ) { dmesg(M_DEBUG,"ipfrag: reassemble error!"); ipfrag_err_reasm++; return; } reasm_len=olen; } /* Copy all the fragments in to the new buffer */ dmesg(M_DEBUG,"Reassemble: %u bytes", olen); buf=reasm_buf; /* Do the header */ dmesg(M_DEBUG," * %u byte header", iph->ihl<<2); memcpy(buf, qp->fragments->fdata, iph->ihl<<2); buf+=iph->ihl<<2; len+=iph->ihl<<2; for(f=qp->fragments; f; f=f->next) { dmesg(M_DEBUG," * %u bytes @ %u", f->len, f->offset); memcpy(buf, f->data, f->len); buf+=f->len; len+=f->len; if ( len >= olen ) break; } dmesg(M_DEBUG,""); /* Build the packet */ ipfr.len=len; ipfr.caplen=len; ipfr.llen=0; ipfr.layer[0].proto=&ipv4_p; ipfr.layer[0].h.raw=reasm_buf; ipfr.layer[0].flags=FLAG_IP_REASM|FLAG_IP_CSUM; ipfr.layer[0].session=NULL; ipfr.base=reasm_buf; ipfr.end=ipfr.base+olen; ipfr.capture=qp->cap; ipfr.flags=(qp->flags & ~FP_LIVE) | FP_CLONE; /* Fill in the timestamp * (time of last packet seen for this frag) */ ipfr.time.tv_sec=qp->time.tv_sec; ipfr.time.tv_usec=qp->time.tv_usec; /* Fixup the IP header */ ipfr.layer[0].h.ip->frag_off=0; ipfr.layer[0].h.ip->tot_len=ntohs(len); ipfrag_csum(ipfr.layer[0].h.ip); /* Inject the packet back in to the flow */ ipfrag_reassembled++; serial_number(&ipfr.serial); ipfr.layer[0].proto->decode(&ipfr); return;}__INLINE__ struct ipq *ip_frag_create(unsigned int hash, struct pkt_iphdr *iph){ struct ipq *q; if ( !(q=calloc(1, sizeof(struct ipq))) ){ ipfrag_err_mem++; return NULL; } ipfrag_mem+=sizeof(struct ipq); q->id=iph->id; q->saddr=iph->saddr; q->daddr=iph->daddr; q->protocol=iph->protocol; if ( (q->next=ipq_hash[hash]) ) { q->next->pprev=&q->next; } ipq_hash[hash]=q; q->pprev=&ipq_hash[hash]; return q;}/* Find (or create) the ipq for this IP fragment */__INLINE__ struct ipq *ip_find(struct pkt_iphdr *iph, unsigned int *hash, struct packet *pkt){ struct ipq *qp; *hash=ipq_hashfn(iph->id, iph->saddr, iph->daddr, iph->protocol); for(qp=ipq_hash[*hash]; qp; qp=qp->next) { if ( qp->id == iph->id && qp->saddr == iph->saddr && qp->daddr == iph->daddr && qp->protocol == iph->protocol ) { return qp; } } qp=ip_frag_create(*hash, iph); qp->time.tv_sec=pkt->time.tv_sec; qp->time.tv_usec=pkt->time.tv_usec; return qp;}/* If a fragment is too old then zap it */__INLINE__ int ipfrag_expire(struct packet *pkt, struct ipq *qp){ struct timeval diff; diff.tv_sec=(pkt->time.tv_sec - qp->time.tv_sec); diff.tv_usec=(1000000+pkt->time.tv_usec)-qp->time.tv_usec; while(diff.tv_usec > 1000000) { diff.tv_usec-=1000000; diff.tv_sec++; } if ( diff.tv_sec >= ipfrag_timeout ) { ipfrag_err_timeout++; return 0; } return 1;}/* Trim down to low memory watermark */__INLINE__ void ip_evictor(struct packet *pkt, struct ipq *cq){ dmesg(M_DEBUG,"Running the ipfrag evictor!"); ipfrag_oom(pkt); while ( (ipfrag_mem > ipfrag_mem_lo) ) { if ( !ipq_oldest || ipq_oldest==cq) return; ipfrag_err_mem++; ipq_kill(ipq_oldest); }}__INLINE__ int ipfrag_queue( unsigned int hash, struct ipq *qp, struct packet *pkt, struct pkt_iphdr *iph){ struct ipfrag *prev, *next, *me; int flags, offset; int ihl, end, len; int chop=0; /* Move to head of LRU list */ if ( qp->next_time) qp->next_time->prev_time=qp->prev_time; if ( qp->prev_time) qp->prev_time->next_time=qp->next_time; if ( qp == ipq_oldest ) ipq_oldest=qp->prev_time; if ( qp == ipq_latest ) ipq_latest=qp->next_time; qp->next_time=ipq_latest; qp->prev_time=NULL; if ( !ipq_oldest ) ipq_oldest=qp; if ( ipq_latest ) ipq_latest->prev_time=qp; ipq_latest=qp; /* Check our timeout */ if ( !ipfrag_expire(pkt, qp) ) { /* We alert if we actually see a fragment * arrive after the timeout because that * is suspicious (read: evasive) */ ipfrag_timedout(pkt); ipq_kill(qp); return 0; } /* Check other timeouts */ while ( ipq_oldest ){ if ( ipfrag_expire(pkt, ipq_oldest) ) break; /* this can't kill qp from under us because * we already know we haven't timed out */ ipq_kill(ipq_oldest); } /* Move to front heuristic */ if ( qp->next ) qp->next->pprev=qp->pprev; *qp->pprev=qp->next; if ( (qp->next=ipq_hash[hash]) ) qp->next->pprev=&qp->next; ipq_hash[hash]=qp; qp->pprev=&ipq_hash[hash]; /* The time for the reassembled packet is equal * to the time of the last packet recieved. This * makes things sane in the sense that time won't * be seen to be going backwards by the higher layers! */ qp->time.tv_sec=pkt->time.tv_sec; qp->time.tv_usec=pkt->time.tv_usec; /* Kill off LRU ipqs, we are OOM */ if ( ipfrag_mem > ipfrag_mem_hi ) ip_evictor(pkt, qp); /* Now we can get on with queueing the packet.. */ ihl=iph->ihl<<2; len=ntohs(iph->tot_len); if ( ((void *)iph)+len > pkt->end ) { ipfrag_truncated(pkt); return 1; } offset=ntohs(iph->frag_off); flags=offset & ~IP_OFFMASK; offset&=IP_OFFMASK; offset<<=3; /* 8 byte granularity */ end = offset + len - ihl; if ( (flags&IP_MF)==0 ) { if ( end < qp->len || ((qp->last_in & LAST_IN) && end!=qp->len)) { ipfrag_teardrop(pkt); return 1; } qp->last_in |= LAST_IN; qp->len = end; }else{ if ( end&7 ) { /* Don't drop the packet stupid! Modern * stacks mask off 0x7 so if we ditch the * frag as invalid we could be evaded. */ ipfrag_boink(pkt); } /* Non-terminal fragments must be multiples of * 8 bytes so mask off low-order bits */ end &=~7; if ( end > qp->len ) { if (qp->last_in & LAST_IN) { ipfrag_attack(pkt); return 1; } qp->len=end; } } if ( end==offset ) { ipfrag_attack(pkt); return 1; } /* Don't bother wasting any more resources * when we know the packet is oversize (invalid) */ if ( qp->len > 0xffff ) { ipfrag_oversize(pkt); return 1; } /* Insert data into fragment chain */ if ( !(me=calloc(1, sizeof(struct ipfrag))) ) return 1; ipfrag_mem+=sizeof(struct ipfrag); /* Find out where to insert this fragment in the list */ prev=NULL; for(next=qp->fragments; next; next=next->next) { if ( next->offset >= offset ) break; prev=next; } /* Check we don't overlap the previous fragment */ if ( prev ) { int i=(prev->offset + prev->len) - offset; if ( i>0 ) { offset += i; chop=i; len-=i; if ( end <= offset ) { ipfrag_attack(pkt); return 1; } } } /* Make sure we don't overlap next packets */ while(next && next->offset < end) { int i = end - next->offset; if ( i < next->len ) { /* Eat head of the next overlapped fragment * and leave the loop. The next ones cannot * overlap. */ next->offset+=i; next->len-=i; next->data+=i; break; }else{ struct ipfrag *free_it=next; /* Old fragment is completely overriden * with new one. Drop it */ next=next->next; if ( prev ){ prev->next=next; }else{ qp->fragments=next; } qp->meat -= free_it->len; free(free_it); ipfrag_mem-=sizeof(struct ipfrag); } } /* Make the fragment */ me->len=len-(chop); me->offset=offset; me->len-=ihl; if ( offset ) { chop=ihl; } /* IP defragmentation can be zerocopy */ if ( pkt->flags & FP_CLONE ) { unsigned int alen=me->len; if ( !offset ) alen+=ihl; if ( !(me->fdata=malloc(alen)) ) { free(me); return 1; } ipfrag_mem+=alen; memcpy(me->fdata, ((void *)iph)+chop, alen); me->free=1; me->flen=alen; }else { me->fdata=((void *)iph)+chop; me->free=0; } me->data=me->fdata; if ( !offset ) me->data+=ihl; /* Insert the fragment */ me->next=next; if ( prev ) { prev->next=me; }else{ qp->fragments=me; } /* Finish up */ qp->cap=pkt->capture; qp->flags=pkt->flags; qp->meat+=me->len; if ( !offset ) qp->last_in |= FIRST_IN; dmesg(M_DEBUG,"0x%x: got a fragment (%u/%u)", (unsigned int)qp, qp->meat, qp->len); return 1;}void ipfrag_free(void){ mesg(M_INFO,"ipfrag: %u reassembled packets, " "%u reasm errors, %u timeouts", ipfrag_reassembled, ipfrag_err_reasm, ipfrag_err_timeout); mesg(M_INFO,"ipfrag: %u times out of memory, %uKB still used", ipfrag_err_mem, ipfrag_mem/1024);}/* Preprocessor entry point */int ipfrag_process(struct packet *pkt, unsigned int i){ struct pkt_iphdr *iph; unsigned int hash; struct ipq *q; iph=pkt->layer[i].h.ip; /* Don't track fragments inside fragments * wait till they are reassembled. This also * catches the case where an ip fragment caused * an icmp error that also gets fragmented */ if ( first_frag && (pkt->serial==last_frag) ) { ipfrag_fraginfrag(pkt); return 0; } first_frag=1; last_frag=pkt->serial; /* Ignore badly checksummed packets */ if ( !(pkt->layer[i].flags&FLAG_IP_CSUM) ) return 0; /* Ignore packets with ttl < min_ttl */ if ( iph->ttl < ipfrag_minttl ) return 0; if ( (q=ip_find(iph, &hash, pkt)) ) { pkt->layer[i].session=q; if ( !ipfrag_queue(hash, q, pkt, iph) ) return 1; if ( q->last_in == (FIRST_IN|LAST_IN) && q->meat == q->len ) { ipfrag_reassemble(q); return 1; } } return 0;}int ipfrag_init(char *args){ if ( use_ipfrag ) { mesg(M_ERR,"ipfrag: can't add ipfrag twice!"); return 0; } if ( args && args_parse(ipfrag_args, args, NULL)<=0 ) { mesg(M_ERR,"ipfrag: parse error: %s", args); return 0; } if ( ipfrag_mem_hi<=ipfrag_mem_lo ) { mesg(M_ERR,"ipfrag: mem_hi must be bigger than mem_lo"); return 0; } if ( ipfrag_minttl>255 ) { mesg(M_ERR,"ipfrag: minttl must be < 256"); return 0; } if ( ipfrag_timeout<10 || ipfrag_timeout>120 ) { mesg(M_WARN,"ipfrag: timeout is unreasonable - " "you will be vulnerable to attack!"); } mesg(M_INFO,"ipfrag: mem_hi=%u mem_lo=%u " "minttl=%u timeout=%us", ipfrag_mem_hi, ipfrag_mem_lo, ipfrag_minttl, ipfrag_timeout); use_ipfrag=1; return 1;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -