1/*
2 Itay Marom
3 Cisco Systems, Inc.
4*/
5
6/*
7Copyright (c) 2015-2016 Cisco Systems, Inc.
8
9Licensed under the Apache License, Version 2.0 (the "License");
10you may not use this file except in compliance with the License.
11You may obtain a copy of the License at
12
13    http://www.apache.org/licenses/LICENSE-2.0
14
15Unless required by applicable law or agreed to in writing, software
16distributed under the License is distributed on an "AS IS" BASIS,
17WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18See the License for the specific language governing permissions and
19limitations under the License.
20*/
21#include "trex_stateless_capture.h"
22#include "trex_exception.h"
23
24/**************************************
25 * Capture
26 *
27 * A single instance of a capture
28 *************************************/
29TrexStatelessCapture::TrexStatelessCapture(capture_id_t id,
30                                           uint64_t limit,
31                                           const CaptureFilter &filter,
32                                           TrexPktBuffer::mode_e mode) {
33    m_id         = id;
34    m_pkt_buffer = new TrexPktBuffer(limit, mode);
35    m_filter     = filter;
36    m_state      = STATE_ACTIVE;
37    m_start_ts   = now_sec();
38    m_pkt_index  = 0;
39}
40
41TrexStatelessCapture::~TrexStatelessCapture() {
42    if (m_pkt_buffer) {
43        delete m_pkt_buffer;
44    }
45}
46
47void
48TrexStatelessCapture::handle_pkt(const rte_mbuf_t *m, int port, TrexPkt::origin_e origin) {
49
50    if (m_state != STATE_ACTIVE) {
51        return;
52    }
53
54    /* if not in filter - back off */
55    if (!m_filter.in_x(port, origin)) {
56        return;
57    }
58
59    m_pkt_buffer->push(m, port, origin, ++m_pkt_index);
60}
61
62
63Json::Value
64TrexStatelessCapture::to_json() const {
65    Json::Value output = Json::objectValue;
66
67    output["id"]     = Json::UInt64(m_id);
68    output["filter"] = m_filter.to_json();
69    output["count"]  = m_pkt_buffer->get_element_count();
70    output["bytes"]  = m_pkt_buffer->get_bytes();
71    output["limit"]  = m_pkt_buffer->get_capacity();
72
73    switch (m_state) {
74    case STATE_ACTIVE:
75        output["state"]  = "ACTIVE";
76        break;
77
78    case STATE_STOPPED:
79        output["state"]  = "STOPPED";
80        break;
81
82    default:
83        assert(0);
84    }
85
86    return output;
87}
88
89/**
90 * fetch up to 'pkt_limit' from the capture
91 *
92 */
93TrexPktBuffer *
94TrexStatelessCapture::fetch(uint32_t pkt_limit, uint32_t &pending) {
95
96    /* if the total sum of packets is within the limit range - take it */
97    if (m_pkt_buffer->get_element_count() <= pkt_limit) {
98        TrexPktBuffer *current = m_pkt_buffer;
99        m_pkt_buffer = new TrexPktBuffer(m_pkt_buffer->get_capacity(), m_pkt_buffer->get_mode());
100        pending = 0;
101        return current;
102    }
103
104    /* partial fetch - take a partial list */
105    TrexPktBuffer *partial = m_pkt_buffer->pop_n(pkt_limit);
106    pending  = m_pkt_buffer->get_element_count();
107
108    return partial;
109}
110
111
112/**************************************
113 * Capture Manager
114 * handles all the captures
115 * in the system
116 *************************************/
117
118/**
119 * holds the global filter in the capture manager
120 * which ports in the entire system are monitored
121 */
122void
123TrexStatelessCaptureMngr::update_global_filter() {
124    CaptureFilter new_filter;
125
126    /* recalculates the global filter */
127    for (TrexStatelessCapture *capture : m_captures) {
128        new_filter += capture->get_filter();
129    }
130
131    m_global_filter = new_filter;
132}
133
134
135/**
136 * lookup a specific capture by ID
137 */
138TrexStatelessCapture *
139TrexStatelessCaptureMngr::lookup(capture_id_t capture_id) const {
140
141    for (int i = 0; i < m_captures.size(); i++) {
142        if (m_captures[i]->get_id() == capture_id) {
143            return m_captures[i];
144        }
145    }
146
147    /* does not exist */
148    return nullptr;
149}
150
151
152int
153TrexStatelessCaptureMngr::lookup_index(capture_id_t capture_id) const {
154    for (int i = 0; i < m_captures.size(); i++) {
155        if (m_captures[i]->get_id() == capture_id) {
156            return i;
157        }
158    }
159    return -1;
160}
161
162
163/**
164 * starts a new capture
165 *
166 */
167void
168TrexStatelessCaptureMngr::start(const CaptureFilter &filter,
169                                uint64_t limit,
170                                TrexPktBuffer::mode_e mode,
171                                TrexCaptureRCStart &rc) {
172
173    /* check for maximum active captures */
174    if (m_captures.size() >= MAX_CAPTURE_SIZE) {
175        rc.set_err(TrexCaptureRC::RC_CAPTURE_LIMIT_REACHED);
176        return;
177    }
178
179    /* create a new capture*/
180    int new_id = m_id_counter++;
181    TrexStatelessCapture *new_capture = new TrexStatelessCapture(new_id, limit, filter, mode);
182
183    /**
184     * add the new capture in a safe mode
185     * (TX might be active)
186     */
187    std::unique_lock<std::mutex> ulock(m_lock);
188    m_captures.push_back(new_capture);
189
190    /* update global filter */
191    update_global_filter();
192
193    /* done with critical section */
194    ulock.unlock();
195
196    /* result */
197    rc.set_rc(new_id, new_capture->get_start_ts());
198}
199
200void
201TrexStatelessCaptureMngr::stop(capture_id_t capture_id, TrexCaptureRCStop &rc) {
202    TrexStatelessCapture *capture = lookup(capture_id);
203    if (!capture) {
204        rc.set_err(TrexCaptureRC::RC_CAPTURE_NOT_FOUND);
205        return;
206    }
207
208    std::unique_lock<std::mutex> ulock(m_lock);
209    capture->stop();
210
211    rc.set_rc(capture->get_pkt_count());
212}
213
214
215void
216TrexStatelessCaptureMngr::fetch(capture_id_t capture_id, uint32_t pkt_limit, TrexCaptureRCFetch &rc) {
217    TrexStatelessCapture *capture = lookup(capture_id);
218    if (!capture) {
219        rc.set_err(TrexCaptureRC::RC_CAPTURE_NOT_FOUND);
220        return;
221    }
222
223    uint32_t pending = 0;
224
225    /* take a lock before fetching all the packets */
226    std::unique_lock<std::mutex> ulock(m_lock);
227    TrexPktBuffer *pkt_buffer = capture->fetch(pkt_limit, pending);
228    ulock.unlock();
229
230    rc.set_rc(pkt_buffer, pending, capture->get_start_ts());
231}
232
233void
234TrexStatelessCaptureMngr::remove(capture_id_t capture_id, TrexCaptureRCRemove &rc) {
235
236    /* lookup index */
237    int index = lookup_index(capture_id);
238    if (index == -1) {
239        rc.set_err(TrexCaptureRC::RC_CAPTURE_NOT_FOUND);
240        return;
241    }
242
243    TrexStatelessCapture *capture =  m_captures[index];
244
245    /* remove from list under lock */
246    std::unique_lock<std::mutex> ulock(m_lock);
247
248    m_captures.erase(m_captures.begin() + index);
249
250    /* update global filter under lock (for barrier) */
251    update_global_filter();
252
253    /* done with critical section */
254    ulock.unlock();
255
256    /* free memory */
257    delete capture;
258
259    rc.set_rc();
260}
261
262void
263TrexStatelessCaptureMngr::reset() {
264    TrexCaptureRCRemove dummy;
265
266    while (m_captures.size() > 0) {
267        remove(m_captures[0]->get_id(), dummy);
268        assert(!!dummy);
269    }
270}
271
272/* define this macro to stress test the critical section */
273//#define STRESS_TEST
274
275void
276TrexStatelessCaptureMngr::handle_pkt_slow_path(const rte_mbuf_t *m, int port, TrexPkt::origin_e origin) {
277
278    #ifdef STRESS_TEST
279        static int sanity = 0;
280        assert(__sync_fetch_and_add(&sanity, 1) == 0);
281    #endif
282
283    for (TrexStatelessCapture *capture : m_captures) {
284        capture->handle_pkt(m, port, origin);
285    }
286
287    #ifdef STRESS_TEST
288        assert(__sync_fetch_and_sub(&sanity, 1) == 1);
289    #endif
290}
291
292
293Json::Value
294TrexStatelessCaptureMngr::to_json() {
295    Json::Value lst = Json::arrayValue;
296
297    std::unique_lock<std::mutex> ulock(m_lock);
298
299    for (TrexStatelessCapture *capture : m_captures) {
300        lst.append(capture->to_json());
301    }
302
303    ulock.unlock();
304
305    return lst;
306}
307
308TrexStatelessCaptureMngr TrexStatelessCaptureMngr::g_instance;
309
310