diff --git a/flow.c b/flow.c index 64de75c..63eefd6 100644 --- a/flow.c +++ b/flow.c @@ -50,6 +50,32 @@ void flow_log_(const struct flow_common *f, int pri, const char *fmt, ...) logmsg(pri, "Flow %u (%s): %s", flow_idx(f), FLOW_TYPE(f), msg); } +/** + * flow_alloc() - Allocate a new flow + * + * Return: pointer to an unused flow entry, or NULL if the table is full + */ +union flow *flow_alloc(void) +{ + if (flow_count >= FLOW_MAX) + return NULL; + + return &flowtab[flow_count++]; +} + +/** + * flow_alloc_cancel() - Free a newly allocated flow + * @flow: Flow to deallocate + * + * @flow must be the last flow allocated by flow_alloc() + */ +void flow_alloc_cancel(union flow *flow) +{ + ASSERT(FLOW_IDX(flow) == flow_count - 1); + memset(flow, 0, sizeof(*flow)); + flow_count--; +} + /** * flow_table_compact() - Perform compaction on flow table * @c: Execution context diff --git a/flow_table.h b/flow_table.h index 4aa2398..2773a2b 100644 --- a/flow_table.h +++ b/flow_table.h @@ -88,4 +88,7 @@ static inline flow_sidx_t flow_sidx(const struct flow_common *f, */ #define FLOW_SIDX(f_, side) (flow_sidx(&(f_)->f, (side))) +union flow *flow_alloc(void); +void flow_alloc_cancel(union flow *flow); + #endif /* FLOW_TABLE_H */ diff --git a/tcp.c b/tcp.c index 6b62896..5b56786 100644 --- a/tcp.c +++ b/tcp.c @@ -1944,17 +1944,18 @@ static void tcp_conn_from_tap(struct ctx *c, }; const struct sockaddr *sa; struct tcp_tap_conn *conn; + union flow *flow; socklen_t sl; int s, mss; (void)saddr; - if (flow_count >= FLOW_MAX) + if (!(flow = flow_alloc())) return; if ((s = tcp_conn_pool_sock(pool)) < 0) if ((s = tcp_conn_new_sock(c, af)) < 0) - return; + goto cancel; if (!c->no_map_gw) { if (af == AF_INET && IN4_ARE_ADDR_EQUAL(daddr, &c->ip4.gw)) @@ -1969,13 +1970,11 @@ static void tcp_conn_from_tap(struct ctx *c, .sin6_addr = c->ip6.addr_ll, .sin6_scope_id = c->ifi6, }; - if (bind(s, (struct sockaddr *)&addr6_ll, sizeof(addr6_ll))) { - close(s); - return; - } + if (bind(s, (struct sockaddr *)&addr6_ll, sizeof(addr6_ll))) + goto cancel; } - conn = CONN(flow_count++); + conn = &flow->tcp; conn->f.type = FLOW_TCP; conn->sock = s; conn->timer = -1; @@ -2047,6 +2046,12 @@ static void tcp_conn_from_tap(struct ctx *c, } tcp_epoll_ctl(c, conn); + return; + +cancel: + if (s >= 0) + close(s); + flow_alloc_cancel(flow); } /** @@ -2724,14 +2729,12 @@ void tcp_listen_handler(struct ctx *c, union epoll_ref ref, union flow *flow; int s; - if (c->no_tcp || flow_count >= FLOW_MAX) + if (c->no_tcp || !(flow = flow_alloc())) return; s = accept4(ref.fd, (struct sockaddr *)&sa, &sl, SOCK_NONBLOCK); if (s < 0) - return; - - flow = flowtab + flow_count++; + goto cancel; if (c->mode == MODE_PASTA && tcp_splice_conn_from_sock(c, ref.tcp_listen, &flow->tcp_splice, @@ -2740,6 +2743,10 @@ void tcp_listen_handler(struct ctx *c, union epoll_ref ref, tcp_tap_conn_from_sock(c, ref.tcp_listen, &flow->tcp, s, (struct sockaddr *)&sa, now); + return; + +cancel: + flow_alloc_cancel(flow); } /**