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 "base/os.h"
7 : #include <boost/statechart/custom_reaction.hpp>
8 : #include <boost/statechart/state.hpp>
9 : #include <boost/statechart/state_machine.hpp>
10 : #include <boost/statechart/transition.hpp>
11 :
12 : #include "services/ndp_entry.h"
13 : #include "services/services_types.h"
14 : #include "services/icmpv6_proto.h"
15 : //#include "services/services_sandesh.h"
16 : #include "services/services_init.h"
17 : #include "oper/route_common.h"
18 :
19 : using std::ostream;
20 : using std::ostringstream;
21 : using std::string;
22 :
23 : namespace mpl = boost::mpl;
24 : namespace sc = boost::statechart;
25 :
26 : const int NdpEntry::kMaxRetries = 3;
27 : const int NdpEntry::kMaxUnicastRetries = 3;
28 :
29 : #define SM_LOG(level, _Msg) \
30 : do { \
31 : ostringstream out; \
32 : out << _Msg; \
33 : if (LoggingDisabled()) break; \
34 : ICMPV6_TRACE(Trace, _Msg); \
35 : } while (false)
36 :
37 : namespace fsm {
38 :
39 : struct EvTestStateChange : sc::event<EvTestStateChange> {
40 0 : EvTestStateChange(NdpEntry::State state, int retry) :state_(state),
41 0 : retry_(retry) {}
42 : static const char *Name() {
43 : return "EvTestStateChange";
44 : }
45 0 : bool validate(NdpEntry *state_machine) const {
46 0 : return true;
47 : }
48 :
49 : NdpEntry::State state_;
50 : int retry_;
51 : };
52 :
53 : struct EvPktOut : sc::event<EvPktOut> {
54 0 : EvPktOut() {
55 0 : }
56 : static const char *Name() {
57 : return "EvPktOut";
58 : }
59 0 : bool validate(NdpEntry *state_machine) const {
60 0 : return true;
61 : }
62 : };
63 :
64 : struct EvDelayTimerExpired : sc::event<EvDelayTimerExpired> {
65 0 : explicit EvDelayTimerExpired() {
66 0 : }
67 : static const char *Name() {
68 : return "EvDelayTimerExpired";
69 : }
70 0 : bool validate(NdpEntry *state_machine) const {
71 0 : return true;
72 : }
73 : };
74 :
75 : struct EvRetransmitTimerExpired : sc::event<EvRetransmitTimerExpired> {
76 0 : explicit EvRetransmitTimerExpired() {
77 0 : }
78 : static const char *Name() {
79 : return "EvRetransmitTimerExpired";
80 : }
81 0 : bool validate(NdpEntry *state_machine) const {
82 0 : return true;
83 : }
84 : };
85 :
86 : struct EvReachableTimerExpired : sc::event<EvReachableTimerExpired> {
87 0 : explicit EvReachableTimerExpired() {
88 0 : }
89 : static const char *Name() {
90 : return "EvReachableTimerExpired";
91 : }
92 0 : bool validate(NdpEntry *state_machine) const {
93 0 : return true;
94 : }
95 : };
96 :
97 : struct EvNsIn : sc::event<EvNsIn> {
98 0 : explicit EvNsIn(nd_neighbor_solicit ns, MacAddress mac) :
99 0 : mac_(mac), ns_(ns) {
100 0 : }
101 : static const char *Name() {
102 : return "EvNsIn";
103 : }
104 0 : bool validate(NdpEntry *state_machine) const {
105 0 : return true;
106 : }
107 :
108 : MacAddress mac_;
109 : nd_neighbor_solicit ns_;
110 : };
111 :
112 : struct EvSolNaIn : sc::event<EvSolNaIn> {
113 0 : explicit EvSolNaIn(nd_neighbor_advert na, MacAddress mac) :
114 0 : mac_(mac), na_(na) {
115 0 : }
116 : static const char *Name() {
117 : return "EvSolNaIn";
118 : }
119 0 : bool validate(NdpEntry *state_machine) const {
120 0 : return true;
121 : }
122 :
123 : MacAddress mac_;
124 : nd_neighbor_advert na_;
125 : };
126 :
127 : struct EvUnsolNaIn : sc::event<EvUnsolNaIn> {
128 0 : explicit EvUnsolNaIn(nd_neighbor_advert na, MacAddress mac) :
129 0 : mac_(mac), na_(na) {
130 0 : }
131 : static const char *Name() {
132 : return "EvUnsolNaIn";
133 : }
134 0 : bool validate(NdpEntry *state_machine) const {
135 0 : return true;
136 : }
137 :
138 : MacAddress mac_;
139 : nd_neighbor_advert na_;
140 : };
141 :
142 : // States for the NDP state machine.
143 : struct NoState;
144 : struct Incomplete;
145 : struct Reachable;
146 : struct Stale;
147 : struct Delay;
148 : struct Probe;
149 :
150 : //
151 : //
152 : struct NoState : sc::state<NoState, NdpEntry> {
153 : typedef mpl::list<
154 : sc::custom_reaction<EvNsIn>,
155 : sc::custom_reaction<EvPktOut>,
156 : sc::custom_reaction<EvTestStateChange>
157 : > reactions;
158 :
159 0 : explicit NoState(my_context ctx) : my_base(ctx) {
160 0 : NdpEntry *state_machine = &context<NdpEntry>();
161 0 : state_machine->set_state(NdpEntry::NOSTATE);
162 0 : }
163 :
164 0 : ~NoState() {
165 0 : }
166 :
167 : // copy the mac and move to stale
168 0 : sc::result react(const EvNsIn &event) {
169 0 : NdpEntry *state_machine = &context<NdpEntry>();
170 0 : state_machine->set_mac(event.mac_);
171 0 : return transit<Stale>();
172 : }
173 :
174 : // Send multicast NS probe and start retransmit timer
175 0 : sc::result react(const EvPktOut &event) {
176 0 : NdpEntry *state_machine = &context<NdpEntry>();
177 : // In UTs, ndp_entry may not be there
178 0 : if (state_machine->get_interface())
179 0 : state_machine->SendNeighborSolicit();
180 0 : state_machine->StartRetransmitTimer();
181 0 : return transit<Incomplete>();
182 : }
183 :
184 0 : sc::result react(const EvTestStateChange &event) {
185 0 : NdpEntry::State state = event.state_;
186 0 : NdpEntry *state_machine = &context<NdpEntry>();
187 0 : state_machine->set_state(state);
188 0 : state_machine->set_mac(MacAddress());
189 0 : switch (state) {
190 0 : case NdpEntry::NOSTATE: return transit<NoState>();
191 0 : case NdpEntry::INCOMPLETE: return transit<Incomplete>();
192 0 : case NdpEntry::REACHABLE: return transit<Reachable>();
193 0 : case NdpEntry::STALE: return transit<Stale>();
194 0 : case NdpEntry::DELAY: return transit<Delay>();
195 0 : case NdpEntry::PROBE: return transit<Probe>();
196 0 : default: return discard_event();
197 : }
198 : }
199 : };
200 :
201 : //
202 : //
203 : struct Incomplete : sc::state<Incomplete, NdpEntry> {
204 : typedef mpl::list<
205 : sc::custom_reaction<EvNsIn>,
206 : sc::custom_reaction<EvUnsolNaIn>,
207 : sc::custom_reaction<EvSolNaIn>,
208 : sc::custom_reaction<EvRetransmitTimerExpired>,
209 : sc::custom_reaction<EvTestStateChange>
210 : > reactions;
211 :
212 0 : explicit Incomplete(my_context ctx) : my_base(ctx) {
213 0 : NdpEntry *state_machine = &context<NdpEntry>();
214 0 : state_machine->retry_count_clear();
215 0 : state_machine->set_state(NdpEntry::INCOMPLETE);
216 0 : }
217 :
218 0 : ~Incomplete() {
219 0 : }
220 :
221 : // If different mac then move to stale
222 0 : sc::result react(const EvNsIn &event) {
223 0 : NdpEntry *state_machine = &context<NdpEntry>();
224 0 : if (state_machine->mac() != event.mac_) {
225 0 : state_machine->mac() = event.mac_;
226 0 : return transit<Stale>();
227 : }
228 0 : return discard_event();
229 : }
230 :
231 : // move to reachable
232 0 : sc::result react(const EvSolNaIn &event) {
233 0 : NdpEntry *state_machine = &context<NdpEntry>();
234 0 : if (state_machine->mac() != event.mac_) {
235 0 : state_machine->mac() = event.mac_;
236 : }
237 0 : return transit<Reachable>();
238 : }
239 :
240 : // move to stale
241 0 : sc::result react(const EvUnsolNaIn &event) {
242 0 : NdpEntry *state_machine = &context<NdpEntry>();
243 0 : state_machine->mac() = event.mac_;
244 0 : return transit<Stale>();
245 : }
246 :
247 :
248 : // If less than N retransmissions then retransmit and restart timer
249 : // Else discard entry and send icmp error
250 0 : sc::result react(const EvRetransmitTimerExpired &event) {
251 0 : NdpEntry *state_machine = &context<NdpEntry>();
252 0 : if (state_machine->retry_count() < NdpEntry::kMaxRetries) {
253 : // In UTs, ndp_entry may not be there
254 0 : if (state_machine->get_interface())
255 0 : state_machine->SendNeighborSolicit();
256 0 : state_machine->StartRetransmitTimer();
257 : } else {
258 0 : state_machine->retry_count_clear();
259 0 : if (state_machine->DeleteNdpRoute())
260 0 : return discard_event();
261 : }
262 0 : return discard_event();
263 : }
264 :
265 0 : sc::result react(const EvTestStateChange &event) {
266 0 : NdpEntry::State state = event.state_;
267 0 : NdpEntry *state_machine = &context<NdpEntry>();
268 0 : state_machine->set_state(state);
269 0 : state_machine->set_mac(MacAddress());
270 0 : switch (state) {
271 0 : case NdpEntry::NOSTATE: return transit<NoState>();
272 0 : case NdpEntry::INCOMPLETE: return transit<Incomplete>();
273 0 : case NdpEntry::REACHABLE: return transit<Reachable>();
274 0 : case NdpEntry::STALE: return transit<Stale>();
275 0 : case NdpEntry::DELAY: return transit<Delay>();
276 0 : case NdpEntry::PROBE: return transit<Probe>();
277 0 : default: return discard_event();
278 : }
279 : }
280 : };
281 :
282 : struct Reachable : sc::state<Reachable, NdpEntry> {
283 : typedef mpl::list<
284 : sc::custom_reaction<EvNsIn>,
285 : sc::custom_reaction<EvUnsolNaIn>,
286 : sc::custom_reaction<EvSolNaIn>,
287 : sc::custom_reaction<EvReachableTimerExpired>,
288 : sc::custom_reaction<EvTestStateChange>
289 : > reactions;
290 :
291 0 : explicit Reachable(my_context ctx) : my_base(ctx) {
292 0 : NdpEntry *state_machine = &context<NdpEntry>();
293 0 : state_machine->set_state(NdpEntry::REACHABLE);
294 0 : state_machine->StartReachableTimer();
295 0 : }
296 :
297 0 : ~Reachable() {
298 0 : }
299 :
300 : // If different mac then move to stale
301 0 : sc::result react(const EvNsIn &event) {
302 0 : NdpEntry *state_machine = &context<NdpEntry>();
303 0 : if (state_machine->mac() != event.mac_) {
304 0 : state_machine->mac() = event.mac_;
305 0 : return transit<Stale>();
306 : }
307 0 : return discard_event();
308 : }
309 :
310 :
311 : // If different mac then move to stale else unchanged
312 0 : sc::result react(const EvUnsolNaIn &event) {
313 0 : NdpEntry *state_machine = &context<NdpEntry>();
314 0 : if (event.na_.nd_na_flags_reserved & ND_NA_FLAG_OVERRIDE) {
315 0 : if (state_machine->mac() != event.mac_) {
316 0 : state_machine->set_mac(event.mac_);
317 0 : return transit<Stale>();
318 : }
319 : }
320 0 : return discard_event();
321 : }
322 :
323 :
324 : // If non override and diff mac then move to stale
325 : // if override then update mac
326 0 : sc::result react(const EvSolNaIn &event) {
327 0 : NdpEntry *state_machine = &context<NdpEntry>();
328 0 : if (!(event.na_.nd_na_flags_reserved & ND_NA_FLAG_OVERRIDE)) {
329 0 : if (state_machine->mac() != event.mac_) {
330 0 : return transit<Stale>();
331 : }
332 : } else {
333 0 : if (state_machine->mac() != event.mac_) {
334 0 : state_machine->set_mac(event.mac_);
335 : }
336 : }
337 0 : return discard_event();
338 : }
339 :
340 : // move to stale
341 0 : sc::result react(const EvReachableTimerExpired &event) {
342 0 : return transit<Stale>();
343 : }
344 :
345 0 : sc::result react(const EvTestStateChange &event) {
346 0 : NdpEntry::State state = event.state_;
347 0 : NdpEntry *state_machine = &context<NdpEntry>();
348 0 : state_machine->set_state(state);
349 0 : state_machine->set_mac(MacAddress());
350 0 : switch (state) {
351 0 : case NdpEntry::NOSTATE: return transit<NoState>();
352 0 : case NdpEntry::INCOMPLETE: return transit<Incomplete>();
353 0 : case NdpEntry::REACHABLE: return transit<Reachable>();
354 0 : case NdpEntry::STALE: return transit<Stale>();
355 0 : case NdpEntry::DELAY: return transit<Delay>();
356 0 : case NdpEntry::PROBE: return transit<Probe>();
357 0 : default: return discard_event();
358 : }
359 : }
360 : };
361 :
362 : //
363 : //
364 : struct Stale : sc::state<Stale, NdpEntry> {
365 : typedef mpl::list<
366 : sc::custom_reaction<EvNsIn>,
367 : sc::custom_reaction<EvUnsolNaIn>,
368 : sc::custom_reaction<EvSolNaIn>,
369 : sc::custom_reaction<EvPktOut>,
370 : sc::custom_reaction<EvTestStateChange>
371 : > reactions;
372 :
373 : // Send a KEEPALIVE and start the keepalive timer on the peer. Also start
374 : // the hold timer based on the negotiated hold time value.
375 0 : explicit Stale(my_context ctx) : my_base(ctx) {
376 0 : NdpEntry *state_machine = &context<NdpEntry>();
377 0 : state_machine->set_state(NdpEntry::STALE);
378 0 : }
379 :
380 : // Cancel the hold timer. If we go to Established, the timer will get
381 : // started again from the constructor for that state.
382 0 : ~Stale() {
383 0 : }
384 :
385 : // If different mac then move to stale
386 0 : sc::result react(const EvNsIn &event) {
387 0 : NdpEntry *state_machine = &context<NdpEntry>();
388 0 : if (state_machine->mac() != event.mac_) {
389 0 : state_machine->set_mac(event.mac_);
390 : }
391 0 : return discard_event();
392 : }
393 :
394 : // If different mac then move to stale else unchanged
395 0 : sc::result react(const EvUnsolNaIn &event) {
396 0 : NdpEntry *state_machine = &context<NdpEntry>();
397 0 : if (event.na_.nd_na_flags_reserved & ND_NA_FLAG_OVERRIDE) {
398 0 : if (state_machine->mac() != event.mac_) {
399 0 : state_machine->set_mac(event.mac_);
400 0 : return transit<Stale>();
401 : }
402 : }
403 0 : return discard_event();
404 : }
405 :
406 :
407 : // If same mac then move to reachable else unchanged
408 0 : sc::result react(const EvSolNaIn &event) {
409 0 : NdpEntry *state_machine = &context<NdpEntry>();
410 0 : if (event.na_.nd_na_flags_reserved & ND_NA_FLAG_OVERRIDE) {
411 0 : if (state_machine->mac() != event.mac_) {
412 0 : state_machine->set_mac(event.mac_);
413 : }
414 0 : return transit<Reachable>();
415 : }
416 0 : return discard_event();
417 : }
418 :
419 : // start delay timer and move to delay
420 0 : sc::result react(const EvPktOut &event) {
421 0 : NdpEntry *state_machine = &context<NdpEntry>();
422 0 : state_machine->StartDelayTimer();
423 0 : return transit<Delay>();
424 : }
425 :
426 0 : sc::result react(const EvTestStateChange &event) {
427 0 : NdpEntry::State state = event.state_;
428 0 : NdpEntry *state_machine = &context<NdpEntry>();
429 0 : state_machine->set_state(state);
430 0 : state_machine->set_mac(MacAddress());
431 0 : state_machine->retry_count_set(event.retry_);
432 0 : switch (state) {
433 0 : case NdpEntry::NOSTATE: return transit<NoState>();
434 0 : case NdpEntry::INCOMPLETE: return transit<Incomplete>();
435 0 : case NdpEntry::REACHABLE: return transit<Reachable>();
436 0 : case NdpEntry::STALE: return transit<Stale>();
437 0 : case NdpEntry::DELAY: return transit<Delay>();
438 0 : case NdpEntry::PROBE: return transit<Probe>();
439 0 : default: return discard_event();
440 : }
441 : }
442 : };
443 :
444 : //
445 : // Established is the final state for an operation peer.
446 : //
447 : struct Delay : sc::state<Delay, NdpEntry> {
448 : typedef mpl::list<
449 : sc::custom_reaction<EvNsIn>,
450 : sc::custom_reaction<EvUnsolNaIn>,
451 : sc::custom_reaction<EvSolNaIn>,
452 : sc::custom_reaction<EvDelayTimerExpired>,
453 : sc::custom_reaction<EvTestStateChange>
454 : > reactions;
455 :
456 0 : explicit Delay(my_context ctx) : my_base(ctx) {
457 0 : NdpEntry *state_machine = &context<NdpEntry>();
458 0 : state_machine->retry_count_clear();
459 0 : state_machine->set_state(NdpEntry::DELAY);
460 0 : }
461 :
462 0 : ~Delay() {
463 0 : }
464 :
465 : // If different mac then move to stale
466 0 : sc::result react(const EvNsIn &event) {
467 0 : NdpEntry *state_machine = &context<NdpEntry>();
468 0 : if (state_machine->mac() != event.mac_) {
469 0 : state_machine->set_mac(event.mac_);
470 0 : return transit<Stale>();
471 : }
472 0 : return discard_event();
473 : }
474 :
475 :
476 : // If different mac then move to stale else unchanged
477 0 : sc::result react(const EvUnsolNaIn &event) {
478 0 : NdpEntry *state_machine = &context<NdpEntry>();
479 0 : if (event.na_.nd_na_flags_reserved & ND_NA_FLAG_OVERRIDE) {
480 0 : if (state_machine->mac() != event.mac_) {
481 0 : state_machine->set_mac(event.mac_);
482 0 : return transit<Stale>();
483 : }
484 : }
485 0 : return discard_event();
486 : }
487 :
488 :
489 : // If same mac then move to reachable else unchanged
490 0 : sc::result react(const EvSolNaIn &event) {
491 0 : NdpEntry *state_machine = &context<NdpEntry>();
492 0 : if (event.na_.nd_na_flags_reserved & ND_NA_FLAG_OVERRIDE) {
493 0 : if (state_machine->mac() != event.mac_) {
494 0 : state_machine->set_mac(event.mac_);
495 : }
496 0 : return transit<Reachable>();
497 : }
498 0 : return discard_event();
499 : }
500 :
501 : // Send unicast NS probe and start retransmit timer
502 0 : sc::result react(const EvDelayTimerExpired &event) {
503 0 : NdpEntry *state_machine = &context<NdpEntry>();
504 : // In UTs, ndp_entry may not be there
505 0 : if (state_machine->get_interface())
506 0 : state_machine->SendNeighborSolicit();
507 0 : state_machine->StartRetransmitTimer();
508 0 : return transit<Probe>();
509 : }
510 :
511 0 : sc::result react(const EvTestStateChange &event) {
512 0 : NdpEntry::State state = event.state_;
513 0 : NdpEntry *state_machine = &context<NdpEntry>();
514 0 : state_machine->set_state(state);
515 0 : state_machine->set_mac(MacAddress());
516 0 : switch (state) {
517 0 : case NdpEntry::NOSTATE: return transit<NoState>();
518 0 : case NdpEntry::INCOMPLETE: return transit<Incomplete>();
519 0 : case NdpEntry::REACHABLE: return transit<Reachable>();
520 0 : case NdpEntry::STALE: return transit<Stale>();
521 0 : case NdpEntry::DELAY: return transit<Delay>();
522 0 : case NdpEntry::PROBE: return transit<Probe>();
523 0 : default: return discard_event();
524 : }
525 : }
526 : };
527 :
528 : //
529 : // Established is the final state for an operation peer.
530 : //
531 : struct Probe : sc::state<Probe, NdpEntry> {
532 : typedef mpl::list<
533 : sc::custom_reaction<EvNsIn>,
534 : sc::custom_reaction<EvUnsolNaIn>,
535 : sc::custom_reaction<EvSolNaIn>,
536 : sc::custom_reaction<EvRetransmitTimerExpired>,
537 : sc::custom_reaction<EvTestStateChange>
538 : > reactions;
539 :
540 0 : explicit Probe(my_context ctx) : my_base(ctx) {
541 0 : NdpEntry *state_machine = &context<NdpEntry>();
542 0 : state_machine->retry_count_clear();
543 0 : state_machine->set_state(NdpEntry::PROBE);
544 0 : }
545 :
546 0 : ~Probe() {
547 0 : }
548 :
549 : // If different mac then move to stale
550 0 : sc::result react(const EvNsIn &event) {
551 0 : NdpEntry *state_machine = &context<NdpEntry>();
552 0 : if (state_machine->mac() != event.mac_) {
553 0 : state_machine->set_mac(event.mac_);
554 0 : return transit<Stale>();
555 : }
556 0 : return discard_event();
557 : }
558 :
559 :
560 : // If different mac then move to stale else unchanged
561 0 : sc::result react(const EvUnsolNaIn &event) {
562 0 : NdpEntry *state_machine = &context<NdpEntry>();
563 0 : if (event.na_.nd_na_flags_reserved & ND_NA_FLAG_OVERRIDE) {
564 0 : if (state_machine->mac() != event.mac_) {
565 0 : state_machine->set_mac(event.mac_);
566 0 : return transit<Stale>();
567 : }
568 : }
569 0 : return discard_event();
570 : }
571 :
572 :
573 : // If same mac then move to reachable else unchanged
574 0 : sc::result react(const EvSolNaIn &event) {
575 0 : if (event.na_.nd_na_flags_reserved & ND_NA_FLAG_OVERRIDE) {
576 0 : NdpEntry *state_machine = &context<NdpEntry>();
577 0 : if (state_machine->mac() != event.mac_) {
578 0 : state_machine->set_mac(event.mac_);
579 : }
580 0 : return transit<Reachable>();
581 : }
582 0 : return discard_event();
583 : }
584 :
585 : // If more than N then stale
586 0 : sc::result react(const EvRetransmitTimerExpired &event) {
587 0 : NdpEntry *state_machine = &context<NdpEntry>();
588 0 : if (state_machine->retry_count() < NdpEntry::kMaxUnicastRetries) {
589 0 : if (state_machine->get_interface())
590 0 : state_machine->SendNeighborSolicit();
591 0 : state_machine->StartRetransmitTimer();
592 0 : return discard_event();
593 : } else {
594 0 : state_machine->retry_count_clear();
595 0 : if (state_machine->DeleteNdpRoute())
596 0 : return discard_event();
597 : }
598 0 : return discard_event();
599 : }
600 :
601 0 : sc::result react(const EvTestStateChange &event) {
602 0 : NdpEntry::State state = event.state_;
603 0 : NdpEntry *state_machine = &context<NdpEntry>();
604 0 : state_machine->set_state(state);
605 0 : state_machine->set_mac(MacAddress());
606 0 : switch (state) {
607 0 : case NdpEntry::NOSTATE: return transit<NoState>();
608 0 : case NdpEntry::INCOMPLETE: return transit<Incomplete>();
609 0 : case NdpEntry::REACHABLE: return transit<Reachable>();
610 0 : case NdpEntry::STALE: return transit<Stale>();
611 0 : case NdpEntry::DELAY: return transit<Delay>();
612 0 : case NdpEntry::PROBE: return transit<Probe>();
613 0 : default: return discard_event();
614 : }
615 : }
616 : };
617 :
618 : } // namespace fsm
619 :
620 0 : NdpEntry::NdpEntry(boost::asio::io_context &io, Icmpv6Handler *handler,
621 0 : NdpKey &key, const VrfEntry *vrf, const Interface *itf)
622 0 : : work_queue_(TaskScheduler::GetInstance()->GetTaskId("Agent::Services"),
623 : NULL,
624 : boost::bind(&NdpEntry::DequeueEvent, this, _1)),
625 0 : delay_timer_(TimerManager::CreateTimer(
626 : io, "Delay timer",
627 : TaskScheduler::GetInstance()->GetTaskId("Agent::Services"),
628 : PktHandler::ICMPV6)),
629 0 : retransmit_timer_(TimerManager::CreateTimer(
630 : io, "Retransmit timer",
631 : TaskScheduler::GetInstance()->GetTaskId("Agent::Services"),
632 : PktHandler::ICMPV6)),
633 0 : reachable_timer_(TimerManager::CreateTimer(
634 : io, "Reachable timer",
635 : TaskScheduler::GetInstance()->GetTaskId("Agent::Services"),
636 : PktHandler::ICMPV6)),
637 0 : retransmit_time_(1000),
638 0 : delay_time_(5000),
639 0 : reachable_time_(30000),
640 0 : retry_count_(0),
641 0 : deleted_(false),
642 0 : state_(NOSTATE),
643 0 : last_state_(NOSTATE),
644 0 : io_(io), key_(key), nh_vrf_(vrf), handler_(handler), interface_(itf) {
645 0 : initiate();
646 0 : }
647 :
648 0 : NdpEntry::~NdpEntry() {
649 0 : work_queue_.Shutdown();
650 0 : terminate();
651 0 : DeleteAllTimers();
652 0 : if (handler_.get())
653 0 : handler_.reset(NULL);
654 0 : }
655 :
656 0 : void NdpEntry::DeleteAllTimers() {
657 0 : TimerManager::DeleteTimer(delay_timer_);
658 0 : TimerManager::DeleteTimer(retransmit_timer_);
659 0 : TimerManager::DeleteTimer(reachable_timer_);
660 0 : }
661 :
662 0 : void NdpEntry::StartDelayTimer() {
663 0 : if (delay_time_ <= 0)
664 0 : return;
665 :
666 0 : delay_timer_->Cancel();
667 0 : delay_timer_->Start(delay_time_,
668 : boost::bind(&NdpEntry::DelayTimerExpired, this), NULL);
669 : }
670 :
671 0 : void NdpEntry::StartReachableTimer() {
672 0 : if (reachable_time_ <= 0)
673 0 : return;
674 :
675 0 : reachable_timer_->Cancel();
676 0 : reachable_timer_->Start(reachable_time_,
677 : boost::bind(&NdpEntry::ReachableTimerExpired, this), NULL);
678 : }
679 :
680 0 : bool NdpEntry::ReachableTimerExpired() {
681 0 : Enqueue(fsm::EvReachableTimerExpired());
682 0 : return false;
683 : }
684 :
685 0 : void NdpEntry::StartRetransmitTimer() {
686 0 : if (retransmit_time_ <= 0)
687 0 : return;
688 :
689 0 : retry_count_inc();
690 0 : retransmit_timer_->Cancel();
691 0 : retransmit_timer_->Start(retransmit_time_,
692 : boost::bind(&NdpEntry::RetransmitTimerExpired, this), NULL);
693 : }
694 :
695 0 : bool NdpEntry::RetransmitTimerExpired() {
696 0 : Enqueue(fsm::EvRetransmitTimerExpired());
697 0 : return false;
698 : }
699 :
700 0 : bool NdpEntry::DelayTimerExpired() {
701 0 : Enqueue(fsm::EvDelayTimerExpired());
702 0 : return false;
703 : }
704 :
705 0 : bool NdpEntry::EnqueuePktOut() {
706 0 : Enqueue(fsm::EvPktOut());
707 0 : return false;
708 : }
709 0 : bool NdpEntry::EnqueueRetransmitTimerExpired() {
710 0 : Enqueue(fsm::EvRetransmitTimerExpired());
711 0 : return false;
712 : }
713 0 : bool NdpEntry::EnqueueDelayTimerExpired() {
714 0 : Enqueue(fsm::EvDelayTimerExpired());
715 0 : return false;
716 : }
717 0 : bool NdpEntry::EnqueueNsIn(nd_neighbor_solicit ns, MacAddress mac) {
718 0 : Enqueue(fsm::EvNsIn(ns, mac));
719 0 : return false;
720 : }
721 0 : bool NdpEntry::EnqueueNaIn(nd_neighbor_advert na, MacAddress mac) {
722 0 : if (na.nd_na_flags_reserved & ND_NA_FLAG_SOLICITED) {
723 0 : Enqueue(fsm::EvSolNaIn(na, mac));
724 : } else {
725 0 : Enqueue(fsm::EvUnsolNaIn(na, mac));
726 : }
727 0 : return false;
728 : }
729 :
730 0 : bool NdpEntry::EnqueueUnsolNaIn(nd_neighbor_advert na, MacAddress mac) {
731 0 : Enqueue(fsm::EvUnsolNaIn(na, mac));
732 0 : return false;
733 : }
734 :
735 0 : bool NdpEntry::EnqueueSolNaIn(nd_neighbor_advert na, MacAddress mac) {
736 0 : Enqueue(fsm::EvSolNaIn(na, mac));
737 0 : return false;
738 : }
739 :
740 0 : bool NdpEntry::EnqueueTestStateChange(State state, int retry_count) {
741 0 : Enqueue(fsm::EvTestStateChange(state, retry_count));
742 0 : return false;
743 : }
744 :
745 : static const string state_names[] = {
746 : "NoState",
747 : "Incomplete",
748 : "Reachable",
749 : "Stale",
750 : "Delay",
751 : "Probe"
752 : };
753 :
754 0 : const string &NdpEntry::StateName() const {
755 0 : return state_names[state_];
756 : }
757 :
758 0 : const string &NdpEntry::LastStateName() const {
759 0 : return state_names[last_state_];
760 : }
761 :
762 0 : const string NdpEntry::last_state_change_at() const {
763 0 : return integerToString(UTCUsecToPTime(last_state_change_at_));
764 : }
765 :
766 0 : const uint64_t NdpEntry::last_state_change_usecs_at() const {
767 0 : return last_state_change_at_;
768 : }
769 :
770 0 : ostream &operator<<(ostream &out, const NdpEntry::State &state) {
771 0 : out << state_names[state];
772 0 : return out;
773 : }
774 :
775 : // This class determines whether a given class has a method called 'validate'.
776 : template <typename Ev>
777 : struct HasValidate {
778 : template <typename T, bool (T::*)(NdpEntry *) const> struct SFINAE {};
779 : template <typename T> static char Test(SFINAE<T, &T::validate>*);
780 : template <typename T> static int Test(...);
781 : static const bool Has = sizeof(Test<Ev>(0)) == sizeof(char);
782 : };
783 :
784 : template <typename Ev, bool has_validate>
785 : struct ValidateFn {
786 : EvValidate operator()(const Ev *event) {
787 : return NULL;
788 : }
789 : };
790 :
791 : template <typename Ev>
792 : struct ValidateFn<Ev, true> {
793 0 : EvValidate operator()(const Ev *event) {
794 0 : return boost::bind(&Ev::validate, event, _1);
795 : }
796 : };
797 :
798 : template <typename Ev>
799 0 : bool NdpEntry::Enqueue(const Ev &event) {
800 0 : LogEvent(TYPE_NAME(event), "Enqueue");
801 0 : EventContainer ec;
802 0 : ec.event = event.intrusive_from_this();
803 0 : ec.validate = ValidateFn<Ev, HasValidate<Ev>::Has>()(
804 0 : static_cast<const Ev *>(ec.event.get()));
805 0 : work_queue_.Enqueue(ec);
806 :
807 0 : return true;
808 0 : }
809 :
810 0 : void NdpEntry::LogEvent(string event_name, string msg,
811 : SandeshLevel::type log_level) {
812 0 : SM_LOG(log_level, msg << " " << event_name << " in state " << StateName());
813 0 : }
814 :
815 0 : bool NdpEntry::DequeueEvent(NdpEntry::EventContainer ec) {
816 0 : set_last_event(TYPE_NAME(*ec.event));
817 0 : if (ec.validate.empty() || ec.validate(this)) {
818 0 : LogEvent(TYPE_NAME(*ec.event), "Dequeue");
819 0 : process_event(*ec.event);
820 : } else {
821 0 : LogEvent(TYPE_NAME(*ec.event), "Discard", SandeshLevel::SYS_INFO);
822 : }
823 0 : ec.event.reset();
824 :
825 0 : return true;
826 : }
827 :
828 0 : void NdpEntry::DequeueEventDone(bool done) {
829 0 : }
830 :
831 0 : void NdpEntry::set_last_event(const std::string &event) {
832 0 : last_event_ = event;
833 0 : last_event_at_ = UTCTimestampUsec();
834 0 : }
835 :
836 0 : void NdpEntry::set_last_notification_out(int code, int subcode,
837 : const string &reason) {
838 0 : last_notification_out_ = std::make_pair(code, subcode);
839 0 : last_notification_out_at_ = UTCTimestampUsec();
840 0 : last_notification_out_error_ = reason;
841 :
842 0 : }
843 :
844 0 : void NdpEntry::set_last_notification_in(int code, int subcode,
845 : const string &reason) {
846 0 : last_notification_in_ = std::make_pair(code, subcode);
847 0 : last_notification_in_at_ = UTCTimestampUsec();
848 0 : last_notification_in_error_ = reason;
849 :
850 0 : }
851 :
852 0 : void NdpEntry::set_state(State state) {
853 0 : if (state == state_)
854 0 : return;
855 0 : last_state_ = state_; state_ = state;
856 0 : last_state_change_at_ = UTCTimestampUsec();
857 :
858 : }
859 :
860 0 : void NdpEntry::reset_last_info() {
861 0 : last_notification_in_ = std::make_pair(0, 0);
862 0 : last_notification_in_at_ = 0;
863 0 : last_notification_in_error_ = std::string();
864 0 : last_notification_out_ = std::make_pair(0, 0);
865 0 : last_notification_out_at_ = 0;
866 0 : last_notification_out_error_ = std::string();
867 0 : last_state_ = NOSTATE;
868 0 : last_event_ = "";
869 0 : last_state_change_at_ = 0;
870 0 : last_event_at_ = 0;
871 :
872 0 : }
873 0 : bool NdpEntry::IsResolved() {
874 0 : return (get_state() != (NdpEntry::NOSTATE) &&
875 0 : get_state() != (NdpEntry::INCOMPLETE));
876 : }
877 :
878 0 : bool NdpEntry::IsDerived() {
879 0 : return false;
880 : if (key_.vrf != nh_vrf_) {
881 : return true;
882 : }
883 : return false;
884 : }
885 :
886 0 : void NdpEntry::SendNeighborSolicit(bool send_unicast) {
887 0 : assert(!IsDerived());
888 0 : IpAddress ip = handler_->agent()->router_id6();
889 0 : uint32_t vrf_id = get_interface()->vrf_id();
890 :
891 0 : const VmInterface *vmi = dynamic_cast<const VmInterface *>(get_interface());
892 0 : if (vrf_id != VrfEntry::kInvalidIndex) {
893 0 : if (ip.is_v6()) {
894 0 : handler_->SendNeighborSolicit(ip.to_v6(), key_.ip,
895 : vmi, vrf_id, send_unicast);
896 : }
897 : }
898 0 : }
899 :
900 0 : void NdpEntry::SendNeighborAdvert(bool solicited) {
901 0 : assert(!IsDerived());
902 0 : Agent *agent = handler_->agent();
903 0 : IpAddress ip;
904 0 : const VmInterface *vmi = NULL;
905 0 : if (interface_->type() == Interface::VM_INTERFACE) {
906 0 : vmi = static_cast<const VmInterface *>(interface_.get());
907 0 : MacAddress smac = vmi->GetVifMac(agent);
908 0 : if (key_.vrf && key_.vrf->vn()) {
909 0 : IpAddress gw_ip = key_.vrf->vn()->GetGatewayFromIpam
910 0 : (Ip6Address(key_.ip));
911 0 : IpAddress dns_ip = key_.vrf->vn()->GetDnsFromIpam
912 0 : (Ip6Address(key_.ip));
913 0 : if (!gw_ip.is_unspecified() && gw_ip.is_v6()) {
914 0 : handler_->SendNeighborAdvert(gw_ip.to_v6(), key_.ip,
915 : smac, vmi->vm_mac(), vmi->id(),
916 0 : key_.vrf->vrf_id(), solicited);
917 : }
918 0 : if (!dns_ip.is_unspecified() && dns_ip.is_v6() && dns_ip != gw_ip) {
919 0 : handler_->SendNeighborAdvert(dns_ip.to_v6(), key_.ip,
920 : smac, vmi->vm_mac(), vmi->id(),
921 0 : key_.vrf->vrf_id(), solicited);
922 : }
923 : }
924 : } else {
925 0 : if (agent->router_id6().is_v6()) {
926 0 : handler_->SendNeighborAdvert(agent->router_id6().to_v6(), key_.ip,
927 : agent->icmpv6_proto()->ip_fabric_interface_mac(),
928 0 : MacAddress(),
929 : agent->icmpv6_proto()->ip_fabric_interface_index(),
930 0 : key_.vrf->vrf_id(), solicited);
931 : }
932 : }
933 0 : }
934 :
935 0 : void NdpEntry::HandleNsRequest(nd_neighbor_solicit ns, MacAddress mac) {
936 0 : if (IsResolved())
937 0 : AddNdpRoute(true);
938 : else {
939 0 : AddNdpRoute(false);
940 : }
941 0 : EnqueueNsIn(ns, mac);
942 0 : }
943 :
944 0 : void NdpEntry::AddNdpRoute(bool resolved) {
945 0 : if (key_.vrf->GetName() == handler_->agent()->linklocal_vrf_name()) {
946 : // Do not squash existing route entry.
947 : // should be smarter and not replace an existing route.
948 0 : return;
949 : }
950 :
951 0 : Ip6Address ip(key_.ip);
952 0 : const string& vrf_name = key_.vrf->GetName();
953 0 : NdpNHKey nh_key(nh_vrf_->GetName(), ip, false);
954 0 : NdpNH *ndp_nh = static_cast<NdpNH *>(handler_->agent()->nexthop_table()->
955 0 : FindActiveEntry(&nh_key));
956 :
957 0 : if (ndp_nh && ndp_nh->GetResolveState() &&
958 0 : mac().CompareTo(ndp_nh->GetMac()) == 0) {
959 : // MAC address unchanged, ignore
960 0 : if (!IsDerived()) {
961 0 : return;
962 : } else {
963 : /* Return if the route is already existing */
964 : InetUnicastRouteKey *rt_key = new InetUnicastRouteKey(
965 0 : handler_->agent()->local_peer(), vrf_name, ip, 32);
966 0 : AgentRoute *entry = key_.vrf->GetInet4UnicastRouteTable()->
967 0 : FindActiveEntry(rt_key);
968 0 : delete rt_key;
969 0 : if (entry) {
970 0 : return;
971 : }
972 0 : resolved = true;
973 : }
974 : }
975 :
976 0 : NDP_TRACE(Trace, "Add", ip.to_string(), vrf_name, mac().ToString());
977 0 : AgentRoute *entry = key_.vrf->GetInet6UnicastRouteTable()->FindLPM(ip);
978 :
979 0 : bool policy = false;
980 0 : SecurityGroupList sg;
981 0 : TagList tag;
982 0 : VnListType vn_list;
983 : const NextHop *nh;
984 0 : if (entry && (nh = entry->GetActiveNextHop()) != NULL) {
985 0 : policy = nh->PolicyEnabled();
986 0 : sg = entry->GetActivePath()->sg_list();
987 0 : tag = entry->GetActivePath()->tag_list();
988 0 : vn_list = entry->GetActivePath()->dest_vn_list();
989 : }
990 :
991 0 : const Interface *itf = handler_->agent()->icmpv6_proto()->ip_fabric_interface();
992 0 : if (interface_->type() == Interface::VM_INTERFACE) {
993 : const VmInterface *vintf =
994 0 : static_cast<const VmInterface *>(interface_.get());
995 0 : if (vintf->vmi_type() == VmInterface::VHOST) {
996 0 : itf = vintf->parent_list()[0];
997 : }
998 : }
999 :
1000 0 : handler_->agent()->fabric_inet4_unicast_table()->NdpRoute(
1001 0 : DBRequest::DB_ENTRY_ADD_CHANGE, vrf_name, ip, mac(),
1002 0 : nh_vrf_->GetName(), *itf, resolved, 128, policy,
1003 : vn_list, sg, tag);
1004 0 : }
1005 :
1006 0 : bool NdpEntry::DeleteNdpRoute() {
1007 0 : if (key_.vrf->GetName() == handler_->agent()->linklocal_vrf_name()) {
1008 0 : return true;
1009 : }
1010 :
1011 0 : Ip6Address ip(key_.ip);
1012 0 : const string& vrf_name = key_.vrf->GetName();
1013 0 : NdpNHKey nh_key(nh_vrf_->GetName(), ip, false);
1014 0 : NdpNH *ndp_nh = static_cast<NdpNH *>(handler_->agent()->nexthop_table()->
1015 0 : FindActiveEntry(&nh_key));
1016 0 : if (!ndp_nh)
1017 0 : return true;
1018 :
1019 0 : NDP_TRACE(Trace, "Delete", ip.to_string(), vrf_name, mac().ToString());
1020 0 : if (IsDerived()) {
1021 : //Just enqueue a delete, no need to mark nexthop invalid
1022 0 : InetUnicastAgentRouteTable::Delete(handler_->agent()->local_peer(),
1023 : vrf_name, ip, 32);
1024 0 : return true;
1025 : }
1026 :
1027 0 : handler_->agent()->fabric_inet4_unicast_table()->NdpRoute(
1028 0 : DBRequest::DB_ENTRY_DELETE, vrf_name, ip, mac(),
1029 0 : nh_vrf_->GetName(), *interface_, false, 128, false,
1030 0 : Agent::NullStringList(), SecurityGroupList(), TagList());
1031 0 : return false;
1032 0 : }
1033 :
1034 0 : void NdpEntry::Resync(bool policy, const VnListType &vnlist,
1035 : const SecurityGroupList &sg,
1036 : const TagList &tag) {
1037 0 : Ip6Address ip(key_.ip);
1038 0 : const string& vrf_name = key_.vrf->GetName();
1039 0 : NDP_TRACE(Trace, "Resync", ip.to_string(), vrf_name,
1040 : mac().ToString());
1041 0 : handler_->agent()->fabric_inet4_unicast_table()->NdpRoute(
1042 0 : DBRequest::DB_ENTRY_ADD_CHANGE, key_.vrf->GetName(), ip,
1043 0 : mac(), nh_vrf_->GetName(), *interface_, IsResolved(),
1044 : 32, policy, vnlist, sg, tag);
1045 0 : }
|