Files
utun2/lib/packet_link.c
T

162 lines
7.6 KiB
C

#include "packet_link_backend.h"
#include "u_async.h"
#include "ll_queue.h"
#include "mem.h"
#include "debug_config.h"
#include <stdlib.h>
struct packet_link {
struct UASYNC* ua;
struct packet_link_driver driver;
char* name;
struct packet_link_callbacks callbacks;
struct packet_link_state state;
void *arg, *notification;
unsigned active;
int closing, rx_enabled, sending;
};
static void destroy_deferred(void* arg) {
struct packet_link* link = arg;
link->driver.destroy(link->driver.ctx);
u_free(link->name);
u_free(link);
}
static void release(struct packet_link* link) {
if (--link->active || !link->closing) return;
/* Backend мог вызвать receive из своего I/O callback: его стек тоже должен завершиться. */
if (!uasync_call_soon(link->ua, link, destroy_deferred)) {
DEBUG_ERROR(DEBUG_CATEGORY_SYS, "packet link: cannot defer destruction"); abort();
}
}
static void notify(void* arg) {
struct packet_link* link = arg;
link->notification = NULL;
link->active++;
if (link->callbacks.state_changed) link->callbacks.state_changed(link, link->arg);
release(link);
}
static int schedule(struct packet_link* link) {
if (link->notification || link->closing) return 0;
link->notification = uasync_call_soon(link->ua, link, notify);
if (link->notification) return 0;
DEBUG_ERROR(DEBUG_CATEGORY_SYS, "packet link: cannot schedule state notification");
return -1;
}
static int valid_state(const struct packet_link_state* s) {
return s && s->phase >= PACKET_LINK_CONNECTING && s->phase <= PACKET_LINK_TERMINATED &&
s->max_packet && s->max_packet <= UINT16_MAX && (!s->tx_ready || s->phase == PACKET_LINK_OPEN);
}
static void driver_changed(void* arg);
static void driver_received(void* arg, struct ll_entry* packet);
struct packet_link* packet_link_bind(const struct packet_link_driver* driver,
const struct packet_link_callbacks* callbacks, void* arg) {
if (!driver || !driver->ua || !driver->name || !driver->name[0] || !driver->bind || !driver->get_state ||
!driver->send || !driver->stop || !driver->destroy || !driver->set_rx_enabled || !callbacks || !callbacks->received ||
driver->direction < PACKET_LINK_DIRECTION_UNKNOWN || driver->direction > PACKET_LINK_INCOMING) {
DEBUG_ERROR(DEBUG_CATEGORY_SYS, "packet link: invalid driver API"); return NULL;
}
struct packet_link_state initial = driver->get_state(driver->ctx);
if (!valid_state(&initial) || initial.phase == PACKET_LINK_TERMINATED) {
DEBUG_ERROR(DEBUG_CATEGORY_SYS, "packet link %s: invalid initial state", driver->name); return NULL;
}
struct packet_link* link = u_calloc(1, sizeof(*link));
if (!link) { DEBUG_ERROR(DEBUG_CATEGORY_SYS, "packet link: allocation failed"); return NULL; }
link->ua = driver->ua; link->driver = *driver; link->state = initial; link->callbacks = *callbacks; link->arg = arg;
link->name = u_strdup(driver->name);
if (!link->name || schedule(link)) {
DEBUG_ERROR(DEBUG_CATEGORY_SYS, "packet link: cannot prepare binding"); u_free(link->name); u_free(link); return NULL;
}
link->driver.name = link->name;
const struct packet_link_events events = {driver_changed, driver_received};
if (driver->bind(driver->ctx, &events, link)) {
DEBUG_ERROR(DEBUG_CATEGORY_SYS, "packet link %s: driver binding rejected", link->name);
uasync_call_soon_cancel(link->ua, link->notification); u_free(link->name); u_free(link); return NULL;
}
DEBUG_DEBUG(DEBUG_CATEGORY_SYS, "packet link=%p driver=%s direction=%d phase=%d max=%zu ready=%d",
(void*)link, link->name, link->driver.direction, initial.phase, initial.max_packet, initial.tx_ready);
return link;
}
const char* packet_link_name(const struct packet_link* link) { return link ? link->name : "unbound"; }
enum packet_link_direction packet_link_get_direction(const struct packet_link* link) {
return link ? link->driver.direction : PACKET_LINK_DIRECTION_UNKNOWN;
}
struct packet_link_state packet_link_get_state(const struct packet_link* link) {
if (!link || link->closing) return (struct packet_link_state){PACKET_LINK_TERMINATED, 0, 0, PACKET_LINK_CLOSED};
return link->state;
}
static void driver_changed(void* arg) {
struct packet_link* link = arg;
if (!link || link->closing) return;
struct packet_link_state snapshot = link->driver.get_state(link->driver.ctx);
const struct packet_link_state* state = &snapshot;
if (!valid_state(state) || state->max_packet != link->state.max_packet || (link->state.phase == PACKET_LINK_TERMINATED && state->phase != PACKET_LINK_TERMINATED)) {
DEBUG_ERROR(DEBUG_CATEGORY_SYS, "packet link: invalid state transition"); return;
}
struct packet_link_state old = link->state;
if (old.phase == state->phase && old.max_packet == state->max_packet && old.tx_ready == state->tx_ready && old.error == state->error) return;
link->state = *state;
DEBUG_DEBUG(DEBUG_CATEGORY_SYS, "packet link=%p phase=%d ready=%d max=%zu error=%d",
(void*)link, state->phase, state->tx_ready, state->max_packet, state->error);
/* Потерять обязательное событие нельзя; синхронный callback нарушил бы контракт send. */
if (schedule(link)) abort();
}
int packet_link_send(struct packet_link* link, struct ll_entry* packet) {
if (!link || link->closing || link->state.phase == PACKET_LINK_TERMINATED) return PACKET_LINK_CLOSED;
if (!packet || packet->owner || packet->next || packet->prev || !packet->dgram || !packet->len || packet->len > packet->memlen) {
DEBUG_ERROR(DEBUG_CATEGORY_SYS, "packet link: invalid TX packet"); return PACKET_LINK_INVALID;
}
if (packet->len > link->state.max_packet) {
DEBUG_ERROR(DEBUG_CATEGORY_SYS, "packet link: oversized TX size=%u max=%zu", packet->len, link->state.max_packet);
return PACKET_LINK_TOO_LARGE;
}
if (!link->state.tx_ready || link->sending) return PACKET_LINK_WOULD_BLOCK;
link->active++; link->sending = 1;
int result = link->driver.send(link->driver.ctx, packet);
link->sending = 0;
release(link);
return result;
}
void packet_link_set_rx_enabled(struct packet_link* link, int enabled) {
if (!link || link->closing || link->rx_enabled == !!enabled) return;
link->active++; link->rx_enabled = !!enabled;
link->driver.set_rx_enabled(link->driver.ctx, !!enabled);
release(link);
}
static void driver_received(void* arg, struct ll_entry* packet) {
struct packet_link* link = arg;
if (!packet || packet->owner || packet->next || packet->prev || !packet->dgram || !packet->len ||
packet->len > packet->memlen || packet->len > link->state.max_packet ||
link->closing || !link->rx_enabled || link->sending || link->state.phase != PACKET_LINK_OPEN) {
DEBUG_ERROR(DEBUG_CATEGORY_SYS, "packet link %s: invalid RX bytes=%u capacity=%u queued=%d phase=%d enabled=%d sending=%d closing=%d",
link->name, packet ? packet->len : 0, packet ? packet->memlen : 0, packet && packet->owner != NULL,
link->state.phase, link->rx_enabled, link->sending, link->closing);
if (packet) { queue_dgram_free(packet); queue_entry_free(packet); }
return;
}
link->active++;
link->callbacks.received(link, packet, link->arg);
release(link);
}
void packet_link_close(struct packet_link* link) {
if (!link || link->closing) return;
link->active++; link->closing = 1;
if (link->notification) { uasync_call_soon_cancel(link->ua, link->notification); link->notification = NULL; }
link->driver.stop(link->driver.ctx);
release(link);
}