1/*
2 * LICENSE NOTICE.
3 *
4 * Use of the Microsoft Windows Rally Development Kit is covered under
5 * the Microsoft Windows Rally Development Kit License Agreement,
6 * which is provided within the Microsoft Windows Rally Development
7 * Kit or at http://www.microsoft.com/whdc/rally/rallykit.mspx. If you
8 * want a license from Microsoft to use the software in the Microsoft
9 * Windows Rally Development Kit, you must (1) complete the designated
10 * "licensee" information in the Windows Rally Development Kit License
11 * Agreement, and (2) sign and return the Agreement AS IS to Microsoft
12 * at the address provided in the Agreement.
13 */
14
15/*
16 * Copyright (c) Microsoft Corporation 2005.  All rights reserved.
17 * This software is provided with NO WARRANTY.
18 */
19
20#include <stdio.h>
21#include <string.h>
22#include <assert.h>
23#include <limits.h>
24
25#include "globals.h"
26
27#include "statemachines.h"
28#include "bandfuncs.h"
29#include "packetio.h"
30
31/****************************
32 * Timeout handlers -
33 * these are the time-based entry points, which also clock the state machine.
34 */
35
36void
37state_block_timeout(void *cookie)
38{
39    g_block_timer = NULL;
40    g_this_event.evtType = evtBlockTimeout;
41    state_process_timeout();
42}
43
44void
45state_charge_timeout(void *cookie)
46{
47    g_charge_timer     = NULL;
48    g_ctc_packets      = 0;
49    g_ctc_bytes        = 0;
50    g_this_event.evtType  = evtChargeTimeout;
51    g_this_event.ssn      = (session_t*) cookie;
52    state_process_timeout();
53}
54
55void
56state_emit_timeout(void *cookie)
57{
58    g_emit_timer       = NULL;
59    g_this_event.evtType  = evtEmitTimeout;
60
61    /* Probes are forced to associate with the mapping session, if there is one. */
62    if (g_topo_session != NULL  &&  g_topo_session->ssn_is_valid)
63    {
64        g_this_event.ssn = g_topo_session;
65    }
66
67    state_process_timeout();
68}
69
70void
71state_hello_delay_timeout(void *cookie)
72{
73    g_hello_timer      = NULL;
74    g_this_event.evtType  = evtHelloDelayTimeout;
75    g_this_event.ssn      = (session_t*) cookie;
76    state_process_timeout();
77}
78
79void
80state_inactivity_timeout(void *cookie)
81{
82    g_this_event.evtType  = evtInactivityTimeout;
83    g_this_event.ssn      = (session_t*) cookie;
84    g_this_event.ssn->ssn_InactivityTimer = NULL;
85    state_process_timeout();
86}
87
88
89/* This function locates an existing session that is associated with the passed-in address */
90static session_t *
91find_session(etheraddr_t *this_addr)
92{
93    session_t   *ssn = &g_sessions[0];
94    int i;
95
96    for (i=0; i < MAX_NUM_SESSIONS; ssn++, i++)
97    {
98        if ( (ssn->ssn_is_valid) && (ETHERADDR_EQUALS(&ssn->ssn_mapper_real, this_addr)) )
99        {
100//*/        DEBUG({printf("find_session returning session %d @ %X\n",i,(uint)ssn);})
101            return ssn;
102        }
103    }
104//*/DEBUG({puts("find_session returning NULL");})
105    return NULL;
106}
107
108
109static session_t *
110new_session()
111{
112    session_t   *ssn = &g_sessions[0];
113    int i;
114
115    for (i=0; i < MAX_NUM_SESSIONS; ssn++, i++)
116    {
117        if ( !(ssn->ssn_is_valid) )
118        {
119            ssn->ssn_is_valid = TRUE;
120	    ssn->ssn_count = BAND_TXC;
121            ssn->ssn_XID = 0;
122            memset(&ssn->ssn_mapper_real,0,sizeof(etheraddr_t));
123            memset(&ssn->ssn_mapper_current,0,sizeof(etheraddr_t));
124            ssn->ssn_use_broadcast = TRUE;
125            ssn->ssn_TypeOfSvc = ToS_Unknown;
126            ssn->ssn_InactivityTimer = NULL;
127            return ssn;
128        }
129    }
130    return NULL;
131}
132
133
134/*****************************************************************************
135 *
136 * This code processes the current packet-event (in g_this_event) by locating
137 * (and possibly creating) the session with which it must be associated,
138 * then passing it to the 3 state machines, smS, smE, and smT, in that order.
139 *
140 *****************************************************************************/
141
142uint
143state_process_packet()
144{
145    session_t           *this_session;
146    enum sm_Status       smStatus;
147
148    IF_TRACED((TRC_STATE|TRC_PACKET))
149        printf("state_process_packet: Entered with event %s",smEvent_names[g_this_event.evtType]);
150        if (g_this_event.evtType==evtPacketRcvd)
151        {
152            printf(" (%s)\n",Topo_opcode_names[g_opcode]);
153        } else {
154            puts("");
155        }
156    END_TRACE
157
158    g_this_event.isInternalEvt = FALSE;	// It's a real event, not internally generated
159
160    /* First, look this RealSrc up in the session table, to
161     * locate any association with an established session.
162     *
163     * If there is no matching session, create a new one, iff the
164     * packet is a valid Discover of either topo- or quick- TOS ... */
165
166    g_this_event.isNewSession = FALSE;
167
168    if ((this_session = find_session(&g_base_hdr->tbh_realsrc)) == NULL)
169    {
170        /* Not found: Check for a Discovery packet (validated in packetio.c) */
171        if (g_opcode == Opcode_Discover)
172        {
173            /* Create a new session for this association */
174            if ((this_session = new_session()) == NULL)
175            {
176                /* No room in the table: drop the packet and whine. */
177                warn("state_process_packet: no room to create new session. Packet dropped.\n");
178                return UINT_MAX;
179            }
180            g_this_event.isNewSession = TRUE;
181
182            /* Fill in the newly valid session table entry with info from the packet */
183            this_session->ssn_XID            = g_sequencenum;
184            this_session->ssn_mapper_real    = g_base_hdr->tbh_realsrc;
185            this_session->ssn_mapper_current = g_ethernet_hdr->eh_src;
186            this_session->ssn_TypeOfSvc      = g_base_hdr->tbh_tos;
187            IF_TRACED(TRC_STATE)
188                printf("New Session:\n\tXID = %X\n\treal address: " ETHERADDR_FMT \
189                       "\n",this_session->ssn_XID, \
190                       ETHERADDR_PRINT(&this_session->ssn_mapper_real) );
191
192                printf("\tcurrent address: " ETHERADDR_FMT "\n\tToS: %s\n",
193                       ETHERADDR_PRINT(&this_session->ssn_mapper_current),
194                       Lld2_tos_names[this_session->ssn_TypeOfSvc] );
195            END_TRACE
196            g_this_event.ssn = this_session;
197
198        }   /*** end of if (g_opcode == Opcode_Discover) ***/
199
200        /* Probes are forced to associate with the mapping session, if there is one. */
201        if (g_opcode == Opcode_Probe)
202        {
203             if (g_topo_session != NULL  &&  g_topo_session->ssn_is_valid)
204             {
205                this_session = g_topo_session;
206             }
207        }
208
209    }   /*** endo of if (find_session()==NULL) ***/
210
211    /* We have associated whatever session that we can with this packet - pass to state machines */
212    g_this_event.ssn = this_session;
213
214    smStatus = smS_process_event( &g_this_event );
215
216    if (smStatus != PROCESSING_ABORTED)
217    {
218        smStatus = smE_process_event( &g_this_event );
219    }
220
221    if (smStatus != PROCESSING_ABORTED)
222    {
223        smStatus = smT_process_event( &g_this_event );
224    }
225
226    /* Remove any "new-session" marking */
227    g_this_event.isNewSession = FALSE;
228
229    IF_TRACED(TRC_PACKET)
230        printf("state_process_packet: Leaving - done with event %s\n",smEvent_names[g_this_event.evtType]);
231    END_TRACE
232    return 0;	/* Success! */
233}
234
235
236/******************************************************************************
237 *
238 * This code processes the current timeout-event (in g_this_event). Any session
239 * associated with this event (only happens with activity-timeouts) is already
240 * noted in the GLOBAL g_this_event (g_this_event).
241 *
242 ******************************************************************************/
243
244uint
245state_process_timeout()
246{
247    enum sm_Status         smStatus;
248
249    IF_TRACED(TRC_STATE)
250        if (g_this_event.evtType!=evtBlockTimeout)
251            printf("state_process_timeout: Entered with event %s\n",smEvent_names[g_this_event.evtType]);
252    END_TRACE
253
254    g_rcvd_pkt_len = 0;
255
256    /* Finish initializing the protocol-event */
257
258    /* g_this_event.evtType    = <set in individual timeout handler> */
259    /* g_this_event.ssn        = <set in individual timeout handler> */
260    g_this_event.isNewSession  = FALSE;
261    g_this_event.isAckingMe    = FALSE;
262    g_this_event.isInternalEvt = FALSE;	// It's a real event, not internally generated
263    g_this_event.numDescrs     = 0;
264
265    /* pass Hello-Delay timer events to each existing session's smS state-machine */
266    if (g_this_event.evtType == evtHelloDelayTimeout)
267    {
268        session_t     *ssn = &g_sessions[0];
269        int            i;
270
271        for (i=0; i < MAX_NUM_SESSIONS; ssn++, i++)
272        {
273            if (ssn->ssn_is_valid)
274            {
275                g_this_event.ssn = ssn;
276                smS_process_event( &g_this_event );
277            }
278        }
279        g_this_event.ssn = NULL;
280    }
281
282    /* Pass per-session activity timeouts to the associated session.  The remaining
283     * timeouts, for charge and emit, need only be processed by smE & smT...        */
284    if (g_this_event.evtType == evtInactivityTimeout)
285    {
286        smS_process_event( &g_this_event );
287    }
288
289    /* send all the timeouts to smE and then smT */
290
291    smStatus = smE_process_event( &g_this_event );
292
293    if (smStatus != PROCESSING_ABORTED)
294    {
295        smT_process_event( &g_this_event );
296    }
297
298    return 0;	/* Success! */
299}
300
301
302/****************************
303 * Helper functions -
304 * actions performed as part of state processing.
305 */
306
307/* Restart the inactivity timer for the session associated with the current event */
308void
309restart_inactivity_timer(uint32_t timeout)
310{
311    struct timeval now;
312
313    if (g_this_event.ssn == NULL  ||  g_this_event.ssn->ssn_is_valid != TRUE)   return;
314
315    gettimeofday(&now, NULL);
316    timeval_add_ms(&now, timeout);
317    CANCEL(g_this_event.ssn->ssn_InactivityTimer);
318    g_this_event.ssn->ssn_InactivityTimer = event_add(&now, state_inactivity_timeout, g_this_event.ssn);
319}
320
321
322/* Searches session table - returns TRUE if all valid sessions are in smS_Complete state */
323
324bool_t
325OnlyCompleteSessions(void)
326{
327    session_t   *ssn = &g_sessions[0];
328    int i;
329
330    for (i=0; i < MAX_NUM_SESSIONS; ssn++, i++)
331    {
332        if (ssn->ssn_is_valid && ssn->ssn_state != smS_Complete)
333        {
334            return FALSE;
335        }
336    }
337    return TRUE;
338}
339
340
341/* Searches session table - returns TRUE if all sessions are invalid */
342
343bool_t
344SessionTableIsEmpty(void)
345{
346    session_t   *ssn = &g_sessions[0];
347    int i;
348
349    for (i=0; i < MAX_NUM_SESSIONS; ssn++, i++)
350    {
351        if (ssn->ssn_is_valid)
352        {
353            return FALSE;
354        }
355    }
356    return TRUE;
357}
358
359bool_t
360set_emit_timer(void)
361{
362    topo_emitee_desc_t *ed;
363
364    assert(g_emit_remaining > 0);
365    assert(g_emit_timer == NULL);
366
367    /* get the next emitee_desc and schedule a callback when it is due
368     * to be transmitted */
369    ed = g_emitdesc;
370    if (ed->ed_pause != 0)
371    {
372	struct timeval now;
373	gettimeofday(&now, NULL);
374	timeval_add_ms(&now, ed->ed_pause);
375	g_emit_timer = event_add(&now, state_emit_timeout, NULL);
376        return TRUE;
377    } else {
378	/* no pause; return PAUSE=FALSE */
379	return FALSE;
380    }
381}
382