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 <stdlib.h>
22#include <ctype.h>
23#include <assert.h>
24
25#include <string.h>
26#include <errno.h>
27#include <sys/ioctl.h>
28
29#include "globals.h"
30#include "statemachines.h"
31#include "packetio.h"
32
33extern void *
34fmt_base(uint8_t *buf, const etheraddr_t *srchw, const etheraddr_t *dsthw, lld2_tos_t tos,
35	 topo_opcode_t g_opcode, uint16_t seqnum, bool_t use_broadcast);
36
37extern void
38tx_write(uint8_t *buf, size_t nbytes);
39
40/* with only two linkages, this isn't really needed...  #include "qospktio.h" */
41
42/********************************* U T I L I T Y   F U N C T I O N S **************************************/
43static
44void
45stamp_time(uint64_t* pTime)
46{
47    struct timeval now;
48    uint64_t	   temp;
49
50    gettimeofday(&now, NULL);
51    temp = now.tv_sec * (uint64_t)1000000UL;
52    *pTime = temp + now.tv_usec;
53}
54
55
56static
57void
58get_raw_samples(void)
59{
60    FILE  *procdev;
61
62    if ( (procdev = fopen("/proc/net/dev", "r")) != (FILE*) 0)
63    {
64        uint32_t     rbytes, rpkts, tbytes, tpkts;
65        bool_t       cntrs_parsedOK = FALSE;
66        char         ifname[16];
67
68        rbytes = rpkts = tbytes = tpkts = -1;
69        strncpy(ifname, g_interface, 14);
70        strcat(ifname,":");
71
72        while (fgets(g_buf, sizeof(g_buf)/sizeof(g_buf[0]), procdev) == g_buf)
73        {
74            char *ifn;
75
76            if ((ifn=strstr(g_buf, ifname)) != 0)
77            {
78                int   skipcol;
79                char *val = ifn;
80                char  dummy[] = {"0 0 0 0 0 0"};
81
82                val += strlen(ifname);
83                rbytes = strtoul(val,&val,10);
84                rpkts  = strtoul(val,&val,10);
85                /* Skip over 6 more un-needed columns */
86                for (skipcol=0;skipcol<6;skipcol++)
87                {
88                    long  discard;
89
90                    discard = strtoul(val,&val,10);
91                    if (*val == '\0')
92                    {
93                        warn("get_raw_samples: using dummy values due to parse error!\n");
94                        val = dummy;
95                        break;
96                    }
97                }
98                /* "val" now points to the tx-byte-counter */
99                tbytes = strtoul(val,&val,10);
100                tpkts  = strtoul(val,NULL,10);
101                cntrs_parsedOK = TRUE;
102                break;  // out of "while (reading lines)..."
103            }
104        }
105        if (cntrs_parsedOK)
106        {
107            warn("get_raw_samples: failed reading /proc/dev for device statistics!\n");
108            g_rbytes = g_rpkts = g_tbytes = g_tpkts  = 0;
109        } else {
110            /* got a valid parse of the /proc/net/dev line for our device */
111            g_rbytes = rbytes;
112            g_rpkts  = rpkts;
113            g_tbytes = tbytes;
114            g_tpkts  = tpkts;
115        }
116        fclose(procdev);
117    } else {
118        warn("get_raw_samples: failed opening /proc/dev for device statistics!\n");
119        g_rbytes = g_rpkts = g_tbytes = g_tpkts  = 0;
120    }
121    IF_TRACED(TRC_QOS)
122	dbgprintf("qos perf-cntr: g_rbytes=" FMT_UINT32 "; g_rpkts=" FMT_UINT32 \
123	          "; g_tbytes=" FMT_UINT32 "; g_tpkts=" FMT_UINT32 "\n",
124	          g_rbytes, g_rpkts, g_tbytes, g_tpkts);
125    END_TRACE
126}
127
128
129static
130void
131get_timestamp(uint64_t* pTime)
132{
133    struct timeval now;
134    uint64_t	   temp;
135
136    ioctl(g_osl->sock, SIOCGSTAMP, &now);
137    temp = now.tv_sec * (uint64_t)1000000UL;
138    *pTime = temp + now.tv_usec;
139}
140
141
142static qos_session_t*
143qos_find_session(void)
144{
145    unsigned int i;
146
147    for (i=0;i<MAX_QOS_SESSIONS;i++)
148    {
149        if (g_QosSessions[i].qssn_is_valid  &&
150            ETHERADDR_EQUALS(&g_base_hdr->tbh_realsrc, &g_QosSessions[i].qssn_ctrlr_real))
151        {
152            return &g_QosSessions[i];
153        }
154    }
155    return  NULL;
156}
157
158static qosEventBucket_t*
159qos_find_bucket(qos_session_t *pSsn)
160{
161    unsigned int i = pSsn->qssn_first_bucket;
162    unsigned int j = i + pSsn->qssn_num_active_buckets;
163
164    while (i < j)
165    {
166        qosEventBucket_t* bucket = &pSsn->qssn_evt_buckets[i & (MAX_QOS_BUCKETS - 1)];
167
168        if (bucket->evt_seqNum == g_sequencenum)
169        {
170            return bucket;
171        }
172
173        i++;
174    }
175
176    return NULL;
177}
178
179/***************************** T I M E R   S E R V I C E   R O U T I N E S *****************************/
180static
181void
182qos_inactivity_timeout(void *state)
183{
184    int			i;
185    qos_session_t*	pSsn = &g_QosSessions[0];
186    struct timeval	now;
187
188    for (i=0; i<MAX_QOS_SESSIONS; i++, pSsn++)
189    {
190        if (pSsn->qssn_is_valid == TRUE)
191        {
192            if (pSsn->qssn_ticks_til_discard)        pSsn->qssn_ticks_til_discard--;
193            if (pSsn->qssn_ticks_til_discard == 0)
194            {
195                pSsn->qssn_is_valid = FALSE;
196                pSsn->qssn_num_active_buckets = 0;
197            }
198        }
199    }
200
201    /* repeats every 30 seconds - a session is killed after 4 ticks' inactivity */
202    gettimeofday(&now, NULL);
203    now.tv_sec += 30;
204    g_qos_inactivity_timer = event_add(&now, qos_inactivity_timeout, /*state:*/NULL);
205}
206
207
208#define BYTE_SCALE_FACTOR 1024  // value for reducing counters
209#define BYTE_SCALE        0     // equivalent value for response
210#define PKT_SCALE_FACTOR  1     // value for reducing counters
211#define PKT_SCALE         0     // equivalent value for response
212static
213void
214interface_counter_recorder(void *state)
215{
216    struct timeval	now;
217
218    if (--g_samples_remaining != 0)
219    {
220        qos_perf_sample *thisSample = &g_perf_samples[g_next_sample];
221
222        uint32_t       rbytes = g_rbytes;
223        uint32_t       rpkts  = g_rpkts;
224        uint32_t       tbytes = g_tbytes;
225        uint32_t       tpkts  = g_tpkts;
226        uint32_t       delta;
227
228        get_raw_samples();      // get current values for g_rbytes, g_rpkts, g_tbytes, g_tpkts
229
230        delta = (g_rbytes-rbytes > 0)? (g_rbytes-rbytes) : 0;
231        IF_TRACED(TRC_QOS)
232            dbgprintf("qos perf-cntr: delta-rbytes=" FMT_UINT32, delta);
233        END_TRACE
234        thisSample->bytes_rcvd = delta / BYTE_SCALE_FACTOR;
235
236        delta = (g_rpkts-rpkts > 0)?   (g_rpkts-rpkts) : 0;
237        IF_TRACED(TRC_QOS)
238            dbgprintf("  delta-rpkts=" FMT_UINT32, delta);
239        END_TRACE
240        thisSample->pkts_rcvd  = delta / PKT_SCALE_FACTOR;
241
242        delta = (g_tbytes-tbytes > 0)? (g_tbytes-tbytes) : 0;
243        IF_TRACED(TRC_QOS)
244            dbgprintf("  delta-tbytes=" FMT_UINT32, delta);
245        END_TRACE
246        thisSample->bytes_sent = delta / BYTE_SCALE_FACTOR;
247
248        delta = (g_tpkts-tpkts > 0)?   (g_tpkts-tpkts) : 0;
249        IF_TRACED(TRC_QOS)
250            dbgprintf("  delta-tpkts=" FMT_UINT32 "\n", delta);
251        END_TRACE
252        thisSample->pkts_sent  = delta / PKT_SCALE_FACTOR;
253
254        stamp_time(&g_perf_timestamp);
255
256        IF_TRACED(TRC_QOS)
257            dbgprintf("qos perf-cntr: sample-rbytes=%d; sample-rpkts=%d; sample-tbytes=%d; sample-tpkts=%d\n",
258                   thisSample->bytes_rcvd, thisSample->pkts_rcvd, thisSample->bytes_sent, thisSample->pkts_sent);
259        END_TRACE
260
261        g_next_sample++;
262        g_next_sample = g_next_sample % 60;
263
264        if (g_sample_count < 60)
265        {
266            g_sample_count++;
267        }
268
269        /* repeats every second - until the lease runs out */
270        gettimeofday(&now, NULL);
271        now.tv_sec += 1;
272        g_qos_CTA_timer = event_add(&now, interface_counter_recorder, /*state:*/NULL);
273    } else {
274        IF_TRACED(TRC_QOS)
275            dbgprintf("qos perf-cntr: lease has run out - zero'ing counters, and stopping the timer...\n");
276        END_TRACE
277
278        g_next_sample = 0;
279        g_sample_count = 0;
280    }
281}
282
283
284extern void qos_init(void);
285
286void
287qos_init(void)
288{
289    int			i;
290    qos_session_t*	pSsn = &g_QosSessions[0];
291    struct timeval	now;
292
293    /* Initialize all the session structures */
294    for (i=0; i<MAX_QOS_SESSIONS; i++, pSsn++)
295    {
296        pSsn->qssn_ticks_til_discard = 0;
297        pSsn->qssn_first_bucket = 0;
298        pSsn->qssn_num_active_buckets = 0;
299        pSsn->qssn_is_valid = FALSE;
300        memset(&pSsn->qssn_ctrlr_real, 0, sizeof(etheraddr_t));
301    }
302    g_qprb_hdr  = NULL;    /* pointer to qos probe-header in rxbuf */
303    g_qinit_hdr = NULL;    /* pointer to qos init-sink--header in rxbuf */
304    g_LinkSpeed = 1000000;
305    g_TimeStampFreq = 1000000;	/* usec granularity */
306    g_pktio_timestamp = 0;
307
308    /* repeats every 30 seconds - a session is killed after 4 ticks' inactivity */
309    gettimeofday(&now, NULL);
310    now.tv_sec += 30;
311    g_qos_inactivity_timer = event_add(&now, qos_inactivity_timeout, /*state:*/NULL);
312
313#ifdef START_WITH_COUNTER_LEASE
314    /* start collection: repeats every second - until the lease runs out */
315    gettimeofday(&now, NULL);
316    now.tv_sec += 1;
317    g_qos_CTA_timer = event_add(&now, interface_counter_recorder, /*state:*/NULL);
318    g_samples_remaining = 300;
319#endif
320}
321
322
323/**************************  Q O S   M S G   H A N D L E R S   **************************/
324
325static
326void
327qos_initsink(void)
328{
329    qos_session_t*	pThisSsn;
330    size_t		nbytes;
331    int			i;
332    uint16_t	errcode;
333
334    if (g_rcvd_pkt_len < sizeof(topo_ether_header_t) + sizeof(topo_base_header_t) +  sizeof(qos_initsink_header_t))
335    {
336        warn("qos_initsink: frame with truncated InitializeSink header (len=" FMT_SIZET " src="
337              ETHERADDR_FMT " dst=" ETHERADDR_FMT "); ignoring\n",
338              g_rcvd_pkt_len, ETHERADDR_PRINT(&g_ethernet_hdr->eh_src), ETHERADDR_PRINT(&g_ethernet_hdr->eh_dst));
339        return;
340    }
341
342    /* Check interrupt moderation request */
343    if (g_qinit_hdr->init_intmod_ctrl != 0xFF)
344    {
345        /* Compose msg to return */
346        fmt_base(
347            g_txbuf,
348            &g_hwaddr,
349            &g_base_hdr->tbh_realsrc,
350            ToS_QoSDiagnostics,
351            (topo_opcode_t)Qopcode_Error,
352            g_sequencenum,
353            FALSE /*no broadcast*/
354            );
355        nbytes = sizeof(topo_ether_header_t) + sizeof(topo_base_header_t);
356
357        /* add Error-Code */
358        errcode = htons(Qoserror_ModerationNotAvailable);
359        memcpy(&g_txbuf[nbytes], &errcode, sizeof(uint16_t));
360        nbytes += sizeof(uint16_t);
361
362        tx_write(g_txbuf, nbytes);
363
364        IF_TRACED(TRC_PACKET)
365            dbgprintf("qos_initsink: unsupported interrupt moderation request (intmod=0x%02x)\n", g_qinit_hdr->init_intmod_ctrl);
366        END_TRACE
367
368        return;
369    }
370
371    /* Check for existing session with this controller and use it, if found; */
372    /* If not found (the usual case), get an unused one... */
373    if ((pThisSsn = qos_find_session()) == NULL)
374    {
375        /* Check for available session slot, reject with ErrBusy if none available */
376        pThisSsn = g_QosSessions;
377        for (i=0;i<MAX_QOS_SESSIONS;i++)
378        {
379            if (!pThisSsn->qssn_is_valid)  break;
380            pThisSsn++;
381        }
382        if (i>=MAX_QOS_SESSIONS)
383        {
384            /* Compose Busy msg to return. */
385            fmt_base(g_txbuf, &g_hwaddr, &g_base_hdr->tbh_realsrc, ToS_QoSDiagnostics,
386                     Qopcode_Error, g_sequencenum, FALSE /*no broadcast*/);
387            nbytes = sizeof(topo_ether_header_t) + sizeof(topo_base_header_t);
388
389            /* add Error-Code */
390
391            errcode = htons(Qoserror_Busy);
392            memcpy(&g_txbuf[nbytes], &errcode, sizeof(uint16_t));
393            nbytes += sizeof(uint16_t);
394
395            tx_write(g_txbuf, nbytes);
396
397            IF_TRACED(TRC_PACKET)
398                dbgprintf("qos_initsink: tx_error_Busy, seq=%d -> " ETHERADDR_FMT "\n",
399                          g_sequencenum, ETHERADDR_PRINT(&g_base_hdr->tbh_realsrc));
400            END_TRACE
401            return;
402        }
403    }
404
405    /* Record session data, */
406    pThisSsn->qssn_is_valid = TRUE;
407    memcpy( &pThisSsn->qssn_ctrlr_real, &g_base_hdr->tbh_realsrc, sizeof(etheraddr_t) );
408    pThisSsn->qssn_ticks_til_discard = 4;
409
410    /* and compose & send a Ready msg. */
411    fmt_base(g_txbuf, &g_hwaddr, &pThisSsn->qssn_ctrlr_real, ToS_QoSDiagnostics,
412             Qopcode_Ready, g_sequencenum, FALSE /*no broadcast*/);
413    nbytes = sizeof(topo_ether_header_t) + sizeof(topo_base_header_t);
414
415    /* add Sink Link Speed */
416    g_reorder_buffer = htonl(g_LinkSpeed);
417    memcpy(&g_txbuf[nbytes], &g_reorder_buffer, sizeof(uint32_t));
418    nbytes += sizeof(uint32_t);
419
420    /* add Performance Counter Frequency */
421
422    cpy_hton64(&g_txbuf[nbytes], &g_TimeStampFreq);
423    nbytes += sizeof(uint64_t);
424
425    tx_write(g_txbuf, nbytes);
426
427    IF_TRACED(TRC_PACKET)
428	dbgprintf("qos_initsink: tx_ready, seq=%d -> " ETHERADDR_FMT "\n",
429		g_sequencenum, ETHERADDR_PRINT(&pThisSsn->qssn_ctrlr_real));
430    END_TRACE
431}
432
433
434static
435void
436qos_reset(void)
437{
438    qos_session_t*	pThisSsn = qos_find_session();
439    size_t		nbytes;
440
441    /* Find associated session, and reject with silence if none found. */
442    if (pThisSsn == NULL)
443    {
444        warn("packetio_recv_handler: no session active for " ETHERADDR_FMT "; ignoring...\n",
445             ETHERADDR_PRINT(&g_base_hdr->tbh_realsrc));
446        return;
447    }
448
449    /* Otherwise, clear the associated session */
450    pThisSsn->qssn_is_valid = FALSE;
451    pThisSsn->qssn_ticks_til_discard = 0;
452    pThisSsn->qssn_num_active_buckets = 0;
453
454    /* and compose & send an ACK msg */
455
456    fmt_base(g_txbuf, &g_hwaddr, &pThisSsn->qssn_ctrlr_real, ToS_QoSDiagnostics,
457             Qopcode_ACK, g_sequencenum, FALSE /*no broadcast*/);
458    nbytes = sizeof(topo_ether_header_t) + sizeof(topo_base_header_t);
459
460    tx_write(g_txbuf, nbytes);
461
462    IF_TRACED(TRC_PACKET)
463	dbgprintf("qos_reset: tx_ack, seq=%d -> " ETHERADDR_FMT "\n",
464		g_sequencenum, ETHERADDR_PRINT(&pThisSsn->qssn_ctrlr_real));
465    END_TRACE
466}
467
468
469
470static
471void
472qos_probe(void)
473{
474    qos_session_t*	pThisSsn;
475
476    /* Pick up the rx-timestamp from the driver, put in global timestamp */
477    get_timestamp(&g_pktio_timestamp);
478
479    /* Find associated session, and reject with silence if none found. */
480    pThisSsn = qos_find_session();
481
482    if (pThisSsn == NULL)
483    {
484        IF_TRACED(TRC_QOS)
485            dbgprintf("qos_probe: no matching session found; ignoring.\n");
486        END_TRACE
487        return;
488    }
489
490    /* Valid session - mark it as "still active" */
491    pThisSsn->qssn_ticks_til_discard = 4;
492
493    /* If ProbeGap, just stamp it and reflect it, unless it has an 802.1p field */
494    if (g_qprb_hdr->probe_testtype == 1)
495    {
496        if ((g_qprb_hdr->probe_pqval & 0x80) == 0)
497        {
498            /* change the test-type to 2 when reflecting */
499            g_qprb_hdr->probe_testtype = 2;
500
501            /* setup ethernet and Base-hdr source and dest addresses to return to sender */
502            memcpy(&g_ethernet_hdr->eh_dst, &g_base_hdr->tbh_realsrc, sizeof(etheraddr_t));
503            memcpy(&g_ethernet_hdr->eh_src, &g_base_hdr->tbh_realdst, sizeof(etheraddr_t));
504            memcpy(&g_base_hdr->tbh_realdst, &g_base_hdr->tbh_realsrc, sizeof(etheraddr_t));
505            memcpy(&g_base_hdr->tbh_realsrc, &g_ethernet_hdr->eh_src, sizeof(etheraddr_t));
506
507            /* Add the rcv timestamp from the global save area */
508            cpy_hton64(&g_qprb_hdr->probe_rxstamp, &g_pktio_timestamp);
509
510            /* Add the rtx timestamp just before sending */
511            get_timestamp(&g_pktio_timestamp);
512            cpy_hton64(&g_qprb_hdr->probe_rtxstamp, &g_pktio_timestamp);
513
514            /* and return the packet - do not save in re_txbuf! */
515            tx_write(g_rxbuf, g_rcvd_pkt_len);
516
517            IF_TRACED(TRC_PACKET)
518                dbgprintf("qos_probegap: reflecting, no 802.1p, seq=%d -> " ETHERADDR_FMT "\n",
519                          g_sequencenum, ETHERADDR_PRINT(&pThisSsn->qssn_ctrlr_real));
520            END_TRACE
521        } else {
522            /* there is a valid 802.1p field, so the reflected packet must be tagged. */
523            qos_ether_header_t     *ethr_hdr;   /* pointer to qos ethernet-header in txbuf */
524            qos_base_header_t      *base_hdr;   /* pointer to qos base-header in txbuf */
525            qos_probe_header_t     *qprb_hdr;   /* pointer to qos probe-header in txbuf */
526
527            ethr_hdr = (qos_ether_header_t*) g_txbuf;
528            base_hdr = (qos_base_header_t*) (ethr_hdr+1);
529            qprb_hdr = (qos_probe_header_t*)(base_hdr+1);
530
531            /* setup ethernet and base-hdr source and dest addresses to return to sender */
532            memcpy(&ethr_hdr->qeh_dst, &g_base_hdr->tbh_realsrc, sizeof(etheraddr_t));
533            memcpy(&ethr_hdr->qeh_src, &g_base_hdr->tbh_realdst, sizeof(etheraddr_t));
534            memcpy(&base_hdr->qbh_realdst, &g_base_hdr->tbh_realsrc, sizeof(etheraddr_t));
535            memcpy(&base_hdr->qbh_realsrc, &g_base_hdr->tbh_realdst, sizeof(etheraddr_t));
536
537            /* Set up the 802.1q tag, then insert the .1p value in the highest 7 bits (vlan==0) */
538            ethr_hdr->qeh_qtag = 0x0081;
539            ethr_hdr->qeh_ptag = htons((g_qprb_hdr->probe_pqval << 9));
540            ethr_hdr->qeh_ethertype = g_ethernet_hdr->eh_ethertype;
541
542            /* fill out rest of base header */
543            base_hdr->qbh_version = g_base_hdr->tbh_version;
544            base_hdr->qbh_tos     = g_base_hdr->tbh_tos;
545            base_hdr->qbh_resrvd  = g_base_hdr->tbh_resrvd;
546            base_hdr->qbh_opcode  = g_base_hdr->tbh_opcode;
547            base_hdr->qbh_seqnum  = g_base_hdr->tbh_seqnum;
548
549            /* Fill out the probe-hdr */
550            qprb_hdr->probe_txstamp = g_qprb_hdr->probe_txstamp;
551            qprb_hdr->probe_testtype = 2;
552            qprb_hdr->probe_pktID = g_qprb_hdr->probe_pktID;
553            qprb_hdr->probe_pqval = g_qprb_hdr->probe_pqval;
554
555            /* Add the rcv timestamp from the global save area */
556            cpy_hton64(&qprb_hdr->probe_rxstamp, &g_pktio_timestamp);
557
558            /* Copy the payload */
559            memcpy(&qprb_hdr->probe_payload, &g_qprb_hdr->probe_payload,
560                   g_rcvd_pkt_len - (((uint8_t*)&g_qprb_hdr->probe_payload) - g_txbuf));
561
562            /* Add the rtx timestamp just before sending */
563            get_timestamp(&g_pktio_timestamp);
564            cpy_hton64(&qprb_hdr->probe_rtxstamp, &g_pktio_timestamp);
565
566            /* and return the packet (4 bytes longer due to tags) - do not save in re_txbuf! */
567            tx_write(g_txbuf, g_rcvd_pkt_len+4);
568
569            IF_TRACED(TRC_PACKET)
570                dbgprintf("qos_probegap: reflecting, with 802.1p priority of: %d, seq=%d -> " ETHERADDR_FMT "\n",
571                          (g_qprb_hdr->probe_pqval & 0x7f), g_sequencenum,
572                           ETHERADDR_PRINT(&pThisSsn->qssn_ctrlr_real));
573            END_TRACE
574        }
575    } else if (g_qprb_hdr->probe_testtype == 0) { /* timed probe */
576        qosEventDescr_t* evt;
577        qosEventBucket_t* bucket = qos_find_bucket(pThisSsn);
578
579        do
580        {
581            if (bucket)
582            {
583                // Make sure we don't try to store more than fits in the bucket
584                if (bucket->evt_numEvts >= MAX_QOS_EVENTS_PER_BUCKET)
585                {
586                    break;
587                }
588            }
589            else
590            {
591                /* There is no existing bucket to dump the event into, so find a new one */
592                if (pThisSsn->qssn_num_active_buckets >= MAX_QOS_BUCKETS)
593                {
594                    /* Reuse the oldest bucket */
595                    bucket = &pThisSsn->qssn_evt_buckets[pThisSsn->qssn_first_bucket];
596                    pThisSsn->qssn_first_bucket = (pThisSsn->qssn_first_bucket + 1) & (MAX_QOS_BUCKETS - 1);
597                }
598                else
599                {
600                    /* Use the next available bucket */
601                    bucket = &pThisSsn->qssn_evt_buckets[(pThisSsn->qssn_first_bucket + pThisSsn->qssn_num_active_buckets) & (MAX_QOS_BUCKETS - 1)];
602                    pThisSsn->qssn_num_active_buckets++;
603                }
604
605                bucket->evt_seqNum = g_sequencenum;
606                bucket->evt_numEvts = 0;
607            }
608
609            evt = &bucket->evt_descs[bucket->evt_numEvts++];
610
611            /* Copy timestamps, packet-ID, and set reserved-byte... */
612            memcpy(&evt->ctrlr_txstamp, &g_qprb_hdr->probe_txstamp, sizeof(uint64_t));
613
614            /* Pick up the rx-timestamp from the driver, put in global timestamp */
615            cpy_hton64(&evt->sink_rxstamp, &g_pktio_timestamp);
616
617            evt->evt_pktID = g_qprb_hdr->probe_pktID;
618            evt->evt_reserved = 0;
619        } while (FALSE);
620
621        IF_TRACED(TRC_PACKET)
622            dbgprintf("qos_timedprobe processed: seq=" FMT_UINT16 ", evtcount=" FMT_UINT32 "\n",
623                      g_sequencenum, bucket->evt_numEvts);
624        END_TRACE
625    }
626}
627
628
629static
630void
631qos_query(void)
632{
633    qosEventBucket_t* bucket;
634    qos_session_t*	pThisSsn = qos_find_session();
635    size_t          nbytes;
636    uint16_t        numEvts;
637
638    /* Find associated session, and reject with silence if none found. */
639    if (pThisSsn == NULL)   return;
640
641    /* Valid session - mark it as "still active" */
642    pThisSsn->qssn_ticks_til_discard = 4;
643
644    /* Compose the response headers in the space left before the events descrs */
645    /* Build the ethernet header and base header */
646    fmt_base(g_txbuf, &g_hwaddr, &pThisSsn->qssn_ctrlr_real, ToS_QoSDiagnostics,
647             Qopcode_QueryResp, g_sequencenum, FALSE /*no broadcast*/);
648    nbytes = sizeof(topo_ether_header_t) + sizeof(topo_base_header_t);
649
650    /* Locate the event bucket */
651    bucket = qos_find_bucket(pThisSsn);
652
653    /* Add the number of events */
654    numEvts = bucket ? bucket->evt_numEvts : 0;
655    g_short_reorder_buffer = htons(numEvts);
656    memcpy(&g_txbuf[nbytes], &g_short_reorder_buffer, sizeof(uint16_t));
657    nbytes += sizeof(uint16_t);
658
659    /* Copy the events */
660    if (numEvts)
661    {
662        memcpy(&g_txbuf[nbytes], bucket->evt_descs, sizeof(qosEventDescr_t) * numEvts);
663        nbytes += sizeof(qosEventDescr_t) * numEvts;
664    }
665
666    /* And send it... */
667    tx_write(g_txbuf, nbytes);
668
669    IF_TRACED(TRC_PACKET)
670        dbgprintf("qos_query_response: sending " FMT_UINT32 " events, seq=" FMT_UINT16 " -> " ETHERADDR_FMT "\n",
671                  numEvts, g_sequencenum, ETHERADDR_PRINT(&pThisSsn->qssn_ctrlr_real));
672    END_TRACE
673}
674
675
676static
677void
678qos_counterlease(void)
679{
680    struct timeval	now;
681
682    IF_TRACED(TRC_QOS)
683        dbgprintf("qos_counter_lease: timeout was: " FMT_UINT32 ", now: 300",g_samples_remaining);
684    END_TRACE
685
686    if (g_samples_remaining == 0)
687    {
688        /* start collection: repeats every second - until the lease runs out */
689        gettimeofday(&now, NULL);
690        now.tv_sec += 1;
691        g_qos_CTA_timer = event_add(&now, interface_counter_recorder, /*state:*/NULL);
692        IF_TRACED(TRC_QOS)
693             dbgprintf("qos_counter_lease: 1-sec timer started\n");
694        END_TRACE
695    }
696    g_samples_remaining = 300;
697}
698
699
700static
701void
702qos_snapshot(void)
703{
704    size_t		nbytes;
705    uint8_t            *pMsg, sample_cnt;
706    uint64_t            perf_timestamp;
707    uint                count, next;
708    qos_perf_sample    *pSample, *thisSample;
709
710    /* Build the ethernet header and base header */
711    pMsg = g_txbuf;
712    fmt_base(pMsg, &g_hwaddr, &g_base_hdr->tbh_realsrc, ToS_QoSDiagnostics,
713             Qopcode_CounterResult, g_sequencenum, FALSE /*no broadcast*/);
714    nbytes = sizeof(topo_ether_header_t) + sizeof(topo_base_header_t);
715
716    /* Calculate the sub-second interval and write the CounterResult header */
717    stamp_time(&perf_timestamp);
718    pMsg[nbytes++] = (uint8_t)(((perf_timestamp - g_perf_timestamp)*256)/1000000);      // sub-second span
719    pMsg[nbytes++] = (uint8_t)BYTE_SCALE;
720    pMsg[nbytes++] = (uint8_t)PKT_SCALE;
721    sample_cnt = (g_snap_hdr->cnt_rqstd <= (uint8_t)g_sample_count) ? g_snap_hdr->cnt_rqstd : (uint8_t)g_sample_count;
722    pMsg[nbytes++] = sample_cnt;
723
724    IF_TRACED(TRC_QOS)
725        dbgprintf("qos_snapshot: subsec: %d; sample-cnt: " FMT_UINT32 "\n",(int)pMsg[32], g_sample_count);
726    END_TRACE
727
728    /* Now copy the samples to the QosCounterResult msg */
729    next = (g_next_sample + (60 - sample_cnt)) % 60;
730    pSample = (qos_perf_sample*)&pMsg[nbytes];
731    for (count=sample_cnt; count>0; count--)
732    {
733        thisSample = &g_perf_samples[next++];
734        next %= 60;
735        pSample->bytes_rcvd = htons(thisSample->bytes_rcvd);
736        pSample->pkts_rcvd  = htons(thisSample->pkts_rcvd);
737        pSample->bytes_sent = htons(thisSample->bytes_sent);
738        pSample->pkts_sent  = htons(thisSample->pkts_sent);
739        IF_TRACED(TRC_QOS)
740            dbgprintf("  sample: rcvd: %d; r-pkts: %d;  sent: %d; s-pkts: %d\n",
741                      thisSample->bytes_rcvd,thisSample->pkts_rcvd,thisSample->bytes_sent,thisSample->pkts_sent);
742        END_TRACE
743        pSample++;
744        nbytes += sizeof(qos_perf_sample);
745    }
746    /* Now take a sub-second sample, and put it in the QosCounterResult msg */
747    {
748        uint32_t       rbytes, rpkts, tbytes, tpkts;
749        uint32_t       delta1, delta2, delta3, delta4 ;
750
751        /* save the counters, so normal sampling won't be disrupted */
752        rbytes = g_rbytes; rpkts = g_rpkts; tbytes = g_tbytes; tpkts = g_tpkts;
753
754        get_raw_samples();      // get current values for g_rbytes, g_rpkts, g_tbytes, g_tpkts
755
756        delta1 = (g_rbytes-rbytes > 0)? (g_rbytes-rbytes) : 0;
757        pSample->bytes_rcvd = htons(delta1 / BYTE_SCALE_FACTOR);
758
759        delta2 = (g_rpkts-rpkts > 0)?   (g_rpkts-rpkts) : 0;
760        pSample->pkts_rcvd  = htons(delta2 / PKT_SCALE_FACTOR);
761
762        delta3 = (g_tbytes-tbytes > 0)? (g_tbytes-tbytes) : 0;
763        pSample->bytes_sent = htons(delta3 / BYTE_SCALE_FACTOR);
764
765        delta4 = (g_tpkts-tpkts > 0)?   (g_tpkts-tpkts) : 0;
766        pSample->pkts_sent  = htons(delta4 / PKT_SCALE_FACTOR);
767
768        IF_TRACED(TRC_QOS)
769            dbgprintf("  sub-sec sample: rcvd: " FMT_UINT32 "; r-pkts: " FMT_UINT32 \
770                      ";  sent: " FMT_UINT32 "; s-pkts: " FMT_UINT32 "\n",
771                      delta1, delta2, delta3, delta4);
772        END_TRACE
773
774        /* restore the saved counters */
775        g_rbytes = rbytes; g_rpkts = rpkts; g_tbytes = tbytes; g_tpkts = tpkts;
776
777        /* count the subsecond sample in the packet length */
778        nbytes += sizeof(qos_perf_sample);
779    }
780
781    /* And send it... */
782    tx_write( g_txbuf, nbytes );
783
784    IF_TRACED(TRC_PACKET)
785        dbgprintf("qos_counter_result: sending " FMT_UINT32 " perf-samples + sub-sec-sample, seq=" FMT_UINT16 \
786                  " -> " ETHERADDR_FMT "\n",
787                  g_sample_count, g_sequencenum, ETHERADDR_PRINT(&g_base_hdr->tbh_realsrc));
788    END_TRACE
789}
790
791
792/************************** Q O S   M E S S A G E   R E C E I V E R **************************/
793
794/* Called by packetio_recv_handler() when msg ToS indicates QOS.
795 * The ether and base headers are validated, and ether- and base-header ptrs are set up. */
796extern void qosrcvpkt(void);
797
798void
799qosrcvpkt(void)
800{
801    uint16_t thisSeqnum;
802
803    /* check global g_opcode for QOS case */
804    if (g_opcode < 0 || g_opcode >= Qopcode_INVALID)
805    {
806    	warn("qospktrcv: g_opcode=%d is out of range for QoS msg; ignoring\n", g_opcode);
807    	return;
808    }
809    IF_TRACED(TRC_PACKET)
810        dbgprintf("QOS: g_opcode=%d\n",g_opcode);
811    END_TRACE
812
813    thisSeqnum = ntohs(g_base_hdr->tbh_seqnum);
814
815    /* QosCounterLease frame must not be sequenced, everything else must be sequenced */
816    /* QosCounterLease is the only one that's broadcasted */
817    if (g_opcode != Qopcode_CounterLease)
818    {
819        if (thisSeqnum == 0)
820        {
821        	warn("qospktrcv: g_opcode=%d must be sequenced; ignoring\n", g_opcode);
822            return;
823        }
824        else if (!ETHERADDR_EQUALS(&g_base_hdr->tbh_realdst, &g_hwaddr))
825        {
826        	warn("qospktrcv: g_opcode=%d must be directed; ignoring\n", g_opcode);
827            return;
828        }
829    }
830    else if (thisSeqnum != 0)
831    {
832    	warn("qospktrcv: QosCounterLease must not be sequenced; ignoring\n");
833        return;
834    }
835    else if (!ETHERADDR_IS_BCAST(&g_base_hdr->tbh_realdst))
836    {
837    	warn("qospktrcv: QosCounterLease must be broadcasted; ignoring\n");
838        return;
839    }
840
841    /* Validate source/dest real/ether addresses */
842    if (!ETHERADDR_EQUALS(&g_ethernet_hdr->eh_src, &g_base_hdr->tbh_realsrc) ||
843        !ETHERADDR_EQUALS(&g_ethernet_hdr->eh_dst, &g_base_hdr->tbh_realdst))
844    {
845        return;
846    }
847
848    /* print the frame */
849    IF_TRACED(TRC_PACKET)
850	dbgprintf(" [" ETHERADDR_FMT "] -> [" ETHERADDR_FMT "] %s (seq=%d)\n",
851		ETHERADDR_PRINT(&g_ethernet_hdr->eh_src), ETHERADDR_PRINT(&g_ethernet_hdr->eh_dst),
852		Qos_opcode_names[g_opcode], thisSeqnum);
853    END_TRACE
854
855    /* By this time, we are pretty sure the sequence number is valid, so save a global copy... */
856    g_sequencenum = thisSeqnum;
857
858    /* Set up global pointers to the 2 possible types of received headers that are bigger than the base hdr */
859    g_qprb_hdr  = (qos_probe_header_t*)   (g_base_hdr + 1);
860    g_qinit_hdr = (qos_initsink_header_t*)(g_base_hdr + 1);
861    g_snap_hdr  = (qos_snapshot_header_t*)(g_base_hdr + 1);
862
863    /* Finally, perform per-g_opcode validation & processing */
864    switch (g_opcode)
865    {
866      case Qopcode_Probe:
867        qos_probe();
868        break;
869
870      case Qopcode_Query:
871        qos_query();
872        break;
873
874      case Qopcode_CounterSnapshot:
875        qos_snapshot();
876        break;
877
878      case Qopcode_CounterLease:
879        qos_counterlease();
880        break;
881
882      case Qopcode_InitializeSink:
883        qos_initsink();
884        break;
885
886      case Qopcode_Reset:
887        qos_reset();
888        break;
889
890      /* (invalid- or Sink-sent-packets are ignored completely) */
891      case Qopcode_ACK:		 // Sent by Sink
892      case Qopcode_QueryResp:	 // Sent by Sink
893      case Qopcode_Ready:	 // Sent by Sink
894      case Qopcode_Error:	 // Sent by Sink
895      case Qopcode_CounterResult:// Sent by Sink
896      case Opcode_INVALID:
897      default:
898        break;
899    }
900}
901