int packet_pool_release(struct packet *p) { struct packet_multipart *multipart = NULL; pom_mutex_lock(&packet_list_mutex); if (p->multipart) { multipart = p->multipart; p->multipart = NULL; } p->refcount--; if (p->refcount) { pom_mutex_unlock(&packet_list_mutex); if (multipart) // Always release the multipart return packet_multipart_cleanup(multipart); return POM_OK; } // Remove the packet from the used list if (p->next) p->next->prev = p->prev; if (p->prev) p->prev->next = p->next; else packet_head = p->next; if (p->pkt_buff) { packet_buffer_pool_release(p->pkt_buff); p->pkt_buff = NULL; p->buff = NULL; } memset(p, 0, sizeof(struct packet)); // Add it back to the unused list #ifdef PACKET_INFO_POOL_ALLOC_DEBUG free(p); #else p->next = packet_unused_head; if (p->next) p->next->prev = p; packet_unused_head = p; #endif pom_mutex_unlock(&packet_list_mutex); int res = POM_OK; if (multipart) res = packet_multipart_cleanup(multipart); return res; }
static int proto_ipv4_fragment_cleanup(struct conntrack_entry *ce, void *priv, ptime now) { struct proto_ipv4_fragment *f = priv; // Remove the frag from the conntrack if (f->prev) f->prev->next = f->next; else ce->priv = f->next; if (f->next) f->next->prev = f->prev; conntrack_unlock(ce); if (!(f->flags & PROTO_IPV4_FLAG_PROCESSED)) { registry_perf_inc(perf_frags_dropped, f->count); } if (f->multipart) packet_multipart_cleanup(f->multipart); if (f->t) conntrack_timer_cleanup(f->t); free(f); return POM_OK; }
int packet_multipart_process(struct packet_multipart *multipart, struct proto_process_stack *stack, unsigned int stack_index) { struct packet *p = packet_pool_get(); if (!p) { packet_multipart_cleanup(multipart); return PROTO_ERR; } // FIXME align offset if (packet_buffer_pool_get(p, multipart->cur, 0)) { packet_pool_release(p); packet_multipart_cleanup(multipart); pom_oom(multipart->cur); return PROTO_ERR; } struct packet_multipart_pkt *tmp = multipart->head; for (; tmp; tmp = tmp->next) { if (tmp->offset + tmp->len > multipart->cur) { pomlog(POMLOG_DEBUG "Offset in packet fragment is bigger than packet size."); packet_pool_release(p); packet_multipart_cleanup(multipart); return PROTO_INVALID; } memcpy(p->buff + tmp->offset, tmp->pkt->buff + tmp->pkt_buff_offset, tmp->len); } memcpy(&p->ts, &multipart->tail->pkt->ts, sizeof(struct timeval)); p->multipart = multipart; p->len = multipart->cur; p->datalink = multipart->proto; p->input = multipart->head->pkt->input; stack[stack_index].pload = p->buff; stack[stack_index].plen = p->len; stack[stack_index].proto = p->datalink; int res = core_process_multi_packet(stack, stack_index, p); packet_pool_release(p); return res; }
int packet_multipart_process(struct packet_multipart *multipart, struct proto_process_stack *stack, unsigned int stack_index) { struct packet *p = packet_alloc(); if (!p) { packet_multipart_cleanup(multipart); return PROTO_ERR; } if (packet_buffer_alloc(p, multipart->cur, multipart->align_offset)) { packet_release(p); packet_multipart_cleanup(multipart); return PROTO_ERR; } struct packet_multipart_pkt *tmp = multipart->head; for (; tmp; tmp = tmp->next) { if (tmp->offset + tmp->len > multipart->cur) { pomlog(POMLOG_DEBUG "Offset in packet fragment is bigger than packet size."); packet_release(p); packet_multipart_cleanup(multipart); return PROTO_INVALID; } memcpy(p->buff + tmp->offset, tmp->pkt->buff + tmp->pkt_buff_offset, tmp->len); } p->ts = multipart->tail->pkt->ts; p->multipart = multipart; p->len = multipart->cur; p->datalink = multipart->proto; p->input = multipart->head->pkt->input; stack[stack_index].pload = p->buff; stack[stack_index].plen = p->len; stack[stack_index].proto = p->datalink; int res = core_process_multi_packet(stack, stack_index, p); packet_release(p); return (res == PROTO_ERR ? POM_ERR : POM_OK); }
int packet_release(struct packet *p) { // Release the multipart struct packet_multipart *multipart = __sync_fetch_and_and(&p->multipart, 0); if (multipart && packet_multipart_cleanup(multipart) != POM_OK) return POM_ERR; // The packet refcount will be 0 afterwards // We can clean up the buffer if any if (p->refcount > 1) { __sync_fetch_and_sub(&p->refcount, 1); return POM_OK; } if (p->pkt_buff) packet_buffer_release(p->pkt_buff); registry_perf_dec(perf_pkt_in_use, 1); free(p); return POM_OK; }
static int proto_ipv4_conntrack_cleanup(void *ce_priv) { struct proto_ipv4_fragment *frag_list = ce_priv; while (frag_list) { struct proto_ipv4_fragment *f = frag_list; frag_list = f->next; if (!(f->flags & PROTO_IPV4_FLAG_PROCESSED)) { registry_perf_inc(perf_frags_dropped, f->count); } if (f->multipart) packet_multipart_cleanup(f->multipart); if (f->t) conntrack_timer_cleanup(f->t); free(f); } return POM_OK; }
static int proto_ipv4_process(void *proto_priv, struct packet *p, struct proto_process_stack *stack, unsigned int stack_index) { struct proto_process_stack *s = &stack[stack_index]; struct proto_process_stack *s_next = &stack[stack_index + 1]; struct ip* hdr = s->pload; unsigned int hdr_len = hdr->ip_hl * 4; if (s->plen < sizeof(struct ip) || // length smaller than header hdr->ip_hl < 5 || // ip header < 5 bytes ntohs(hdr->ip_len) < hdr_len || // datagram size < ip header length ntohs(hdr->ip_len) > s->plen) { // datagram size > given size return PROTO_INVALID; } PTYPE_IPV4_SETADDR(s->pkt_info->fields_value[proto_ipv4_field_src], hdr->ip_src); PTYPE_IPV4_SETADDR(s->pkt_info->fields_value[proto_ipv4_field_dst], hdr->ip_dst); PTYPE_UINT8_SETVAL(s->pkt_info->fields_value[proto_ipv4_field_tos], hdr->ip_tos); PTYPE_UINT8_SETVAL(s->pkt_info->fields_value[proto_ipv4_field_ttl], hdr->ip_ttl); // Handle conntrack stuff if (conntrack_get(stack, stack_index) != POM_OK) return PROTO_ERR; s_next->pload = s->pload + hdr_len; s_next->plen = ntohs(hdr->ip_len) - hdr_len; s_next->proto = proto_get_by_number(s->proto, hdr->ip_p); int res = POM_ERR; if (s->ce->children) { res = conntrack_delayed_cleanup(s->ce, 0, p->ts); } else { uint32_t *conntrack_timeout = PTYPE_UINT32_GETVAL(param_conntrack_timeout); res = conntrack_delayed_cleanup(s->ce, *conntrack_timeout, p->ts); } if (res == POM_ERR) { conntrack_unlock(s->ce); return PROTO_ERR; } uint16_t frag_off = ntohs(hdr->ip_off); // Check if packet is fragmented and need more handling if (frag_off & IP_DONT_FRAG) { conntrack_unlock(s->ce); return PROTO_OK; // Nothing to do } if (!(frag_off & IP_MORE_FRAG) && !(frag_off & IP_OFFSET_MASK)) { conntrack_unlock(s->ce); return PROTO_OK; // Nothing to do, full packet } uint16_t offset = (frag_off & IP_OFFSET_MASK) << 3; size_t frag_size = ntohs(hdr->ip_len) - (hdr->ip_hl * 4); // Ignore invalid fragments if (frag_size > 0xFFFF) { conntrack_unlock(s->ce); return PROTO_INVALID; } if (frag_size > s->plen + hdr_len) { conntrack_unlock(s->ce); return PROTO_INVALID; } // Account for one more fragment registry_perf_inc(perf_frags, 1); struct proto_ipv4_fragment *tmp = s->ce->priv; // Let's find the right buffer for (; tmp && tmp->id != hdr->ip_id; tmp = tmp->next); if (!tmp) { // Buffer not found, create it tmp = malloc(sizeof(struct proto_ipv4_fragment)); if (!tmp) { pom_oom(sizeof(struct proto_ipv4_fragment)); conntrack_unlock(s->ce); return PROTO_ERR; } memset(tmp, 0, sizeof(struct proto_ipv4_fragment)); tmp->t = conntrack_timer_alloc(s->ce, proto_ipv4_fragment_cleanup, tmp); if (!tmp->t) { conntrack_unlock(s->ce); free(tmp); return PROTO_ERR; } tmp->id = hdr->ip_id; if (!s_next->proto) { // Set processed flag so no attempt to process this will be done tmp->flags |= PROTO_IPV4_FLAG_PROCESSED; conntrack_unlock(s->ce); conntrack_timer_cleanup(tmp->t); free(tmp); return PROTO_STOP; } tmp->multipart = packet_multipart_alloc(s_next->proto, 0); if (!tmp->multipart) { conntrack_unlock(s->ce); conntrack_timer_cleanup(tmp->t); free(tmp); return PROTO_ERR; } tmp->next = s->ce->priv; if (tmp->next) tmp->next->prev = tmp; s->ce->priv = tmp; } // Fragment was already handled if (tmp->flags & PROTO_IPV4_FLAG_PROCESSED) { conntrack_unlock(s->ce); registry_perf_inc(perf_frags_dropped, 1); return PROTO_STOP; } // Add the fragment if (packet_multipart_add_packet(tmp->multipart, p, offset, frag_size, (s->pload - (void*)p->buff) + (hdr->ip_hl * 4)) != POM_OK) { conntrack_unlock(s->ce); packet_multipart_cleanup(tmp->multipart); conntrack_timer_cleanup(tmp->t); free(tmp); return PROTO_ERR; } tmp->count++; // Schedule the timeout for the fragment uint32_t *frag_timeout = PTYPE_UINT32_GETVAL(param_frag_timeout); conntrack_timer_queue(tmp->t, *frag_timeout, p->ts); if (!(frag_off & IP_MORE_FRAG)) tmp->flags |= PROTO_IPV4_FLAG_GOT_LAST; if ((tmp->flags & PROTO_IPV4_FLAG_GOT_LAST) && !tmp->multipart->gaps) tmp->flags |= PROTO_IPV4_FLAG_PROCESSED; conntrack_unlock(s->ce); if ((tmp->flags & PROTO_IPV4_FLAG_PROCESSED)) { int res = packet_multipart_process(tmp->multipart, stack, stack_index + 1); tmp->multipart = NULL; // Multipart will be cleared automatically if (res == PROTO_ERR) { return PROTO_ERR; } else if (res == PROTO_INVALID) { registry_perf_inc(perf_frags_dropped, tmp->count); } else { registry_perf_inc(perf_reassembled_pkts, 1); } } return PROTO_STOP; // Stop processing the packet }