/* * OpenVPN -- An application to securely tunnel IP networks * over a single TCP/UDP port, with support for SSL/TLS-based * session authentication and key exchange, * packet encryption, packet authentication, and * packet compression. * * Copyright (C) 2002-2018 OpenVPN Inc * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifdef HAVE_CONFIG_H #include "config.h" #elif defined(_MSC_VER) #include "config-msvc.h" #endif #include "syshead.h" #if P2MP #include "buffer.h" #include "error.h" #include "integer.h" #include "misc.h" #include "mbuf.h" #include "memdbg.h" struct mbuf_set * mbuf_init(unsigned int size) { struct mbuf_set *ret; ALLOC_OBJ_CLEAR(ret, struct mbuf_set); ret->capacity = adjust_power_of_2(size); ALLOC_ARRAY(ret->array, struct mbuf_item, ret->capacity); return ret; } void mbuf_free(struct mbuf_set *ms) { if (ms) { int i; for (i = 0; i < (int) ms->len; ++i) { struct mbuf_item *item = &ms->array[MBUF_INDEX(ms->head, i, ms->capacity)]; mbuf_free_buf(item->buffer); } free(ms->array); free(ms); } } struct mbuf_buffer * mbuf_alloc_buf(const struct buffer *buf) { struct mbuf_buffer *ret; ALLOC_OBJ(ret, struct mbuf_buffer); ret->buf = clone_buf(buf); ret->refcount = 1; ret->flags = 0; return ret; } void mbuf_free_buf(struct mbuf_buffer *mb) { if (mb) { if (--mb->refcount <= 0) { free_buf(&mb->buf); free(mb); } } } void mbuf_add_item(struct mbuf_set *ms, const struct mbuf_item *item) { ASSERT(ms); if (ms->len == ms->capacity) { struct mbuf_item rm; ASSERT(mbuf_extract_item(ms, &rm)); mbuf_free_buf(rm.buffer); msg(D_MULTI_DROPPED, "MBUF: mbuf packet dropped"); } ASSERT(ms->len < ms->capacity); ms->array[MBUF_INDEX(ms->head, ms->len, ms->capacity)] = *item; if (++ms->len > ms->max_queued) { ms->max_queued = ms->len; } ++item->buffer->refcount; } bool mbuf_extract_item(struct mbuf_set *ms, struct mbuf_item *item) { bool ret = false; if (ms) { while (ms->len) { *item = ms->array[ms->head]; ms->head = MBUF_INDEX(ms->head, 1, ms->capacity); --ms->len; if (item->instance) /* ignore dereferenced instances */ { ret = true; break; } } } return ret; } struct multi_instance * mbuf_peek_dowork(struct mbuf_set *ms) { struct multi_instance *ret = NULL; if (ms) { int i; for (i = 0; i < (int) ms->len; ++i) { struct mbuf_item *item = &ms->array[MBUF_INDEX(ms->head, i, ms->capacity)]; if (item->instance) { ret = item->instance; break; } } } return ret; } void mbuf_dereference_instance(struct mbuf_set *ms, struct multi_instance *mi) { if (ms) { int i; for (i = 0; i < (int) ms->len; ++i) { struct mbuf_item *item = &ms->array[MBUF_INDEX(ms->head, i, ms->capacity)]; if (item->instance == mi) { mbuf_free_buf(item->buffer); item->buffer = NULL; item->instance = NULL; msg(D_MBUF, "MBUF: dereferenced queued packet"); } } } } #else /* if P2MP */ static void dummy(void) { } #endif /* P2MP */