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_stream_cleanup(struct packet_stream *stream) { if (stream->wait_list_head) { pomlog(POMLOG_ERR "Internal error, cleaning up stream while packets still present!"); return POM_ERR; } while (stream->head[0] || stream->head[1]) { if (packet_stream_force_dequeue(stream) == POM_ERR) { pomlog(POMLOG_ERR "Error while processing remaining packets in the stream"); break; } } if (stream->t) conntrack_timer_cleanup(stream->t); if (pthread_mutex_destroy(&stream->lock)) pomlog(POMLOG_ERR "Error while destroying stream lock : %s", pom_strerror(errno)); if (pthread_mutex_destroy(&stream->wait_lock)) pomlog(POMLOG_ERR "Error while destroying stream wait lock : %s", pom_strerror(errno)); while (stream->wait_list_unused) { struct packet_stream_thread_wait *tmp = stream->wait_list_unused; stream->wait_list_unused = tmp->next; if (pthread_cond_destroy(&tmp->cond)) pomlog(POMLOG_WARN "Error while destroying list condition"); free(tmp); } free(stream); debug_stream("thread %p, entry %p, released", pthread_self(), stream); 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 }
int conntrack_cleanup(struct conntrack_tables *ct, uint32_t hash, struct conntrack_entry *ce) { // Remove the conntrack from the conntrack table pom_mutex_lock(&ct->locks[hash]); // Try to find the conntrack in the list struct conntrack_list *lst = NULL; for (lst = ct->table[hash]; lst && lst->ce != ce; lst = lst->next); if (!lst) { pom_mutex_unlock(&ct->locks[hash]); pomlog(POMLOG_ERR "Trying to cleanup a non existing conntrack : %p", ce); return POM_OK; } conntrack_lock(ce); if (ce->refcount) { debug_conntrack(POMLOG_ERR "Conntrack %p is still being referenced : %u !", ce, ce->refcount); conntrack_delayed_cleanup(ce, 1, core_get_clock_last()); conntrack_unlock(ce); pom_mutex_unlock(&ct->locks[hash]); return POM_OK; } if (lst->prev) lst->prev->next = lst->next; else ct->table[hash] = lst->next; if (lst->next) lst->next->prev = lst->prev; free(lst); pom_mutex_unlock(&ct->locks[hash]); if (ce->cleanup_timer && ce->cleanup_timer != (void *) -1) { conntrack_timer_cleanup(ce->cleanup_timer); ce->cleanup_timer = (void *) -1; // Mark that the conntrack is being cleaned up } // Once the conntrack is removed from the hash table, it will not be referenced ever again conntrack_unlock(ce); if (ce->parent) { debug_conntrack("Cleaning up conntrack %p, with parent %p", ce, ce->parent->ce); } else { debug_conntrack("Cleaning up conntrack %p, with no parent", ce); } if (ce->parent) { // Remove the child from the parent // Make sure the parent still exists uint32_t hash = ce->parent->hash; pom_mutex_lock(&ce->parent->ct->locks[hash]); for (lst = ce->parent->ct->table[hash]; lst && lst->ce != ce->parent->ce; lst = lst->next); if (lst) { conntrack_lock(ce->parent->ce); struct conntrack_node_list *tmp = ce->parent->ce->children; for (; tmp && tmp->ce != ce; tmp = tmp->next); if (tmp) { if (tmp->prev) tmp->prev->next = tmp->next; else ce->parent->ce->children = tmp->next; if (tmp->next) tmp->next->prev = tmp->prev; free(tmp); } else { pomlog(POMLOG_WARN "Conntrack %s not found in parent's %s children list", ce, ce->parent->ce); } if (!ce->parent->ce->children) // Parent has no child anymore, clean it up after some time conntrack_delayed_cleanup(ce->parent->ce, CONNTRACK_CHILDLESS_TIMEOUT, core_get_clock_last()); conntrack_unlock(ce->parent->ce); } else { debug_conntrack("Parent conntrack %p not found while cleaning child %p !", ce->parent->ce, ce); } pom_mutex_unlock(&ce->parent->ct->locks[hash]); free(ce->parent); } if (ce->session) conntrack_session_refcount_dec(ce->session); // Cleanup private stuff from the conntrack if (ce->priv && ce->proto->info->ct_info->cleanup_handler) { if (ce->proto->info->ct_info->cleanup_handler(ce->priv) != POM_OK) pomlog(POMLOG_WARN "Unable to free the private memory of a conntrack"); } // Cleanup the priv_list struct conntrack_priv_list *priv_lst = ce->priv_list; while (priv_lst) { if (priv_lst->cleanup) { if (priv_lst->cleanup(priv_lst->obj, priv_lst->priv) != POM_OK) pomlog(POMLOG_WARN "Error while cleaning up private objects in conntrack_entry"); } ce->priv_list = priv_lst->next; free(priv_lst); priv_lst = ce->priv_list; } // Cleanup the children while (ce->children) { struct conntrack_node_list *child = ce->children; ce->children = child->next; if (conntrack_cleanup(child->ct, child->hash, child->ce) != POM_OK) return POM_ERR; free(child); } if (ce->fwd_value) ptype_cleanup(ce->fwd_value); if (ce->rev_value) ptype_cleanup(ce->rev_value); pthread_mutex_destroy(&ce->lock); registry_perf_dec(ce->proto->perf_conn_cur, 1); free(ce); return POM_OK; }