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