1/*
2  Itay Marom
3  Cisco Systems, Inc.
4*/
5
6/*
7  Copyright (c) 2016-2016 Cisco Systems, Inc.
8
9  Licensed under the Apache License, Version 2.0 (the "License");
10  you may not use this file except in compliance with the License.
11  You may obtain a copy of the License at
12
13  http://www.apache.org/licenses/LICENSE-2.0
14
15  Unless required by applicable law or agreed to in writing, software
16  distributed under the License is distributed on an "AS IS" BASIS,
17  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18  See the License for the specific language governing permissions and
19  limitations under the License.
20*/
21
22#include "trex_stateless_pkt.h"
23#include <assert.h>
24
25
26/**
27 * copy MBUF to a flat buffer
28 *
29 * @author imarom (12/20/2016)
30 *
31 * @param dest - buffer with at least rte_pktmbuf_pkt_len(m)
32 *               bytes
33 * @param m - MBUF to copy
34 *
35 * @return uint8_t*
36 */
37void mbuf_to_buffer(uint8_t *dest, const rte_mbuf_t *m) {
38
39    int index = 0;
40    for (const rte_mbuf_t *it = m; it != NULL; it = it->next) {
41        const uint8_t *src = rte_pktmbuf_mtod(it, const uint8_t *);
42        memcpy(dest + index, src, it->data_len);
43        index += it->data_len;
44    }
45}
46
47/**************************************
48 * TRex packet
49 *
50 *************************************/
51TrexPkt::TrexPkt(const rte_mbuf_t *m, int port, origin_e origin, uint64_t index) {
52
53    /* allocate buffer */
54    m_size = m->pkt_len;
55    m_raw = new uint8_t[m_size];
56
57    /* copy data */
58    mbuf_to_buffer(m_raw, m);
59
60    /* generate a packet timestamp */
61    m_timestamp = now_sec();
62
63    m_port   = port;
64    m_origin = origin;
65    m_index  = index;
66}
67
68TrexPkt::TrexPkt(const TrexPkt &other) {
69    m_size = other.m_size;
70    memcpy(m_raw, other.m_raw, m_size);
71
72    m_timestamp = other.m_timestamp;
73
74    m_port   = other.m_port;
75    m_origin = other.m_origin;
76    m_index  = other.m_index;
77}
78
79
80/**************************************
81 * TRex packet buffer
82 *
83 *************************************/
84
85TrexPktBuffer::TrexPktBuffer(uint64_t size, mode_e mode) {
86    m_mode             = mode;
87    m_buffer           = nullptr;
88    m_head             = 0;
89    m_tail             = 0;
90    m_bytes            = 0;
91    m_size             = (size + 1); // for the empty/full difference 1 slot reserved
92
93    /* generate queue */
94    m_buffer = new const TrexPkt*[m_size](); // zeroed
95}
96
97TrexPktBuffer::~TrexPktBuffer() {
98    assert(m_buffer);
99
100    while (!is_empty()) {
101        const TrexPkt *pkt = pop();
102        delete pkt;
103    }
104    delete [] m_buffer;
105}
106
107/**
108 * packet will be copied to an internal object
109 */
110void
111TrexPktBuffer::push(const rte_mbuf_t *m, int port, TrexPkt::origin_e origin, uint64_t pkt_index) {
112
113    /* if full - decide by the policy */
114    if (is_full()) {
115        if (m_mode == MODE_DROP_HEAD) {
116            delete pop();
117        } else {
118            /* drop the tail (current packet) */
119            return;
120        }
121    }
122
123    push_internal(new TrexPkt(m, port, origin, pkt_index));
124}
125
126/**
127 * packet will be handled internally
128 * packet pointer is invalid after this call
129 */
130void
131TrexPktBuffer::push(const TrexPkt *pkt, uint64_t pkt_index) {
132    /* if full - decide by the policy */
133    if (is_full()) {
134        if (m_mode == MODE_DROP_HEAD) {
135            delete pop();
136        } else {
137            /* drop the tail (current packet) */
138            return;
139        }
140    }
141
142    /* duplicate packet */
143    TrexPkt *dup = new TrexPkt(*pkt);
144
145    /* update packet index if given */
146    if (pkt_index != 0) {
147        dup->set_index(pkt_index);
148    }
149
150    push_internal(dup);
151}
152
153
154void
155TrexPktBuffer::push_internal(const TrexPkt *pkt) {
156    /* push the packet */
157    m_buffer[m_head] = pkt;
158    m_bytes += pkt->get_size();
159
160    m_head = next(m_head);
161}
162
163const TrexPkt *
164TrexPktBuffer::pop() {
165    assert(!is_empty());
166
167    const TrexPkt *pkt = m_buffer[m_tail];
168    m_tail = next(m_tail);
169
170    m_bytes -= pkt->get_size();
171
172    return pkt;
173}
174
175uint32_t
176TrexPktBuffer::get_element_count() const {
177    if (m_head >= m_tail) {
178        return (m_head - m_tail);
179    } else {
180        return ( get_capacity() - (m_tail - m_head - 1) );
181    }
182}
183
184Json::Value
185TrexPktBuffer::to_json() const {
186
187    Json::Value output = Json::arrayValue;
188
189    int tmp = m_tail;
190    while (tmp != m_head) {
191        const TrexPkt *pkt = m_buffer[tmp];
192        output.append(pkt->to_json());
193        tmp = next(tmp);
194    }
195
196    return output;
197}
198
199TrexPktBuffer *
200TrexPktBuffer::pop_n(uint32_t count) {
201    /* can't pop more than total */
202    assert(count <= get_element_count());
203
204    // TODO: consider returning NULL if no packets exists
205    //       to avoid mallocing
206
207    TrexPktBuffer *partial = new TrexPktBuffer(count);
208
209    for (int i = 0; i < count; i++) {
210        const TrexPkt *pkt = pop();
211        partial->push_internal(pkt);
212    }
213
214    return partial;
215}
216