Line data Source code
1 : /*
2 : * Copyright (c) 2015 Juniper Networks, Inc. All rights reserved.
3 : */
4 :
5 : #include "bgp/routing-policy/routing_policy.h"
6 :
7 : #include <boost/assign/list_of.hpp>
8 : #include <boost/foreach.hpp>
9 :
10 : #include "base/task_annotations.h"
11 : #include "base/task_trigger.h"
12 : #include "bgp/bgp_config.h"
13 : #include "bgp/bgp_factory.h"
14 : #include "bgp/bgp_server.h"
15 : #include "bgp/bgp_table.h"
16 : #include "bgp/routing-instance/routing_instance.h"
17 : #include "bgp/routing-policy/routing_policy_action.h"
18 : #include "bgp/routing-policy/routing_policy_match.h"
19 :
20 :
21 : class RoutingPolicyMgr::DeleteActor : public LifetimeActor {
22 : public:
23 9742 : explicit DeleteActor(RoutingPolicyMgr *manager)
24 9742 : : LifetimeActor(manager->server_->lifetime_manager()),
25 9742 : manager_(manager) {
26 9742 : }
27 9638 : virtual bool MayDelete() const {
28 9638 : return true;
29 : }
30 9638 : virtual void Shutdown() {
31 9638 : }
32 9638 : virtual void Destroy() {
33 : // memory is deallocated by BgpServer scoped_ptr.
34 9638 : manager_->server_delete_ref_.Reset(NULL);
35 9638 : }
36 :
37 : private:
38 : RoutingPolicyMgr *manager_;
39 : };
40 :
41 9742 : RoutingPolicyMgr::RoutingPolicyMgr(BgpServer *server) :
42 9742 : server_(server),
43 9742 : deleter_(new DeleteActor(this)),
44 9742 : server_delete_ref_(this, server->deleter()),
45 19484 : trace_buf_(SandeshTraceBufferCreate("RoutingPolicyMgr", 500)) {
46 9742 : }
47 :
48 19484 : RoutingPolicyMgr::~RoutingPolicyMgr() {
49 19484 : }
50 :
51 9638 : void RoutingPolicyMgr::ManagedDelete() {
52 9638 : deleter_->Delete();
53 9638 : }
54 :
55 322 : LifetimeActor *RoutingPolicyMgr::deleter() {
56 322 : return deleter_.get();
57 : }
58 :
59 161 : bool RoutingPolicyMgr::deleted() {
60 161 : return deleter()->IsDeleted();
61 : }
62 :
63 161 : RoutingPolicy *RoutingPolicyMgr::CreateRoutingPolicy(
64 : const BgpRoutingPolicyConfig *config) {
65 161 : RoutingPolicy *policy = GetRoutingPolicy(config->name());
66 :
67 161 : if (policy) {
68 0 : if (policy->deleted()) {
69 0 : return NULL;
70 : }
71 0 : return policy;
72 : }
73 :
74 161 : policy = BgpStaticObjectFactory::Create<RoutingPolicy>(
75 161 : config->name(), server_, this, config);
76 161 : routing_policies_.insert(std::make_pair(config->name(), policy));
77 161 : policy->ProcessConfig();
78 :
79 161 : return policy;
80 : }
81 :
82 235 : void RoutingPolicyMgr::UpdateRoutingPolicy(
83 : const BgpRoutingPolicyConfig *config) {
84 235 : CHECK_CONCURRENCY("bgp::Config");
85 :
86 235 : RoutingPolicy *policy = GetRoutingPolicy(config->name());
87 235 : if (policy && policy->deleted()) {
88 0 : return;
89 235 : } else if (!policy) {
90 0 : return;
91 : }
92 :
93 235 : policy->UpdateConfig(config);
94 : }
95 :
96 : //
97 : // Concurrency: BGP Config task
98 : //
99 : // Trigger deletion of a particular routing-policy
100 : //
101 43 : void RoutingPolicyMgr::DeleteRoutingPolicy(const std::string &name) {
102 43 : CHECK_CONCURRENCY("bgp::Config");
103 :
104 43 : RoutingPolicy *policy = GetRoutingPolicy(name);
105 :
106 43 : if (policy && policy->deleted()) {
107 0 : return;
108 43 : } else if (!policy) {
109 0 : return;
110 : }
111 :
112 43 : policy->ClearConfig();
113 :
114 43 : policy->ManagedDelete();
115 : }
116 :
117 : //
118 : // Concurrency: Called from BGP config task manager
119 : //
120 161 : void RoutingPolicyMgr::DestroyRoutingPolicy(RoutingPolicy *policy) {
121 161 : CHECK_CONCURRENCY("bgp::Config");
122 :
123 161 : const std::string name = policy->name();
124 161 : routing_policies_.erase(name);
125 161 : delete policy;
126 :
127 161 : if (deleted()) return;
128 :
129 : const BgpRoutingPolicyConfig *config
130 43 : = server()->config_manager()->FindRoutingPolicy(name);
131 43 : if (config) {
132 0 : CreateRoutingPolicy(config);
133 0 : return;
134 : }
135 161 : }
136 :
137 : // Given a routing instance re-evaluate routes/paths by applying routing policy
138 : // Walks all the tables of the given routing instance and apply the policy
139 : // This function puts the table into the walk request queue and triggers the
140 : // task to start the actual walk
141 37 : void RoutingPolicyMgr::ApplyRoutingPolicy(RoutingInstance *instance) {
142 37 : CHECK_CONCURRENCY("bgp::Config", "bgp::ConfigHelper");
143 :
144 37 : std::scoped_lock lock(mutex_);
145 407 : BOOST_FOREACH(RoutingInstance::RouteTableList::value_type &entry,
146 : instance->GetTables()) {
147 185 : BgpTable *table = entry.second;
148 185 : if (table->IsRoutingPolicySupported())
149 74 : RequestWalk(table);
150 : }
151 37 : }
152 :
153 : // On a given path of the route, apply the policy
154 766 : RoutingPolicy::PolicyResult RoutingPolicyMgr::ExecuteRoutingPolicy(
155 : const RoutingPolicy *policy, const BgpRoute *route,
156 : const BgpPath *path, BgpAttr *attr) const {
157 766 : return (*policy)(route, path, attr);
158 : }
159 :
160 : //
161 : // Concurrency: Called in the context of the DB partition task.
162 : // On a given route, apply routing policy
163 : // Walk through all the paths of the given route, and evaluate the result of the
164 : // routing policy
165 : //
166 282 : bool RoutingPolicyMgr::EvaluateRoutingPolicy(DBTablePartBase *root,
167 : DBEntryBase *entry) {
168 282 : CHECK_CONCURRENCY("db::DBTable");
169 :
170 281 : BgpTable *table = static_cast<BgpTable *>(root->parent());
171 281 : BgpRoute *route = static_cast<BgpRoute *>(entry);
172 281 : const RoutingInstance *rtinstance = table->routing_instance();
173 281 : if (route->IsDeleted()) return true;
174 :
175 281 : bool sort_and_notify = false;
176 281 : const Path *prev_front = route->front();
177 282 : for (Route::PathList::iterator it = route->GetPathList().begin();
178 1141 : it != route->GetPathList().end(); ++it) {
179 289 : BgpPath *path = static_cast<BgpPath *>(it.operator->());
180 289 : uint32_t old_flags = path->GetFlags();
181 289 : const BgpAttr *old_attr = path->GetAttr();
182 289 : rtinstance->ProcessRoutingPolicy(route, path);
183 852 : if ((sort_and_notify == false) &&
184 563 : (old_flags != path->GetFlags() || old_attr != path->GetAttr())) {
185 22 : sort_and_notify = true;
186 : }
187 : }
188 :
189 281 : if (sort_and_notify) {
190 22 : route->Sort(&BgpTable::PathSelection, prev_front);
191 22 : root->Notify(entry);
192 : }
193 282 : return true;
194 : }
195 :
196 : //
197 : //
198 6738 : bool RoutingPolicyMgr::UpdateRoutingPolicyList(
199 : const RoutingPolicyConfigList &cfg_list,
200 : RoutingPolicyAttachList *oper_list) {
201 6738 : CHECK_CONCURRENCY("bgp::Config", "bgp::ConfigHelper");
202 :
203 6743 : std::scoped_lock lock(mutex_);
204 6743 : bool update_policy = false;
205 : // Number of routing policies is different
206 6743 : if (oper_list->size() != cfg_list.size())
207 12 : update_policy = true;
208 :
209 6743 : RoutingPolicyAttachList::iterator oper_it = oper_list->begin(), oper_next;
210 6743 : RoutingPolicyConfigList::const_iterator config_it = cfg_list.begin();
211 6908 : while (oper_it != oper_list->end() &&
212 6908 : config_it != cfg_list.end()) {
213 : // Compare the configured routing policies on the routing-instance
214 : // with operational data.
215 80 : if (oper_it->first->name() == config_it->routing_policy_) {
216 66 : if (oper_it->second != oper_it->first->generation()) {
217 : // Policy content is updated
218 21 : oper_it->second = oper_it->first->generation();
219 21 : update_policy = true;
220 : }
221 66 : ++oper_it;
222 66 : ++config_it;
223 : } else {
224 : // Policy Order is updated or new policy is added
225 : // or policy is deleted
226 : RoutingPolicy *policy =
227 14 : GetRoutingPolicy(config_it->routing_policy_);
228 14 : if (policy) {
229 14 : *oper_it = std::make_pair(policy, policy->generation());
230 14 : ++oper_it;
231 14 : ++config_it;
232 14 : update_policy = true;
233 : } else {
234 : // points to routing policy that doesn't exists
235 : // will revisit in next config notification
236 0 : ++config_it;
237 : }
238 : }
239 : }
240 6748 : for (oper_next = oper_it; oper_it != oper_list->end();
241 5 : oper_it = oper_next) {
242 : // Existing policy(ies) are removed
243 5 : ++oper_next;
244 5 : oper_list->erase(oper_it);
245 5 : update_policy = true;
246 : }
247 6750 : for (; config_it != cfg_list.end(); ++config_it) {
248 : // new policy(ies) are added
249 7 : RoutingPolicy *policy = GetRoutingPolicy(config_it->routing_policy_);
250 7 : if (policy) {
251 7 : oper_list->push_back(std::make_pair(policy, policy->generation()));
252 : }
253 7 : update_policy = true;
254 : }
255 :
256 6743 : return update_policy;
257 6743 : }
258 :
259 : void
260 74 : RoutingPolicyMgr::RequestWalk(BgpTable *table) {
261 74 : CHECK_CONCURRENCY("bgp::Config", "bgp::ConfigHelper");
262 74 : RoutingPolicyWalkRequests::iterator it = routing_policy_sync_.find(table);
263 74 : if (it == routing_policy_sync_.end()) {
264 : DBTable::DBTableWalkRef walk_ref = table->AllocWalker(
265 : boost::bind(&RoutingPolicyMgr::EvaluateRoutingPolicy, this, _1, _2),
266 148 : boost::bind(&RoutingPolicyMgr::WalkDone, this, _2));
267 74 : table->WalkTable(walk_ref);
268 74 : routing_policy_sync_.insert(std::make_pair(table, walk_ref));
269 74 : } else {
270 0 : table->WalkAgain(it->second);
271 : }
272 74 : }
273 :
274 : void
275 74 : RoutingPolicyMgr::WalkDone(DBTableBase *dbtable) {
276 74 : CHECK_CONCURRENCY("db::Walker");
277 74 : BgpTable *table = static_cast<BgpTable *>(dbtable);
278 74 : RoutingPolicyWalkRequests::iterator it = routing_policy_sync_.find(table);
279 74 : assert(it != routing_policy_sync_.end());
280 74 : DBTable::DBTableWalkRef walk_ref = it->second;
281 74 : routing_policy_sync_.erase(it);
282 74 : table->ReleaseWalker(walk_ref);
283 74 : }
284 :
285 : class RoutingPolicy::DeleteActor : public LifetimeActor {
286 : public:
287 161 : DeleteActor(BgpServer *server, RoutingPolicy *parent)
288 161 : : LifetimeActor(server->lifetime_manager()), parent_(parent) {
289 161 : }
290 313 : virtual bool MayDelete() const {
291 313 : return parent_->MayDelete();
292 : }
293 161 : virtual void Shutdown() {
294 161 : parent_->Shutdown();
295 161 : }
296 161 : virtual void Destroy() {
297 161 : parent_->mgr_->DestroyRoutingPolicy(parent_);
298 161 : }
299 :
300 : private:
301 : RoutingPolicy *parent_;
302 : };
303 :
304 161 : RoutingPolicy::RoutingPolicy(std::string name, BgpServer *server,
305 : RoutingPolicyMgr *mgr,
306 161 : const BgpRoutingPolicyConfig *config)
307 322 : : name_(name), server_(server), mgr_(mgr), config_(config),
308 161 : deleter_(new DeleteActor(server, this)),
309 322 : manager_delete_ref_(this, mgr->deleter()), generation_(0) {
310 161 : refcount_ = 0;
311 161 : }
312 :
313 322 : RoutingPolicy::~RoutingPolicy() {
314 161 : terms_.clear();
315 322 : }
316 :
317 477 : RoutingPolicy::PolicyTermPtr RoutingPolicy::BuildTerm(
318 : const RoutingPolicyTermConfig &cfg_term) {
319 477 : PolicyTerm::ActionList actions;
320 477 : PolicyTerm::MatchList matches;
321 :
322 477 : if (!cfg_term.match.community_match.empty()) {
323 : MatchCommunity *community = new MatchCommunity(
324 284 : cfg_term.match.community_match,
325 284 : cfg_term.match.community_match_all);
326 284 : matches.push_back(community);
327 : }
328 :
329 477 : if (!cfg_term.match.ext_community_match.empty()) {
330 : MatchExtCommunity *ext_community = new MatchExtCommunity(
331 12 : cfg_term.match.ext_community_match,
332 12 : cfg_term.match.ext_community_match_all);
333 12 : matches.push_back(ext_community);
334 : }
335 :
336 477 : if (!cfg_term.match.protocols_match.empty()) {
337 : MatchProtocol *protocol =
338 16 : new MatchProtocol(cfg_term.match.protocols_match);
339 16 : matches.push_back(protocol);
340 : }
341 :
342 477 : if (!cfg_term.match.prefixes_to_match.empty()) {
343 210 : PrefixMatchConfigList inet_prefix_list;
344 210 : PrefixMatchConfigList inet6_prefix_list;
345 666 : BOOST_FOREACH(PrefixMatchConfig match,
346 : cfg_term.match.prefixes_to_match) {
347 228 : boost::system::error_code ec;
348 228 : Ip4Address ip4;
349 : int plen;
350 228 : ec = Ip4PrefixParse(match.prefix_to_match, &ip4, &plen);
351 228 : if (ec.value() == 0) {
352 140 : inet_prefix_list.push_back(match);
353 : } else {
354 88 : Ip6Address ip6;
355 88 : ec = Inet6PrefixParse(match.prefix_to_match, &ip6, &plen);
356 88 : if (ec.value() == 0) {
357 50 : inet6_prefix_list.push_back(match);
358 : }
359 : }
360 228 : }
361 210 : if (!inet_prefix_list.empty()) {
362 122 : MatchPrefixInet *prefix = new MatchPrefixInet(inet_prefix_list);
363 122 : matches.push_back(prefix);
364 : }
365 210 : if (!inet6_prefix_list.empty()) {
366 50 : MatchPrefixInet6 *prefix = new MatchPrefixInet6(inet6_prefix_list);
367 50 : matches.push_back(prefix);
368 : }
369 210 : }
370 :
371 : // Build the Action object
372 477 : if (cfg_term.action.action == RoutingPolicyActionConfig::REJECT) {
373 134 : RoutingPolicyRejectAction *action = new RoutingPolicyRejectAction();
374 134 : actions.push_back(action);
375 343 : } else if (cfg_term.action.action == RoutingPolicyActionConfig::NEXT_TERM) {
376 102 : RoutingPolicyNexTermAction *action = new RoutingPolicyNexTermAction();
377 102 : actions.push_back(action);
378 241 : } else if (cfg_term.action.action == RoutingPolicyActionConfig::ACCEPT) {
379 241 : RoutingPolicyAcceptAction *action = new RoutingPolicyAcceptAction();
380 241 : actions.push_back(action);
381 : }
382 :
383 477 : if (!cfg_term.action.update.aspath_expand.empty()) {
384 : UpdateAsPath *expand_aspath =
385 4 : new UpdateAsPath(cfg_term.action.update.aspath_expand);
386 4 : actions.push_back(expand_aspath);
387 : }
388 :
389 477 : if (!cfg_term.action.update.community_set.empty()) {
390 : UpdateCommunity *set_comm =
391 24 : new UpdateCommunity(cfg_term.action.update.community_set, "set");
392 24 : actions.push_back(set_comm);
393 : }
394 :
395 477 : if (!cfg_term.action.update.community_remove.empty()) {
396 : UpdateCommunity *remove_comm =
397 24 : new UpdateCommunity(cfg_term.action.update.community_remove, "remove");
398 24 : actions.push_back(remove_comm);
399 : }
400 :
401 477 : if (!cfg_term.action.update.community_add.empty()) {
402 : UpdateCommunity *add_comm =
403 76 : new UpdateCommunity(cfg_term.action.update.community_add, "add");
404 76 : actions.push_back(add_comm);
405 : }
406 :
407 477 : if (!cfg_term.action.update.ext_community_set.empty()) {
408 : UpdateExtCommunity *set_comm = new UpdateExtCommunity(
409 4 : cfg_term.action.update.ext_community_set, "set");
410 4 : actions.push_back(set_comm);
411 : }
412 :
413 477 : if (!cfg_term.action.update.ext_community_remove.empty()) {
414 : UpdateExtCommunity *remove_comm = new UpdateExtCommunity(
415 10 : cfg_term.action.update.ext_community_remove, "remove");
416 10 : actions.push_back(remove_comm);
417 : }
418 :
419 477 : if (!cfg_term.action.update.ext_community_add.empty()) {
420 : UpdateExtCommunity *add_comm = new UpdateExtCommunity(
421 8 : cfg_term.action.update.ext_community_add, "add");
422 8 : actions.push_back(add_comm);
423 : }
424 :
425 477 : if (cfg_term.action.update.local_pref) {
426 : UpdateLocalPref *local_pref =
427 180 : new UpdateLocalPref(cfg_term.action.update.local_pref);
428 180 : actions.push_back(local_pref);
429 : }
430 :
431 477 : if (cfg_term.action.update.med) {
432 : UpdateMed *med =
433 2 : new UpdateMed(cfg_term.action.update.med);
434 2 : actions.push_back(med);
435 : }
436 :
437 477 : PolicyTermPtr ret_term;
438 477 : if (!actions.empty() || !matches.empty()) {
439 477 : ret_term = PolicyTermPtr(new PolicyTerm());
440 477 : ret_term->set_actions(actions);
441 477 : ret_term->set_matches(matches);
442 : }
443 :
444 954 : return ret_term;
445 477 : }
446 :
447 161 : void RoutingPolicy::ProcessConfig() {
448 543 : BOOST_FOREACH(const RoutingPolicyTermConfig cfg_term, config_->terms()) {
449 : // Build each terms and insert to operational data
450 191 : PolicyTermPtr term = BuildTerm(cfg_term);
451 191 : if (term)
452 191 : add_term(term);
453 382 : }
454 161 : }
455 :
456 : //
457 : // Reprogram policy terms based on new config.
458 : // If the policy term has changed (number of terms got updated, or new term is
459 : // added or earlier term is deleted or existing term is updated), increment the
460 : // generation number to indicate the update
461 : //
462 235 : void RoutingPolicy::UpdateConfig(const BgpRoutingPolicyConfig *cfg) {
463 235 : CHECK_CONCURRENCY("bgp::Config");
464 235 : config_ = cfg;
465 235 : bool update_policy = false;
466 235 : if (terms()->size() != config_->terms().size())
467 10 : update_policy = true;
468 :
469 235 : RoutingPolicyTermList::iterator oper_it = terms()->begin(), oper_next;
470 : BgpRoutingPolicyConfig::RoutingPolicyTermList::const_iterator
471 235 : config_it = config_->terms().begin();
472 514 : while (oper_it != terms()->end() && config_it != config_->terms().end()) {
473 279 : PolicyTermPtr term = BuildTerm(*config_it);
474 279 : if (**oper_it == *term) {
475 256 : ++oper_it;
476 256 : ++config_it;
477 : } else {
478 23 : if (term) {
479 23 : *oper_it = term;
480 23 : update_policy = true;
481 23 : ++oper_it;
482 23 : ++config_it;
483 : } else {
484 0 : ++config_it;
485 : }
486 : }
487 279 : }
488 239 : for (oper_next = oper_it; oper_it != terms()->end(); oper_it = oper_next) {
489 4 : ++oper_next;
490 4 : terms()->erase(oper_it);
491 4 : update_policy = true;
492 : }
493 242 : for (; config_it != config_->terms().end(); ++config_it) {
494 7 : PolicyTermPtr term = BuildTerm(*config_it);
495 7 : if (term)
496 7 : add_term(term);
497 7 : update_policy = true;
498 7 : }
499 :
500 235 : if (update_policy) generation_++;
501 235 : }
502 :
503 204 : void RoutingPolicy::ClearConfig() {
504 204 : CHECK_CONCURRENCY("bgp::Config");
505 204 : config_ = NULL;
506 204 : }
507 :
508 161 : void RoutingPolicy::ManagedDelete() {
509 161 : deleter_->Delete();
510 161 : }
511 :
512 161 : void RoutingPolicy::Shutdown() {
513 161 : CHECK_CONCURRENCY("bgp::Config");
514 161 : ClearConfig();
515 161 : }
516 :
517 476 : bool RoutingPolicy::MayDelete() const {
518 476 : return (refcount_ == 0);
519 : }
520 :
521 0 : LifetimeActor *RoutingPolicy::deleter() {
522 0 : return deleter_.get();
523 : }
524 :
525 291 : const LifetimeActor *RoutingPolicy::deleter() const {
526 291 : return deleter_.get();
527 : }
528 :
529 291 : bool RoutingPolicy::deleted() const {
530 291 : return deleter()->IsDeleted();
531 : }
532 :
533 : //
534 : // Attempt to enqueue a delete for the RoutingPolicy.
535 : //
536 163 : void RoutingPolicy::RetryDelete() {
537 163 : if (!deleter_->IsDeleted())
538 11 : return;
539 152 : deleter_->RetryDelete();
540 : }
541 :
542 766 : RoutingPolicy::PolicyResult RoutingPolicy::operator()(const BgpRoute *route,
543 : const BgpPath *path, BgpAttr *attr) const {
544 2549 : BOOST_FOREACH(PolicyTermPtr term, terms()) {
545 1037 : bool terminal = term->terminal();
546 1037 : bool matched = term->ApplyTerm(route, path, attr);
547 1037 : if (matched && terminal) {
548 145 : return std::make_pair(terminal,
549 145 : (*term->actions().begin())->accept());
550 : }
551 1037 : }
552 620 : return std::make_pair(false, true);
553 : }
554 :
555 477 : PolicyTerm::PolicyTerm() {
556 477 : }
557 :
558 477 : PolicyTerm::~PolicyTerm() {
559 477 : STLDeleteValues(&actions_);
560 477 : STLDeleteValues(&matches_);
561 477 : }
562 :
563 1060 : bool PolicyTerm::terminal() const {
564 1060 : if (!actions().empty())
565 1060 : return (*actions().begin())->terminal();
566 0 : return false;
567 : }
568 :
569 1037 : bool PolicyTerm::ApplyTerm(const BgpRoute *route, const BgpPath *path,
570 : BgpAttr *attr) const {
571 1037 : bool matched = true;
572 2282 : BOOST_FOREACH(RoutingPolicyMatch *match, matches()) {
573 1045 : if (!(*match)(route, path, attr)) {
574 845 : matched = false;
575 845 : break;
576 : }
577 : }
578 1037 : if (matched) {
579 192 : bool first = true;
580 920 : BOOST_FOREACH(RoutingPolicyAction *action, actions()) {
581 : // First action defines what to do with the route
582 : // accept/reject/next-term
583 371 : if (first) {
584 192 : if (action->terminal()) {
585 145 : if (!action->accept()) {
586 : // out_attr is unaltered
587 14 : break;
588 : }
589 : }
590 178 : first = false;
591 : } else {
592 179 : RoutingPolicyUpdateAction *update =
593 : static_cast<RoutingPolicyUpdateAction *>(action);
594 179 : (*update)(attr);
595 : }
596 : }
597 : }
598 1037 : return matched;
599 : }
600 :
601 : // Compare two terms
602 279 : bool PolicyTerm::operator==(const PolicyTerm &rhs) const {
603 : // Different number of match conditions
604 279 : if (matches().size() != rhs.matches().size()) return false;
605 : // Different number of actions conditions
606 270 : if (actions().size() != rhs.actions().size()) return false;
607 :
608 : // Walk the list of match conditions and compare each match
609 530 : for (MatchList::const_iterator rhs_matches_cit = rhs.matches().begin(),
610 265 : lhs_matches_cit = matches().begin();
611 527 : lhs_matches_cit != matches().end();
612 262 : lhs_matches_cit++, rhs_matches_cit++) {
613 269 : if (**rhs_matches_cit != **lhs_matches_cit)
614 7 : return false;
615 : }
616 :
617 : // Walk the list of actions and compare each action
618 516 : for (ActionList::const_iterator lhs_actions_cit = actions().begin(),
619 258 : rhs_actions_cit = rhs.actions().begin();
620 687 : lhs_actions_cit != actions().end();
621 429 : lhs_actions_cit++, rhs_actions_cit++) {
622 431 : if (**rhs_actions_cit != **lhs_actions_cit)
623 2 : return false;
624 : }
625 256 : return true;
626 : }
|