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