struct ip_reassdata* ip_reass_lookup_dg( struct ip_hdr *fraghdr ) { struct ip_reassdata *ipr; sys_lock_acquire( &ip_frag_lock ); for (ipr = reassdatagrams; ipr != NULL; ipr = ipr->next) { if (IP_ADDRESSES_AND_ID_MATCH(&ipr->iphdr, fraghdr)) { break; } } sys_lock_release( &ip_frag_lock ); return ipr; }
/** * Free the oldest datagram to make room for enqueueing new fragments. * The datagram 'fraghdr' belongs to is not freed! * * @param fraghdr IP header of the current fragment * @param pbufs_needed number of pbufs needed to enqueue * (used for freeing other datagrams if not enough space) * @return the number of pbufs freed */ static int ip_reass_remove_oldest_datagram(struct ip_hdr *fraghdr, int pbufs_needed) { /* @todo Can't we simply remove the last datagram in the * linked list behind reassdatagrams? */ struct ip_reassdata *r, *oldest, *prev, *oldest_prev; int pbufs_freed = 0, pbufs_freed_current; int other_datagrams; /* Free datagrams until being allowed to enqueue 'pbufs_needed' pbufs, * but don't free the datagram that 'fraghdr' belongs to! */ do { oldest = NULL; prev = NULL; oldest_prev = NULL; other_datagrams = 0; r = reassdatagrams; while (r != NULL) { if (!IP_ADDRESSES_AND_ID_MATCH(&r->iphdr, fraghdr)) { /* Not the same datagram as fraghdr */ other_datagrams++; if (oldest == NULL) { oldest = r; oldest_prev = prev; } else if (r->timer <= oldest->timer) { /* older than the previous oldest */ oldest = r; oldest_prev = prev; } } if (r->next != NULL) { prev = r; } r = r->next; } if (oldest != NULL) { pbufs_freed_current = ip_reass_free_complete_datagram(oldest, oldest_prev); pbufs_freed += pbufs_freed_current; } } while ((pbufs_freed < pbufs_needed) && (other_datagrams > 1)); return pbufs_freed; }
/** * Reassembles incoming IP fragments into an IP datagram. * * @param p points to a pbuf chain of the fragment * @return NULL if reassembly is incomplete, ? otherwise */ struct pbuf * ip_reass(struct pbuf *p) { struct pbuf *r; struct ip_hdr *fraghdr; struct ip_reassdata *ipr; struct ip_reass_helper *iprh; u16_t offset, len; u8_t clen; struct ip_reassdata *ipr_prev = NULL; IPFRAG_STATS_INC(ip_frag.recv); snmp_inc_ipreasmreqds(); fraghdr = (struct ip_hdr*)p->payload; if ((IPH_HL(fraghdr) * 4) != IP_HLEN) { LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass: IP options currently not supported!\n")); IPFRAG_STATS_INC(ip_frag.err); goto nullreturn; } offset = (ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) * 8; len = ntohs(IPH_LEN(fraghdr)) - IPH_HL(fraghdr) * 4; /* Check if we are allowed to enqueue more datagrams. */ clen = pbuf_clen(p); if ((ip_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS) { #if IP_REASS_FREE_OLDEST if (!ip_reass_remove_oldest_datagram(fraghdr, clen) || ((ip_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS)) #endif /* IP_REASS_FREE_OLDEST */ { /* No datagram could be freed and still too many pbufs enqueued */ LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass: Overflow condition: pbufct=%d, clen=%d, MAX=%d\n", ip_reass_pbufcount, clen, IP_REASS_MAX_PBUFS)); IPFRAG_STATS_INC(ip_frag.memerr); /* @todo: send ICMP time exceeded here? */ /* drop this pbuf */ goto nullreturn; } } /* Look for the datagram the fragment belongs to in the current datagram queue, * remembering the previous in the queue for later dequeueing. */ for (ipr = reassdatagrams; ipr != NULL; ipr = ipr->next) { /* Check if the incoming fragment matches the one currently present in the reassembly buffer. If so, we proceed with copying the fragment into the buffer. */ if (IP_ADDRESSES_AND_ID_MATCH(&ipr->iphdr, fraghdr)) { LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass: matching previous fragment ID=%"X16_F"\n", ntohs(IPH_ID(fraghdr)))); IPFRAG_STATS_INC(ip_frag.cachehit); break; } ipr_prev = ipr; } if (ipr == NULL) { /* Enqueue a new datagram into the datagram queue */ ipr = ip_reass_enqueue_new_datagram(fraghdr, clen); /* Bail if unable to enqueue */ if(ipr == NULL) { goto nullreturn; } } else { if (((ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) == 0) && ((ntohs(IPH_OFFSET(&ipr->iphdr)) & IP_OFFMASK) != 0)) { /* ipr->iphdr is not the header from the first fragment, but fraghdr is * -> copy fraghdr into ipr->iphdr since we want to have the header * of the first fragment (for ICMP time exceeded and later, for copying * all options, if supported)*/ SMEMCPY(&ipr->iphdr, fraghdr, IP_HLEN); } } /* Track the current number of pbufs current 'in-flight', in order to limit the number of fragments that may be enqueued at any one time */ ip_reass_pbufcount += clen; /* At this point, we have either created a new entry or pointing * to an existing one */ /* check for 'no more fragments', and update queue entry*/ if ((IPH_OFFSET(fraghdr) & PP_NTOHS(IP_MF)) == 0) { ipr->flags |= IP_REASS_FLAG_LASTFRAG; ipr->datagram_len = offset + len; LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass: last fragment seen, total len %"S16_F"\n", ipr->datagram_len)); } /* find the right place to insert this pbuf */ /* @todo: trim pbufs if fragments are overlapping */ if (ip_reass_chain_frag_into_datagram_and_validate(ipr, p)) { /* the totally last fragment (flag more fragments = 0) was received at least * once AND all fragments are received */ ipr->datagram_len += IP_HLEN; /* save the second pbuf before copying the header over the pointer */ r = ((struct ip_reass_helper*)ipr->p->payload)->next_pbuf; /* copy the original ip header back to the first pbuf */ fraghdr = (struct ip_hdr*)(ipr->p->payload); SMEMCPY(fraghdr, &ipr->iphdr, IP_HLEN); IPH_LEN_SET(fraghdr, htons(ipr->datagram_len)); IPH_OFFSET_SET(fraghdr, 0); IPH_CHKSUM_SET(fraghdr, 0); /* @todo: do we need to set calculate the correct checksum? */ IPH_CHKSUM_SET(fraghdr, inet_chksum(fraghdr, IP_HLEN)); p = ipr->p; /* chain together the pbufs contained within the reass_data list. */ while(r != NULL) { iprh = (struct ip_reass_helper*)r->payload; /* hide the ip header for every succeding fragment */ pbuf_header(r, -IP_HLEN); pbuf_cat(p, r); r = iprh->next_pbuf; } /* release the sources allocate for the fragment queue entry */ ip_reass_dequeue_datagram(ipr, ipr_prev); /* and adjust the number of pbufs currently queued for reassembly. */ ip_reass_pbufcount -= pbuf_clen(p); /* Return the pbuf chain */ return p; } /* the datagram is not (yet?) reassembled completely */ LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass_pbufcount: %d out\n", ip_reass_pbufcount)); return NULL; nullreturn: LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass: nullreturn\n")); IPFRAG_STATS_INC(ip_frag.drop); pbuf_free(p); return NULL; }