Line data Source code
1 : /*
2 : * Copyright (c) 2013 Juniper Networks, Inc. All rights reserved.
3 : */
4 :
5 : #include <vector>
6 : #include <bitset>
7 : #include <mutex>
8 :
9 : #include <boost/date_time/posix_time/posix_time.hpp>
10 : #include <boost/assign/list_of.hpp>
11 : #include <boost/unordered_map.hpp>
12 : #include <sandesh/sandesh_trace.h>
13 : #include <base/address_util.h>
14 : #include <pkt/flow_table.h>
15 : #include <vrouter/ksync/ksync_init.h>
16 : #include <vrouter/ksync/ksync_flow_index_manager.h>
17 :
18 : #include <arpa/inet.h>
19 : #include <netinet/in.h>
20 : #include <base/os.h>
21 :
22 : #include <route/route.h>
23 : #include <cmn/agent_cmn.h>
24 : #include <oper/interface_common.h>
25 : #include <oper/nexthop.h>
26 :
27 : #include <init/agent_param.h>
28 : #include <cmn/agent_cmn.h>
29 : #include <cmn/agent_stats.h>
30 : #include <oper/route_common.h>
31 : #include <oper/vrf.h>
32 : #include <oper/vm.h>
33 : #include <oper/sg.h>
34 : #include <oper/tunnel_nh.h>
35 :
36 : #include <filter/packet_header.h>
37 : #include <filter/acl.h>
38 :
39 : #include <pkt/proto.h>
40 : #include <pkt/proto_handler.h>
41 : #include <pkt/pkt_handler.h>
42 : #include <pkt/flow_proto.h>
43 : #include <pkt/pkt_types.h>
44 : #include <pkt/pkt_sandesh_flow.h>
45 : #include <pkt/flow_mgmt.h>
46 : #include <pkt/flow_event.h>
47 :
48 : const uint32_t FlowEntryFreeList::kInitCount;
49 : const uint32_t FlowEntryFreeList::kTestInitCount;
50 : const uint32_t FlowEntryFreeList::kGrowSize;
51 : const uint32_t FlowEntryFreeList::kMinThreshold;
52 : const uint32_t FlowEntryFreeList::kMaxThreshold;
53 :
54 : SandeshTraceBufferPtr FlowTraceBuf(SandeshTraceBufferCreate("Flow", 5000));
55 :
56 : /////////////////////////////////////////////////////////////////////////////
57 : // FlowTable constructor/destructor
58 : /////////////////////////////////////////////////////////////////////////////
59 2 : FlowTable::FlowTable(Agent *agent, uint16_t table_index) :
60 2 : agent_(agent),
61 : rand_gen_(boost::uuids::random_generator()),
62 2 : table_index_(table_index),
63 2 : ksync_object_(NULL),
64 2 : flow_entry_map_(),
65 2 : free_list_(this),
66 2 : flow_task_id_(0),
67 2 : flow_update_task_id_(0),
68 2 : flow_delete_task_id_(0),
69 2 : flow_ksync_task_id_(0),
70 4 : flow_logging_task_id_(0) {
71 2 : }
72 :
73 4 : FlowTable::~FlowTable() {
74 2 : assert(flow_entry_map_.size() == 0);
75 4 : }
76 :
77 2 : void FlowTable::Init() {
78 2 : flow_task_id_ = agent_->task_scheduler()->GetTaskId(kTaskFlowEvent);
79 2 : flow_update_task_id_ = agent_->task_scheduler()->GetTaskId(kTaskFlowUpdate);
80 2 : flow_delete_task_id_ = agent_->task_scheduler()->GetTaskId(kTaskFlowDelete);
81 2 : flow_ksync_task_id_ = agent_->task_scheduler()->GetTaskId(kTaskFlowKSync);
82 2 : flow_logging_task_id_ = agent_->task_scheduler()->GetTaskId(kTaskFlowLogging);
83 2 : FlowEntry::Init();
84 2 : return;
85 : }
86 :
87 2 : void FlowTable::InitDone() {
88 2 : }
89 :
90 2 : void FlowTable::Shutdown() {
91 2 : }
92 :
93 : // Concurrency check to ensure all flow-table and free-list manipulations
94 : // are done from FlowEvent task context only
95 : //exception: freelist free function can be accessed by flow logging task
96 1311 : bool FlowTable::ConcurrencyCheck(int task_id, bool check_task_instance) {
97 1311 : Task *current = Task::Running();
98 : // test code invokes FlowTable API from main thread. The running task
99 : // will be NULL in such cases
100 1312 : if (current == NULL) {
101 42 : return true;
102 : }
103 :
104 1270 : if (current->task_code_id() != task_id)
105 44 : return false;
106 1226 : if (check_task_instance) {
107 1226 : if (current->task_data_id() != table_index_)
108 0 : return false;
109 : }
110 1226 : return true;
111 : }
112 :
113 1311 : bool FlowTable::ConcurrencyCheck(int task_id) {
114 1311 : return ConcurrencyCheck(task_id, true);
115 : }
116 :
117 : /////////////////////////////////////////////////////////////////////////////
118 : // FlowTable Add/Delete routines
119 : /////////////////////////////////////////////////////////////////////////////
120 :
121 92 : FlowEntry *FlowTable::Find(const FlowKey &key) {
122 92 : assert(ConcurrencyCheck(flow_task_id_) == true);
123 92 : FlowEntryMap::iterator it;
124 :
125 92 : it = flow_entry_map_.find(key);
126 92 : if (it != flow_entry_map_.end()) {
127 92 : return it->second;
128 : } else {
129 0 : return NULL;
130 : }
131 : }
132 :
133 50 : void FlowTable::Copy(FlowEntry *lhs, FlowEntry *rhs, bool update) {
134 : /* Flow copy, if results in UUID change, stop updating UVE stats
135 : * for old flow
136 : */
137 50 : if (update==false)
138 0 : DeleteFlowUveInfo(lhs);
139 :
140 50 : RevFlowDepParams params;
141 50 : lhs->RevFlowDepInfo(¶ms);
142 50 : DeleteFlowInfo(lhs, params);
143 50 : if (rhs)
144 50 : lhs->Copy(rhs, update);
145 50 : }
146 :
147 44 : FlowEntry *FlowTable::Locate(FlowEntry *flow, uint64_t time) {
148 44 : assert(ConcurrencyCheck(flow_task_id_) == true);
149 44 : std::pair<FlowEntryMap::iterator, bool> ret;
150 44 : ret = flow_entry_map_.insert(FlowEntryMapPair(flow->key(), flow));
151 44 : if (ret.second == true) {
152 44 : agent_->stats()->incr_flow_created();
153 44 : ret.first->second->set_on_tree();
154 44 : return flow;
155 : }
156 :
157 0 : return ret.first->second;
158 : }
159 :
160 22 : void FlowTable::Add(FlowEntry *flow, FlowEntry *rflow) {
161 22 : uint64_t time = UTCTimestampUsec();
162 22 : FlowEntry *new_flow = Locate(flow, time);
163 22 : FlowEntry *new_rflow = (rflow != NULL) ? Locate(rflow, time) : NULL;
164 :
165 22 : FLOW_LOCK(new_flow, new_rflow, FlowEvent::FLOW_MESSAGE);
166 22 : AddInternal(flow, new_flow, rflow, new_rflow, false, false);
167 22 : }
168 :
169 25 : void FlowTable::Update(FlowEntry *flow, FlowEntry *rflow) {
170 25 : bool fwd_flow_update = true;
171 25 : FlowEntry *new_flow = Find(flow->key());
172 :
173 25 : FlowEntry *new_rflow = (rflow != NULL) ? Find(rflow->key()) : NULL;
174 25 : bool rev_flow_update = true;
175 25 : if (rflow && new_rflow == NULL) {
176 0 : uint64_t time = UTCTimestampUsec();
177 0 : new_rflow = Locate(rflow, time);
178 0 : rev_flow_update = false;
179 : }
180 :
181 25 : FLOW_LOCK(new_flow, new_rflow, FlowEvent::FLOW_MESSAGE);
182 25 : AddInternal(flow, new_flow, rflow, new_rflow, fwd_flow_update,
183 : rev_flow_update);
184 25 : }
185 :
186 47 : void FlowTable::AddInternal(FlowEntry *flow_req, FlowEntry *flow,
187 : FlowEntry *rflow_req, FlowEntry *rflow,
188 : bool fwd_flow_update, bool rev_flow_update) {
189 : // Set trace flags for a flow
190 47 : bool trace = agent_->pkt()->get_flow_proto()->ShouldTrace(flow, rflow);
191 47 : if (flow)
192 47 : flow->set_trace(trace);
193 47 : if (rflow)
194 47 : rflow->set_trace(trace);
195 :
196 : // The forward and reverse flow in request are linked. Unlink the flows
197 : // first. Flow table processing will link them if necessary
198 47 : flow_req->set_reverse_flow_entry(NULL);
199 47 : if (rflow_req)
200 47 : rflow_req->set_reverse_flow_entry(NULL);
201 :
202 47 : bool force_update_rflow = false;
203 47 : if (fwd_flow_update) {
204 25 : if (flow == NULL)
205 0 : return;
206 :
207 25 : if (flow->deleted() || flow->IsShortFlow()) {
208 0 : return;
209 : }
210 : }
211 :
212 47 : if (flow_req != flow) {
213 25 : if (flow->flow_handle() == FlowEntry::kInvalidFlowHandle &&
214 0 : !flow->deleted()) {
215 : // In this scenario packet trap for forward flow should
216 : // not cause eviction of the reverse flow due to add event
217 : // so trigger a force update instead of add for reverse flow
218 0 : force_update_rflow = true;
219 : }
220 25 : Copy(flow, flow_req, fwd_flow_update);
221 25 : flow->set_deleted(false);
222 : // this flow entry is reused , increment the transaction id
223 : // so that flow events with old transaction id will be ingnored
224 25 : flow->IncrementTransactionId();
225 : }
226 :
227 47 : if (rflow) {
228 47 : if (rflow_req != rflow) {
229 25 : Copy(rflow, rflow_req, (rev_flow_update || force_update_rflow));
230 : // if the reverse flow was marked delete, reset its flow handle
231 : // to invalid index to assure it is attempted to reprogram using
232 : // kInvalidFlowHandle, this also ensures that flow entry wont
233 : // give fake notion of being available in the flow index tree
234 : // delete for which has already happend while triggering delete
235 : // for flow entry
236 25 : if (rflow->deleted()) {
237 0 : rflow->flow_handle_ = FlowEntry::kInvalidFlowHandle;
238 : // rflow was delete marked skip force update
239 0 : force_update_rflow = false;
240 : }
241 25 : rflow->set_deleted(false);
242 : // this flow entry is reused , increment the transaction id
243 : // so that flow events with old transaction id will be ingnored
244 25 : rflow->IncrementTransactionId();
245 : } else {
246 : // we are creating a new reverse flow, so avoid triggering
247 : // force update in this case
248 22 : force_update_rflow = false;
249 : }
250 : }
251 :
252 47 : if (flow) {
253 47 : if (fwd_flow_update) {
254 25 : flow->set_last_event(FlowEvent::FLOW_MESSAGE);
255 : } else {
256 22 : flow->set_last_event(FlowEvent::VROUTER_FLOW_MSG);
257 : }
258 : }
259 47 : if (rflow) {
260 47 : if (rev_flow_update) {
261 25 : rflow->set_last_event(FlowEvent::FLOW_MESSAGE);
262 : } else {
263 22 : rflow->set_last_event(FlowEvent::VROUTER_FLOW_MSG);
264 : }
265 : }
266 :
267 : // If the flows are already present, we want to retain the Forward and
268 : // Reverse flow characteristics for flow.
269 : // We have following conditions,
270 : // flow has ReverseFlow set, rflow has ReverseFlow reset
271 : // Swap flow and rflow
272 : // flow has ReverseFlow set, rflow has ReverseFlow set
273 : // Unexpected case. Continue with flow as forward flow
274 : // flow has ReverseFlow reset, rflow has ReverseFlow reset
275 : // Unexpected case. Continue with flow as forward flow
276 : // flow has ReverseFlow reset, rflow has ReverseFlow set
277 : // No change in forward/reverse flow. Continue as forward-flow
278 47 : if (flow->is_flags_set(FlowEntry::ReverseFlow) &&
279 47 : rflow && !rflow->is_flags_set(FlowEntry::ReverseFlow)) {
280 0 : FlowEntry *tmp = flow;
281 0 : flow = rflow;
282 0 : rflow = tmp;
283 : }
284 :
285 47 : UpdateReverseFlow(flow, rflow);
286 :
287 : // Add the forward flow after adding the reverse flow first to avoid
288 : // following sequence
289 : // 1. Agent adds forward flow
290 : // 2. vrouter releases the packet
291 : // 3. Packet reaches destination VM and destination VM replies
292 : // 4. Agent tries adding reverse flow. vrouter processes request in core-0
293 : // 5. vrouter gets reverse packet in core-1
294 : // 6. If (4) and (3) happen together, vrouter can allocate 2 hash entries
295 : // for the flow.
296 : //
297 : // While the scenario above cannot be totally avoided, programming reverse
298 : // flow first will reduce the probability
299 47 : if (rflow) {
300 47 : UpdateKSync(rflow, (rev_flow_update || force_update_rflow));
301 47 : AddFlowInfo(rflow);
302 : }
303 :
304 47 : UpdateKSync(flow, fwd_flow_update);
305 47 : AddFlowInfo(flow);
306 : }
307 :
308 44 : void FlowTable::DeleteInternal(FlowEntry *fe, uint64_t time,
309 : const RevFlowDepParams ¶ms) {
310 44 : fe->set_deleted(true);
311 :
312 : // Unlink the reverse flow, if one exists
313 44 : FlowEntry *rflow = fe->reverse_flow_entry();
314 44 : if (rflow) {
315 22 : rflow->set_reverse_flow_entry(NULL);
316 : }
317 44 : fe->set_reverse_flow_entry(NULL);
318 :
319 44 : ReleasePort(fe, true);
320 :
321 44 : DeleteFlowInfo(fe, params);
322 44 : DeleteKSync(fe);
323 :
324 44 : agent_->stats()->incr_flow_aged();
325 44 : }
326 :
327 37 : bool FlowTable::DeleteFlows(FlowEntry *flow, FlowEntry *rflow) {
328 37 : uint64_t time = UTCTimestampUsec();
329 :
330 : /* Fetch reverse-flow info for both flows before their reverse-flow
331 : * links are broken. This info is required during FlowExport
332 : *
333 : * DeleteFlows() is invoked for both forward and reverse flows. So, get
334 : * reverse-flow info only when flows are not deleted
335 : */
336 37 : RevFlowDepParams r_params;
337 37 : if (rflow && rflow->deleted() == false) {
338 22 : rflow->RevFlowDepInfo(&r_params);
339 : }
340 37 : if (flow && flow->deleted() == false) {
341 22 : RevFlowDepParams f_params;
342 22 : flow->RevFlowDepInfo(&f_params);
343 : /* Delete the forward flow */
344 22 : DeleteInternal(flow, time, f_params);
345 22 : }
346 :
347 37 : if (rflow && rflow->deleted() == false) {
348 22 : DeleteInternal(rflow, time, r_params);
349 : }
350 37 : return true;
351 37 : }
352 :
353 0 : void FlowTable::PopulateFlowEntriesUsingKey(const FlowKey &key,
354 : bool reverse_flow,
355 : FlowEntry** flow,
356 : FlowEntry** rflow) {
357 0 : *flow = Find(key);
358 0 : *rflow = NULL;
359 :
360 : //No flow entry, nothing to populate
361 0 : if (!(*flow)) {
362 0 : return;
363 : }
364 :
365 : //No reverse flow requested, so dont populate rflow
366 0 : if (!reverse_flow) {
367 0 : return;
368 : }
369 :
370 0 : FlowEntry *reverse_flow_entry = (*flow)->reverse_flow_entry();
371 0 : if (reverse_flow_entry) {
372 0 : *rflow = Find(reverse_flow_entry->key());
373 : }
374 : }
375 :
376 : //Caller makes sure lock is taken on flow.
377 37 : bool FlowTable::DeleteUnLocked(bool del_reverse_flow,
378 : FlowEntry *flow,
379 : FlowEntry *rflow) {
380 37 : if (!flow) {
381 0 : return false;
382 : }
383 :
384 37 : DeleteFlows(flow, rflow);
385 :
386 : //If deletion of reverse flow is to be done,
387 : //make sure that rflow is populated if flow has a reverse flow pointer.
388 : //In case rflow is not located with the reverse flow key, consider it as
389 : //failure.
390 37 : if (del_reverse_flow && flow->reverse_flow_entry() && !rflow) {
391 0 : return false;
392 : }
393 37 : return true;
394 : }
395 :
396 : //Caller has to ensure lock is taken for flow.
397 0 : bool FlowTable::DeleteUnLocked(const FlowKey &key, bool del_reverse_flow) {
398 0 : FlowEntry *flow = NULL;
399 0 : FlowEntry *rflow = NULL;
400 :
401 0 : PopulateFlowEntriesUsingKey(key, del_reverse_flow, &flow, &rflow);
402 0 : return DeleteUnLocked(del_reverse_flow, flow, rflow);
403 : }
404 :
405 0 : bool FlowTable::Delete(const FlowKey &key, bool del_reverse_flow) {
406 0 : FlowEntry *flow = NULL;
407 0 : FlowEntry *rflow = NULL;
408 :
409 0 : PopulateFlowEntriesUsingKey(key, del_reverse_flow, &flow, &rflow);
410 0 : FLOW_LOCK(flow, rflow, FlowEvent::DELETE_FLOW);
411 0 : return DeleteUnLocked(del_reverse_flow, flow, rflow);
412 0 : }
413 :
414 2 : void FlowTable::DeleteAll() {
415 2 : FlowEntryMap::iterator it;
416 :
417 2 : it = flow_entry_map_.begin();
418 2 : while (it != flow_entry_map_.end()) {
419 0 : FlowEntry *entry = it->second;
420 0 : FlowEntry *reverse_entry = NULL;
421 0 : ++it;
422 0 : if (it != flow_entry_map_.end() &&
423 0 : it->second == entry->reverse_flow_entry()) {
424 0 : reverse_entry = it->second;
425 0 : ++it;
426 : }
427 0 : FLOW_LOCK(entry, reverse_entry, FlowEvent::DELETE_FLOW);
428 0 : DeleteUnLocked(true, entry, reverse_entry);
429 0 : }
430 2 : }
431 :
432 47 : void FlowTable::UpdateReverseFlow(FlowEntry *flow, FlowEntry *rflow) {
433 47 : FlowEntry *flow_rev = flow->reverse_flow_entry();
434 47 : FlowEntry *rflow_rev = NULL;
435 :
436 47 : if (rflow) {
437 47 : rflow_rev = rflow->reverse_flow_entry();
438 : }
439 :
440 47 : if (rflow_rev) {
441 25 : assert(rflow_rev->reverse_flow_entry() == rflow);
442 25 : rflow_rev->set_reverse_flow_entry(NULL);
443 : }
444 :
445 47 : if (flow_rev) {
446 25 : flow_rev->set_reverse_flow_entry(NULL);
447 : }
448 :
449 47 : flow->set_reverse_flow_entry(rflow);
450 47 : if (rflow) {
451 47 : rflow->set_reverse_flow_entry(flow);
452 : }
453 :
454 47 : if (flow_rev && (flow_rev->reverse_flow_entry() == NULL)) {
455 0 : flow_rev->MakeShortFlow(FlowEntry::SHORT_NO_REVERSE_FLOW);
456 : }
457 :
458 47 : if (rflow_rev && (rflow_rev->reverse_flow_entry() == NULL)) {
459 0 : rflow_rev->MakeShortFlow(FlowEntry::SHORT_NO_REVERSE_FLOW);
460 : }
461 :
462 47 : if (flow->reverse_flow_entry() == NULL) {
463 0 : flow->MakeShortFlow(FlowEntry::SHORT_NO_REVERSE_FLOW);
464 : }
465 :
466 47 : if (rflow && rflow->reverse_flow_entry() == NULL) {
467 0 : rflow->MakeShortFlow(FlowEntry::SHORT_NO_REVERSE_FLOW);
468 : }
469 :
470 47 : if (rflow) {
471 83 : if (flow->is_flags_set(FlowEntry::ShortFlow) ||
472 83 : rflow->is_flags_set(FlowEntry::ShortFlow)) {
473 11 : flow->MakeShortFlow(FlowEntry::SHORT_REVERSE_FLOW_CHANGE);
474 : }
475 47 : if (flow->is_flags_set(FlowEntry::Multicast)) {
476 0 : rflow->set_flags(FlowEntry::Multicast);
477 : }
478 : }
479 47 : }
480 :
481 0 : void FlowTable::DeleteFlowUveInfo(FlowEntry *fe) {
482 0 : agent_->pkt()->flow_mgmt_manager(table_index_)->EnqueueUveDeleteEvent(fe);
483 0 : }
484 :
485 : ////////////////////////////////////////////////////////////////////////////
486 : // Flow Info tree management
487 : ////////////////////////////////////////////////////////////////////////////
488 100 : void FlowTable::AddFlowInfo(FlowEntry *fe) {
489 100 : agent_->pkt()->flow_mgmt_manager(table_index_)->AddEvent(fe);
490 100 : }
491 :
492 94 : void FlowTable::DeleteFlowInfo(FlowEntry *fe, const RevFlowDepParams ¶ms) {
493 94 : agent_->pkt()->flow_mgmt_manager(table_index_)->DeleteEvent(fe, params);
494 94 : }
495 :
496 : /////////////////////////////////////////////////////////////////////////////
497 : // Flow revluation routines. Processing will vary based on DBEntry type
498 : /////////////////////////////////////////////////////////////////////////////
499 40376 : boost::uuids::uuid FlowTable::rand_gen() {
500 40376 : return rand_gen_();
501 : }
502 :
503 : // Enqueue message to recompute a flow
504 27 : void FlowTable::RecomputeFlow(FlowEntry *flow) {
505 27 : if (flow->is_flags_set(FlowEntry::ShortFlow))
506 0 : return;
507 :
508 : // If this is reverse flow, enqueue the corresponding forward flow
509 27 : if (flow->is_flags_set(FlowEntry::ReverseFlow)) {
510 11 : flow = flow->reverse_flow_entry();
511 : }
512 :
513 27 : if (flow != NULL)
514 27 : agent_->pkt()->get_flow_proto()->MessageRequest(flow);
515 : }
516 :
517 : // Handle deletion of a Route. Flow management module has identified that route
518 : // must be deleted
519 37 : void FlowTable::DeleteMessage(FlowEntry *flow) {
520 37 : DeleteUnLocked(true, flow, flow->reverse_flow_entry());
521 37 : }
522 :
523 44 : void FlowTable::ReleasePort(FlowEntry *flow, bool evict) {
524 44 : if (flow->allocated_port() == 0) {
525 44 : return;
526 : }
527 :
528 0 : PortTableManager *pm = agent_->pkt()->get_flow_proto()->port_table_manager();
529 0 : pm->Free(flow->key(), flow->allocated_port(), evict);
530 : }
531 :
532 0 : void FlowTable::EvictFlow(FlowEntry *flow, FlowEntry *reverse_flow,
533 : uint32_t evict_gen_id) {
534 0 : DisableKSyncSend(flow, evict_gen_id);
535 0 : DeleteUnLocked(false, flow, NULL);
536 :
537 : // Reverse flow unlinked with forward flow. Make it short-flow
538 : // Dont update ksync, it will shortly get either evicted or deleted by
539 : // ageing process
540 0 : if (reverse_flow)
541 0 : reverse_flow->MakeShortFlow(FlowEntry::SHORT_NO_REVERSE_FLOW);
542 0 : }
543 :
544 3 : void FlowTable::HandleRevaluateDBEntry(const DBEntry *entry, FlowEntry *flow,
545 : bool active_flow, bool deleted_flow) {
546 : // Ignore revluate of deleted/short flows
547 3 : if (flow->IsShortFlow())
548 0 : return;
549 :
550 3 : if (flow->deleted())
551 0 : return;
552 :
553 3 : FlowEntry *rflow = flow->reverse_flow_entry();
554 : // Update may happen for reverse-flow. We act on both forward and
555 : // reverse-flow. Get both forward and reverse flows
556 3 : if (flow->is_flags_set(FlowEntry::ReverseFlow)) {
557 0 : FlowEntry *tmp = flow;
558 0 : flow = rflow;
559 0 : rflow = tmp;
560 : }
561 :
562 : // We want to update only if both forward and reverse flow are valid
563 3 : if (flow == NULL || rflow == NULL)
564 0 : return;
565 :
566 : // Ignore update, if any of the DBEntries referred is deleted
567 3 : if (flow->vn_entry() && flow->vn_entry()->IsDeleted())
568 0 : return;
569 :
570 3 : if (flow->rpf_nh() && flow->rpf_nh()->IsDeleted())
571 0 : return;
572 :
573 3 : if (flow->intf_entry() && flow->intf_entry()->IsDeleted())
574 0 : return;
575 :
576 : // Revaluate flood unknown-unicast flag. If flow has UnknownUnicastFlood and
577 : // VN doesnt allow it, make Short Flow
578 3 : if (flow->vn_entry() &&
579 6 : flow->vn_entry()->flood_unknown_unicast() == false &&
580 6 : flow->is_flags_set(FlowEntry::UnknownUnicastFlood)) {
581 0 : flow->MakeShortFlow(FlowEntry::SHORT_NO_DST_ROUTE);
582 : }
583 :
584 : // On a l3mh compute, when physical interfaces state is down on host
585 : // make all flows with underlay_gw_index same as phy intf encap index short
586 3 : if (flow->data().underlay_gw_index_ != 0xff && agent()->is_l3mh()) {
587 0 : InetUnicastRouteEntry *rt = NULL;
588 0 : if (flow->is_flags_set(FlowEntry::IngressDir)) {
589 : rt = static_cast<InetUnicastRouteEntry *>
590 0 : (FlowEntry::GetUcRoute(flow->GetDestinationVrf(), flow->key().dst_addr));
591 0 : } else if (rflow->is_flags_set(FlowEntry::IngressDir)) {
592 : rt = static_cast<InetUnicastRouteEntry *>
593 0 : (FlowEntry::GetUcRoute(rflow->GetDestinationVrf(), rflow->key().dst_addr));
594 : }
595 0 : if (rt != NULL) {
596 : const TunnelNH *tunnel_nh =
597 0 : dynamic_cast<const TunnelNH *>(rt->GetActiveNextHop());
598 0 : if (tunnel_nh != NULL) {
599 0 : TunnelNH::EncapDataList encap_list = tunnel_nh->GetEncapDataList();
600 0 : if (encap_list[flow->data().underlay_gw_index_].get()->valid_ == false) {
601 0 : flow->MakeShortFlow(FlowEntry::SHORT_L3MH_PHY_INTF_DOWN);
602 : }
603 0 : }
604 : }
605 : }
606 3 : flow->UpdateL2RouteInfo();
607 3 : rflow->UpdateL2RouteInfo();
608 :
609 : // Get policy attributes again and redo the flows
610 3 : flow->GetPolicyInfo();
611 3 : rflow->GetPolicyInfo();
612 :
613 : // Resync reverse flow first and then forward flow
614 : // as forward flow resync will try to update reverse flow
615 3 : rflow->ResyncFlow();
616 3 : flow->ResyncFlow();
617 :
618 : // RPF computation can be done only after policy processing.
619 : // Do RPF computation now
620 3 : flow->RpfUpdate();
621 3 : rflow->RpfUpdate();
622 :
623 : // the SG action could potentially have changed
624 : // due to reflexive nature. Update KSync for reverse flow first
625 3 : UpdateKSync(rflow, true);
626 3 : UpdateKSync(flow, true);
627 :
628 : // Update flow-mgmt with new values
629 3 : AddFlowInfo(flow);
630 3 : AddFlowInfo(rflow);
631 3 : return;
632 : }
633 :
634 0 : void FlowTable::HandleKSyncError(FlowEntry *flow,
635 : FlowTableKSyncEntry *ksync_entry,
636 : int ksync_error, uint32_t flow_handle,
637 : uint32_t gen_id) {
638 : // flow not associated with ksync anymore. Ignore the message
639 0 : if (flow == NULL || flow != ksync_entry->flow_entry()) {
640 0 : return;
641 : }
642 :
643 : // VRouter can return EBADF and ENONENT error if flow-handle changed before
644 : // getting KSync response back. Avoid making short-flow in such case
645 0 : if ((ksync_error == EBADF || ksync_error == ENOENT)) {
646 0 : if (flow->flow_handle() != flow_handle || flow->gen_id() != gen_id) {
647 0 : return;
648 : }
649 : }
650 :
651 : // If VRouter returns error, mark the flow entry as short flow and
652 : // update ksync error event to ksync index manager
653 : //
654 : // For EEXIST error donot mark the flow as ShortFlow since Vrouter
655 : // generates EEXIST only for cases where another add should be
656 : // coming from the pkt trap from Vrouter
657 0 : if (ksync_error != EEXIST || flow->is_flags_set(FlowEntry::NatFlow)) {
658 : // FIXME : We dont have good scheme to handle following scenario,
659 : // - VM1 in VN1 has floating-ip FIP1 in VN2
660 : // - VM2 in VN2
661 : // - VM1 pings VM2 (using floating-ip)
662 : // The forward and reverse flows go to different partitions.
663 : //
664 : // If packets for both forward and reverse flows are trapped together
665 : // we try to setup following flows from different partitions,
666 : // FlowPair-1
667 : // - VM1 to VM2
668 : // - VM2 to FIP1
669 : // FlowPair-2
670 : // - VM2 to FIP1
671 : // - VM1 to VM2
672 : //
673 : // CEM-18166: BGPaaS corrupted NAT flow after repeated BGP session flap
674 : // In the test environment it was observed that flows would remain stuck
675 : // with old reverse indexes, after a few operations of disabling and
676 : // re-enabling the interface on the VNF.
677 : // A previous attempt to fix this in CEM-10354 proved to be incomplete.
678 : // A better way to handle this case is to make the flows in question
679 : // short, but since EEXIST is returned for the RF (from Controller), it
680 : // is necessary to update the ksync_entry to the flow handle returned by
681 : // vrouter DP, Agent does not know it and is thus -1.
682 0 : if (flow->is_flags_set(FlowEntry::ReverseFlow) &&
683 0 : ksync_error == EEXIST &&
684 0 : flow->is_flags_set(FlowEntry::BgpRouterService)) {
685 : KSyncFlowIndexManager *mgr =
686 0 : agent()->ksync()->ksync_flow_index_manager();
687 0 : mgr->UpdateFlowHandle(ksync_entry,
688 0 : ksync_entry->ksync_response_info()->flow_handle_,
689 0 : ksync_entry->ksync_response_info()->gen_id_);
690 0 : flow->MakeShortFlow(FlowEntry::SHORT_FAILED_VROUTER_INSTALL);
691 0 : return;
692 : }
693 : // The reverse flows for both FlowPair-1 and FlowPair-2 are not
694 : // installed due to EEXIST error. We are converting flows to
695 : // short-flow till this case is handled properly
696 0 : flow->MakeShortFlow(FlowEntry::SHORT_FAILED_VROUTER_INSTALL);
697 : }
698 0 : if (ksync_error == EEXIST) {
699 : const VmInterface *intf =
700 0 : dynamic_cast<const VmInterface*>(flow->data().intf_entry.get());
701 0 : KSyncFlowMemory *ksync_obj = agent()->ksync()->ksync_flow_memory();
702 0 : const vr_flow_entry *kflow = ksync_obj->GetKernelFlowEntry(flow_handle, false);
703 0 : if (kflow != nullptr &&
704 0 : intf != nullptr &&
705 0 : (kflow->fe_action != VR_FLOW_ACTION_HOLD)) {
706 0 : if (flow->flow_handle() == FlowEntry::kInvalidFlowHandle) {
707 : KSyncFlowIndexManager *mgr =
708 0 : agent()->ksync()->ksync_flow_index_manager();
709 0 : mgr->UpdateFlowHandle(ksync_entry,
710 : flow_handle, //ksync_entry->ksync_response_info()->flow_handle_,
711 : gen_id); //ksync_entry->ksync_response_info()->gen_id_);
712 : }
713 0 : flow->MakeShortFlow(FlowEntry::SHORT_FAILED_VROUTER_INSTALL);
714 : }
715 : }
716 0 : return;
717 : }
718 :
719 : /////////////////////////////////////////////////////////////////////////////
720 : // KSync Routines
721 : /////////////////////////////////////////////////////////////////////////////
722 44 : void FlowTable::DeleteKSync(FlowEntry *flow) {
723 44 : KSyncFlowIndexManager *mgr = agent()->ksync()->ksync_flow_index_manager();
724 44 : mgr->Delete(flow);
725 44 : }
726 :
727 122 : void FlowTable::UpdateKSync(FlowEntry *flow, bool update) {
728 122 : KSyncFlowIndexManager *mgr = agent()->ksync()->ksync_flow_index_manager();
729 122 : if (flow->deleted()) {
730 : // ignore update on a deleted flow
731 : // flow should already be non deleted of an Add case
732 0 : assert(update == false);
733 0 : return;
734 : }
735 122 : mgr->Update(flow);
736 : }
737 :
738 0 : void FlowTable::DisableKSyncSend(FlowEntry *flow, uint32_t evict_gen_id) {
739 0 : KSyncFlowIndexManager *mgr = agent()->ksync()->ksync_flow_index_manager();
740 0 : mgr->DisableSend(flow, evict_gen_id);
741 0 : }
742 :
743 : /////////////////////////////////////////////////////////////////////////////
744 : // Link local flow information tree
745 : /////////////////////////////////////////////////////////////////////////////
746 0 : void FlowTable::AddLinkLocalFlowInfo(int fd, uint32_t index, const FlowKey &key,
747 : const uint64_t timestamp) {
748 0 : std::scoped_lock mutext(mutex_);
749 0 : LinkLocalFlowInfoMap::iterator it = linklocal_flow_info_map_.find(fd);
750 0 : if (it == linklocal_flow_info_map_.end()) {
751 0 : linklocal_flow_info_map_.insert(
752 0 : LinkLocalFlowInfoPair(fd, LinkLocalFlowInfo(index, key, timestamp)));
753 : } else {
754 0 : it->second.flow_index = index;
755 0 : it->second.flow_key = key;
756 : }
757 0 : }
758 :
759 0 : void FlowTable::DelLinkLocalFlowInfo(int fd) {
760 0 : std::scoped_lock mutext(mutex_);
761 0 : linklocal_flow_info_map_.erase(fd);
762 0 : }
763 :
764 : /////////////////////////////////////////////////////////////////////////////
765 : // Event handler routines
766 : /////////////////////////////////////////////////////////////////////////////
767 :
768 : // KSync flow event handler. Handles response for both vr_flow message only
769 144 : void FlowTable::ProcessKSyncFlowEvent(const FlowEventKSync *req,
770 : FlowEntry *flow) {
771 : FlowTableKSyncEntry *ksync_entry =
772 144 : (static_cast<FlowTableKSyncEntry *> (req->ksync_entry()));
773 144 : KSyncFlowIndexManager *imgr = agent()->ksync()->ksync_flow_index_manager();
774 :
775 : // flow not associated with ksync anymore. Ignore the message
776 144 : if (flow == NULL) {
777 0 : return;
778 : }
779 :
780 : // Ignore error for Delete messages
781 144 : if (req->ksync_event() == KSyncEntry::DEL_ACK) {
782 44 : return;
783 : }
784 :
785 : // if transaction id is not same, then ignore the old
786 : // vrouter add-ack response. this is possible that
787 : // after vrouter add-ack response, flows will be evicted
788 : // and new wflow with same flow tuple as evicted one will
789 : // trigger new flow request to agent. this old add-ack response
790 : // has no relevance, so should be ignored.
791 100 : if (req->transaction_id() != flow->GetTransactionId()) {
792 0 : return;
793 : }
794 :
795 100 : if (req->ksync_error() != 0) {
796 : // Handle KSync Errors
797 0 : HandleKSyncError(flow, ksync_entry, req->ksync_error(),
798 : req->flow_handle(), req->gen_id());
799 : } else {
800 : // Operation succeeded. Update flow-handle if not assigned
801 : KSyncFlowIndexManager *mgr =
802 100 : agent()->ksync()->ksync_flow_index_manager();
803 100 : mgr->UpdateFlowHandle(ksync_entry, req->flow_handle(),
804 100 : req->gen_id());
805 : }
806 :
807 : // Log message if flow-handle change
808 100 : if (flow->flow_handle() != FlowEntry::kInvalidFlowHandle) {
809 100 : if (flow->flow_handle() != req->flow_handle()) {
810 0 : LOG(DEBUG, "Flow index changed from <"
811 : << flow->flow_handle() << "> to <"
812 : << req->flow_handle() << ">");
813 : }
814 : }
815 :
816 : // When vrouter allocates a flow-index or changes flow-handle, its
817 : // possible that a flow in vrouter is evicted. Update stats for
818 : // evicted flow
819 200 : if (req->flow_handle() != FlowEntry::kInvalidFlowHandle &&
820 100 : req->flow_handle() != flow->flow_handle()) {
821 0 : FlowEntryPtr evicted_flow = imgr->FindByIndex(req->flow_handle());
822 0 : if (evicted_flow.get() && evicted_flow->deleted() == false) {
823 0 : FlowMgmtManager *mgr = agent()->pkt()->flow_mgmt_manager(table_index_);
824 0 : mgr->FlowStatsUpdateEvent(evicted_flow.get(),
825 0 : req->evict_flow_bytes(),
826 0 : req->evict_flow_packets(),
827 0 : req->evict_flow_oflow(),
828 : evicted_flow->uuid());
829 : }
830 0 : }
831 :
832 100 : return;
833 : }
834 :
835 217 : bool FlowTable::ProcessFlowEvent(const FlowEvent *req, FlowEntry *flow,
836 : FlowEntry *rflow) {
837 : //Take lock
838 276 : FLOW_LOCK(flow, rflow, req->event());
839 :
840 217 : if (flow)
841 217 : flow->set_last_event(req->event());
842 217 : if (rflow)
843 158 : rflow->set_last_event(req->event());
844 217 : bool active_flow = true;
845 217 : bool deleted_flow = flow->deleted();
846 217 : if (deleted_flow || flow->is_flags_set(FlowEntry::ShortFlow))
847 98 : active_flow = false;
848 :
849 : //Now process events.
850 217 : switch (req->event()) {
851 0 : case FlowEvent::DELETE_FLOW: {
852 : //In case of continous stream of short lived TCP flows with same 5 tuple,
853 : //flow eviction logic might cause below set of event
854 : //1> F1 and R1 flow are added to flow table
855 : //2> R1 is written to vrouter
856 : //3> F1 is written to vrouter
857 : //4> R1 flow add response is received, triggering update of
858 : // F1(not needed now as reverse flow index is not written to kernel?)
859 : //5> In the meantime flow is evicted in vrouter, hence flow update for F1
860 : // would result in error from vrouter resulting in short flow
861 : //6> Since F1 is shortflow Flow delete gets enqueued
862 : //7> Since R1 is evict marked, flow evict gets enqueued
863 : //8> Both event F1 and R1 delete and evict event can run in parallel,
864 : // and hence reverse flow pointer obtained before FLOW lock could
865 : // be invalid, hence read back the same
866 0 : rflow = flow->reverse_flow_entry();
867 0 : DeleteUnLocked(true, flow, rflow);
868 0 : break;
869 : }
870 :
871 37 : case FlowEvent::DELETE_DBENTRY: {
872 37 : DeleteMessage(flow);
873 37 : break;
874 : }
875 :
876 3 : case FlowEvent::REVALUATE_DBENTRY: {
877 3 : const DBEntry *entry = req->db_entry();
878 3 : HandleRevaluateDBEntry(entry, flow, active_flow, deleted_flow);
879 3 : break;
880 : }
881 :
882 33 : case FlowEvent::RECOMPUTE_FLOW: {
883 33 : if (active_flow)
884 27 : RecomputeFlow(flow);
885 33 : break;
886 : }
887 :
888 : // Check if flow-handle changed. This can happen if vrouter tries to
889 : // setup the flow which was evicted earlier
890 0 : case FlowEvent::EVICT_FLOW: {
891 0 : if (flow->flow_handle() != req->flow_handle() ||
892 0 : flow->gen_id() != req->gen_id())
893 0 : break;
894 0 : EvictFlow(flow, rflow, req->evict_gen_id());
895 0 : break;
896 : }
897 :
898 144 : case FlowEvent::KSYNC_EVENT: {
899 144 : const FlowEventKSync *ksync_event =
900 : static_cast<const FlowEventKSync *>(req);
901 : // Handle vr_flow message
902 144 : ProcessKSyncFlowEvent(ksync_event, flow);
903 : // Handle vr_response message
904 : // Trigger the ksync flow event to move ksync state-machine
905 : KSyncFlowIndexManager *imgr =
906 144 : agent()->ksync()->ksync_flow_index_manager();
907 : FlowTableKSyncEntry *ksync_entry = static_cast<FlowTableKSyncEntry *>
908 144 : (ksync_event->ksync_entry());
909 144 : imgr->TriggerKSyncEvent(ksync_entry, ksync_event->ksync_event());
910 144 : break;
911 : }
912 :
913 0 : case FlowEvent::UNRESOLVED_FLOW_ENTRY: {
914 0 : if (flow->deleted()) {
915 0 : break;
916 : }
917 :
918 0 : if (flow->GetMaxRetryAttempts() < FlowEntry::kFlowRetryAttempts) {
919 0 : flow->IncrementRetrycount();
920 : } else {
921 0 : flow->MakeShortFlow(FlowEntry::SHORT_NO_MIRROR_ENTRY);
922 0 : flow->ResetRetryCount();
923 : }
924 :
925 0 : UpdateKSync(flow, true);
926 0 : break;
927 : }
928 :
929 0 : default: {
930 0 : assert(0);
931 : break;
932 : }
933 : }
934 217 : return true;
935 217 : }
936 :
937 172 : void FlowTable::GetFlowSandeshActionParams(const FlowAction &action_info,
938 : std::string &action_str) {
939 172 : std::bitset<32> bs(action_info.action);
940 5647 : for (unsigned int i = 0; i < bs.size(); i++) {
941 5475 : if (bs[i]) {
942 135 : if (!action_str.empty()) {
943 9 : action_str += "|";
944 : }
945 270 : action_str += TrafficAction::ActionToString(
946 135 : static_cast<TrafficAction::Action>(i));
947 : }
948 : }
949 171 : }
950 : /////////////////////////////////////////////////////////////////////////////
951 : // FlowEntryFreeList implementation
952 : /////////////////////////////////////////////////////////////////////////////
953 0 : void FlowTable::GrowFreeList() {
954 0 : free_list_.Grow();
955 0 : ksync_object_->GrowFreeList();
956 0 : }
957 :
958 2 : FlowEntryFreeList::FlowEntryFreeList(FlowTable *table) :
959 2 : table_(table), max_count_(0), grow_pending_(false), total_alloc_(0),
960 2 : total_free_(0), free_list_() {
961 2 : uint32_t count = kInitCount;
962 2 : if (table->agent()->test_mode()) {
963 2 : count = kTestInitCount;
964 : }
965 :
966 10002 : while (max_count_ < count) {
967 10000 : free_list_.push_back(*new FlowEntry(table));
968 10000 : max_count_++;
969 : }
970 2 : }
971 :
972 2 : FlowEntryFreeList::~FlowEntryFreeList() {
973 10002 : while (free_list_.empty() == false) {
974 10000 : FreeList::iterator it = free_list_.begin();
975 10000 : FlowEntry *flow = &(*it);
976 20000 : free_list_.erase(it);
977 10000 : delete flow;
978 : }
979 2 : }
980 :
981 : // Allocate a chunk of FlowEntries
982 0 : void FlowEntryFreeList::Grow() {
983 0 : assert(table_->ConcurrencyCheck(table_->flow_task_id()) == true);
984 0 : grow_pending_ = false;
985 0 : if (free_list_.size() >= kMinThreshold)
986 0 : return;
987 :
988 0 : for (uint32_t i = 0; i < kGrowSize; i++) {
989 0 : free_list_.push_back(*new FlowEntry(table_));
990 0 : max_count_++;
991 : }
992 : }
993 :
994 94 : FlowEntry *FlowEntryFreeList::Allocate(const FlowKey &key) {
995 94 : assert(table_->ConcurrencyCheck(table_->flow_task_id()) == true);
996 94 : FlowEntry *flow = NULL;
997 94 : if (free_list_.size() == 0) {
998 0 : flow = new FlowEntry(table_);
999 0 : max_count_++;
1000 : } else {
1001 94 : FreeList::iterator it = free_list_.begin();
1002 94 : flow = &(*it);
1003 188 : free_list_.erase(it);
1004 : }
1005 :
1006 94 : if (grow_pending_ == false && free_list_.size() < kMinThreshold) {
1007 0 : grow_pending_ = true;
1008 0 : FlowProto *proto = table_->agent()->pkt()->get_flow_proto();
1009 0 : proto->GrowFreeListRequest(table_);
1010 : }
1011 94 : flow->Reset(key);
1012 94 : total_alloc_++;
1013 94 : return flow;
1014 : }
1015 :
1016 94 : void FlowEntryFreeList::Free(FlowEntry *flow) {
1017 : // only flow logging task and flow event task can free up the flow entry ,
1018 94 : assert((table_->ConcurrencyCheck(table_->flow_task_id()) == true) ||
1019 : (table_->ConcurrencyCheck(
1020 : table_->flow_logging_task_id(), false) == true));
1021 94 : total_free_++;
1022 94 : flow->Reset();
1023 94 : if (free_list_.size() < kMaxThreshold) {
1024 94 : free_list_.push_back(*flow);
1025 94 : assert(flow->flow_mgmt_info() == NULL);
1026 : } else {
1027 0 : delete flow;
1028 0 : --max_count_;
1029 : }
1030 94 : }
|