📄 wtp_init.c
字号:
/* * wtp_init.c - WTP initiator implementation * * By Aarno Syv鋘en for Wapit Ltd */#include "gwlib/gwlib.h"#include "wtp_init.h"#include "wtp_pack.h"#include "wap.h"/***************************************************************************** * Internal data structures. * * List of initiator WTP machines */static List *init_machines = NULL;/* * The tid to use for the next transaction. */static unsigned short gentid = -1;/* * When we restart an iniator, we must set tidnew flag to avoid excessive tid * validations (WTP 8.8.3.2). Only an iniator uses this flag. */static int tidnew = 1;/* * Queue of events to be handled by WTP initiator. */static List *queue = NULL;/* * Give the status of the wtp initiator: * * limbo * not running at all * running * operating normally * terminating * waiting for operations to terminate, returning to limbo */static enum { limbo, running, terminating} initiator_run_status = limbo;static wap_dispatch_func_t *dispatch_to_wdp;static wap_dispatch_func_t *dispatch_to_wsp;/* * This is a timer 'tick'. All timer values multiplies of this value. */static long init_timer_freq = -1;/*************************************************************************** * * Prototypes for internal functions: */static void main_thread(void *arg);/* * Create and destroy an uniniatilised wtp initiator state machine */static WTPInitMachine *init_machine_create(WAPAddrTuple *tuple, unsigned short tid, int tidnew, long mid, long tcl);static void init_machine_destroy(void *sm);static void handle_init_event(WTPInitMachine *machine, WAPEvent *event);/* * Checks whether wtp initiator machines data structure includes a specific * machine. * The machine in question is identified with with source and destination * address and port and tid. */static WTPInitMachine *init_machine_find_or_create(WAPEvent *event);/* * Creates TR-Abort.ind event. */static WAPEvent *create_tr_abort_ind(WTPInitMachine *sm, long abort_reason);/* * Creates TR-Invoke.cnf event */static WAPEvent *create_tr_invoke_cnf(WTPInitMachine *machine);static int tid_wrapped(unsigned short tid);/* * Create a datagram with an Abort PDU and send it to the WDP layer. */static void send_abort(WTPInitMachine *machine, long type, long reason);/* * Create a datagram with an Ack PDU and send it to the WDP layer. */static void send_ack(WTPInitMachine *machine, long ack_type, int rid_flag);/* * We use RcvTID consistently as a internal tid representation. So newly * created tids are converted. SendTID = RcvTID ^ 0x8000 (WTP 10.4.3) and for * an initiator, GenTID = SendTID (WTP 10.5). */static unsigned short rcv_tid(unsigned short tid);static void start_initiator_timer_R(WTPInitMachine *machine); static void start_initiator_timer_W(WTPInitMachine *machine);static void start_initiator_timer_A(WTPInitMachine *machine);static void stop_initiator_timer(Timer *timer);/* * SAR related functions. */static WAPEvent *assembly_sar_event (WTPInitMachine *machine, int last_psn);static int add_sar_transaction (WTPInitMachine *machine, Octstr *data, int psn);/* static int is_wanted_sar_data (void *a, void *b); */static int process_sar_transaction(WTPInitMachine *machine, WAPEvent **event);static void begin_sar_invoke(WTPInitMachine *init_machine, WAPEvent *event);static void continue_sar_invoke(WTPInitMachine *machine, WAPEvent *event);static void resend_sar_invoke(WTPInitMachine *init_machine, WAPEvent *event);static void sar_info_destroy(void *sar_info);static void sardata_destroy(void *sardata);/************************************************************************** * * EXTERNAL FUNCTIONS */void wtp_initiator_init(wap_dispatch_func_t *datagram_dispatch, wap_dispatch_func_t *session_dispatch, long timer_freq) { init_machines = list_create(); queue = list_create(); list_add_producer(queue); dispatch_to_wdp = datagram_dispatch; dispatch_to_wsp = session_dispatch; timers_init(); init_timer_freq = timer_freq; gw_assert(initiator_run_status == limbo); initiator_run_status = running; gwthread_create(main_thread, NULL);}void wtp_initiator_shutdown(void) { gw_assert(initiator_run_status == running); initiator_run_status = terminating; list_remove_producer(queue); gwthread_join_every(main_thread); debug("wap.wtp", 0, "wtp_initiator_shutdown: %ld init_machines left", list_len(init_machines)); list_destroy(init_machines, init_machine_destroy); list_destroy(queue, wap_event_destroy_item); timers_shutdown();}void wtp_initiator_dispatch_event(WAPEvent *event) { list_produce(queue, event);}/************************************************************************** * * INTERNAL FUNCTIONS: */static void main_thread(void *arg) { WTPInitMachine *sm; WAPEvent *e; while (initiator_run_status == running && (e = list_consume(queue)) != NULL) { sm = init_machine_find_or_create(e); if (sm == NULL) wap_event_destroy(e); else handle_init_event(sm, e); }}static WTPInitMachine *init_machine_create(WAPAddrTuple *tuple, unsigned short tid, int tidnew, long mid, long tcl) { WTPInitMachine *init_machine; init_machine = gw_malloc(sizeof(WTPInitMachine)); #define ENUM(name) init_machine->name = INITIATOR_NULL_STATE;#define INTEGER(name) init_machine->name = 0; #define EVENT(name) init_machine->name = NULL;#define TIMER(name) init_machine->name = gwtimer_create(queue); #define ADDRTUPLE(name) init_machine->name = NULL; #define LIST(name) init_machine->name = NULL;#define SARDATA(name) init_machine->name = NULL;#define MACHINE(field) field#include "wtp_init_machine.def" list_append(init_machines, init_machine); init_machine->tcl = tcl; init_machine->mid = mid; init_machine->addr_tuple = wap_addr_tuple_duplicate(tuple); init_machine->tid = tid; init_machine->tidnew = tidnew; debug("wap.wtp", 0, "WTP: Created WTPInitMachine %p (%ld)", (void *) init_machine, init_machine->mid); return init_machine;}/* * Destroys a WTPInitMachine. Assumes it is safe to do so. Assumes it has * already been deleted from the machines list. */static void init_machine_destroy(void *p) { WTPInitMachine *init_machine; init_machine = p; debug("wap.wtp", 0, "WTP: Destroying WTPInitMachine %p (%ld)", (void *) init_machine, init_machine->mid); list_delete_equal(init_machines, init_machine);#define ENUM(name) init_machine->name = INITIATOR_NULL_STATE;#define INTEGER(name) init_machine->name = 0; #define EVENT(name) wap_event_destroy(init_machine->name); #define TIMER(name) gwtimer_destroy(init_machine->name); #define ADDRTUPLE(name) wap_addr_tuple_destroy(init_machine->name); #define LIST(name) list_destroy(init_machine->name,sar_info_destroy);#define SARDATA(name) sardata_destroy(init_machine->name);#define MACHINE(field) field#include "wtp_init_machine.def" gw_free(init_machine);}/* * Give the name of an initiator state in a readable form. */static unsigned char *name_init_state(int s) { switch (s) {#define INIT_STATE_NAME(state) case state: return #state;#define ROW(state, event, condition, action, new_state)#include "wtp_init_states.def" default: return "unknown state"; }}/* * Feed an event to a WTP initiator state machine. Handle all errors by do not * report them to the caller. WSP indication or conformation is handled by an * included state table. Note: Do not put {}s of the else block inside the * macro definition . */static void handle_init_event(WTPInitMachine *init_machine, WAPEvent *event) { WAPEvent *wsp_event = NULL; debug("wap.wtp", 0, "WTP_INIT: initiator machine %ld, state %s," " event %s.", init_machine->mid, name_init_state(init_machine->state), wap_event_name(event->type));#define INIT_STATE_NAME(state)#define ROW(init_state, event_type, condition, action, next_state) \ if (init_machine->state == init_state && \ event->type == event_type && \ (condition)) { \ action \ init_machine->state = next_state; \ debug("wap.wtp", 0, "WTP_INIT %event->u.RcvAck.tclld: New state %s", \ init_machine->mid, #next_state); \ } else #include "wtp_init_states.def" { error(1, "WTP_INIT: handle_init_event: unhandled event!"); debug("wap.wtp.init", 0, "WTP_INIT: handle_init_event:" "Unhandled event was:"); wap_event_dump(event); wap_event_destroy(event); return; } if (event != NULL) { wap_event_destroy(event); } if (init_machine->state == INITIATOR_NULL_STATE) init_machine_destroy(init_machine);}static int is_wanted_init_machine(void *a, void *b) { struct machine_pattern *pat; WTPInitMachine *m; m = a; pat = b; if (m->mid == pat->mid) return 1; if (pat->mid != -1) return 0; return m->tid == pat->tid && wap_addr_tuple_same(m->addr_tuple, pat->tuple);}static WTPInitMachine *init_machine_find(WAPAddrTuple *tuple, long tid, long mid) { struct machine_pattern pat; WTPInitMachine *m; pat.tuple = tuple; pat.tid = tid; pat.mid = mid; m = list_search(init_machines, &pat, is_wanted_init_machine); return m;}/* * Checks whether wtp initiator machines data structure includes a specific * machine. The machine in question is identified with with source and * destination address and port and tid. First test incoming events * (WTP 10.2) (Exception are tests nro 4 and 5: if we have a memory error, * we panic (nro 4); nro 5 is already checked). If we have an ack with tid * verification flag set and no corresponding transaction, we abort.(case nro * 2). If the event was a normal ack or an abort, it is ignored (error nro 3). * In the case of TR-Invoke.req a new machine is created, in the case of * TR-Abort.req we have a serious error. We must create a new tid for a new * transaction here, because machines are identified by an address tuple and a * tid. This tid is GenTID (WTP 10.4.2), which is used only by the wtp iniator * thread. * Note that as internal tid representation, module uses RcvTID (as required * by module wtp_pack). So we we turn the first bit of the tid stored by the * init machine. */static WTPInitMachine *init_machine_find_or_create(WAPEvent *event) { WTPInitMachine *machine = NULL; long mid; static long tid = -1; WAPAddrTuple *tuple; mid = -1; tuple = NULL; switch (event->type) { case RcvAck: tid = event->u.RcvAck.tid; tuple = event->u.RcvAck.addr_tuple; break; case RcvAbort: tid = event->u.RcvAbort.tid; tuple = event->u.RcvAbort.addr_tuple; break; case RcvErrorPDU: mid = event->u.RcvErrorPDU.tid; tid = event->u.RcvErrorPDU.tid; tuple = event->u.RcvErrorPDU.addr_tuple; break; case RcvResult: tid = event->u.RcvResult.tid; tuple = event->u.RcvResult.addr_tuple; break; case TR_Invoke_Req: ++gentid; if (tid_wrapped(gentid)) { tidnew = 1; gentid = 0; } tid = rcv_tid(gentid); tuple = event->u.TR_Invoke_Req.addr_tuple; mid = event->u.TR_Invoke_Req.handle; break; case TR_Result_Res: tid = event->u.TR_Result_Res.handle; break; case TR_Abort_Req: tid = event->u.TR_Abort_Req.handle; break; case TimerTO_R: mid = event->u.TimerTO_R.handle; break; case TimerTO_A: mid = event->u.TimerTO_A.handle; break; case TimerTO_W: mid = event->u.TimerTO_W.handle; break; default: error(0, "WTP_INIT: machine_find_or_create: unhandled event"); wap_event_dump(event); return NULL; } gw_assert(tuple != NULL || mid != -1); machine = init_machine_find(tuple, tid, mid); if (machine == NULL) { switch (event->type) { case RcvAck: /* * Case nro 2 If we do not have a tid asked for, we send a negative answer, * i.e. an abort with reason INVALIDTID.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -