Main Page | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals

wtp_init.c

Go to the documentation of this file.
00001 /* ==================================================================== 
00002  * The Kannel Software License, Version 1.0 
00003  * 
00004  * Copyright (c) 2001-2008 Kannel Group  
00005  * Copyright (c) 1998-2001 WapIT Ltd.   
00006  * All rights reserved. 
00007  * 
00008  * Redistribution and use in source and binary forms, with or without 
00009  * modification, are permitted provided that the following conditions 
00010  * are met: 
00011  * 
00012  * 1. Redistributions of source code must retain the above copyright 
00013  *    notice, this list of conditions and the following disclaimer. 
00014  * 
00015  * 2. Redistributions in binary form must reproduce the above copyright 
00016  *    notice, this list of conditions and the following disclaimer in 
00017  *    the documentation and/or other materials provided with the 
00018  *    distribution. 
00019  * 
00020  * 3. The end-user documentation included with the redistribution, 
00021  *    if any, must include the following acknowledgment: 
00022  *       "This product includes software developed by the 
00023  *        Kannel Group (http://www.kannel.org/)." 
00024  *    Alternately, this acknowledgment may appear in the software itself, 
00025  *    if and wherever such third-party acknowledgments normally appear. 
00026  * 
00027  * 4. The names "Kannel" and "Kannel Group" must not be used to 
00028  *    endorse or promote products derived from this software without 
00029  *    prior written permission. For written permission, please  
00030  *    contact org@kannel.org. 
00031  * 
00032  * 5. Products derived from this software may not be called "Kannel", 
00033  *    nor may "Kannel" appear in their name, without prior written 
00034  *    permission of the Kannel Group. 
00035  * 
00036  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 
00037  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 
00038  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
00039  * DISCLAIMED.  IN NO EVENT SHALL THE KANNEL GROUP OR ITS CONTRIBUTORS 
00040  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,  
00041  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT  
00042  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR  
00043  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,  
00044  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE  
00045  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,  
00046  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
00047  * ==================================================================== 
00048  * 
00049  * This software consists of voluntary contributions made by many 
00050  * individuals on behalf of the Kannel Group.  For more information on  
00051  * the Kannel Group, please see <http://www.kannel.org/>. 
00052  * 
00053  * Portions of this software are based upon software originally written at  
00054  * WapIT Ltd., Helsinki, Finland for the Kannel project.  
00055  */ 
00056 
00057 /*
00058  * wtp_init.c - WTP initiator implementation
00059  *
00060  * By Aarno Syvänen for Wapit Ltd
00061  */
00062 
00063 #include "gwlib/gwlib.h"
00064 #include "wtp_init.h"
00065 #include "wtp_pack.h"
00066 #include "wap.h"
00067 
00068 /*****************************************************************************
00069  * Internal data structures.
00070  *
00071  * List of initiator WTP machines
00072  */
00073 static List *init_machines = NULL;
00074 
00075 /*
00076  * Counter for initiator WTP machine id numbers, to make sure they are unique.
00077  */
00078 static Counter *init_machine_id_counter = NULL;
00079 
00080 /*
00081  * When we restart an iniator, we must set tidnew flag to avoid excessive tid
00082  * validations (WTP 8.8.3.2). Only an iniator uses this flag.
00083  */
00084 static int tidnew = 1;
00085 
00086 /*
00087  * Queue of events to be handled by WTP initiator.
00088  */
00089 static List *queue = NULL;
00090 
00091 /*
00092  * Give the status of the wtp initiator:
00093  *
00094  *  limbo
00095  *      not running at all
00096  *  running
00097  *      operating normally
00098  *  terminating
00099  *      waiting for operations to terminate, returning to limbo
00100  */
00101 static enum { limbo, running, terminating } initiator_run_status = limbo;
00102 
00103 static wap_dispatch_func_t *dispatch_to_wdp;
00104 static wap_dispatch_func_t *dispatch_to_wsp;
00105 
00106 /*
00107  * This is a timer 'tick'. All timer values multiplies of this value.
00108  */
00109 static long init_timer_freq = -1;
00110 
00111 /***************************************************************************
00112  *
00113  * Prototypes for internal functions:
00114  */
00115 static void main_thread(void *arg);
00116  
00117 /*
00118  * Create and destroy an uniniatilised wtp initiator state machine
00119  */
00120 static WTPInitMachine *init_machine_create(WAPAddrTuple *tuple, unsigned short
00121                                            tid, int tidnew);
00122 static void init_machine_destroy(void *sm);
00123 static void handle_init_event(WTPInitMachine *machine, WAPEvent *event);
00124 
00125 /*
00126  * Checks whether wtp initiator machines data structure includes a specific 
00127  * machine.
00128  * The machine in question is identified with with source and destination
00129  * address and port and tid. 
00130  */
00131 static WTPInitMachine *init_machine_find_or_create(WAPEvent *event);
00132 
00133 /*
00134  * Creates TR-Abort.ind event.
00135  */
00136 static WAPEvent *create_tr_abort_ind(WTPInitMachine *sm, long abort_reason);
00137 
00138 /*
00139  * Creates TR-Invoke.cnf event 
00140  */
00141 static WAPEvent *create_tr_invoke_cnf(WTPInitMachine *machine);
00142 static int tid_wrapped(unsigned short tid);
00143 
00144 /*
00145  * Create a datagram with an Abort PDU and send it to the WDP layer.
00146  */
00147 static void send_abort(WTPInitMachine *machine, long type, long reason);
00148 
00149 /*
00150  * Create a datagram with an Ack PDU and send it to the WDP layer.
00151  */
00152 static void send_ack(WTPInitMachine *machine, long ack_type, int rid_flag);
00153 
00154 /*
00155  * We use RcvTID consistently as a internal tid representation. So newly 
00156  * created tids are converted. SendTID = RcvTID ^ 0x8000 (WTP 10.4.3) and for 
00157  * an initiator, GenTID = SendTID (WTP 10.5). 
00158  */
00159 static unsigned short rcv_tid(unsigned short tid);
00160 static void start_initiator_timer_R(WTPInitMachine *machine); 
00161 static void stop_initiator_timer(Timer *timer);
00162 
00163 /**************************************************************************
00164  *
00165  * EXTERNAL FUNCTIONS
00166  */
00167 
00168 void wtp_initiator_init(wap_dispatch_func_t *datagram_dispatch,
00169             wap_dispatch_func_t *session_dispatch, long timer_freq) 
00170 {
00171     init_machines = gwlist_create();
00172     init_machine_id_counter = counter_create();
00173      
00174     queue = gwlist_create();
00175     gwlist_add_producer(queue);
00176 
00177     dispatch_to_wdp = datagram_dispatch;
00178     dispatch_to_wsp = session_dispatch;
00179 
00180     timers_init();
00181     init_timer_freq = timer_freq;
00182 
00183     gw_assert(initiator_run_status == limbo);
00184     initiator_run_status = running;
00185     gwthread_create(main_thread, NULL);
00186 }
00187 
00188 void wtp_initiator_shutdown(void) 
00189 {
00190     gw_assert(initiator_run_status == running);
00191     initiator_run_status = terminating;
00192     gwlist_remove_producer(queue);
00193     gwthread_join_every(main_thread);
00194 
00195     debug("wap.wtp", 0, "wtp_initiator_shutdown: %ld init_machines left",
00196           gwlist_len(init_machines));
00197     gwlist_destroy(init_machines, init_machine_destroy);
00198     gwlist_destroy(queue, wap_event_destroy_item);
00199 
00200     counter_destroy(init_machine_id_counter);
00201     timers_shutdown();
00202 }
00203 
00204 void wtp_initiator_dispatch_event(WAPEvent *event) 
00205 {
00206     gwlist_produce(queue, event);
00207 }
00208 
00209 /**************************************************************************
00210  *
00211  * INTERNAL FUNCTIONS:
00212  */
00213 
00214 static void main_thread(void *arg) 
00215 {
00216     WTPInitMachine *sm;
00217     WAPEvent *e;
00218 
00219     while (initiator_run_status == running && 
00220           (e = gwlist_consume(queue)) != NULL) {
00221         sm = init_machine_find_or_create(e);
00222     if (sm == NULL)
00223         wap_event_destroy(e);
00224     else
00225         handle_init_event(sm, e);
00226     }
00227 }
00228 
00229 static WTPInitMachine *init_machine_create(WAPAddrTuple *tuple, unsigned short
00230                                            tid, int tidnew)
00231 {
00232      WTPInitMachine *init_machine;
00233     
00234      init_machine = gw_malloc(sizeof(WTPInitMachine)); 
00235         
00236      #define ENUM(name) init_machine->name = INITIATOR_NULL_STATE;
00237      #define INTEGER(name) init_machine->name = 0; 
00238      #define EVENT(name) init_machine->name = NULL;
00239      #define TIMER(name) init_machine->name = gwtimer_create(queue); 
00240      #define ADDRTUPLE(name) init_machine->name = NULL; 
00241      #define MACHINE(field) field
00242      #include "wtp_init_machine.def"
00243 
00244      gwlist_append(init_machines, init_machine);
00245 
00246      init_machine->mid = counter_increase(init_machine_id_counter);
00247      init_machine->addr_tuple = wap_addr_tuple_duplicate(tuple);
00248      init_machine->tid = tid;
00249      init_machine->tidnew = tidnew;
00250     
00251      debug("wap.wtp", 0, "WTP: Created WTPInitMachine %p (%ld)", 
00252        (void *) init_machine, init_machine->mid);
00253 
00254      return init_machine;
00255 }
00256 
00257 /*
00258  * Destroys a WTPInitMachine. Assumes it is safe to do so. Assumes it has 
00259  * already been deleted from the machines list.
00260  */
00261 static void init_machine_destroy(void *p)
00262 {
00263      WTPInitMachine *init_machine;
00264 
00265      init_machine = p;
00266      debug("wap.wtp", 0, "WTP: Destroying WTPInitMachine %p (%ld)", 
00267         (void *) init_machine, init_machine->mid);
00268     
00269      gwlist_delete_equal(init_machines, init_machine);
00270         
00271      #define ENUM(name) init_machine->name = INITIATOR_NULL_STATE;
00272      #define INTEGER(name) init_machine->name = 0; 
00273      #define EVENT(name) wap_event_destroy(init_machine->name); 
00274      #define TIMER(name) gwtimer_destroy(init_machine->name); 
00275      #define ADDRTUPLE(name) wap_addr_tuple_destroy(init_machine->name); 
00276      #define MACHINE(field) field
00277      #include "wtp_init_machine.def"
00278      gw_free(init_machine);
00279 }
00280 
00281 /*
00282  * Give the name of an initiator state in a readable form. 
00283  */
00284 static unsigned char *name_init_state(int s)
00285 {
00286        switch (s){
00287        #define INIT_STATE_NAME(state) case state: return (unsigned char *) #state;
00288        #define ROW(state, event, condition, action, new_state)
00289        #include "wtp_init_states.def"
00290        default:
00291            return (unsigned char *)"unknown state";
00292        }
00293 }
00294 
00295 /*
00296  * Feed an event to a WTP initiator state machine. Handle all errors by do not
00297  * report them to the caller. WSP indication or conformation is handled by an
00298  * included state table. Note: Do not put {}s of the else block inside the 
00299  * macro definition . 
00300  */
00301 static void handle_init_event(WTPInitMachine *init_machine, WAPEvent *event)
00302 {
00303      WAPEvent *wsp_event = NULL;
00304 
00305      debug("wap.wtp", 0, "WTP_INIT: initiator machine %ld, state %s,"
00306            " event %s.", 
00307        init_machine->mid, 
00308        name_init_state(init_machine->state), 
00309        wap_event_name(event->type));
00310        
00311      #define INIT_STATE_NAME(state)
00312      #define ROW(init_state, event_type, condition, action, next_state) \
00313      if (init_machine->state == init_state && \
00314          event->type == event_type && \
00315          (condition)) { \
00316          action \
00317          init_machine->state = next_state; \
00318          debug("wap.wtp", 0, "WTP_INIT %ld: New state %s", \
00319                    init_machine->mid, #next_state); \
00320      } else 
00321       #include "wtp_init_states.def"
00322      {
00323          error(1, "WTP_INIT: handle_init_event: unhandled event!");
00324          debug("wap.wtp.init", 0, "WTP_INIT: handle_init_event:"
00325                    "Unhandled event was:");
00326          wap_event_dump(event);
00327              wap_event_destroy(event);
00328              return;
00329      }
00330 
00331       if (event != NULL) {
00332       wap_event_destroy(event);  
00333       }
00334 
00335       if (init_machine->state == INITIATOR_NULL_STATE)
00336           init_machine_destroy(init_machine);      
00337 }
00338 
00339 static int is_wanted_init_machine(void *a, void *b) 
00340 {
00341     struct machine_pattern *pat;
00342     WTPInitMachine *m;
00343     
00344     m = a;
00345     pat = b;
00346 
00347     if (m->mid == pat->mid)
00348     return 1;
00349 
00350     if (pat->mid != -1)
00351     return 0;
00352 
00353     return m->tid == pat->tid && 
00354        wap_addr_tuple_same(m->addr_tuple, pat->tuple);
00355 }
00356 
00357 static WTPInitMachine *init_machine_find(WAPAddrTuple *tuple, long tid, 
00358                                          long mid) 
00359 {
00360     struct machine_pattern pat;
00361     WTPInitMachine *m;
00362     
00363     pat.tuple = tuple;
00364     pat.tid = tid;
00365     pat.mid = mid;
00366     
00367     m = gwlist_search(init_machines, &pat, is_wanted_init_machine);
00368     return m;
00369 }
00370 
00371 /*
00372  * Checks whether wtp initiator machines data structure includes a specific 
00373  * machine. The machine in question is identified with with source and 
00374  * destination address and port and tid.  First test incoming events 
00375  * (WTP 10.2) (Exception are tests nro 4 and 5: if we have a memory error, 
00376  * we panic (nro 4); nro 5 is already checked). If we have an ack with tid 
00377  * verification flag set and no corresponding transaction, we abort.(case nro 
00378  * 2). If the event was a normal ack or an abort, it is ignored (error nro 3).
00379  * In the case of TR-Invoke.req a new machine is created, in the case of 
00380  * TR-Abort.req we have a serious error. We must create a new tid for a new
00381  * transaction here, because machines are identified by an address tuple and a
00382  * tid. This tid is GenTID (WTP 10.4.2), which is used only by the wtp iniator 
00383  * thread.
00384  * Note that as internal tid representation, module uses RcvTID (as required
00385  * by module wtp_pack). So we we turn the first bit of the tid stored by the
00386  * init machine.
00387  */
00388 static WTPInitMachine *init_machine_find_or_create(WAPEvent *event)
00389 {
00390     WTPInitMachine *machine = NULL;
00391     long mid;
00392     static long tid = -1; 
00393     WAPAddrTuple *tuple;
00394 
00395     mid = -1;
00396     tuple = NULL;
00397 
00398     switch (event->type) {
00399     case RcvAck:
00400         tid = event->u.RcvAck.tid;
00401         tuple = event->u.RcvAck.addr_tuple;
00402     break;
00403 
00404     case RcvAbort:
00405         tid = event->u.RcvAbort.tid;
00406         tuple = event->u.RcvAbort.addr_tuple;
00407     break;
00408 
00409     case RcvErrorPDU:
00410         mid = event->u.RcvErrorPDU.tid;
00411         tid = event->u.RcvErrorPDU.tid;
00412         tuple = event->u.RcvErrorPDU.addr_tuple;
00413     break;
00414 /*
00415  * When we are receiving an invoke requirement, we must create a new trans-
00416  * action and generate a new tid. This can be wrapped, and should have its 
00417  * first bit turned.
00418  */
00419     case TR_Invoke_Req:
00420     ++tid;
00421         if (tid_wrapped(tid)) {
00422         tidnew = 1;
00423             tid = 0;
00424         }
00425                    
00426     tid = rcv_tid(tid);
00427         tuple = event->u.TR_Invoke_Req.addr_tuple;
00428         mid = event->u.TR_Invoke_Req.handle;
00429     break;
00430 
00431     case TR_Abort_Req:
00432         tid = event->u.TR_Abort_Req.handle;
00433     break;
00434 
00435     case TimerTO_R:
00436         mid = event->u.TimerTO_R.handle;
00437     break;
00438 
00439     default:
00440     error(0, "WTP_INIT: machine_find_or_create: unhandled event");
00441         wap_event_dump(event);
00442         return NULL;
00443     }
00444 
00445     gw_assert(tuple != NULL || mid != -1);
00446     machine = init_machine_find(tuple, tid, mid);
00447 
00448     if (machine == NULL){
00449 
00450     switch (event->type){
00451     case RcvAck:   
00452    
00453 /* 
00454  * Case nro 2 If we do not have a tid asked for, we send a negative answer, 
00455  * i.e. an abort with reason INVALIDTID. 
00456  */
00457          if (event->u.RcvAck.tid_ok) {
00458          dispatch_to_wdp(wtp_pack_abort(PROVIDER, INVALIDTID,
00459                                                 tid, tuple));
00460              }
00461 
00462 /* Case nro 3, normal ack */
00463              else
00464                  info(0, "WTP_INIT: machine_find_or_create: ack "
00465                      "received, yet having no machine");
00466     break;
00467 
00468 /* Case nro 3, abort */
00469         case RcvAbort:
00470             info(0, "WTP_INIT: machine_find_or_create: abort "
00471                  "received, yet having no machine");
00472     break;
00473 
00474     case TR_Invoke_Req:
00475         machine = init_machine_create(tuple, tid, tidnew);
00476             machine->mid = event->u.TR_Invoke_Req.handle;
00477     break;
00478 
00479     case TR_Abort_Req:
00480             error(0, "WTP_INIT: machine_find_or_create: WSP "
00481                   "primitive to a wrong WTP machine");
00482     break;
00483 
00484     case TimerTO_R:
00485         error(0, "WTP_INIT: machine_find_or_create: timer "
00486                        "event without a corresponding machine");
00487         break;
00488        
00489         default:
00490             error(0, "WTP_INIT: machine_find_or_create: unhandled"
00491                   "event");
00492             wap_event_dump(event);
00493         break; 
00494         }
00495    } 
00496 
00497    return machine;
00498 }
00499 
00500 /*
00501  * Creates TR-Invoke.cnf event
00502  */
00503 static WAPEvent *create_tr_invoke_cnf(WTPInitMachine *init_machine)
00504 {
00505     WAPEvent *event;
00506 
00507     gw_assert(init_machine != NULL);
00508     event = wap_event_create(TR_Invoke_Cnf);
00509     event->u.TR_Invoke_Cnf.handle = init_machine->mid;
00510     event->u.TR_Invoke_Cnf.addr_tuple = 
00511         wap_addr_tuple_duplicate(init_machine->addr_tuple);
00512 
00513     return event;
00514 }
00515 
00516 /*
00517  * Creates TR-Abort.ind event from an initiator state machine. In addtion, set
00518  * the ir_flag on.
00519  */
00520 static WAPEvent *create_tr_abort_ind(WTPInitMachine *sm, long abort_reason) 
00521 {
00522     WAPEvent *event;
00523     
00524     event = wap_event_create(TR_Abort_Ind);
00525 
00526     event->u.TR_Abort_Ind.abort_code = abort_reason;
00527     event->u.TR_Abort_Ind.addr_tuple = 
00528     wap_addr_tuple_duplicate(sm->addr_tuple);
00529     event->u.TR_Abort_Ind.handle = sm->mid;
00530     event->u.TR_Abort_Ind.ir_flag = INITIATOR_INDICATION;
00531 
00532     return event;
00533 }
00534 
00535 
00536 static int tid_wrapped(unsigned short tid)
00537 {
00538     return tid > (1 << 15);
00539 }
00540 
00541 static unsigned short rcv_tid(unsigned short tid)
00542 {
00543     return tid ^ 0x8000;
00544 }
00545 
00546 /*
00547  * Start retry interval timer (strictly speaking, timer iniatilised with retry
00548  * interval). Multiply timer value with init_timer_freq.
00549  */
00550 static void start_initiator_timer_R(WTPInitMachine *machine) 
00551 {
00552     WAPEvent *timer_event;
00553     int seconds;
00554 
00555     timer_event = wap_event_create(TimerTO_R);
00556     timer_event->u.TimerTO_R.handle = machine->mid;
00557     if (machine->u_ack)
00558         seconds = S_R_WITH_USER_ACK * init_timer_freq;
00559     else
00560         seconds = S_R_WITHOUT_USER_ACK * init_timer_freq;
00561     gwtimer_start(machine->timer, seconds, timer_event);
00562 }
00563 
00564 static void stop_initiator_timer(Timer *timer)
00565 {
00566     debug("wap.wtp_init", 0, "stopping timer");
00567     gw_assert(timer);
00568     gwtimer_stop(timer);
00569 }
00570 
00571 static void send_abort(WTPInitMachine *machine, long type, long reason)
00572 {
00573     WAPEvent *e;
00574 
00575     e = wtp_pack_abort(type, reason, machine->tid, machine->addr_tuple);
00576     dispatch_to_wdp(e);
00577 }
00578 
00579 static void send_ack(WTPInitMachine *machine, long ack_type, int rid_flag)
00580 {
00581     WAPEvent *e;
00582 
00583     e = wtp_pack_ack(ack_type, rid_flag, machine->tid, machine->addr_tuple);
00584     dispatch_to_wdp(e);
00585 }
See file LICENSE for details about the license agreement for using, modifying, copying or deriving work from this software.