1ea10422cSimarom/*
2ea10422cSimarom Itay Marom
3ea10422cSimarom Cisco Systems, Inc.
4ea10422cSimarom*/
5ea10422cSimarom
6ea10422cSimarom/*
7ea10422cSimaromCopyright (c) 2015-2016 Cisco Systems, Inc.
8ea10422cSimarom
9ea10422cSimaromLicensed under the Apache License, Version 2.0 (the "License");
10ea10422cSimaromyou may not use this file except in compliance with the License.
11ea10422cSimaromYou may obtain a copy of the License at
12ea10422cSimarom
13ea10422cSimarom    http://www.apache.org/licenses/LICENSE-2.0
14ea10422cSimarom
15ea10422cSimaromUnless required by applicable law or agreed to in writing, software
16ea10422cSimaromdistributed under the License is distributed on an "AS IS" BASIS,
17ea10422cSimaromWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18ea10422cSimaromSee the License for the specific language governing permissions and
19ea10422cSimaromlimitations under the License.
20ea10422cSimarom*/
21ea10422cSimarom#include "trex_stateless_capture.h"
22ea10422cSimarom#include "trex_exception.h"
23ea10422cSimarom
243689edf3Simarom/**************************************
253689edf3Simarom * Capture
263689edf3Simarom *
273689edf3Simarom * A single instance of a capture
283689edf3Simarom *************************************/
293689edf3SimaromTrexStatelessCapture::TrexStatelessCapture(capture_id_t id,
303689edf3Simarom                                           uint64_t limit,
313689edf3Simarom                                           const CaptureFilter &filter,
323689edf3Simarom                                           TrexPktBuffer::mode_e mode) {
33ea10422cSimarom    m_id         = id;
343689edf3Simarom    m_pkt_buffer = new TrexPktBuffer(limit, mode);
35ea10422cSimarom    m_filter     = filter;
36ac2e93d4Simarom    m_state      = STATE_ACTIVE;
37f5f92b06Simarom    m_start_ts   = now_sec();
38f5f92b06Simarom    m_pkt_index  = 0;
39ea10422cSimarom}
40ea10422cSimarom
41ea10422cSimaromTrexStatelessCapture::~TrexStatelessCapture() {
42ea10422cSimarom    if (m_pkt_buffer) {
43ea10422cSimarom        delete m_pkt_buffer;
44ea10422cSimarom    }
45ea10422cSimarom}
46ea10422cSimarom
47ea10422cSimaromvoid
48d9e19ba4SimaromTrexStatelessCapture::handle_pkt(const rte_mbuf_t *m, int port, TrexPkt::origin_e origin) {
4917d58dbaSimarom
50ac2e93d4Simarom    if (m_state != STATE_ACTIVE) {
51ac2e93d4Simarom        return;
52ac2e93d4Simarom    }
53ea10422cSimarom
54ea10422cSimarom    /* if not in filter - back off */
55d9e19ba4Simarom    if (!m_filter.in_x(port, origin)) {
56ea10422cSimarom        return;
57ea10422cSimarom    }
58ea10422cSimarom
59d9e19ba4Simarom    m_pkt_buffer->push(m, port, origin, ++m_pkt_index);
60ea10422cSimarom}
61ea10422cSimarom
62ac2e93d4Simarom
63ac2e93d4SimaromJson::Value
64ac2e93d4SimaromTrexStatelessCapture::to_json() const {
65ac2e93d4Simarom    Json::Value output = Json::objectValue;
66ac2e93d4Simarom
67ac2e93d4Simarom    output["id"]     = Json::UInt64(m_id);
68ac2e93d4Simarom    output["filter"] = m_filter.to_json();
69ac2e93d4Simarom    output["count"]  = m_pkt_buffer->get_element_count();
70ac2e93d4Simarom    output["bytes"]  = m_pkt_buffer->get_bytes();
71ac2e93d4Simarom    output["limit"]  = m_pkt_buffer->get_capacity();
72ac2e93d4Simarom
73ac2e93d4Simarom    switch (m_state) {
74ac2e93d4Simarom    case STATE_ACTIVE:
75ac2e93d4Simarom        output["state"]  = "ACTIVE";
76ac2e93d4Simarom        break;
77ac2e93d4Simarom
78ac2e93d4Simarom    case STATE_STOPPED:
79ac2e93d4Simarom        output["state"]  = "STOPPED";
80ac2e93d4Simarom        break;
81ac2e93d4Simarom
82ac2e93d4Simarom    default:
83ac2e93d4Simarom        assert(0);
84ac2e93d4Simarom    }
85ac2e93d4Simarom
86ac2e93d4Simarom    return output;
87ac2e93d4Simarom}
88ac2e93d4Simarom
893689edf3Simarom/**
903689edf3Simarom * fetch up to 'pkt_limit' from the capture
913689edf3Simarom *
923689edf3Simarom */
93ac2e93d4SimaromTrexPktBuffer *
94ac2e93d4SimaromTrexStatelessCapture::fetch(uint32_t pkt_limit, uint32_t &pending) {
953689edf3Simarom
96ac2e93d4Simarom    /* if the total sum of packets is within the limit range - take it */
97ac2e93d4Simarom    if (m_pkt_buffer->get_element_count() <= pkt_limit) {
98ac2e93d4Simarom        TrexPktBuffer *current = m_pkt_buffer;
99ac2e93d4Simarom        m_pkt_buffer = new TrexPktBuffer(m_pkt_buffer->get_capacity(), m_pkt_buffer->get_mode());
100ac2e93d4Simarom        pending = 0;
101ac2e93d4Simarom        return current;
102ac2e93d4Simarom    }
103ac2e93d4Simarom
1043689edf3Simarom    /* partial fetch - take a partial list */
1053689edf3Simarom    TrexPktBuffer *partial = m_pkt_buffer->pop_n(pkt_limit);
106f5f92b06Simarom    pending  = m_pkt_buffer->get_element_count();
107f5f92b06Simarom
108ac2e93d4Simarom    return partial;
109ac2e93d4Simarom}
110ac2e93d4Simarom
1113689edf3Simarom
1123689edf3Simarom/**************************************
1133689edf3Simarom * Capture Manager
1143689edf3Simarom * handles all the captures
1153689edf3Simarom * in the system
1163689edf3Simarom *************************************/
1173689edf3Simarom
1183689edf3Simarom/**
1193689edf3Simarom * holds the global filter in the capture manager
1203689edf3Simarom * which ports in the entire system are monitored
1213689edf3Simarom */
1225257dbb8Simaromvoid
1235257dbb8SimaromTrexStatelessCaptureMngr::update_global_filter() {
1245257dbb8Simarom    CaptureFilter new_filter;
1255257dbb8Simarom
1263689edf3Simarom    /* recalculates the global filter */
1275257dbb8Simarom    for (TrexStatelessCapture *capture : m_captures) {
1285257dbb8Simarom        new_filter += capture->get_filter();
1295257dbb8Simarom    }
13017d58dbaSimarom
1315257dbb8Simarom    m_global_filter = new_filter;
1325257dbb8Simarom}
1335257dbb8Simarom
1343689edf3Simarom
1353689edf3Simarom/**
1363689edf3Simarom * lookup a specific capture by ID
1373689edf3Simarom */
138ac2e93d4SimaromTrexStatelessCapture *
13917d58dbaSimaromTrexStatelessCaptureMngr::lookup(capture_id_t capture_id) const {
140ea10422cSimarom
141ac2e93d4Simarom    for (int i = 0; i < m_captures.size(); i++) {
142ac2e93d4Simarom        if (m_captures[i]->get_id() == capture_id) {
143ac2e93d4Simarom            return m_captures[i];
144ac2e93d4Simarom        }
145ac2e93d4Simarom    }
146ac2e93d4Simarom
147ac2e93d4Simarom    /* does not exist */
148ac2e93d4Simarom    return nullptr;
149ac2e93d4Simarom}
150ac2e93d4Simarom
1513689edf3Simarom
1523689edf3Simaromint
15317d58dbaSimaromTrexStatelessCaptureMngr::lookup_index(capture_id_t capture_id) const {
1543689edf3Simarom    for (int i = 0; i < m_captures.size(); i++) {
1553689edf3Simarom        if (m_captures[i]->get_id() == capture_id) {
1563689edf3Simarom            return i;
1573689edf3Simarom        }
1583689edf3Simarom    }
1593689edf3Simarom    return -1;
1603689edf3Simarom}
1613689edf3Simarom
1623689edf3Simarom
1633689edf3Simarom/**
1643689edf3Simarom * starts a new capture
1653689edf3Simarom *
1663689edf3Simarom */
167ac2e93d4Simaromvoid
1683689edf3SimaromTrexStatelessCaptureMngr::start(const CaptureFilter &filter,
1693689edf3Simarom                                uint64_t limit,
1703689edf3Simarom                                TrexPktBuffer::mode_e mode,
1713689edf3Simarom                                TrexCaptureRCStart &rc) {
172ac2e93d4Simarom
1733689edf3Simarom    /* check for maximum active captures */
1743689edf3Simarom    if (m_captures.size() >= MAX_CAPTURE_SIZE) {
175ac2e93d4Simarom        rc.set_err(TrexCaptureRC::RC_CAPTURE_LIMIT_REACHED);
176ac2e93d4Simarom        return;
177ea10422cSimarom    }
178ea10422cSimarom
1793689edf3Simarom    /* create a new capture*/
180ea10422cSimarom    int new_id = m_id_counter++;
1813689edf3Simarom    TrexStatelessCapture *new_capture = new TrexStatelessCapture(new_id, limit, filter, mode);
18217d58dbaSimarom
18317d58dbaSimarom    /**
18417d58dbaSimarom     * add the new capture in a safe mode
18517d58dbaSimarom     * (TX might be active)
18617d58dbaSimarom     */
18717d58dbaSimarom    std::unique_lock<std::mutex> ulock(m_lock);
18819df0634Simarom    m_captures.push_back(new_capture);
18917d58dbaSimarom
1905257dbb8Simarom    /* update global filter */
1915257dbb8Simarom    update_global_filter();
192ea10422cSimarom
19317d58dbaSimarom    /* done with critical section */
19417d58dbaSimarom    ulock.unlock();
19517d58dbaSimarom
196ac2e93d4Simarom    /* result */
19719df0634Simarom    rc.set_rc(new_id, new_capture->get_start_ts());
198ac2e93d4Simarom}
199ac2e93d4Simarom
200ac2e93d4Simaromvoid
201ac2e93d4SimaromTrexStatelessCaptureMngr::stop(capture_id_t capture_id, TrexCaptureRCStop &rc) {
202ac2e93d4Simarom    TrexStatelessCapture *capture = lookup(capture_id);
203ac2e93d4Simarom    if (!capture) {
204ac2e93d4Simarom        rc.set_err(TrexCaptureRC::RC_CAPTURE_NOT_FOUND);
205ac2e93d4Simarom        return;
206ac2e93d4Simarom    }
207ac2e93d4Simarom
20817d58dbaSimarom    std::unique_lock<std::mutex> ulock(m_lock);
209ac2e93d4Simarom    capture->stop();
21017d58dbaSimarom
21119df0634Simarom    rc.set_rc(capture->get_pkt_count());
212ea10422cSimarom}
213ea10422cSimarom
2143689edf3Simarom
215ac2e93d4Simaromvoid
216ac2e93d4SimaromTrexStatelessCaptureMngr::fetch(capture_id_t capture_id, uint32_t pkt_limit, TrexCaptureRCFetch &rc) {
217ac2e93d4Simarom    TrexStatelessCapture *capture = lookup(capture_id);
218ac2e93d4Simarom    if (!capture) {
219ac2e93d4Simarom        rc.set_err(TrexCaptureRC::RC_CAPTURE_NOT_FOUND);
220ac2e93d4Simarom        return;
221ac2e93d4Simarom    }
222ea10422cSimarom
223ac2e93d4Simarom    uint32_t pending = 0;
22417d58dbaSimarom
22517d58dbaSimarom    /* take a lock before fetching all the packets */
22617d58dbaSimarom    std::unique_lock<std::mutex> ulock(m_lock);
227ac2e93d4Simarom    TrexPktBuffer *pkt_buffer = capture->fetch(pkt_limit, pending);
22817d58dbaSimarom    ulock.unlock();
229ac2e93d4Simarom
2303689edf3Simarom    rc.set_rc(pkt_buffer, pending, capture->get_start_ts());
231ac2e93d4Simarom}
232ac2e93d4Simarom
233ac2e93d4Simaromvoid
234ac2e93d4SimaromTrexStatelessCaptureMngr::remove(capture_id_t capture_id, TrexCaptureRCRemove &rc) {
235ea10422cSimarom
2363689edf3Simarom    /* lookup index */
2373689edf3Simarom    int index = lookup_index(capture_id);
238ea10422cSimarom    if (index == -1) {
239ac2e93d4Simarom        rc.set_err(TrexCaptureRC::RC_CAPTURE_NOT_FOUND);
240ac2e93d4Simarom        return;
241ea10422cSimarom    }
242ea10422cSimarom
243ea10422cSimarom    TrexStatelessCapture *capture =  m_captures[index];
2445257dbb8Simarom
24517d58dbaSimarom    /* remove from list under lock */
24617d58dbaSimarom    std::unique_lock<std::mutex> ulock(m_lock);
2475257dbb8Simarom
24817d58dbaSimarom    m_captures.erase(m_captures.begin() + index);
24917d58dbaSimarom
25017d58dbaSimarom    /* update global filter under lock (for barrier) */
2515257dbb8Simarom    update_global_filter();
252951b09efSimarom
25317d58dbaSimarom    /* done with critical section */
25417d58dbaSimarom    ulock.unlock();
25517d58dbaSimarom
25617d58dbaSimarom    /* free memory */
25717d58dbaSimarom    delete capture;
25817d58dbaSimarom
2593689edf3Simarom    rc.set_rc();
260ea10422cSimarom}
261ea10422cSimarom
262ea10422cSimaromvoid
263ea10422cSimaromTrexStatelessCaptureMngr::reset() {
264ac2e93d4Simarom    TrexCaptureRCRemove dummy;
265ac2e93d4Simarom
266ea10422cSimarom    while (m_captures.size() > 0) {
267ac2e93d4Simarom        remove(m_captures[0]->get_id(), dummy);
2683689edf3Simarom        assert(!!dummy);
269ea10422cSimarom    }
270ea10422cSimarom}
271ea10422cSimarom
272b22e3ed1Simarom/* define this macro to stress test the critical section */
27317d58dbaSimarom//#define STRESS_TEST
274b22e3ed1Simarom
275ea10422cSimaromvoid
276d9e19ba4SimaromTrexStatelessCaptureMngr::handle_pkt_slow_path(const rte_mbuf_t *m, int port, TrexPkt::origin_e origin) {
27717d58dbaSimarom
27817d58dbaSimarom    #ifdef STRESS_TEST
27917d58dbaSimarom        static int sanity = 0;
28017d58dbaSimarom        assert(__sync_fetch_and_add(&sanity, 1) == 0);
28117d58dbaSimarom    #endif
282d9e19ba4Simarom
283ea10422cSimarom    for (TrexStatelessCapture *capture : m_captures) {
284d9e19ba4Simarom        capture->handle_pkt(m, port, origin);
285ea10422cSimarom    }
28617d58dbaSimarom
28717d58dbaSimarom    #ifdef STRESS_TEST
28817d58dbaSimarom        assert(__sync_fetch_and_sub(&sanity, 1) == 1);
28917d58dbaSimarom    #endif
290ea10422cSimarom}
291ea10422cSimarom
29217d58dbaSimarom
293ac2e93d4SimaromJson::Value
29417d58dbaSimaromTrexStatelessCaptureMngr::to_json() {
295ac2e93d4Simarom    Json::Value lst = Json::arrayValue;
29617d58dbaSimarom
29717d58dbaSimarom    std::unique_lock<std::mutex> ulock(m_lock);
29817d58dbaSimarom
299ac2e93d4Simarom    for (TrexStatelessCapture *capture : m_captures) {
300ac2e93d4Simarom        lst.append(capture->to_json());
301ac2e93d4Simarom    }
302ac2e93d4Simarom
30317d58dbaSimarom    ulock.unlock();
30417d58dbaSimarom
305ac2e93d4Simarom    return lst;
306ac2e93d4Simarom}
307ac2e93d4Simarom
308d9e19ba4SimaromTrexStatelessCaptureMngr TrexStatelessCaptureMngr::g_instance;
30917d58dbaSimarom
310